You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2017/03/23 17:19:57 UTC
[2/3] brooklyn-server git commit: require bundle name/version defined
in catalog.bom when POSTing a ZIP/JAR
require bundle name/version defined in catalog.bom when POSTing a ZIP/JAR
accepts id, symbolicName, or symbolic-name under brooklyn.catalog;
if osgi bundle manifest supplied, it must match.
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/67419b65
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/67419b65
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/67419b65
Branch: refs/heads/master
Commit: 67419b65c3976292ca35b7c26cd3804fca7cd182
Parents: 5df2824
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Mar 9 09:59:56 2017 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Mar 9 10:02:33 2017 +0000
----------------------------------------------------------------------
.../catalog/internal/BasicBrooklynCatalog.java | 51 +++++++-
.../apache/brooklyn/rest/api/CatalogApi.java | 13 +-
.../rest/resources/CatalogResource.java | 126 +++++++++++--------
.../rest/resources/CatalogResourceTest.java | 19 +--
4 files changed, 142 insertions(+), 67 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/67419b65/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
index 106240e..6e2a802 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
@@ -57,11 +57,13 @@ import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.javalang.AggregateClassLoader;
import org.apache.brooklyn.util.javalang.JavaClassNames;
import org.apache.brooklyn.util.javalang.LoadedClassLoader;
+import org.apache.brooklyn.util.osgi.VersionedName;
import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.time.Duration;
import org.apache.brooklyn.util.time.Time;
import org.apache.brooklyn.util.yaml.Yamls;
import org.apache.brooklyn.util.yaml.Yamls.YamlExtract;
+import org.osgi.framework.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
@@ -407,7 +409,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
}
@SuppressWarnings({ "unchecked", "rawtypes" })
- private Maybe<Map<?,?>> getFirstAsMap(Map<?,?> map, String firstKey, String ...otherKeys) {
+ private static Maybe<Map<?,?>> getFirstAsMap(Map<?,?> map, String firstKey, String ...otherKeys) {
return (Maybe) getFirstAs(map, Map.class, firstKey, otherKeys);
}
@@ -417,6 +419,53 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
return result;
}
+ public static Map<?,?> getCatalogMetadata(String yaml) {
+ Map<?,?> itemDef = Yamls.getAs(Yamls.parseAll(yaml), Map.class);
+ return getFirstAsMap(itemDef, "brooklyn.catalog").orNull();
+ }
+
+ public static VersionedName getVersionedName(Map<?,?> catalogMetadata) {
+ String id = getFirstAs(catalogMetadata, String.class, "id").orNull();
+ String version = getFirstAs(catalogMetadata, String.class, "version").orNull();
+ String symbolicName = getFirstAs(catalogMetadata, String.class, "symbolicName").orNull();
+ symbolicName = findAssertingConsistentIfSet("symbolicName", symbolicName, getFirstAs(catalogMetadata, String.class, "symbolic-name").orNull());
+ // prefer symbolic name and version, if supplied
+ // else use id as symbolic name : version or just symbolic name
+ // (must match if both supplied)
+ if (Strings.isNonBlank(id)) {
+ if (CatalogUtils.looksLikeVersionedId(id)) {
+ symbolicName = findAssertingConsistentIfSet("symbolicName", symbolicName, CatalogUtils.getSymbolicNameFromVersionedId(id));
+ version = findAssertingConsistentIfSet("version", version, CatalogUtils.getVersionFromVersionedId(id));
+ } else {
+ symbolicName = findAssertingConsistentIfSet("symbolicName", symbolicName, id);
+ }
+ }
+ if (Strings.isBlank(symbolicName) && Strings.isBlank(version)) {
+ throw new IllegalStateException("Catalog BOM must define symbolicName and version");
+ }
+ if (Strings.isBlank(symbolicName)) {
+ throw new IllegalStateException("Catalog BOM must define symbolicName");
+ }
+ if (Strings.isBlank(version)) {
+ throw new IllegalStateException("Catalog BOM must define version");
+ }
+ return new VersionedName(symbolicName, Version.valueOf(version));
+ }
+
+ private static String findAssertingConsistentIfSet(String context, String ...values) {
+ String v = null;
+ for (String vi: values) {
+ if (Strings.isNonBlank(vi)) {
+ if (Strings.isNonBlank(v)) {
+ if (!v.equals(vi)) throw new IllegalStateException("Mismatch in "+context+": '"+v+"' or '"+vi+"' supplied");
+ } else {
+ v = vi;
+ }
+ }
+ }
+ return v;
+ }
+
private void collectCatalogItems(String yaml, List<CatalogItemDtoAbstract<?, ?>> result, Map<?, ?> parentMeta) {
Map<?,?> itemDef = Yamls.getAs(Yamls.parseAll(yaml), Map.class);
Map<?,?> catalogMetadata = getFirstAsMap(itemDef, "brooklyn.catalog").orNull();
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/67419b65/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
index 93a812e..79b6186 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
@@ -76,23 +76,20 @@ public interface CatalogApi {
name = "item",
value = "Item to install, as JAR/ZIP or Catalog YAML (autodetected)",
required = true)
- byte[] item,
- @ApiParam(name="name", value="Symbolic name to use for bundle", required=false, defaultValue="") String bundleName,
- @ApiParam(name="version", value="Version to set for bundle", required=false, defaultValue="") String bundleVersion);
+ byte[] item);
@POST
@Beta
@Consumes({"application/x-zip", "application/x-jar"})
- @ApiOperation(value = "Add a catalog item (e.g. new type of entity, policy or location) by uploading OSGi bundle JAR, or ZIP if bundle name and optionally version are supplied. "
+ @ApiOperation(value = "Add a catalog item (e.g. new type of entity, policy or location) by uploading OSGi bundle JAR, or ZIP which will be turned into bundle JAR, "
+ + "containing catalog.bom containing bundle name and version. "
+ "Return value is 201 CREATED if bundle could be added.", response = String.class)
public Response createFromArchive(
@ApiParam(
name = "archive",
- value = "Bundle to install, in JAR format, or ZIP if bundle name and optionally version are supplied, optionally with catalog.bom contained within",
+ value = "Bundle to install, in ZIP or JAR format, requiring catalog.bom containing bundle name and version",
required = true)
- byte[] archive,
- @ApiParam(name="name", value="Symbolic name to use for bundle", required=false, defaultValue="") String bundleName,
- @ApiParam(name="version", value="Version to set for bundle", required=false, defaultValue="") String bundleVersion);
+ byte[] archive);
@POST
@Consumes(MediaType.APPLICATION_XML)
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/67419b65/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
index edc8b4d..9fdf977 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
@@ -30,7 +30,6 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.jar.Attributes;
-import java.util.jar.JarFile;
import java.util.jar.Manifest;
import javax.annotation.Nullable;
@@ -78,9 +77,13 @@ import org.apache.brooklyn.util.core.osgi.BundleMaker;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.osgi.VersionedName;
+import org.apache.brooklyn.util.stream.Streams;
import org.apache.brooklyn.util.text.StringPredicates;
import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.yaml.Yamls;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipFile;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.slf4j.Logger;
@@ -112,10 +115,7 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
static Set<String> missingIcons = MutableSet.of();
@Override
- public Response createPoly(
- byte[] item,
- String bundleName,
- String bundleVersion) {
+ public Response createPoly(byte[] item) {
Throwable yamlException = null;
try {
MutableList.copyOf( Yamls.parseAll(new InputStreamReader(new ByteArrayInputStream(item))) );
@@ -125,15 +125,12 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
}
if (yamlException==null) {
- // treat as yaml
- if (Strings.isNonBlank(bundleName) || Strings.isNonBlank(bundleVersion)) {
- throw new IllegalArgumentException("Bundle name/version not permitted when installing catalog YAML");
- }
+ // treat as yaml if it parsed
return createFromYaml(new String(item));
}
try {
- return createFromArchive(item, bundleName, bundleVersion);
+ return createFromArchive(item);
} catch (Exception e) {
throw Exceptions.propagate("Unable to handle input: not YAML or compatible ZIP. Specify Content-Type to clarify.", e);
}
@@ -180,54 +177,85 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
}
@Override
- public Response createFromArchive(byte[] zipInput, String bundleName, String bundleVersion) {
+ public Response createFromArchive(byte[] zipInput) {
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.ROOT, null)) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to add catalog item",
Entitlements.getEntitlementContext().user());
}
BundleMaker bm = new BundleMaker(mgmt());
- File f = Os.newTempFile("brooklyn-posted-archive", "zip");
+ File f=null, f2=null;
try {
- Files.write(zipInput, f);
- } catch (IOException e) {
- Exceptions.propagate(e);
- }
- Manifest mf = bm.getManifest(f);
- if (mf==null) {
- mf = new Manifest();
- }
- String bundleNameInMF = mf.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
- if (Strings.isBlank(bundleName) && Strings.isBlank(bundleNameInMF)) {
- throw new IllegalStateException("Require either "+Constants.BUNDLE_SYMBOLICNAME+" in "+JarFile.MANIFEST_NAME+" or name supplied in REST API");
- }
- if (!Strings.isBlank(bundleName)) {
- mf.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, bundleName);
- }
- if (!Strings.isBlank(bundleVersion) || Strings.isBlank(mf.getMainAttributes().getValue(Constants.BUNDLE_VERSION))) {
- if (Strings.isBlank(bundleVersion)) {
- bundleVersion = "0.0.0.SNAPSHOT";
+ f = Os.newTempFile("brooklyn-posted-archive", "zip");
+ try {
+ Files.write(zipInput, f);
+ } catch (IOException e) {
+ Exceptions.propagate(e);
}
- mf.getMainAttributes().putValue(Constants.BUNDLE_VERSION, bundleVersion);
- }
- if (mf.getMainAttributes().getValue(Attributes.Name.MANIFEST_VERSION)==null) {
- mf.getMainAttributes().putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
- }
-
- File f2 = bm.copyAddingManifest(f, mf);
- f.delete();
-
- Bundle bundle = bm.installBundle(f2, true);
- f2.delete();
-
- if (!BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_LOAD_BUNDLE_CATALOG_BOM)) {
- // if the above feature is not enabled, let's do it manually (as a contract of this method)
- new CatalogBomScanner().new CatalogPopulator(
- ((LocalManagementContext) mgmt()).getOsgiManager().get().getFramework().getBundleContext(),
- mgmt()).addingBundle(bundle, null);
+
+ ZipFile zf;
+ try {
+ zf = new ZipFile(f);
+ } catch (IOException e) {
+ throw WebResourceUtils.badRequest("Invalid ZIP/JAR archive: "+e);
+ }
+ ZipArchiveEntry bom = zf.getEntry("catalog.bom");
+ if (bom==null) {
+ bom = zf.getEntry("/catalog.bom");
+ }
+ if (bom==null) {
+ throw WebResourceUtils.badRequest("Archive must contain a catalog.bom file in the root");
+ }
+ String bomS;
+ try {
+ bomS = Streams.readFullyString(zf.getInputStream(bom));
+ } catch (IOException e) {
+ throw WebResourceUtils.badRequest("Error reading catalog.bom from ZIP/JAR archive: "+e);
+ }
+ VersionedName vn = BasicBrooklynCatalog.getVersionedName( BasicBrooklynCatalog.getCatalogMetadata(bomS) );
+
+ Manifest mf = bm.getManifest(f);
+ if (mf==null) {
+ mf = new Manifest();
+ }
+ String bundleNameInMF = mf.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+ if (Strings.isNonBlank(bundleNameInMF)) {
+ if (!bundleNameInMF.equals(vn.getSymbolicName())) {
+ throw new IllegalStateException("JAR MANIFEST symbolic-name '"+bundleNameInMF+"' does not match '"+vn.getSymbolicName()+"' defined in BOM");
+ }
+ } else {
+ mf.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, vn.getSymbolicName());
+ }
+
+ String bundleVersionInMF = mf.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+ if (Strings.isNonBlank(bundleVersionInMF)) {
+ if (!bundleVersionInMF.equals(vn.getVersion().toString())) {
+ throw new IllegalStateException("JAR MANIFEST version '"+bundleVersionInMF+"' does not match '"+vn.getVersion()+"' defined in BOM");
+ }
+ } else {
+ mf.getMainAttributes().putValue(Constants.BUNDLE_VERSION, vn.getVersion().toString());
+ }
+ if (mf.getMainAttributes().getValue(Attributes.Name.MANIFEST_VERSION)==null) {
+ mf.getMainAttributes().putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
+ }
+
+ f2 = bm.copyAddingManifest(f, mf);
+
+ Bundle bundle = bm.installBundle(f2, true);
+
+ if (!BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_LOAD_BUNDLE_CATALOG_BOM)) {
+ // if the above feature is not enabled, let's do it manually (as a contract of this method)
+ new CatalogBomScanner().new CatalogPopulator(
+ ((LocalManagementContext) mgmt()).getOsgiManager().get().getFramework().getBundleContext(),
+ mgmt()).addingBundle(bundle, null);
+ }
+
+ return Response.status(Status.CREATED).build();
+
+ } finally {
+ if (f!=null) f.delete();
+ if (f2!=null) f2.delete();
}
-
- return Response.status(Status.CREATED).build();
}
@SuppressWarnings("deprecation")
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/67419b65/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
index d1a74ae..9e70156 100644
--- a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
@@ -499,16 +499,17 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
@Test
public void testOsgiBundleWithBom() throws Exception {
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
- String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+ final String symbolicName = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_SYMBOLIC_NAME_FULL;
+ final String version = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_VERSION;
+ final String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
BundleMaker bm = new BundleMaker(manager);
File f = Os.newTempFile("osgi", "jar");
Files.copyFile(ResourceUtils.create(this).getResourceFromUrl(bundleUrl), f);
- String symbolicName = "my.catalog.entity.id.testOsgiBundleWithBom";
String bom = Joiner.on("\n").join(
"brooklyn.catalog:",
" id: " + symbolicName,
- " version: " + TEST_VERSION,
+ " version: " + version,
" itemType: entity",
" name: My Catalog App",
" description: My description",
@@ -525,18 +526,18 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
- CatalogEntitySummary entityItem = client().path("/catalog/entities/"+symbolicName + "/" + TEST_VERSION)
+ CatalogEntitySummary entityItem = client().path("/catalog/entities/"+symbolicName + "/" + version)
.get(CatalogEntitySummary.class);
Assert.assertNotNull(entityItem.getPlanYaml());
Assert.assertTrue(entityItem.getPlanYaml().contains("org.apache.brooklyn.core.test.entity.TestEntity"));
- assertEquals(entityItem.getId(), ver(symbolicName));
+ assertEquals(entityItem.getId(), CatalogUtils.getVersionedId(symbolicName, version));
assertEquals(entityItem.getSymbolicName(), symbolicName);
- assertEquals(entityItem.getVersion(), TEST_VERSION);
+ assertEquals(entityItem.getVersion(), version);
// and internally let's check we have libraries
- RegisteredType item = getManagementContext().getTypeRegistry().get(symbolicName, TEST_VERSION);
+ RegisteredType item = getManagementContext().getTypeRegistry().get(symbolicName, version);
Assert.assertNotNull(item);
Collection<OsgiBundleWithUrl> libs = item.getLibraries();
assertEquals(libs.size(), 1);
@@ -544,7 +545,7 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
Assert.assertNull(lib.getUrl());
assertEquals(lib.getSymbolicName(), "org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities");
- assertEquals(lib.getVersion(), "0.1.0");
+ assertEquals(lib.getVersion(), version);
// now let's check other things on the item
URI expectedIconUrl = URI.create(getEndpointAddress() + "/catalog/icon/" + symbolicName + "/" + entityItem.getVersion()).normalize();
@@ -564,7 +565,7 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
assertTrue(actualInterfaces.contains(expectedInterface.getName()));
}
- byte[] iconData = client().path("/catalog/icon/" + symbolicName + "/" + TEST_VERSION).get(byte[].class);
+ byte[] iconData = client().path("/catalog/icon/" + symbolicName + "/" + version).get(byte[].class);
assertEquals(iconData.length, 43);
}