You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ge...@apache.org on 2017/06/06 12:25:23 UTC

[03/17] brooklyn-server git commit: basic support for uninstalling bundle, and sketch of reinstall

basic support for uninstalling bundle, and sketch of reinstall

doesn't yet remove catalog items, and no effort yet to test reinstall.
(need to record the defining bundle on registered types.)


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/bc11ab25
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/bc11ab25
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/bc11ab25

Branch: refs/heads/master
Commit: bc11ab250efcf316c975edac85f4ffa1f6cb0c07
Parents: 5b6bf73
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed May 3 15:21:11 2017 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 5 12:43:59 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/api/typereg/ManagedBundle.java     |  3 +
 .../BrooklynComponentTemplateResolver.java      |  2 +
 .../CatalogOsgiVersionMoreEntityRebindTest.java | 69 +++++++++++++++
 .../CatalogOsgiVersionMoreEntityTest.java       |  1 +
 .../brooklyn/core/mgmt/ha/OsgiManager.java      | 91 ++++++++++++++++++--
 .../core/typereg/BasicManagedBundle.java        | 11 ++-
 6 files changed, 170 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc11ab25/api/src/main/java/org/apache/brooklyn/api/typereg/ManagedBundle.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/typereg/ManagedBundle.java b/api/src/main/java/org/apache/brooklyn/api/typereg/ManagedBundle.java
index 4f975b7..66ca592 100644
--- a/api/src/main/java/org/apache/brooklyn/api/typereg/ManagedBundle.java
+++ b/api/src/main/java/org/apache/brooklyn/api/typereg/ManagedBundle.java
@@ -20,6 +20,7 @@ package org.apache.brooklyn.api.typereg;
 
 import org.apache.brooklyn.api.mgmt.rebind.Rebindable;
 import org.apache.brooklyn.api.objs.BrooklynObject;
