Adds CatalogItem.disabled - BrooklynCatalog filters these out for getDefaultVersion - RESTâs CatalogResource filters these out for listApplications, listEntities, listLocations and listPolicies. - Applications refuse to deploy when referenced catalog item is disabled - log.warn when deprecated catalog item is used.
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/da70a8e3 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/da70a8e3 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/da70a8e3 Branch: refs/heads/master Commit: da70a8e3acffe216b3366ee38a24d3c158c1a00f Parents: c1793c9 Author: Aled Sage <[email protected]> Authored: Wed Aug 19 14:46:33 2015 +0100 Committer: Aled Sage <[email protected]> Committed: Wed Aug 26 11:10:14 2015 +0100 ---------------------------------------------------------------------- .../brooklyn/api/catalog/CatalogItem.java | 9 +- .../rebind/mementos/CatalogItemMemento.java | 1 + .../brooklyn/camp/spi/resolve/PdpProcessor.java | 2 +- .../core/catalog/CatalogPredicates.java | 64 ++++- .../catalog/internal/BasicBrooklynCatalog.java | 4 +- .../catalog/internal/CatalogItemBuilder.java | 5 + .../core/catalog/internal/CatalogItemDo.java | 10 + .../internal/CatalogItemDtoAbstract.java | 11 + .../core/location/CatalogLocationResolver.java | 6 + .../rebind/BasicCatalogItemRebindSupport.java | 1 + .../rebind/dto/BasicCatalogItemMemento.java | 17 +- .../mgmt/rebind/dto/MementosGenerators.java | 3 +- .../core/catalog/CatalogPredicatesTest.java | 31 ++- .../catalog/internal/CatalogVersioningTest.java | 20 ++ .../core/mgmt/rebind/RebindOptions.java | 9 + .../core/mgmt/rebind/RebindTestUtils.java | 8 + .../BrooklynComponentTemplateResolver.java | 27 +- .../camp/brooklyn/AbstractYamlRebindTest.java | 11 +- .../brooklyn/catalog/CatalogYamlRebindTest.java | 249 +++++++++++++++++-- .../apache/brooklyn/rest/api/CatalogApi.java | 31 ++- .../rest/resources/ApplicationResource.java | 19 +- .../rest/resources/CatalogResource.java | 48 +++- .../rest/resources/ApplicationResourceTest.java | 141 +++++++++-- .../rest/resources/CatalogResourceTest.java | 150 ++++++++--- 24 files changed, 760 insertions(+), 117 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/api/src/main/java/org/apache/brooklyn/api/catalog/CatalogItem.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/brooklyn/api/catalog/CatalogItem.java b/api/src/main/java/org/apache/brooklyn/api/catalog/CatalogItem.java index 1164ae1..3758d08 100644 --- a/api/src/main/java/org/apache/brooklyn/api/catalog/CatalogItem.java +++ b/api/src/main/java/org/apache/brooklyn/api/catalog/CatalogItem.java @@ -100,8 +100,15 @@ public interface CatalogItem<T,SpecT> extends BrooklynObject, Rebindable { public void setDeprecated(boolean deprecated); + public void setDisabled(boolean disabled); + /** - * @return True if the item has been deprecated and should not be shown in the catalog + * @return True if the item has been deprecated (i.e. its use is discouraged) */ boolean isDeprecated(); + + /** + * @return True if the item has been disabled (i.e. its use is forbidden, except for pre-existing apps) + */ + boolean isDisabled(); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/CatalogItemMemento.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/CatalogItemMemento.java b/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/CatalogItemMemento.java index 9f8bdca..ad16b4a 100644 --- a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/CatalogItemMemento.java +++ b/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/CatalogItemMemento.java @@ -46,4 +46,5 @@ public interface CatalogItemMemento extends Memento { boolean isDeprecated(); + boolean isDisabled(); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/PdpProcessor.java ---------------------------------------------------------------------- diff --git a/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/PdpProcessor.java b/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/PdpProcessor.java index 61c0302..49e7ee1 100644 --- a/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/PdpProcessor.java +++ b/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/PdpProcessor.java @@ -163,7 +163,7 @@ public class PdpProcessor { return; } } - throw new UnsupportedOperationException("Deployment plan item "+deploymentPlanItem+" cannot be matched"); + throw new IllegalArgumentException("Deployment plan item "+deploymentPlanItem+" cannot be matched"); } // ---------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogPredicates.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogPredicates.java b/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogPredicates.java index e30d278..8d5ea9e 100644 --- a/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogPredicates.java +++ b/core/src/main/java/org/apache/brooklyn/core/catalog/CatalogPredicates.java @@ -46,13 +46,16 @@ public class CatalogPredicates { return (item != null) && item.getCatalogItemType()==ciType; } }; - return new CatalogItemTypeEquals<T, SpecT>(ciType); + return new CatalogItemTypeEqualTo<T, SpecT>(ciType); } - private static class CatalogItemTypeEquals<T,SpecT> implements Predicate<CatalogItem<T,SpecT>> { + /** + * @since 0.8.0 + */ + private static class CatalogItemTypeEqualTo<T,SpecT> implements Predicate<CatalogItem<T,SpecT>> { private final CatalogItemType ciType; - public CatalogItemTypeEquals(final CatalogItemType ciType) { + public CatalogItemTypeEqualTo(final CatalogItemType ciType) { this.ciType = ciType; } @Override @@ -69,13 +72,16 @@ public class CatalogPredicates { return (item != null) && item.isDeprecated() == deprecated; } }; - return new DeprecatedEquals<T, SpecT>(deprecated); + return new DeprecatedEqualTo<T, SpecT>(deprecated); } - private static class DeprecatedEquals<T,SpecT> implements Predicate<CatalogItem<T,SpecT>> { + /** + * @since 0.8.0 + */ + private static class DeprecatedEqualTo<T,SpecT> implements Predicate<CatalogItem<T,SpecT>> { private final boolean deprecated; - public DeprecatedEquals(boolean deprecated) { + public DeprecatedEqualTo(boolean deprecated) { this.deprecated = deprecated; } @Override @@ -84,6 +90,28 @@ public class CatalogPredicates { } } + /** + * @since 0.8.0 + */ + public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> disabled(boolean disabled) { + return new DisabledEqualTo<T, SpecT>(disabled); + } + + /** + * @since 0.8.0 + */ + private static class DisabledEqualTo<T,SpecT> implements Predicate<CatalogItem<T,SpecT>> { + private final boolean disabled; + + public DisabledEqualTo(boolean disabled) { + this.disabled = disabled; + } + @Override + public boolean apply(@Nullable CatalogItem<T,SpecT> item) { + return (item != null) && item.isDisabled() == disabled; + } + } + public static final Predicate<CatalogItem<Application,EntitySpec<? extends Application>>> IS_TEMPLATE = CatalogPredicates.<Application,EntitySpec<? extends Application>>isCatalogItemType(CatalogItemType.TEMPLATE); public static final Predicate<CatalogItem<Entity,EntitySpec<?>>> IS_ENTITY = @@ -106,6 +134,9 @@ public class CatalogPredicates { // TODO PERSISTENCE WORKAROUND kept anonymous function in case referenced in persisted state public static final Function<CatalogItem<?,?>,String> ID_OF_ITEM_TRANSFORMER = new IdOfItemTransformer(); + /** + * @since 0.8.0 + */ private static class IdOfItemTransformer implements Function<CatalogItem<?,?>,String> { @Override @Nullable public String apply(@Nullable CatalogItem<?,?> input) { @@ -120,6 +151,9 @@ public class CatalogPredicates { return displayName(filter); } + /** + * @since 0.7.0 + */ public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> displayName(final Predicate<? super String> filter) { // TODO PERSISTENCE WORKAROUND kept anonymous function in case referenced in persisted state new Predicate<CatalogItem<T,SpecT>>() { @@ -131,6 +165,9 @@ public class CatalogPredicates { return new DisplayNameMatches<T,SpecT>(filter); } + /** + * @since 0.8.0 + */ private static class DisplayNameMatches<T,SpecT> implements Predicate<CatalogItem<T,SpecT>> { private final Predicate<? super String> filter; @@ -159,6 +196,9 @@ public class CatalogPredicates { return new SymbolicNameMatches<T,SpecT>(filter); } + /** + * @since 0.8.0 + */ private static class SymbolicNameMatches<T,SpecT> implements Predicate<CatalogItem<T,SpecT>> { private final Predicate<? super String> filter; @@ -182,6 +222,9 @@ public class CatalogPredicates { return new JavaTypeMatches<T, SpecT>(filter); } + /** + * @since 0.8.0 + */ private static class JavaTypeMatches<T,SpecT> implements Predicate<CatalogItem<T,SpecT>> { private final Predicate<? super String> filter; @@ -205,6 +248,9 @@ public class CatalogPredicates { return new XmlMatches<T,SpecT>(filter); } + /** + * @since 0.8.0 + */ private static class XmlMatches<T,SpecT> implements Predicate<CatalogItem<T,SpecT>> { private final Predicate<? super String> filter; @@ -229,6 +275,9 @@ public class CatalogPredicates { return new EntitledToSee<T, SpecT>(mgmt); } + /** + * @since 0.8.0 + */ private static class EntitledToSee<T,SpecT> implements Predicate<CatalogItem<T,SpecT>> { private final ManagementContext mgmt; @@ -253,6 +302,9 @@ public class CatalogPredicates { return new IsBestVersion<T, SpecT>(mgmt); } + /** + * @since 0.8.0 + */ private static class IsBestVersion<T,SpecT> implements Predicate<CatalogItem<T,SpecT>> { private final ManagementContext mgmt; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/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 a25bc73..b2ec24d 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 @@ -195,7 +195,9 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { } private String getDefaultVersion(String symbolicName) { - Iterable<CatalogItem<Object, Object>> versions = getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName))); + Iterable<CatalogItem<Object, Object>> versions = getCatalogItems(Predicates.and( + CatalogPredicates.disabled(false), + CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName)))); Collection<CatalogItem<Object, Object>> orderedVersions = sortVersionsDesc(versions); if (!orderedVersions.isEmpty()) { return orderedVersions.iterator().next().getVersion(); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java index e157a51..8918a74 100644 --- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java +++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java @@ -99,6 +99,11 @@ public class CatalogItemBuilder<CatalogItemType extends CatalogItemDtoAbstract<? return this; } + public CatalogItemBuilder<CatalogItemType> disabled(boolean disabled) { + dto.setDisabled(disabled); + return this; + } + public CatalogItemBuilder<CatalogItemType> libraries(Collection<CatalogBundle> libraries) { dto.setLibraries(libraries); return this; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java index 4312f6b..5029d8d 100644 --- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java +++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java @@ -92,6 +92,16 @@ public class CatalogItemDo<T,SpecT> implements CatalogItem<T,SpecT>, BrooklynObj } @Override + public void setDisabled(boolean diabled) { + itemDto.setDisabled(diabled); + } + + @Override + public boolean isDisabled() { + return itemDto.isDisabled(); + } + + @Override public void setCatalogItemId(String id) { itemDto.setCatalogItemId(id); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java index c8be5f7..b281941 100644 --- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java +++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java @@ -63,6 +63,7 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO private @SetFromFlag Collection<CatalogBundle> libraries; private @SetFromFlag Set<Object> tags = Sets.newLinkedHashSet(); private @SetFromFlag boolean deprecated; + private @SetFromFlag boolean disabled; /** * Config not supported for catalog item. See {@link #getPlanYaml()}. @@ -146,6 +147,16 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO this.deprecated = deprecated; } + @Override + public boolean isDisabled() { + return disabled; + } + + @Override + public void setDisabled(boolean disabled) { + this.disabled = disabled; + } + @Nonnull @Override public Collection<CatalogBundle> getLibraries() { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/core/src/main/java/org/apache/brooklyn/core/location/CatalogLocationResolver.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/location/CatalogLocationResolver.java b/core/src/main/java/org/apache/brooklyn/core/location/CatalogLocationResolver.java index 2dd832c..2cc7b9f 100644 --- a/core/src/main/java/org/apache/brooklyn/core/location/CatalogLocationResolver.java +++ b/core/src/main/java/org/apache/brooklyn/core/location/CatalogLocationResolver.java @@ -55,6 +55,12 @@ public class CatalogLocationResolver implements LocationResolver { public Location newLocationFromString(Map locationFlags, String spec, LocationRegistry registry) { String id = spec.substring(NAME.length()+1); CatalogItem<?, ?> item = CatalogUtils.getCatalogItemOptionalVersion(managementContext, id); + if (item.isDisabled()) { + throw new IllegalStateException("Illegal use of disabled catalog item "+item.getSymbolicName()+":"+item.getVersion()); + } else if (item.isDeprecated()) { + log.warn("Use of deprecated catalog item "+item.getSymbolicName()+":"+item.getVersion()); + } + LocationSpec<?> origLocSpec = managementContext.getCatalog().createSpec((CatalogItem<Location, LocationSpec<?>>)item); LocationSpec<?> locSpec = LocationSpec.create(origLocSpec) .configure(locationFlags); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicCatalogItemRebindSupport.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicCatalogItemRebindSupport.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicCatalogItemRebindSupport.java index edfcb08..c8f2dfc 100644 --- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicCatalogItemRebindSupport.java +++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicCatalogItemRebindSupport.java @@ -51,6 +51,7 @@ public class BasicCatalogItemRebindSupport extends AbstractBrooklynObjectRebindS .put("libraries", memento.getLibraries()) .put("planYaml", memento.getPlanYaml()) .put("deprecated", memento.isDeprecated()) + .put("disabled", memento.isDisabled()) .build(), instance); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicCatalogItemMemento.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicCatalogItemMemento.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicCatalogItemMemento.java index 93892cd..be21dde 100644 --- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicCatalogItemMemento.java +++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicCatalogItemMemento.java @@ -53,6 +53,7 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI protected Class<?> catalogItemJavaType; protected Class<?> specType; protected boolean deprecated; + protected boolean disabled; public Builder description(String description) { this.description = description; @@ -109,6 +110,11 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI return self(); } + public Builder disabled(boolean disabled) { + this.disabled = disabled; + return self(); + } + public Builder from(CatalogItemMemento other) { super.from(other); description = other.getDescription(); @@ -122,6 +128,7 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI catalogItemJavaType = other.getCatalogItemJavaType(); specType = other.getSpecType(); deprecated = other.isDeprecated(); + disabled = other.isDisabled(); return self(); } @@ -141,6 +148,7 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI private Class<?> catalogItemJavaType; private Class<?> specType; private boolean deprecated; + private boolean disabled; @SuppressWarnings("unused") // For deserialisation private BasicCatalogItemMemento() {} @@ -158,6 +166,7 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI this.specType = builder.specType; this.javaType = builder.javaType; this.deprecated = builder.deprecated; + this.disabled = builder.disabled; } @Override @@ -225,6 +234,11 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI } @Override + public boolean isDisabled() { + return disabled; + } + + @Override protected void setCustomFields(Map<String, Object> fields) { if (!fields.isEmpty()) { throw new UnsupportedOperationException("Cannot set custom fields on " + this + ". " + @@ -250,7 +264,8 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI .add("catalogItemType", getCatalogItemType()) .add("javaType", getJavaType()) .add("specType", getSpecType()) - .add("deprecated", isDeprecated()); + .add("deprecated", isDeprecated()) + .add("disabled", isDisabled()); } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java index 36daf49..b49bf52 100644 --- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java +++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java @@ -424,7 +424,8 @@ public class MementosGenerators { .specType(catalogItem.getSpecType()) .version(catalogItem.getVersion()) .planYaml(catalogItem.getPlanYaml()) - .deprecated(catalogItem.isDeprecated()); + .deprecated(catalogItem.isDeprecated()) + .disabled(catalogItem.isDisabled()); return builder.build(); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/core/src/test/java/org/apache/brooklyn/core/catalog/CatalogPredicatesTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/CatalogPredicatesTest.java b/core/src/test/java/org/apache/brooklyn/core/catalog/CatalogPredicatesTest.java index 32860a4..e858061 100644 --- a/core/src/test/java/org/apache/brooklyn/core/catalog/CatalogPredicatesTest.java +++ b/core/src/test/java/org/apache/brooklyn/core/catalog/CatalogPredicatesTest.java @@ -25,17 +25,17 @@ import org.apache.brooklyn.api.catalog.BrooklynCatalog; import org.apache.brooklyn.api.catalog.CatalogItem; import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType; import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.entity.proxying.EntitySpec; +import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder; -import org.apache.brooklyn.core.management.internal.LocalManagementContext; -import org.apache.brooklyn.test.entity.LocalManagementContextForTests; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext; +import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import com.google.common.base.Predicates; -import brooklyn.entity.basic.Entities; public class CatalogPredicatesTest { private LocalManagementContext mgmt; @@ -79,6 +79,21 @@ public class CatalogPredicatesTest { } @Test + public void testDisabled() { + CatalogItem<Entity, EntitySpec<?>> item = createItem(CatalogItemBuilder.newEntity("foo", "1.0") + .plan("services:\n- type: brooklyn.entity.basic.BasicEntity") + .build()); + + assertTrue(CatalogPredicates.<Entity,EntitySpec<?>>disabled(false).apply(item)); + assertFalse(CatalogPredicates.<Entity,EntitySpec<?>>disabled(true).apply(item)); + + item = disableItem(item); + + assertFalse(CatalogPredicates.<Entity,EntitySpec<?>>disabled(false).apply(item)); + assertTrue(CatalogPredicates.<Entity,EntitySpec<?>>disabled(true).apply(item)); + } + + @Test public void testIsCatalogItemType() { CatalogItem<Entity, EntitySpec<?>> item = createItem(CatalogItemBuilder.newEntity("foo", "1.0") .plan("services:\n- type: brooklyn.entity.basic.BasicEntity") @@ -150,4 +165,12 @@ public class CatalogPredicatesTest { catalog.persist(item); return item; } + + @SuppressWarnings("unchecked") + protected <T, SpecT> CatalogItem<T, SpecT> disableItem(CatalogItem<T, SpecT> orig) { + CatalogItem<T, SpecT> item = (CatalogItem<T, SpecT>) catalog.getCatalogItem(orig.getSymbolicName(), orig.getVersion()); + item.setDisabled(true); + catalog.persist(item); + return item; + } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogVersioningTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogVersioningTest.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogVersioningTest.java index e14e291..e7eeba9 100644 --- a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogVersioningTest.java +++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogVersioningTest.java @@ -119,6 +119,20 @@ public class CatalogVersioningTest { } @Test + public void testGetLatestSkipsDisabled() { + String symbolicName = "sampleId"; + String v1 = "0.1.0"; + String v2 = "0.2.0"; + createCatalogItem(symbolicName, v1); + createCatalogItem(symbolicName, v2); + disableCatalogItem(symbolicName, v2); + + CatalogItem<?, ?> item = catalog.getCatalogItem(symbolicName, BasicBrooklynCatalog.DEFAULT_VERSION); + assertEquals(item.getSymbolicName(), symbolicName); + assertEquals(item.getVersion(), v1); + } + + @Test public void testDelete() { String symbolicName = "sampleId"; String version = "0.1.0"; @@ -147,6 +161,12 @@ public class CatalogVersioningTest { .build()); } + private void disableCatalogItem(String symbolicName, String version) { + CatalogItem<?, ?> item = catalog.getCatalogItem(symbolicName, version); + item.setDisabled(true); + catalog.persist(item); + } + private void assertSingleCatalogItem(String symbolicName, String version) { Iterable<CatalogItem<Object, Object>> items = catalog.getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName))); CatalogItem<Object, Object> item = Iterables.getOnlyElement(items); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindOptions.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindOptions.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindOptions.java index 71ce08a..eea64d6 100644 --- a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindOptions.java +++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindOptions.java @@ -22,8 +22,11 @@ import java.io.File; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.api.mgmt.rebind.RebindExceptionHandler; +import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoPersister; import org.apache.brooklyn.core.mgmt.persist.PersistenceObjectStore; +import com.google.common.base.Function; + /** * See {@link RebindTestFixture#rebind(RebindOptions)} and {@link RebindTestUtils#rebind(RebindOptions)}. */ @@ -35,6 +38,7 @@ public class RebindOptions { public ManagementContext newManagementContext; public File mementoDir; public File mementoDirBackup; + public Function<BrooklynMementoPersister, Void> stateTransformer; public ClassLoader classLoader; public PersistenceObjectStore objectStore; @@ -50,6 +54,7 @@ public class RebindOptions { result.newManagementContext(options.newManagementContext); result.mementoDir(options.mementoDir); result.mementoDirBackup(options.mementoDirBackup); + result.stateTransformer(options.stateTransformer); result.classLoader(options.classLoader); result.objectStore(options.objectStore); return result; @@ -82,6 +87,10 @@ public class RebindOptions { this.mementoDirBackup = val; return this; } + public RebindOptions stateTransformer(Function<BrooklynMementoPersister, Void> val) { + this.stateTransformer = val; + return this; + } public RebindOptions classLoader(ClassLoader val) { this.classLoader = val; return this; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestUtils.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestUtils.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestUtils.java index e5696c2f..24c8024 100644 --- a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestUtils.java +++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestUtils.java @@ -35,6 +35,7 @@ import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode; import org.apache.brooklyn.api.mgmt.ha.ManagementNodeState; import org.apache.brooklyn.api.mgmt.rebind.RebindExceptionHandler; import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMemento; +import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoPersister; import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoRawData; import org.apache.brooklyn.api.objs.BrooklynObjectType; import org.apache.brooklyn.api.objs.Identifiable; @@ -60,6 +61,7 @@ import org.apache.brooklyn.util.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; @@ -386,6 +388,7 @@ public class RebindTestUtils { boolean hasPersister = newManagementContext != null && newManagementContext.getRebindManager().getPersister() != null; boolean checkSerializable = options.checkSerializable; boolean terminateOrigManagementContext = options.terminateOrigManagementContext; + Function<BrooklynMementoPersister, Void> stateTransformer = options.stateTransformer; LOG.info("Rebinding app, using mementoDir " + mementoDir + "; object store " + objectStore); @@ -425,6 +428,11 @@ public class RebindTestUtils { FileUtil.setFilePermissionsTo700(mementoDirBackup); } + if (stateTransformer != null) { + BrooklynMementoPersister persister = newManagementContext.getRebindManager().getPersister(); + stateTransformer.apply(persister); + } + List<Application> newApps = newManagementContext.getRebindManager().rebind(classLoader, exceptionHandler, ManagementNodeState.MASTER); newManagementContext.getRebindManager().startPersistence(); return newApps; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java ---------------------------------------------------------------------- diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java index 6fc71e8..52e6fb1 100644 --- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java +++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java @@ -175,11 +175,18 @@ public class BrooklynComponentTemplateResolver { } protected boolean canResolve() { - if (typeResolver.getCatalogItem(this, type)!=null) - return true; - if (loader.tryLoadClass(getJavaType(), Entity.class).isPresent()) - return true; - return false; + CatalogItem<Entity, EntitySpec<?>> item = typeResolver.getCatalogItem(this, type); + if (item == null) { + return loader.tryLoadClass(getJavaType(), Entity.class).isPresent(); + } + + if (item.isDisabled()) { + log.warn("Disallowed attempt to use disabled catalog item "+item.getId()); + return false; + } else if (item.isDeprecated()) { + log.warn("Use of deprecated catalog item "+item.getId()); + } + return true; } /** returns the entity class, if needed in contexts which scan its statics for example */ @@ -223,9 +230,17 @@ public class BrooklynComponentTemplateResolver { @SuppressWarnings({ "unchecked" }) protected <T extends Entity> EntitySpec<T> createSpec(Set<String> encounteredCatalogTypes) { CatalogItem<Entity, EntitySpec<?>> item = getServiceTypeResolver().getCatalogItem(this, getDeclaredType()); + if (item == null) { + // ignore; presumably a java type or some such? + } else if (item.isDisabled()) { + throw new IllegalStateException("Illegal use of disabled catalog item "+item.getSymbolicName()+":"+item.getVersion()); + } else if (item.isDeprecated()) { + log.warn("Use of deprecated catalog item "+item.getSymbolicName()+":"+item.getVersion()); + } + if (encounteredCatalogTypes==null) encounteredCatalogTypes = MutableSet.of(); - //Take the symoblicName part of the catalog item only for recursion detection to prevent + //Take the symbolicName part of the catalog item only for recursion detection to prevent //cross referencing of different versions. Not interested in non-catalog item types. //Prevent catalog items self-referencing even if explicitly different version. boolean firstOccurrence = (item == null || encounteredCatalogTypes.add(item.getSymbolicName())); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java ---------------------------------------------------------------------- diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java index 2b394f7..6982507 100644 --- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java +++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java @@ -22,6 +22,7 @@ import java.io.Reader; import java.io.StringReader; import java.util.Set; +import org.apache.brooklyn.api.catalog.CatalogItem; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.api.mgmt.Task; @@ -111,7 +112,7 @@ public class AbstractYamlRebindTest extends RebindTestFixture<StartableApplicati /////////////////////////////////////////////////// protected void waitForApplicationTasks(Entity app) { - Set<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(origManagementContext.getExecutionManager(), app); + Set<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(mgmt().getExecutionManager(), app); getLogger().info("Waiting on " + tasks.size() + " task(s)"); for (Task<?> t : tasks) { t.blockUntilEnded(); @@ -144,11 +145,11 @@ public class AbstractYamlRebindTest extends RebindTestFixture<StartableApplicati throw e; } getLogger().info("Test - created " + assembly); - final Entity app = origManagementContext.getEntityManager().getEntity(assembly.getId()); + final Entity app = mgmt().getEntityManager().getEntity(assembly.getId()); getLogger().info("App - " + app); // wait for app to have started - Set<Task<?>> tasks = origManagementContext.getExecutionManager().getTasksWithAllTags(ImmutableList.of( + Set<Task<?>> tasks = mgmt().getExecutionManager().getTasksWithAllTags(ImmutableList.of( BrooklynTaskTags.EFFECTOR_TAG, BrooklynTaskTags.tagForContextEntity(app), BrooklynTaskTags.tagForEffectorCall(app, "start", ConfigBag.newInstance(ImmutableMap.of("locations", ImmutableMap.of()))))); @@ -175,8 +176,8 @@ public class AbstractYamlRebindTest extends RebindTestFixture<StartableApplicati addCatalogItems(joinLines(catalogYaml)); } - protected void addCatalogItems(String catalogYaml) { - mgmt().getCatalog().addItems(catalogYaml, forceUpdate); + protected Iterable<? extends CatalogItem<?,?>> addCatalogItems(String catalogYaml) { + return mgmt().getCatalog().addItems(catalogYaml, forceUpdate); } protected void deleteCatalogEntity(String catalogItem) { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java ---------------------------------------------------------------------- diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java index 77f0d51..2956478 100644 --- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java +++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java @@ -18,9 +18,27 @@ */ package org.apache.brooklyn.camp.brooklyn.catalog; +import static com.google.common.base.Preconditions.checkNotNull; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.List; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.apache.brooklyn.api.catalog.CatalogItem; +import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoPersister; import org.apache.brooklyn.api.policy.Policy; import org.apache.brooklyn.api.sensor.Enricher; import org.apache.brooklyn.camp.brooklyn.AbstractYamlRebindTest; @@ -28,13 +46,24 @@ import org.apache.brooklyn.core.BrooklynFeatureEnablement; import org.apache.brooklyn.core.catalog.internal.CatalogUtils; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.entity.StartableApplication; +import org.apache.brooklyn.core.mgmt.persist.BrooklynMementoPersisterToObjectStore; +import org.apache.brooklyn.core.mgmt.persist.PersistenceObjectStore; +import org.apache.brooklyn.core.mgmt.rebind.RebindOptions; import org.apache.brooklyn.core.test.policy.TestEnricher; import org.apache.brooklyn.core.test.policy.TestPolicy; import org.apache.brooklyn.entity.stock.BasicEntity; +import org.apache.brooklyn.util.exceptions.Exceptions; import org.testng.annotations.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; public class CatalogYamlRebindTest extends AbstractYamlRebindTest { @@ -46,6 +75,9 @@ public class CatalogYamlRebindTest extends AbstractYamlRebindTest { enum RebindWithCatalogTestMode { NO_OP, + STRIP_DEPRECATION_AND_ENABLEMENT_FROM_CATALOG_ITEM, + DEPRECATE_CATALOG, + DISABLE_CATALOG, DELETE_CATALOG, REPLACE_CATALOG_WITH_NEWER_VERSION; } @@ -55,6 +87,16 @@ public class CatalogYamlRebindTest extends AbstractYamlRebindTest { runRebindWithCatalogAndApp(RebindWithCatalogTestMode.NO_OP); } + @Test + public void testRebindWithCatalogDeprecatedAndAppExisting() throws Exception { + runRebindWithCatalogAndApp(RebindWithCatalogTestMode.DEPRECATE_CATALOG); + } + + @Test + public void testRebindWithCatalogDisabledAndAppExisting() throws Exception { + runRebindWithCatalogAndApp(RebindWithCatalogTestMode.DISABLE_CATALOG); + } + // See https://issues.apache.org/jira/browse/BROOKLYN-149. // Deletes the catalog item before rebind, but the referenced types are still on the // default classpath. @@ -72,13 +114,22 @@ public class CatalogYamlRebindTest extends AbstractYamlRebindTest { runRebindWithCatalogAndApp(RebindWithCatalogTestMode.REPLACE_CATALOG_WITH_NEWER_VERSION); } + /** + * Old persisted state for catalog items may not have a "deprecated" or "disabled" + * value. Need to check that their absence will default to false. + */ + @Test + public void testRebindWithCatalogPropertiesForDeprecationAndEnablementAbsent() throws Exception { + runRebindWithCatalogAndApp(RebindWithCatalogTestMode.STRIP_DEPRECATION_AND_ENABLEMENT_FROM_CATALOG_ITEM); + } + @SuppressWarnings("unused") protected void runRebindWithCatalogAndApp(RebindWithCatalogTestMode mode) throws Exception { - String symbolicName = "my.catalog.app.id.load"; - String version = "0.1.2"; - String catalogFormat = Joiner.on("\n").join( + String appSymbolicName = "my.catalog.app.id.load"; + String appVersion = "0.1.0"; + String appCatalogFormat = Joiner.on("\n").join( "brooklyn.catalog:", - " id: " + symbolicName, + " id: " + appSymbolicName, " version: %s", " item:", " type: "+ BasicEntity.class.getName(), @@ -87,45 +138,203 @@ public class CatalogYamlRebindTest extends AbstractYamlRebindTest { " brooklyn.policies:", " - type: "+TestPolicy.class.getName()); - // Create the catalog item - addCatalogItems(String.format(catalogFormat, version)); - + String locSymbolicName = "my.catalog.loc.id.load"; + String locVersion = "1.0.0"; + String locCatalogFormat = Joiner.on("\n").join( + "brooklyn.catalog:", + " id: " + locSymbolicName, + " version: %s", + " item.type: location", + " item:", + " type: localhost"); + + // Create the catalog items + CatalogItem<?, ?> appItem = Iterables.getOnlyElement(addCatalogItems(String.format(appCatalogFormat, appVersion))); + CatalogItem<?, ?> locItem = Iterables.getOnlyElement(addCatalogItems(String.format(locCatalogFormat, locVersion))); + final String appItemId = appItem.getId(); + final String locItemId = locItem.getId(); + // Create an app, using that catalog item String yaml = "name: simple-app-yaml\n" + - "location: localhost\n" + + "location: \"brooklyn.catalog:"+CatalogUtils.getVersionedId(locSymbolicName, locVersion)+"\"\n" + "services: \n" + - "- type: "+CatalogUtils.getVersionedId(symbolicName, version); + "- type: "+CatalogUtils.getVersionedId(appSymbolicName, appVersion); origApp = (StartableApplication) createAndStartApplication(yaml); BasicEntity origEntity = (BasicEntity) Iterables.getOnlyElement(origApp.getChildren()); TestPolicy origPolicy = (TestPolicy) Iterables.getOnlyElement(origEntity.getPolicies()); TestEnricher origEnricher = (TestEnricher) Iterables.tryFind(origEntity.getEnrichers(), Predicates.instanceOf(TestEnricher.class)).get(); - assertEquals(origEntity.getCatalogItemId(), symbolicName+":"+version); + assertEquals(origEntity.getCatalogItemId(), appSymbolicName+":"+appVersion); // Depending on test-mode, delete the catalog item, and then rebind switch (mode) { + case DEPRECATE_CATALOG: + appItem = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), appSymbolicName+":"+appVersion); + appItem.setDeprecated(true); + mgmt().getCatalog().persist(appItem); + locItem = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), locSymbolicName+":"+locVersion); + locItem.setDeprecated(true); + mgmt().getCatalog().persist(locItem); + break; + case DISABLE_CATALOG: + appItem = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), appSymbolicName+":"+appVersion); + appItem.setDisabled(true); + mgmt().getCatalog().persist(appItem); + locItem = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), locSymbolicName+":"+locVersion); + locItem.setDisabled(true); + mgmt().getCatalog().persist(locItem); + break; case DELETE_CATALOG: - mgmt().getCatalog().deleteCatalogItem(symbolicName, version); + mgmt().getCatalog().deleteCatalogItem(appSymbolicName, appVersion); + mgmt().getCatalog().deleteCatalogItem(locSymbolicName, locVersion); break; case REPLACE_CATALOG_WITH_NEWER_VERSION: - mgmt().getCatalog().deleteCatalogItem(symbolicName, version); - version = "0.1.3"; - addCatalogItems(String.format(catalogFormat, version)); + mgmt().getCatalog().deleteCatalogItem(appSymbolicName, appVersion); + mgmt().getCatalog().deleteCatalogItem(locSymbolicName, locVersion); + appVersion = "0.2.0"; + locVersion = "1.1.0"; + addCatalogItems(String.format(appCatalogFormat, appVersion)); + addCatalogItems(String.format(locCatalogFormat, locVersion)); break; + case STRIP_DEPRECATION_AND_ENABLEMENT_FROM_CATALOG_ITEM: case NO_OP: - // no-op + break; // no-op + default: + throw new IllegalStateException("Unknown mode: "+mode); + } + + // Rebind + if (mode == RebindWithCatalogTestMode.STRIP_DEPRECATION_AND_ENABLEMENT_FROM_CATALOG_ITEM) { + // Edit the persisted state to remove the "deprecated" and "enablement" tags for our catalog items + rebind(RebindOptions.create() + .stateTransformer(new Function<BrooklynMementoPersister, Void>() { + @Override public Void apply(BrooklynMementoPersister input) { + PersistenceObjectStore objectStore = ((BrooklynMementoPersisterToObjectStore)input).getObjectStore(); + String appItemMemento = checkNotNull(objectStore.newAccessor("catalog/"+appItemId.replace(":", "_")).get(), "appItem in catalog"); + String locItemMemento = checkNotNull(objectStore.newAccessor("catalog/"+locItemId.replace(":", "_")).get(), "locItem in catalog"); + String newAppItemMemento = removeFromXml(appItemMemento, ImmutableList.of("catalogItem/deprecated", "catalogItem/disabled")); + String newLocItemMemento = removeFromXml(appItemMemento, ImmutableList.of("catalogItem/deprecated", "catalogItem/disabled")); + objectStore.newAccessor("catalog/"+appItemId).put(newAppItemMemento); + objectStore.newAccessor("catalog/"+locItemId).put(newLocItemMemento); + return null; + }})); + } else { + rebind(); } - - rebind(); - // Ensure app is still there + // Ensure app is still there, and that it is usabe - e.g. "stop" effector functions as expected BasicEntity newEntity = (BasicEntity) Iterables.getOnlyElement(newApp.getChildren()); Policy newPolicy = Iterables.getOnlyElement(newEntity.getPolicies()); Enricher newEnricher = Iterables.tryFind(newEntity.getEnrichers(), Predicates.instanceOf(TestEnricher.class)).get(); - assertEquals(newEntity.getCatalogItemId(), symbolicName+":"+version); + assertEquals(newEntity.getCatalogItemId(), appSymbolicName+":"+appVersion); - // Ensure app is still usable - e.g. "stop" effector functions as expected newApp.stop(); assertFalse(Entities.isManaged(newApp)); assertFalse(Entities.isManaged(newEntity)); + + // Ensure catalog item is as expecpted + CatalogItem<?, ?> newAppItem = mgmt().getCatalog().getCatalogItem(appSymbolicName, appVersion); + CatalogItem<?, ?> newLocItem = mgmt().getCatalog().getCatalogItem(locSymbolicName, locVersion); + + boolean itemDeployable; + switch (mode) { + case DISABLE_CATALOG: + assertTrue(newAppItem.isDisabled()); + assertTrue(newLocItem.isDisabled()); + itemDeployable = false; + break; + case DELETE_CATALOG: + assertNull(newAppItem); + assertNull(newLocItem); + itemDeployable = false; + break; + case DEPRECATE_CATALOG: + assertTrue(newAppItem.isDeprecated()); + assertTrue(newLocItem.isDeprecated()); + itemDeployable = true; + break; + case NO_OP: + case STRIP_DEPRECATION_AND_ENABLEMENT_FROM_CATALOG_ITEM: + case REPLACE_CATALOG_WITH_NEWER_VERSION: + assertNotNull(newAppItem); + assertNotNull(newLocItem); + assertFalse(newAppItem.isDeprecated()); + assertFalse(newAppItem.isDisabled()); + assertFalse(newLocItem.isDeprecated()); + assertFalse(newLocItem.isDisabled()); + itemDeployable = true; + break; + default: + throw new IllegalStateException("Unknown mode: "+mode); + } + + // Try to deploy a new app + String yaml2 = "name: simple-app-yaml2\n" + + "location: \"brooklyn.catalog:"+CatalogUtils.getVersionedId(locSymbolicName, locVersion)+"\"\n" + + "services: \n" + + "- type: "+CatalogUtils.getVersionedId(appSymbolicName, appVersion); + + if (itemDeployable) { + StartableApplication app2 = (StartableApplication) createAndStartApplication(yaml2); + BasicEntity entity2 = (BasicEntity) Iterables.getOnlyElement(app2.getChildren()); + assertEquals(entity2.getCatalogItemId(), appSymbolicName+":"+appVersion); + } else { + try { + StartableApplication app2 = (StartableApplication) createAndStartApplication(yaml2); + fail(); + } catch (Exception e) { + if (mode == RebindWithCatalogTestMode.DELETE_CATALOG) { + if (!e.toString().contains("cannot be matched")) throw e; + } else { + assertEquals(mode, RebindWithCatalogTestMode.DISABLE_CATALOG); + if (!e.toString().contains("cannot be matched")) throw e; + } + } + } + } + + /** + * Given the "/"-separated path for the elements to be removed, it removes these from the xml + * and returns the transformed XML. + */ + private String removeFromXml(String xml, List<String> elementsToRemove) { + try { + InputSource source = new InputSource(new StringReader(xml)); + Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(source); + + for (String elementToRemove : elementsToRemove) { + Node current = null; + boolean first = true; + for (String tag : elementToRemove.split("/")) { + NodeList matches; + if (first) { + matches = doc.getElementsByTagName(tag); + first = false; + } else { + matches = ((Element)current).getElementsByTagName(tag); + } + if (matches.getLength() > 0) { + current = matches.item(0); + } else { + current = null; + break; + } + } + if (current != null) { + current.getParentNode().removeChild(current); + } + } + + DOMSource domSource = new DOMSource(doc); + StringWriter writer = new StringWriter(); + StreamResult result = new StreamResult(writer); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.transform(domSource, result); + + return writer.toString(); + + } catch (Exception e) { + throw Exceptions.propagate(e); + } } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/usage/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java ---------------------------------------------------------------------- diff --git a/usage/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java b/usage/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java index 818038f..fcbf337 100644 --- a/usage/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java +++ b/usage/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java @@ -314,12 +314,39 @@ public interface CatalogApi { @ApiParam(name = "version", value = "version identifier of catalog item (application, entity, policy, location)", required=true) @PathParam("version") String version); + /** + * @deprecated since 0.8.0; use "/entities/{itemId}/deprecated" with payload of true/false + */ + @Deprecated @POST @Path("/entities/{itemId}/deprecated/{deprecated}") - public void setDeprecated( + public void setDeprecatedLegacy( @ApiParam(name = "itemId", value = "The ID of the catalog item to be deprecated", required = true) @PathParam("itemId") String itemId, @ApiParam(name = "deprecated", value = "Whether or not the catalog item is deprecated", required = true) @PathParam("deprecated") boolean deprecated); + + @POST + @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM, MediaType.TEXT_PLAIN}) + @ApiErrors(value = { + @ApiError(code = 404, reason = "Undefined catalog item"), + }) + @Path("/entities/{itemId}/deprecated") + public void setDeprecated( + @ApiParam(name = "itemId", value = "The ID of the catalog item to be deprecated", required = true) + @PathParam("itemId") String itemId, + @ApiParam(name = "deprecated", value = "Whether or not the catalog item is deprecated", required = true) + boolean deprecated); + + @POST + @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM, MediaType.TEXT_PLAIN}) + @ApiErrors(value = { + @ApiError(code = 404, reason = "Undefined catalog item"), + }) + @Path("/entities/{itemId}/disabled") + public void setDisabled( + @ApiParam(name = "itemId", value = "The ID of the catalog item to be disabled", required = true) + @PathParam("itemId") String itemId, + @ApiParam(name = "disabled", value = "Whether or not the catalog item is disabled", required = true) + boolean disabled); } - http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java index 25e80e0..33752b2 100644 --- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java +++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java @@ -272,7 +272,7 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements } log.debug("Creating app from yaml:\n{}", yaml); - EntitySpec<? extends Application> spec = EntityManagementUtils.createEntitySpecForApplication(mgmt(), yaml); + EntitySpec<? extends Application> spec = createEntitySpecForApplication(yaml); if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, spec)) { throw WebResourceUtils.unauthorized("User '%s' is not authorized to start application %s", @@ -330,7 +330,7 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements //TODO infer encoding from request String potentialYaml = new String(inputToAutodetectType); - EntitySpec<? extends Application> spec = EntityManagementUtils.createEntitySpecForApplication(mgmt(), potentialYaml); + EntitySpec<? extends Application> spec = createEntitySpecForApplication(potentialYaml); // TODO not json - try ZIP, etc @@ -362,6 +362,21 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements return status(ACCEPTED).entity(ts).build(); } + private EntitySpec<? extends Application> createEntitySpecForApplication(String potentialYaml) { + try { + return EntityManagementUtils.createEntitySpecForApplication(mgmt(), potentialYaml); + } catch (IllegalStateException e) { + // An IllegalArgumentException for creating the entity spec gets wrapped in a ISE. + // But we want to return a 400 rather than 500, so ensure we throw IAE. + if (e.getCause() != null && Exceptions.getFirstInteresting(e.getCause()) instanceof IllegalArgumentException) { + IllegalArgumentException iae = (IllegalArgumentException) Exceptions.getFirstInteresting(e.getCause()); + throw new IllegalArgumentException("Cannot create spec for app: "+iae.getMessage(), e); + } else { + throw e; + } + } + } + private void checkApplicationTypesAreValid(ApplicationSpec applicationSpec) { String appType = applicationSpec.getType(); if (appType != null) { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java index 972a134..3dc578b 100644 --- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java +++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java @@ -33,8 +33,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.brooklyn.api.catalog.CatalogItem; import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType; import org.apache.brooklyn.api.entity.Application; @@ -67,6 +65,8 @@ import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.stream.Streams; import org.apache.brooklyn.util.text.StringPredicates; import org.apache.brooklyn.util.text.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.base.Function; import com.google.common.base.Predicate; @@ -216,15 +216,21 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat @Override public List<CatalogEntitySummary> listEntities(String regex, String fragment, boolean allVersions) { - List<CatalogItemSummary> result = getCatalogItemSummariesMatchingRegexFragment(CatalogPredicates.IS_ENTITY, regex, fragment, allVersions); + Predicate<CatalogItem<Entity, EntitySpec<?>>> filter = + Predicates.and( + CatalogPredicates.IS_ENTITY, + CatalogPredicates.<Entity, EntitySpec<?>>disabled(false)); + List<CatalogItemSummary> result = getCatalogItemSummariesMatchingRegexFragment(filter, regex, fragment, allVersions); return castList(result, CatalogEntitySummary.class); } @Override public List<CatalogItemSummary> listApplications(String regex, String fragment, boolean allVersions) { Predicate<CatalogItem<Application, EntitySpec<? extends Application>>> filter = - Predicates.and(CatalogPredicates.<Application,EntitySpec<? extends Application>>deprecated(false), - CatalogPredicates.IS_TEMPLATE); + Predicates.and( + CatalogPredicates.IS_TEMPLATE, + CatalogPredicates.<Application,EntitySpec<? extends Application>>deprecated(false), + CatalogPredicates.<Application,EntitySpec<? extends Application>>disabled(false)); return getCatalogItemSummariesMatchingRegexFragment(filter, regex, fragment, allVersions); } @@ -279,7 +285,11 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat @Override public List<CatalogPolicySummary> listPolicies(String regex, String fragment, boolean allVersions) { - List<CatalogItemSummary> result = getCatalogItemSummariesMatchingRegexFragment(CatalogPredicates.IS_POLICY, regex, fragment, allVersions); + Predicate<CatalogItem<Policy, PolicySpec<?>>> filter = + Predicates.and( + CatalogPredicates.IS_POLICY, + CatalogPredicates.<Policy, PolicySpec<?>>disabled(false)); + List<CatalogItemSummary> result = getCatalogItemSummariesMatchingRegexFragment(filter, regex, fragment, allVersions); return castList(result, CatalogPolicySummary.class); } @@ -321,7 +331,11 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat @Override public List<CatalogLocationSummary> listLocations(String regex, String fragment, boolean allVersions) { - List<CatalogItemSummary> result = getCatalogItemSummariesMatchingRegexFragment(CatalogPredicates.IS_LOCATION, regex, fragment, allVersions); + Predicate<CatalogItem<Location, LocationSpec<?>>> filter = + Predicates.and( + CatalogPredicates.IS_LOCATION, + CatalogPredicates.<Location, LocationSpec<?>>disabled(false)); + List<CatalogItemSummary> result = getCatalogItemSummariesMatchingRegexFragment(filter, regex, fragment, allVersions); return castList(result, CatalogLocationSummary.class); } @@ -405,6 +419,13 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat } @Override + public void setDeprecatedLegacy(String itemId, boolean deprecated) { + log.warn("Use of deprecated \"/v1/catalog/entities/{itemId}/deprecated/{deprecated}\" for "+itemId + +"; use \"/v1/catalog/entities/{itemId}/deprecated\" with request body"); + setDeprecated(itemId, deprecated); + } + + @Override public void setDeprecated(String itemId, boolean deprecated) { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(itemId, "deprecated"))) { throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify catalog", @@ -417,6 +438,19 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat mgmt().getCatalog().persist(item); } + @Override + public void setDisabled(String itemId, boolean disabled) { + if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(itemId, "disabled"))) { + throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify catalog", + Entitlements.getEntitlementContext().user()); + } + CatalogItem<?, ?> item = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), itemId); + if (item==null) + throw WebResourceUtils.notFound("Catalog item with id '%s' not found", itemId); + item.setDisabled(disabled); + mgmt().getCatalog().persist(item); + } + private Response getCatalogItemIcon(CatalogItem<?, ?> result) { String url = result.getIconUrl(); if (url==null) { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java index fd495d8..1d342d2 100644 --- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java @@ -26,6 +26,7 @@ import static org.testng.Assert.assertTrue; import java.io.IOException; import java.net.URI; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeoutException; @@ -34,28 +35,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; -import org.apache.brooklyn.test.Asserts; -import org.apache.brooklyn.test.HttpTestUtils; -import org.apache.brooklyn.util.collections.CollectionFunctionals; -import org.apache.brooklyn.util.exceptions.Exceptions; -import org.apache.brooklyn.util.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.annotations.Test; - -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Maps; -import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.client.GenericType; -import com.sun.jersey.api.client.UniformInterfaceException; -import com.sun.jersey.api.client.WebResource; -import com.sun.jersey.core.util.MultivaluedMapImpl; - import org.apache.brooklyn.api.entity.Application; import org.apache.brooklyn.api.entity.EntityLocal; import org.apache.brooklyn.core.entity.Attributes; @@ -72,6 +51,8 @@ import org.apache.brooklyn.entity.stock.BasicEntity; import org.apache.brooklyn.rest.domain.ApiError; import org.apache.brooklyn.rest.domain.ApplicationSpec; import org.apache.brooklyn.rest.domain.ApplicationSummary; +import org.apache.brooklyn.rest.domain.CatalogEntitySummary; +import org.apache.brooklyn.rest.domain.CatalogItemSummary; import org.apache.brooklyn.rest.domain.EffectorSummary; import org.apache.brooklyn.rest.domain.EntityConfigSummary; import org.apache.brooklyn.rest.domain.EntitySpec; @@ -86,6 +67,29 @@ import org.apache.brooklyn.rest.testing.mocks.NameMatcherGroup; import org.apache.brooklyn.rest.testing.mocks.RestMockApp; import org.apache.brooklyn.rest.testing.mocks.RestMockAppBuilder; import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.test.HttpTestUtils; +import org.apache.brooklyn.util.collections.CollectionFunctionals; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.time.Duration; +import org.apache.http.HttpHeaders; +import org.apache.http.entity.ContentType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.GenericType; +import com.sun.jersey.api.client.UniformInterfaceException; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.core.util.MultivaluedMapImpl; @Test(singleThreaded = true) public class ApplicationResourceTest extends BrooklynRestResourceTest { @@ -595,4 +599,97 @@ public class ApplicationResourceTest extends BrooklynRestResourceTest { EntityFunctions.applications(getManagementContext()), Predicates.compose(Predicates.equalTo(size-1), CollectionFunctionals.sizeFunction()) ); } + + @Test + public void testDisabledApplicationCatalog() throws TimeoutException, InterruptedException { + String itemSymbolicName = "my.catalog.item.id.for.disabling"; + String itemVersion = "1.0"; + String serviceType = "org.apache.brooklyn.entity.stock.BasicApplication"; + + // Deploy the catalog item + addTestCatalogItem(itemSymbolicName, "template", itemVersion, serviceType); + List<CatalogEntitySummary> itemSummaries = client().resource("/v1/catalog/applications") + .queryParam("fragment", itemSymbolicName).queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {}); + CatalogItemSummary itemSummary = Iterables.getOnlyElement(itemSummaries); + String itemVersionedId = String.format("%s:%s", itemSummary.getSymbolicName(), itemSummary.getVersion()); + assertEquals(itemSummary.getId(), itemVersionedId); + + try { + // Create an app before disabling: this should work + String yaml = "{ name: my-app, location: localhost, services: [ { type: \""+itemVersionedId+"\" } ] }"; + ClientResponse response = client().resource("/v1/applications") + .entity(yaml, "application/x-yaml") + .post(ClientResponse.class); + assertTrue(response.getStatus()/100 == 2, "response is "+response); + waitForPageFoundResponse("/v1/applications/my-app", ApplicationSummary.class); + + // Deprecate + deprecateCatalogItem(itemSymbolicName, itemVersion, true); + + // Create an app when deprecated: this should work + String yaml2 = "{ name: my-app2, location: localhost, services: [ { type: \""+itemVersionedId+"\" } ] }"; + ClientResponse response2 = client().resource("/v1/applications") + .entity(yaml2, "application/x-yaml") + .post(ClientResponse.class); + assertTrue(response2.getStatus()/100 == 2, "response is "+response2); + waitForPageFoundResponse("/v1/applications/my-app2", ApplicationSummary.class); + + // Disable + disableCatalogItem(itemSymbolicName, itemVersion, true); + + // Now try creating an app; this should fail because app is disabled + String yaml3 = "{ name: my-app3, location: localhost, services: [ { type: \""+itemVersionedId+"\" } ] }"; + ClientResponse response3 = client().resource("/v1/applications") + .entity(yaml3, "application/x-yaml") + .post(ClientResponse.class); + assertTrue(response3.getStatus()/100 == 4, "response is "+response3); + assertTrue(response3.getEntity(String.class).contains("cannot be matched")); + waitForPageNotFoundResponse("/v1/applications/my-app3", ApplicationSummary.class); + + } finally { + client().resource("/v1/applications/my-app") + .delete(ClientResponse.class); + + client().resource("/v1/applications/my-app2") + .delete(ClientResponse.class); + + client().resource("/v1/applications/my-app3") + .delete(ClientResponse.class); + + client().resource("/v1/catalog/entities/"+itemVersionedId+"/"+itemVersion) + .delete(ClientResponse.class); + } + } + + private void deprecateCatalogItem(String symbolicName, String version, boolean deprecated) { + String id = String.format("%s:%s", symbolicName, version); + ClientResponse response = client().resource(String.format("/v1/catalog/entities/%s/deprecated", id)) + .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON) + .post(ClientResponse.class, deprecated); + assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode()); + } + + private void disableCatalogItem(String symbolicName, String version, boolean disabled) { + String id = String.format("%s:%s", symbolicName, version); + ClientResponse response = client().resource(String.format("/v1/catalog/entities/%s/disabled", id)) + .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON) + .post(ClientResponse.class, disabled); + assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode()); + } + + private void addTestCatalogItem(String catalogItemId, String itemType, String version, String service) { + String yaml = + "brooklyn.catalog:\n"+ + " id: " + catalogItemId + "\n"+ + " name: My Catalog App\n"+ + (itemType!=null ? " item_type: "+itemType+"\n" : "")+ + " description: My description\n"+ + " icon_url: classpath:///redis-logo.png\n"+ + " version: " + version + "\n"+ + "\n"+ + "services:\n"+ + "- type: " + service + "\n"; + + client().resource("/v1/catalog").post(yaml); + } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/da70a8e3/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java index b2991b2..3ce0e56 100644 --- a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java @@ -33,13 +33,6 @@ import java.util.Set; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.eclipse.jetty.http.HttpStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import org.testng.reporters.Files; import org.apache.brooklyn.api.catalog.CatalogItem; import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle; import org.apache.brooklyn.core.catalog.internal.CatalogUtils; @@ -51,6 +44,15 @@ import org.apache.brooklyn.rest.domain.CatalogLocationSummary; import org.apache.brooklyn.rest.domain.CatalogPolicySummary; import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; import org.apache.brooklyn.test.support.TestResourceUnavailableException; +import org.apache.http.HttpHeaders; +import org.apache.http.entity.ContentType; +import org.eclipse.jetty.http.HttpStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.testng.reporters.Files; import com.google.common.base.Joiner; import com.google.common.collect.Iterables; @@ -254,7 +256,31 @@ public class CatalogResourceTest extends BrooklynRestResourceTest { client().resource("/v1/catalog").post(yaml); } + private enum DeprecateStyle { + NEW_STYLE, + LEGACY_STYLE + } + private void deprecateCatalogItem(DeprecateStyle style, String symbolicName, String version, boolean deprecated) { + String id = String.format("%s:%s", symbolicName, version); + ClientResponse response; + if (style == DeprecateStyle.NEW_STYLE) { + response = client().resource(String.format("/v1/catalog/entities/%s/deprecated", id)) + .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON) + .post(ClientResponse.class, deprecated); + } else { + response = client().resource(String.format("/v1/catalog/entities/%s/deprecated/%s", id, deprecated)) + .post(ClientResponse.class); + } + assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode()); + } + private void disableCatalogItem(String symbolicName, String version, boolean disabled) { + String id = String.format("%s:%s", symbolicName, version); + ClientResponse getDisableResponse = client().resource(String.format("/v1/catalog/entities/%s/disabled", id)) + .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON) + .post(ClientResponse.class, disabled); + assertEquals(getDisableResponse.getStatus(), Response.Status.NO_CONTENT.getStatusCode()); + } @Test public void testListPolicies() { @@ -352,39 +378,87 @@ public class CatalogResourceTest extends BrooklynRestResourceTest { @Test public void testSetDeprecated() { - String itemId = "my.catalog.item.id.for.deprecation"; - String serviceType = "org.apache.brooklyn.entity.stock.BasicApplication"; - addTestCatalogItem(itemId, "template", TEST_VERSION, serviceType); - addTestCatalogItem(itemId, "template", "2.0", serviceType); - List<CatalogEntitySummary> applications = client().resource("/v1/catalog/applications") - .queryParam("fragment", itemId).queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {}); - assertEquals(applications.size(), 2); - CatalogItemSummary summary0 = applications.get(0); - CatalogItemSummary summary1 = applications.get(1); - - // Ensure that the ID required by the API is in the 'usual' format of name:id - String id = String.format("%s:%s", summary0.getSymbolicName(), summary0.getVersion()); - assertEquals(summary0.getId(), id); - ClientResponse getDeprecationResponse = client().resource(String.format("/v1/catalog/entities/%s/deprecated/true", id)) - .post(ClientResponse.class); - - assertEquals(getDeprecationResponse.getStatus(), Response.Status.NO_CONTENT.getStatusCode()); - - List<CatalogEntitySummary> applicationsAfterDeprecation = client().resource("/v1/catalog/applications") - .queryParam("fragment", "basicapp").queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {}); - - assertEquals(applicationsAfterDeprecation.size(), 1); - assertTrue(applicationsAfterDeprecation.contains(summary1)); - - ClientResponse getUnDeprecationResponse = client().resource(String.format("/v1/catalog/entities/%s/deprecated/false", summary0.getId())) - .post(ClientResponse.class); - - assertEquals(getUnDeprecationResponse.getStatus(), Response.Status.NO_CONTENT.getStatusCode()); + runSetDeprecated(DeprecateStyle.NEW_STYLE); + } + + // Uses old-style "/v1/catalog/{itemId}/deprecated/true", rather than the "true" in the request body. + @Test + @Deprecated + public void testSetDeprecatedLegacy() { + runSetDeprecated(DeprecateStyle.LEGACY_STYLE); + } - List<CatalogEntitySummary> applicationsAfterUnDeprecation = client().resource("/v1/catalog/applications") - .queryParam("fragment", "basicapp").queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {}); + protected void runSetDeprecated(DeprecateStyle style) { + String symbolicName = "my.catalog.item.id.for.deprecation"; + String serviceType = "org.apache.brooklyn.entity.stock.BasicApplication"; + addTestCatalogItem(symbolicName, "template", TEST_VERSION, serviceType); + addTestCatalogItem(symbolicName, "template", "2.0", serviceType); + try { + List<CatalogEntitySummary> applications = client().resource("/v1/catalog/applications") + .queryParam("fragment", symbolicName).queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {}); + assertEquals(applications.size(), 2); + CatalogItemSummary summary0 = applications.get(0); + CatalogItemSummary summary1 = applications.get(1); + + // Deprecate: that app should be excluded + deprecateCatalogItem(style, summary0.getSymbolicName(), summary0.getVersion(), true); + + List<CatalogEntitySummary> applicationsAfterDeprecation = client().resource("/v1/catalog/applications") + .queryParam("fragment", "basicapp").queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {}); + + assertEquals(applicationsAfterDeprecation.size(), 1); + assertTrue(applicationsAfterDeprecation.contains(summary1)); + + // Un-deprecate: that app should be included again + deprecateCatalogItem(style, summary0.getSymbolicName(), summary0.getVersion(), false); + + List<CatalogEntitySummary> applicationsAfterUnDeprecation = client().resource("/v1/catalog/applications") + .queryParam("fragment", "basicapp").queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {}); + + assertEquals(applications, applicationsAfterUnDeprecation); + } finally { + client().resource("/v1/catalog/entities/"+symbolicName+"/"+TEST_VERSION) + .delete(ClientResponse.class); + client().resource("/v1/catalog/entities/"+symbolicName+"/"+"2.0") + .delete(ClientResponse.class); + } + } - assertEquals(applications, applicationsAfterUnDeprecation); + @Test + public void testSetDisabled() { + String symbolicName = "my.catalog.item.id.for.disabling"; + String serviceType = "org.apache.brooklyn.entity.stock.BasicApplication"; + addTestCatalogItem(symbolicName, "template", TEST_VERSION, serviceType); + addTestCatalogItem(symbolicName, "template", "2.0", serviceType); + try { + List<CatalogEntitySummary> applications = client().resource("/v1/catalog/applications") + .queryParam("fragment", symbolicName).queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {}); + assertEquals(applications.size(), 2); + CatalogItemSummary summary0 = applications.get(0); + CatalogItemSummary summary1 = applications.get(1); + + // Disable: that app should be excluded + disableCatalogItem(summary0.getSymbolicName(), summary0.getVersion(), true); + + List<CatalogEntitySummary> applicationsAfterDisabled = client().resource("/v1/catalog/applications") + .queryParam("fragment", "basicapp").queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {}); + + assertEquals(applicationsAfterDisabled.size(), 1); + assertTrue(applicationsAfterDisabled.contains(summary1)); + + // Un-disable: that app should be included again + disableCatalogItem(summary0.getSymbolicName(), summary0.getVersion(), false); + + List<CatalogEntitySummary> applicationsAfterUnDisabled = client().resource("/v1/catalog/applications") + .queryParam("fragment", "basicapp").queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {}); + + assertEquals(applications, applicationsAfterUnDisabled); + } finally { + client().resource("/v1/catalog/entities/"+symbolicName+"/"+TEST_VERSION) + .delete(ClientResponse.class); + client().resource("/v1/catalog/entities/"+symbolicName+"/"+"2.0") + .delete(ClientResponse.class); + } } @Test
