Skip to content

Commit f3aa339

Browse files
committed
Closes #672
Bundle uninstall and snapshot Supports bundle install and auto-reinstall for snapshot POSTs. Doesn't do anything for auto-upgrade but if you POST a new snapshot ZIP then redeploy, the newer version gets used. Also cleanly uninstalls catalog items.
2 parents 27feabc + 55def2e commit f3aa339

49 files changed

Lines changed: 1220 additions & 267 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
import java.util.Collection;
2222
import java.util.NoSuchElementException;
2323

24+
import javax.annotation.Nullable;
25+
2426
import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
27+
import org.apache.brooklyn.api.typereg.ManagedBundle;
28+
2529
import com.google.common.annotations.VisibleForTesting;
2630
import com.google.common.base.Predicate;
2731

@@ -111,12 +115,17 @@ public interface BrooklynCatalog {
111115
CatalogItem<?,?> addItem(String yaml, boolean forceUpdate);
112116

113117
/**
114-
* Adds items (represented in yaml) to the catalog.
115-
* Fails if the same version exists in catalog.
118+
* As {@link #addItemsFromBundle(String, ManagedBundle)} with a null bundle.
119+
*/
120+
Iterable<? extends CatalogItem<?,?>> addItems(String yaml);
121+
122+
/**
123+
* Adds items (represented in yaml) to the catalog coming from the indicated managed bundle.
124+
* Fails if the same version exists in catalog (unless snapshot).
116125
*
117126
* @throws IllegalArgumentException if the yaml was invalid
118127
*/
119-
Iterable<? extends CatalogItem<?,?>> addItems(String yaml);
128+
Iterable<? extends CatalogItem<?,?>> addItems(String yaml, @Nullable ManagedBundle definingBundle);
120129

121130
/**
122131
* Adds items (represented in yaml) to the catalog.

api/src/main/java/org/apache/brooklyn/api/catalog/CatalogItem.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ public static interface CatalogItemLibraries {
128128
@Nullable public String getIconUrl();
129129

130130
public String getSymbolicName();
131+
132+
public String getContainingBundle();
131133

132134
public String getVersion();
133135

api/src/main/java/org/apache/brooklyn/api/typereg/ManagedBundle.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.apache.brooklyn.api.mgmt.rebind.Rebindable;
2222
import org.apache.brooklyn.api.objs.BrooklynObject;
23+
import org.apache.brooklyn.util.osgi.VersionedName;
2324

2425
/** Describes an OSGi bundle which Brooklyn manages, including persisting */
2526
public interface ManagedBundle extends BrooklynObject, Rebindable, OsgiBundleWithUrl {
@@ -29,4 +30,6 @@ public interface ManagedBundle extends BrooklynObject, Rebindable, OsgiBundleWit
2930
* This typically includes the unique {@link #getId()} of this item. */
3031
String getOsgiUniqueUrl();
3132

33+
VersionedName getVersionedName();
34+
3235
}

api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ public interface RegisteredType extends Identifiable {
3737

3838
String getSymbolicName();
3939
String getVersion();
40+
/** Bundle in symbolicname:id format where this type is defined */
41+
// TODO would prefer this to be VersionedName if/when everything comes from OSGi bundles
42+
// unrevert 7260bf9cf3f3ebaaa790693e1b7217a81bef78a7 to start that, and adjust serialization
43+
// as described in that commit message (supporting String in xstream serialization for VN)
44+
String getContainingBundle();
4045

4146
Collection<OsgiBundleWithUrl> getLibraries();
4247

camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ public <T extends Entity> EntitySpec<T> resolveSpec(Set<String> encounteredRegis
174174
// TODO propagate exception so we can provide better error messages
175175
msgDetails = "The reference " + type + " looks like a URL (running the CAMP Brooklyn assembly-template instantiator) but couldn't load it (missing or invalid syntax?). " +
176176
"It's also neither a catalog item nor a java type.";
177+
} else if ("brooklyn".equals(proto)){
178+
msgDetails = "The reference " + type + " is not a registered catalog item nor a java type.";
177179
} else {
178180
msgDetails = "The reference " + type + " looks like a URL (running the CAMP Brooklyn assembly-template instantiator) but the protocol " +
179181
proto + " isn't white listed (" + BrooklynCampConstants.YAML_URL_PROTOCOL_WHITELIST + "). " +

camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogMakeOsgiBundleTest.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,19 @@
3737
import org.apache.brooklyn.core.catalog.internal.CatalogBomScanner;
3838
import org.apache.brooklyn.core.entity.Entities;
3939
import org.apache.brooklyn.core.entity.EntityAsserts;
40+
import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult;
4041
import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
42+
import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
4143
import org.apache.brooklyn.core.sensor.Sensors;
4244
import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
43-
import org.apache.brooklyn.core.typereg.BasicManagedBundle;
4445
import org.apache.brooklyn.test.Asserts;
4546
import org.apache.brooklyn.util.collections.MutableList;
4647
import org.apache.brooklyn.util.collections.MutableMap;
4748
import org.apache.brooklyn.util.collections.MutableSet;
4849
import org.apache.brooklyn.util.core.ResourceUtils;
4950
import org.apache.brooklyn.util.core.osgi.BundleMaker;
5051
import org.apache.brooklyn.util.exceptions.Exceptions;
52+
import org.apache.brooklyn.util.osgi.VersionedName;
5153
import org.apache.brooklyn.util.text.Identifiers;
5254
import org.apache.brooklyn.util.text.Strings;
5355
import org.osgi.framework.Bundle;
@@ -89,7 +91,8 @@ public void cleanUpButKeepMgmt() throws Exception {
8991
Entities.destroy(app);
9092
}
9193
for (Bundle b: bundlesToRemove) {
92-
b.uninstall();
94+
((ManagementContextInternal)mgmt()).getOsgiManager().get().uninstallUploadedBundle(
95+
((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundle(new VersionedName(b)));
9396
}
9497
bundlesToRemove.clear();
9598
}
@@ -156,7 +159,8 @@ public void testCatalogBomFromBundleWithManualManifest() throws Exception {
156159

157160
jf = bm.copyAddingManifest(jf, MutableMap.of(
158161
"Manifest-Version", "1.0",
159-
"Bundle-SymbolicName", customName));
162+
"Bundle-SymbolicName", customName,
163+
"Bundle-Version", "0.0.0.SNAPSHOT"));
160164

161165
Assert.assertTrue(bm.hasOsgiManifest(jf));
162166

@@ -172,10 +176,8 @@ public void testCatalogBomFromBundleWithManualManifest() throws Exception {
172176

173177
private void installBundle(File jf) {
174178
try (FileInputStream fin = new FileInputStream(jf)) {
175-
BasicManagedBundle bundleMetadata = new BasicManagedBundle();
176-
Bundle bundle =
177-
((LocalManagementContext)mgmt()).getOsgiManager().get().installUploadedBundle(bundleMetadata, fin, true);
178-
bundlesToRemove.add(bundle);
179+
OsgiBundleInstallationResult br = ((ManagementContextInternal)mgmt()).getOsgiManager().get().install(fin).get();
180+
bundlesToRemove.add(br.getBundle());
179181
} catch (Exception e) {
180182
throw Exceptions.propagate(e);
181183
}

camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiLibraryTest.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,8 @@ public void testLibraryUrlDoesNotExist() throws Exception {
178178
" - type: " + BasicApplication.class.getName());
179179
Asserts.shouldHaveFailedPreviously();
180180
} catch (Exception e) {
181-
if (!e.toString().contains("Bundle from " + wrongUrl + " failed to install")) {
182-
throw e;
183-
}
181+
Asserts.expectedFailureContains(e, wrongUrl);
182+
Asserts.expectedFailureContainsIgnoreCase(e, "not found");
184183
}
185184
}
186185

@@ -199,9 +198,7 @@ public void testLibraryMalformed() throws Exception {
199198
" - type: " + BasicApplication.class.getName());
200199
Asserts.shouldHaveFailedPreviously();
201200
} catch (Exception e) {
202-
if (!e.toString().contains("not a jar file")) {
203-
throw e;
204-
}
201+
Asserts.expectedFailureContainsIgnoreCase(e, "opening zip");
205202
}
206203
}
207204

camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,23 @@
3030
import org.apache.brooklyn.api.typereg.ManagedBundle;
3131
import org.apache.brooklyn.api.typereg.RegisteredType;
3232
import org.apache.brooklyn.camp.brooklyn.AbstractYamlRebindTest;
33+
import org.apache.brooklyn.core.effector.Effectors;
3334
import org.apache.brooklyn.core.entity.EntityInternal;
3435
import org.apache.brooklyn.core.entity.StartableApplication;
36+
import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult;
3537
import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
3638
import org.apache.brooklyn.core.mgmt.osgi.OsgiVersionMoreEntityTest;
3739
import org.apache.brooklyn.core.test.entity.TestEntity;
38-
import org.apache.brooklyn.core.typereg.BasicManagedBundle;
3940
import org.apache.brooklyn.entity.stock.BasicApplication;
4041
import org.apache.brooklyn.test.Asserts;
4142
import org.apache.brooklyn.test.support.TestResourceUnavailableException;
43+
import org.apache.brooklyn.util.collections.MutableMap;
4244
import org.apache.brooklyn.util.core.ClassLoaderUtils;
4345
import org.apache.brooklyn.util.core.ResourceUtils;
4446
import org.apache.brooklyn.util.javalang.Reflections;
4547
import org.apache.brooklyn.util.osgi.OsgiTestResources;
48+
import org.apache.brooklyn.util.text.Strings;
49+
import org.osgi.framework.Bundle;
4650
import org.slf4j.Logger;
4751
import org.slf4j.LoggerFactory;
4852
import org.testng.Assert;
@@ -66,8 +70,8 @@ protected boolean useOsgi() {
6670
@Test
6771
public void testRebindAppIncludingBundle() throws Exception {
6872
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH);
69-
((ManagementContextInternal)mgmt()).getOsgiManager().get().installUploadedBundle(new BasicManagedBundle(),
70-
new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V1_URL), true);
73+
((ManagementContextInternal)mgmt()).getOsgiManager().get().install(
74+
new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V1_URL) );
7175

7276
createAndStartApplication("services: [ { type: "+BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY+" } ]");
7377

@@ -198,4 +202,113 @@ public void testEffectorInBundleReferencedByStockCatalogItem() throws Exception
198202
Effector<?> newEffector = newEntity.getEntityType().getEffectorByName("myEffector").get();
199203
newEntity.invoke(newEffector, ImmutableMap.<String, Object>of()).get();
200204
}
205+
206+
@Test
207+
public void testClassAccessAfterUninstall() throws Exception {
208+
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_MORE_ENTITIES_0_1_0_PATH);
209+
210+
// install dependency
211+
((ManagementContextInternal)mgmt()).getOsgiManager().get().install(
212+
new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_OSGI_ENTITIES_URL) );
213+
214+
// now the v2 bundle
215+
OsgiBundleInstallationResult b = ((ManagementContextInternal)mgmt()).getOsgiManager().get().install(
216+
new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_URL) ).get();
217+
218+
Assert.assertEquals(b.getVersionedName().toString(), BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL+":"+"0.2.0");
219+
220+
String yaml = Strings.lines("name: simple-app-yaml",
221+
"services:",
222+
"- type: " + BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY);
223+
Entity app = createAndStartApplication(yaml);
224+
Entity more = Iterables.getOnlyElement( app.getChildren() );
225+
226+
Assert.assertEquals(
227+
more.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Bob")).get(),
228+
"HI BOB FROM V2");
229+
230+
((ManagementContextInternal)mgmt()).getOsgiManager().get().uninstallUploadedBundle(b.getMetadata());
231+
Assert.assertEquals(b.getBundle().getState(), Bundle.UNINSTALLED);
232+
233+
// can still call things
234+
Assert.assertEquals(
235+
more.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Claudia")).get(),
236+
"HI CLAUDIA FROM V2");
237+
238+
// but still uninstalled, and attempt to create makes error
239+
Assert.assertEquals(b.getBundle().getState(), Bundle.UNINSTALLED);
240+
try {
241+
Entity app2 = createAndStartApplication(yaml);
242+
Asserts.shouldHaveFailedPreviously("Expected deployment to fail after uninstall; instead got "+app2);
243+
} catch (Exception e) {
244+
Asserts.expectedFailureContainsIgnoreCase(e, "unable to match", BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY);
245+
}
246+
247+
try {
248+
StartableApplication app2 = rebind();
249+
Asserts.shouldHaveFailedPreviously("Expected deployment to fail rebind; instead got "+app2);
250+
} catch (Exception e) {
251+
// should fail to rebind this entity
252+
Asserts.expectedFailureContainsIgnoreCase(e, more.getId(), "unable to load", BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY);
253+
}
254+
}
255+
256+
@Test
257+
public void testClassAccessAfterUpgrade() throws Exception {
258+
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_MORE_ENTITIES_0_1_0_PATH);
259+
260+
// install dependency
261+
((ManagementContextInternal)mgmt()).getOsgiManager().get().install(
262+
new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_OSGI_ENTITIES_URL) ).checkNoError();
263+
264+
// now the v2 bundle
265+
OsgiBundleInstallationResult b2a = ((ManagementContextInternal)mgmt()).getOsgiManager().get().install(
266+
new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_URL) ).get();
267+
268+
Assert.assertEquals(b2a.getVersionedName().toString(), BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL+":"+"0.2.0");
269+
Assert.assertEquals(b2a.getCode(), OsgiBundleInstallationResult.ResultCode.INSTALLED_NEW_BUNDLE);
270+
271+
String yaml = Strings.lines("name: simple-app-yaml",
272+
"services:",
273+
"- type: " + BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY);
274+
Entity app = createAndStartApplication(yaml);
275+
Entity more = Iterables.getOnlyElement( app.getChildren() );
276+
277+
Assert.assertEquals(
278+
more.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Bob")).get(),
279+
"HI BOB FROM V2");
280+
281+
// unforced upgrade should report already installed
282+
Assert.assertEquals( ((ManagementContextInternal)mgmt()).getOsgiManager().get().install(
283+
new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL) ).get().getCode(),
284+
OsgiBundleInstallationResult.ResultCode.IGNORING_BUNDLE_AREADY_INSTALLED);
285+
286+
// force upgrade
287+
OsgiBundleInstallationResult b2b = ((ManagementContextInternal)mgmt()).getOsgiManager().get().install(b2a.getMetadata(),
288+
new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL), true, true, true).get();
289+
Assert.assertEquals(b2a.getBundle(), b2b.getBundle());
290+
Assert.assertEquals(b2b.getCode(), OsgiBundleInstallationResult.ResultCode.UPDATED_EXISTING_BUNDLE);
291+
292+
// calls to things previously instantiated get the old behaviour
293+
Assert.assertEquals(
294+
more.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Claudia")).get(),
295+
"HI CLAUDIA FROM V2");
296+
297+
// but new deployment gets the new behaviour
298+
StartableApplication app2 = (StartableApplication) createAndStartApplication(yaml);
299+
Entity more2 = Iterables.getOnlyElement( app2.getChildren() );
300+
Assert.assertEquals(
301+
more2.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Daphne")).get(),
302+
"HO DAPHNE FROM V2 EVIL TWIN");
303+
app2.stop();
304+
305+
// and after rebind on the old we get new behaviour
306+
StartableApplication app1 = rebind();
307+
Entity more1 = Iterables.getOnlyElement( app1.getChildren() );
308+
Assert.assertEquals(
309+
more1.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Eric")).get(),
310+
"HO ERIC FROM V2 EVIL TWIN");
311+
}
312+
313+
201314
}

camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,18 @@
3333
import org.apache.brooklyn.api.typereg.RegisteredType;
3434
import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
3535
import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynEntityMatcher;
36+
import org.apache.brooklyn.core.entity.Entities;
37+
import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult;
3638
import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
3739
import org.apache.brooklyn.core.mgmt.osgi.OsgiVersionMoreEntityTest;
3840
import org.apache.brooklyn.core.objs.BrooklynTypes;
39-
import org.apache.brooklyn.core.typereg.BasicManagedBundle;
4041
import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
4142
import org.apache.brooklyn.core.typereg.RegisteredTypes;
4243
import org.apache.brooklyn.test.Asserts;
4344
import org.apache.brooklyn.test.support.TestResourceUnavailableException;
4445
import org.apache.brooklyn.util.core.ResourceUtils;
4546
import org.apache.brooklyn.util.osgi.OsgiTestResources;
4647
import org.apache.brooklyn.util.text.Strings;
47-
import org.osgi.framework.Bundle;
4848
import org.slf4j.Logger;
4949
import org.slf4j.LoggerFactory;
5050
import org.testng.Assert;
@@ -69,19 +69,19 @@ private static String getLocalResource(String filename) {
6969

7070
@Test
7171
public void testBrooklynManagedBundleInstall() throws Exception {
72-
BasicManagedBundle mb = new BasicManagedBundle();
73-
Bundle b = ((ManagementContextInternal)mgmt()).getOsgiManager().get().installUploadedBundle(mb,
74-
new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V1_URL), true);
75-
Assert.assertEquals(mb.getSymbolicName(), b.getSymbolicName());
72+
OsgiBundleInstallationResult br = ((ManagementContextInternal)mgmt()).getOsgiManager().get().install(
73+
new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V1_URL) ).get();
74+
Assert.assertEquals(br.getVersionedName().toString(), BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL+":"+"0.1.0");
7675