+import org.apache.brooklyn.util.osgi.VersionedName;
 
 /** Describes an OSGi bundle which Brooklyn manages, including persisting */
 public interface ManagedBundle extends BrooklynObject, Rebindable, OsgiBundleWithUrl {
@@ -29,4 +30,6 @@ public interface ManagedBundle extends BrooklynObject, Rebindable, OsgiBundleWit
      * This typically includes the unique {@link #getId()} of this item. */
     String getOsgiUniqueUrl();
 
+    VersionedName getVersionedName();
+
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc11ab25/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
index 34f67c3..7e9b562 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -174,6 +174,8 @@ public class BrooklynComponentTemplateResolver {
                     // TODO propagate exception so we can provide better error messages
                     msgDetails = "The reference " + type + " looks like a URL (running the CAMP Brooklyn assembly-template instantiator) but couldn't load it (missing or invalid syntax?). " +
                             "It's also neither a catalog item nor a java type.";
+                } else if ("brooklyn".equals(proto)){
+                    msgDetails = "The reference " + type + " is not a registered catalog item nor a java type.";
                 } else {
                     msgDetails = "The reference " + type + " looks like a URL (running the CAMP Brooklyn assembly-template instantiator) but the protocol " +
                             proto + " isn't white listed (" + BrooklynCampConstants.YAML_URL_PROTOCOL_WHITELIST + "). " +

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc11ab25/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
index a3ca51b..16814c0 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.typereg.ManagedBundle;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlRebindTest;
+import org.apache.brooklyn.core.effector.Effectors;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.entity.StartableApplication;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
@@ -39,10 +40,13 @@ import org.apache.brooklyn.core.typereg.BasicManagedBundle;
 import org.apache.brooklyn.entity.stock.BasicApplication;
 import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.test.support.TestResourceUnavailableException;
+import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.ClassLoaderUtils;
 import org.apache.brooklyn.util.core.ResourceUtils;
 import org.apache.brooklyn.util.javalang.Reflections;
 import org.apache.brooklyn.util.osgi.OsgiTestResources;
+import org.apache.brooklyn.util.text.Strings;
+import org.osgi.framework.Bundle;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -198,4 +202,69 @@ public class CatalogOsgiVersionMoreEntityRebindTest extends AbstractYamlRebindTe
         Effector<?> newEffector = newEntity.getEntityType().getEffectorByName("myEffector").get();
         newEntity.invoke(newEffector, ImmutableMap.<String, Object>of()).get();
     }
+    
+    @Test
+    public void testClassAccessAfterUninstall() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_MORE_ENTITIES_0_1_0_PATH);
+        
+        // install dependency
+        ((ManagementContextInternal)mgmt()).getOsgiManager().get().installUploadedBundle(new BasicManagedBundle(), 
+            new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_OSGI_ENTITIES_URL), true);
+
+        // now the v2 bundle
+        BasicManagedBundle mb = new BasicManagedBundle();
+        Bundle b2a = ((ManagementContextInternal)mgmt()).getOsgiManager().get().installUploadedBundle(mb, 
+            new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_URL), true);
+
+        Assert.assertEquals(mb.getVersionedName().toString(), BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL+":"+"0.2.0");
+        
+        String yaml = Strings.lines("name: simple-app-yaml",
+                "services:",
+                "- type: " + BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY);
+        Entity app = createAndStartApplication(yaml);
+        Entity more = Iterables.getOnlyElement( app.getChildren() );
+        
+        Assert.assertEquals(
+            more.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Bob")).get(),
+            "HI BOB FROM V2");
+        
+        ((ManagementContextInternal)mgmt()).getOsgiManager().get().uninstallUploadedBundle(mb);
+        Assert.assertEquals(b2a.getState(), Bundle.UNINSTALLED);
+
+        // can still call things
+        Assert.assertEquals(
+            more.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Claudia")).get(),
+            "HI CLAUDIA FROM V2");
+        
+        // but still uninstalled, and attempt to create makes error 
+        Assert.assertEquals(b2a.getState(), Bundle.UNINSTALLED);
+        try {
+            Entity app2 = createAndStartApplication(yaml);
+            Asserts.shouldHaveFailedPreviously("Expected deployment to fail after uninstall; instead got "+app2);
+        } catch (Exception e) {
+            // org.apache.brooklyn.util.exceptions.CompoundRuntimeException: Unable to instantiate item; 2 errors including: 
+            // Transformer for brooklyn-camp gave an error creating this plan: Transformer for catalog gave an error creating this plan: 
+            // Unable to instantiate org.apache.brooklyn.test.osgi.entities.more.MoreEntity; 
+            // 2 errors including: Error in definition of org.apache.brooklyn.test.osgi.entities.more.MoreEntity:0.12.0-SNAPSHOT: 
+            // Unable to create spec for type brooklyn:org.apache.brooklyn.test.osgi.entities.more.MoreEntity. 
+            // The reference brooklyn:org.apache.brooklyn.test.osgi.entities.more.MoreEntity looks like a URL 
+            // (running the CAMP Brooklyn assembly-template instantiator) but 
+            // the protocol brooklyn isn't white listed ([classpath, http, https]). 
+            // It's also neither a catalog item nor a java type.
+            // TODO different error after catalog item uninstalled
+            Asserts.expectedFailureContainsIgnoreCase(e, BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, "not", "registered");
+        }
+        
+        try {
+            StartableApplication app2 = rebind();
+            Asserts.shouldHaveFailedPreviously("Expected deployment to fail rebind; instead got "+app2);
+        } catch (Exception e) {
+            Asserts.expectedFailure(e);
+            // TODO should fail to rebind this app
+            // (currently fails to load the catalog item, since it wasn't removed)
+            // Asserts.expectedFailureContainsIgnoreCase(e, BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, "simple-app-yaml");
+        }
+    }
+    
+
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc11ab25/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
index 118c518..5c28c8b 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
@@ -73,6 +73,7 @@ public class CatalogOsgiVersionMoreEntityTest extends AbstractYamlTest implement
         Bundle b = ((ManagementContextInternal)mgmt()).getOsgiManager().get().installUploadedBundle(mb, 
             new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V1_URL), true);
         Assert.assertEquals(mb.getSymbolicName(), b.getSymbolicName());
+        Assert.assertEquals(mb.getVersion(), "0.1.0");
         
         // bundle installed
         Map<String, ManagedBundle> bundles = ((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundles();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc11ab25/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java
index 6f6d230..a56bb52 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java
@@ -58,6 +58,7 @@ import org.apache.brooklyn.util.exceptions.UserFacingException;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.os.Os;
 import org.apache.brooklyn.util.os.Os.DeletionResult;
+import org.apache.brooklyn.util.osgi.VersionedName;
 import org.apache.brooklyn.util.repeat.Repeater;
 import org.apache.brooklyn.util.stream.Streams;
 import org.apache.brooklyn.util.text.Strings;
@@ -100,6 +101,7 @@ public class OsgiManager {
     private Set<Bundle> bundlesAtStartup;
     protected File osgiCacheDir;
     protected Map<String, ManagedBundle> managedBundles = MutableMap.of();
+    protected Map<VersionedName, String> managedBundleIds = MutableMap.of();
     protected AtomicInteger numberOfReusableFrameworksCreated = new AtomicInteger();
 
     
@@ -209,9 +211,17 @@ public class OsgiManager {
     }
 
     public Map<String, ManagedBundle> getManagedBundles() {
-        return ImmutableMap.copyOf(managedBundles);
+        synchronized (managedBundles) {
+            return ImmutableMap.copyOf(managedBundles);
+        }
     }
-    
+
+    public String getManagedBundleId(VersionedName vn) {
+        synchronized (managedBundles) {
+            return managedBundleIds.get(vn);
+        }
+    }
+
     public Bundle installUploadedBundle(ManagedBundle bundleMetadata, InputStream zipIn, boolean loadCatalogBom) {
         try {
             Bundle alreadyBundle = checkBundleInstalledThrowIfInconsistent(bundleMetadata, false);
@@ -225,8 +235,30 @@ public class OsgiManager {
             zipIn.close();
             fos.close();
             
-            Bundle bundleInstalled = framework.getBundleContext().installBundle(bundleMetadata.getOsgiUniqueUrl(), 
-                new FileInputStream(zipF));
+            ManagedBundle existingBundleToUpdate = null;
+            synchronized (managedBundles) {
+                String id = managedBundleIds.get(bundleMetadata.getVersionedName());
+                if (id!=null) {
+                    existingBundleToUpdate = managedBundles.get(id); 
+                }
+            }
+            
+            Bundle bundleInstalled;
+            if (existingBundleToUpdate==null) {
+                // install new
+                bundleInstalled = framework.getBundleContext().installBundle(bundleMetadata.getOsgiUniqueUrl(), 
+                    new FileInputStream(zipF));
+            } else {
+                // update
+                bundleInstalled = framework.getBundleContext().getBundle(existingBundleToUpdate.getOsgiUniqueUrl());
+                if (bundleInstalled==null) {
+                    throw new IllegalStateException("Detected bundle "+existingBundleToUpdate+" should be installed but framework cannot find it");
+                }
+                try (InputStream fin = new FileInputStream(zipF)) {
+                    bundleInstalled.update(fin);
+                }
+                bundleMetadata = existingBundleToUpdate;
+            }
             checkCorrectlyInstalled(bundleMetadata, bundleInstalled);
             if (!bundleMetadata.isNameResolved()) {
                 ((BasicManagedBundle)bundleMetadata).setSymbolicName(bundleInstalled.getSymbolicName());
@@ -236,6 +268,7 @@ public class OsgiManager {
             
             synchronized (managedBundles) {
                 managedBundles.put(bundleMetadata.getId(), bundleMetadata);
+                managedBundleIds.put(bundleMetadata.getVersionedName(), bundleMetadata.getId());
             }
             mgmt.getRebindManager().getChangeListener().onChanged(bundleMetadata);
             
@@ -243,6 +276,10 @@ public class OsgiManager {
             // but may break some things running from the IDE
             bundleInstalled.start();
 
+            if (existingBundleToUpdate!=null) {
+                // TODO remove old catalog items (see below)
+                // (ideally the removal and addition would be atomic)
+            }
             if (loadCatalogBom) {
                 loadCatalogBom(bundleInstalled);
             }
@@ -254,8 +291,50 @@ public class OsgiManager {
         }
     }
     
-    // TODO uninstall bundle, and call change listener onRemoved ?
-    // TODO on snapshot install, uninstall old equivalent snapshots (items in use might stay in use though?)
+    /**
+     * Removes this bundle from Brooklyn management, 
+     * removes all catalog items it defined,
+     * and then uninstalls the bundle from OSGi.
+     * <p>
+     * No checking is done whether anything is using the bundle;
+     * behaviour of such things is not guaranteed. They will work for many things
+     * but attempts to load new classes may fail.
+     * <p>
+     * Callers should typically fail if anything from this bundle is in use.
+     */
+    public void uninstallUploadedBundle(ManagedBundle bundleMetadata) {
+        synchronized (managedBundles) {
+            ManagedBundle metadata = managedBundles.remove(bundleMetadata.getId());
+            if (metadata==null) {
+                throw new IllegalStateException("No such bundle registered: "+bundleMetadata);
+            }
+            managedBundleIds.remove(bundleMetadata.getVersionedName());
+        }
+        mgmt.getRebindManager().getChangeListener().onUnmanaged(bundleMetadata);
+        
+        // TODO uninstall things that come from this bundle
+//        mgmt.getTypeRegistry().getMatching(new Predicate<RegisteredType>() {
+//            @Override
+//            public boolean apply(RegisteredType input) {
+//                XXX;
+//                input.getLibraries();
+//                return false;
+//            }
+//        });
+        
+        Bundle bundle = framework.getBundleContext().getBundle(bundleMetadata.getOsgiUniqueUrl());
+        if (bundle==null) {
+            throw new IllegalStateException("No such bundle installed: "+bundleMetadata);
+        }
+        try {
+            bundle.stop();
+            bundle.uninstall();
+        } catch (BundleException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+    // TODO DO on snapshot install, uninstall old equivalent snapshots (items in use might stay in use though?)
     
     public synchronized Bundle registerBundle(CatalogBundle bundleMetadata) {
         try {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bc11ab25/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java
index 1ee8aa5..37e706f 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java
@@ -28,6 +28,8 @@ import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.mgmt.rebind.BasicManagedBundleRebindSupport;
 import org.apache.brooklyn.core.objs.AbstractBrooklynObject;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
+import org.apache.brooklyn.util.osgi.VersionedName;
+import org.osgi.framework.Version;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.MoreObjects;
@@ -51,7 +53,8 @@ public class BasicManagedBundle extends AbstractBrooklynObject implements Manage
             Preconditions.checkNotNull(name, "Either a URL or both name and version are required");
             Preconditions.checkNotNull(version, "Either a URL or both name and version are required");
         }
-
+        Version.parseVersion(version);
+        
         this.symbolicName = name;
         this.version = version;
         this.url = url;
@@ -81,6 +84,12 @@ public class BasicManagedBundle extends AbstractBrooklynObject implements Manage
     }
     
     @Override
+    public VersionedName getVersionedName() {
+        if (symbolicName==null) return null;
+        return new VersionedName(symbolicName, Version.parseVersion(version));
+    }
+    
+    @Override
     public String getUrl() {
         return url;
     }