7776
// bundle installed
7877
Map<String, ManagedBundle> bundles = ((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundles();
7978
Asserts.assertSize(bundles.keySet(), 1);
80-
Assert.assertEquals(mb.getId(), Iterables.getOnlyElement( bundles.keySet() ));
79+
Assert.assertEquals(br.getMetadata().getId(), Iterables.getOnlyElement( bundles.keySet() ));
8180

8281
// types installed
8382
RegisteredType t = mgmt().getTypeRegistry().get(BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY);
8483
Assert.assertNotNull(t);
84+
Assert.assertEquals(t.getContainingBundle(), br.getVersionedName().toString());
8585

8686
// can deploy
8787
createAndStartApplication("services: [ { type: "+BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY+" } ]");
@@ -181,7 +181,8 @@ public void testMoreEntityV1ThenV2GivesV2() throws Exception {
181181
OsgiVersionMoreEntityTest.assertV2MethodCall(moreEntity);
182182
}
183183

184-
@Test
184+
@Test(groups="Broken") // won't work until search path is based on bundles instead of registered types
185+
// (though it would work if we set versions properly in the OSGi bundles, but brooklyn types there all declare brooklyn version)
185186
public void testMoreEntityBothV1AndV2() throws Exception {
186187
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.1.0.jar");
187188
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.2.0.jar");

0 commit comments

Comments
 (0)