/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.brooklyn.rest.resources; import static com.google.common.collect.Iterables.find; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; 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; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import org.apache.brooklyn.api.entity.Application; import org.apache.brooklyn.core.entity.Attributes; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.entity.EntityFunctions; import org.apache.brooklyn.core.entity.EntityPredicates; import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; import org.apache.brooklyn.core.location.AbstractLocation; import org.apache.brooklyn.core.location.LocationConfigKeys; import org.apache.brooklyn.core.location.geo.HostGeoInfo; import org.apache.brooklyn.core.location.internal.LocationInternal; import org.apache.brooklyn.entity.stock.BasicApplication; 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; import org.apache.brooklyn.rest.domain.EntitySummary; import org.apache.brooklyn.rest.domain.PolicySummary; import org.apache.brooklyn.rest.domain.SensorSummary; import org.apache.brooklyn.rest.domain.Status; import org.apache.brooklyn.rest.domain.TaskSummary; import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; import org.apache.brooklyn.rest.testing.mocks.CapitalizePolicy; 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.util.collections.CollectionFunctionals; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.http.HttpAsserts; 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 { /* * In simpleSpec, not using EverythingGroup because caused problems! The group is a child of the * app, and the app is a member of the group. It failed in jenkins with: * BasicApplicationImpl{id=GSPjBCe4} GSPjBCe4 * service.isUp: true * service.problems: {service-lifecycle-indicators-from-children-and-members=Required entity not healthy: EverythingGroupImpl{id=KQ4mSEOJ}} * service.state: on-fire * service.state.expected: running @ 1412003485617 / Mon Sep 29 15:11:25 UTC 2014 * EverythingGroupImpl{id=KQ4mSEOJ} KQ4mSEOJ * service.isUp: true * service.problems: {service-lifecycle-indicators-from-children-and-members=Required entities not healthy: BasicApplicationImpl{id=GSPjBCe4}, EverythingGroupImpl{id=KQ4mSEOJ}} * service.state: on-fire * I'm guessing there's a race: the app was not yet healthy because EverythingGroup hadn't set itself to running; * but then the EverythingGroup would never transition to healthy because one of its members was not healthy. */ private static final Logger log = LoggerFactory.getLogger(ApplicationResourceTest.class); private final ApplicationSpec simpleSpec = ApplicationSpec.builder().name("simple-app") .entities(ImmutableSet.of( new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName()), new EntitySpec("simple-group", NameMatcherGroup.class.getName(), ImmutableMap.of("namematchergroup.regex", "simple-ent")) )) .locations(ImmutableSet.of("localhost")) .build(); // Convenience for finding an EntitySummary within a collection, based on its name private static Predicate<EntitySummary> withName(final String name) { return new Predicate<EntitySummary>() { public boolean apply(EntitySummary input) { return name.equals(input.getName()); } }; } // Convenience for finding a Map within a collection, based on the value of one of its keys private static Predicate<? super Map<?,?>> withValueForKey(final Object key, final Object value) { return new Predicate<Object>() { public boolean apply(Object input) { if (!(input instanceof Map)) return false; return value.equals(((Map<?, ?>) input).get(key)); } }; } @Test public void testGetUndefinedApplication() { try { client().resource("/v1/applications/dummy-not-found").get(ApplicationSummary.class); } catch (UniformInterfaceException e) { assertEquals(e.getResponse().getStatus(), 404); } } private static void assertRegexMatches(String actual, String patternExpected) { if (actual==null) Assert.fail("Actual value is null; expected "+patternExpected); if (!actual.matches(patternExpected)) { Assert.fail("Text '"+actual+"' does not match expected pattern "+patternExpected); } } @Test public void testDeployApplication() throws Exception { ClientResponse response = clientDeploy(simpleSpec); HttpAsserts.assertHealthyStatusCode(response.getStatus()); assertEquals(getManagementContext().getApplications().size(), 1); assertRegexMatches(response.getLocation().getPath(), "/v1/applications/.*"); // Object taskO = response.getEntity(Object.class); TaskSummary task = response.getEntity(TaskSummary.class); log.info("deployed, got " + task); assertEquals(task.getEntityId(), getManagementContext().getApplications().iterator().next().getApplicationId()); waitForApplicationToBeRunning(response.getLocation()); } @Test(dependsOnMethods = { "testDeleteApplication" }) // this must happen after we've deleted the main application, as testLocatedLocations assumes a single location public void testDeployApplicationImpl() throws Exception { ApplicationSpec spec = ApplicationSpec.builder() .type(RestMockApp.class.getCanonicalName()) .name("simple-app-impl") .locations(ImmutableSet.of("localhost")) .build(); ClientResponse response = clientDeploy(spec); assertTrue(response.getStatus() / 100 == 2, "response is " + response); // Expect app to be running URI appUri = response.getLocation(); waitForApplicationToBeRunning(response.getLocation()); assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-impl"); } @Test(dependsOnMethods = { "testDeployApplication", "testLocatedLocation" }) public void testDeployApplicationFromInterface() throws Exception { ApplicationSpec spec = ApplicationSpec.builder() .type(BasicApplication.class.getCanonicalName()) .name("simple-app-interface") .locations(ImmutableSet.of("localhost")) .build(); ClientResponse response = clientDeploy(spec); assertTrue(response.getStatus() / 100 == 2, "response is " + response); // Expect app to be running URI appUri = response.getLocation(); waitForApplicationToBeRunning(response.getLocation()); assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-interface"); } @Test(dependsOnMethods = { "testDeployApplication", "testLocatedLocation" }) public void testDeployApplicationFromBuilder() throws Exception { ApplicationSpec spec = ApplicationSpec.builder() .type(RestMockAppBuilder.class.getCanonicalName()) .name("simple-app-builder") .locations(ImmutableSet.of("localhost")) .build(); ClientResponse response = clientDeploy(spec); assertTrue(response.getStatus() / 100 == 2, "response is " + response); // Expect app to be running URI appUri = response.getLocation(); waitForApplicationToBeRunning(response.getLocation(), Duration.TEN_SECONDS); assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-builder"); // Expect app to have the child-entity Set<EntitySummary> entities = client().resource(appUri.toString() + "/entities") .get(new GenericType<Set<EntitySummary>>() {}); assertEquals(entities.size(), 1); assertEquals(Iterables.getOnlyElement(entities).getName(), "child1"); assertEquals(Iterables.getOnlyElement(entities).getType(), RestMockSimpleEntity.class.getCanonicalName()); } @Test(dependsOnMethods = { "testDeployApplication", "testLocatedLocation" }) public void testDeployApplicationYaml() throws Exception { String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+BasicApplication.class.getCanonicalName()+" } ] }"; ClientResponse response = client().resource("/v1/applications") .entity(yaml, "application/x-yaml") .post(ClientResponse.class); assertTrue(response.getStatus()/100 == 2, "response is "+response); // Expect app to be running URI appUri = response.getLocation(); waitForApplicationToBeRunning(response.getLocation()); assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-yaml"); } @Test public void testReferenceCatalogEntity() throws Exception { getManagementContext().getCatalog().addItems("{ name: "+BasicEntity.class.getName()+", " + "services: [ { type: "+BasicEntity.class.getName()+" } ] }"); String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { type: " + BasicEntity.class.getName() + " } ] }"; ClientResponse response = client().resource("/v1/applications") .entity(yaml, "application/x-yaml") .post(ClientResponse.class); assertTrue(response.getStatus()/100 == 2, "response is "+response); // Expect app to be running URI appUri = response.getLocation(); waitForApplicationToBeRunning(response.getLocation()); assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-yaml"); ClientResponse response2 = client().resource(appUri.getPath()) .delete(ClientResponse.class); assertEquals(response2.getStatus(), Response.Status.ACCEPTED.getStatusCode()); } @Test public void testDeployWithInvalidEntityType() { try { clientDeploy(ApplicationSpec.builder() .name("invalid-app") .entities(ImmutableSet.of(new EntitySpec("invalid-ent", "not.existing.entity"))) .locations(ImmutableSet.of("localhost")) .build()); } catch (UniformInterfaceException e) { ApiError error = e.getResponse().getEntity(ApiError.class); assertEquals(error.getMessage(), "Undefined type 'not.existing.entity'"); } } @Test public void testDeployWithInvalidLocation() { try { clientDeploy(ApplicationSpec.builder() .name("invalid-app") .entities(ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName()))) .locations(ImmutableSet.of("3423")) .build()); } catch (UniformInterfaceException e) { ApiError error = e.getResponse().getEntity(ApiError.class); assertEquals(error.getMessage(), "Undefined location '3423'"); } } @Test(dependsOnMethods = "testDeployApplication") public void testListEntities() { Set<EntitySummary> entities = client().resource("/v1/applications/simple-app/entities") .get(new GenericType<Set<EntitySummary>>() {}); assertEquals(entities.size(), 2); EntitySummary entity = Iterables.find(entities, withName("simple-ent"), null); EntitySummary group = Iterables.find(entities, withName("simple-group"), null); Assert.assertNotNull(entity); Assert.assertNotNull(group); client().resource(entity.getLinks().get("self")).get(ClientResponse.class); Set<EntitySummary> children = client().resource(entity.getLinks().get("children")) .get(new GenericType<Set<EntitySummary>>() {}); assertEquals(children.size(), 0); } @Test(dependsOnMethods = "testDeployApplication") public void testListApplications() { Set<ApplicationSummary> applications = client().resource("/v1/applications") .get(new GenericType<Set<ApplicationSummary>>() { }); log.info("Applications listed are: " + applications); for (ApplicationSummary app : applications) { if (simpleSpec.getName().equals(app.getSpec().getName())) return; } Assert.fail("simple-app not found in list of applications: "+applications); } @Test(dependsOnMethods = "testDeployApplication") public void testGetApplicationOnFire() { Application app = Iterables.find(manager.getApplications(), EntityPredicates.displayNameEqualTo(simpleSpec.getName())); Lifecycle origState = app.getAttribute(Attributes.SERVICE_STATE_ACTUAL); ApplicationSummary summary = client().resource("/v1/applications/"+app.getId()) .get(ApplicationSummary.class); assertEquals(summary.getStatus(), Status.RUNNING); app.sensors().set(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); try { ApplicationSummary summary2 = client().resource("/v1/applications/"+app.getId()) .get(ApplicationSummary.class); log.info("Application: " + summary2); assertEquals(summary2.getStatus(), Status.ERROR); } finally { app.sensors().set(Attributes.SERVICE_STATE_ACTUAL, origState); } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test(dependsOnMethods = "testDeployApplication") public void testFetchApplicationsAndEntity() { Collection apps = client().resource("/v1/applications/fetch").get(Collection.class); log.info("Applications fetched are: " + apps); Map app = null; for (Object appI : apps) { Object name = ((Map) appI).get("name"); if ("simple-app".equals(name)) { app = (Map) appI; } if (ImmutableSet.of("simple-ent", "simple-group").contains(name)) Assert.fail(name + " should not be listed at high level: " + apps); } Assert.assertNotNull(app); Collection children = (Collection) app.get("children"); Assert.assertEquals(children.size(), 2); Map entitySummary = (Map) Iterables.find(children, withValueForKey("name", "simple-ent"), null); Map groupSummary = (Map) Iterables.find(children, withValueForKey("name", "simple-group"), null); Assert.assertNotNull(entitySummary); Assert.assertNotNull(groupSummary); String itemIds = app.get("id") + "," + entitySummary.get("id") + "," + groupSummary.get("id"); Collection entities = client().resource("/v1/applications/fetch?items="+itemIds) .get(Collection.class); log.info("Applications+Entities fetched are: " + entities); Assert.assertEquals(entities.size(), apps.size() + 2); Map entityDetails = (Map) Iterables.find(entities, withValueForKey("name", "simple-ent"), null); Map groupDetails = (Map) Iterables.find(entities, withValueForKey("name", "simple-group"), null); Assert.assertNotNull(entityDetails); Assert.assertNotNull(groupDetails); Assert.assertEquals(entityDetails.get("parentId"), app.get("id")); Assert.assertNull(entityDetails.get("children")); Assert.assertEquals(groupDetails.get("parentId"), app.get("id")); Assert.assertNull(groupDetails.get("children")); Collection entityGroupIds = (Collection) entityDetails.get("groupIds"); Assert.assertNotNull(entityGroupIds); Assert.assertEquals(entityGroupIds.size(), 1); Assert.assertEquals(entityGroupIds.iterator().next(), groupDetails.get("id")); Collection groupMembers = (Collection) groupDetails.get("members"); Assert.assertNotNull(groupMembers); for (Application appi : getManagementContext().getApplications()) { Entities.dumpInfo(appi); } log.info("MEMBERS: " + groupMembers); Assert.assertEquals(groupMembers.size(), 1); Map entityMemberDetails = (Map) Iterables.find(groupMembers, withValueForKey("name", "simple-ent"), null); Assert.assertNotNull(entityMemberDetails); Assert.assertEquals(entityMemberDetails.get("id"), entityDetails.get("id")); } @Test(dependsOnMethods = "testDeployApplication") public void testListSensors() { Set<SensorSummary> sensors = client().resource("/v1/applications/simple-app/entities/simple-ent/sensors") .get(new GenericType<Set<SensorSummary>>() { }); assertTrue(sensors.size() > 0); SensorSummary sample = Iterables.find(sensors, new Predicate<SensorSummary>() { @Override public boolean apply(SensorSummary sensorSummary) { return sensorSummary.getName().equals(RestMockSimpleEntity.SAMPLE_SENSOR.getName()); } }); assertEquals(sample.getType(), "java.lang.String"); } @Test(dependsOnMethods = "testDeployApplication") public void testListConfig() { Set<EntityConfigSummary> config = client().resource("/v1/applications/simple-app/entities/simple-ent/config") .get(new GenericType<Set<EntityConfigSummary>>() { }); assertTrue(config.size() > 0); System.out.println(("CONFIG: " + config)); } @Test(dependsOnMethods = "testListConfig") public void testListConfig2() { Set<EntityConfigSummary> config = client().resource("/v1/applications/simple-app/entities/simple-ent/config") .get(new GenericType<Set<EntityConfigSummary>>() {}); assertTrue(config.size() > 0); System.out.println(("CONFIG: " + config)); } @Test(dependsOnMethods = "testDeployApplication") public void testListEffectors() { Set<EffectorSummary> effectors = client().resource("/v1/applications/simple-app/entities/simple-ent/effectors") .get(new GenericType<Set<EffectorSummary>>() {}); assertTrue(effectors.size() > 0); EffectorSummary sampleEffector = find(effectors, new Predicate<EffectorSummary>() { @Override public boolean apply(EffectorSummary input) { return input.getName().equals("sampleEffector"); } }); assertEquals(sampleEffector.getReturnType(), "java.lang.String"); } @Test(dependsOnMethods = "testListSensors") public void testTriggerSampleEffector() throws InterruptedException, IOException { ClientResponse response = client() .resource("/v1/applications/simple-app/entities/simple-ent/effectors/"+RestMockSimpleEntity.SAMPLE_EFFECTOR.getName()) .type(MediaType.APPLICATION_JSON_TYPE) .post(ClientResponse.class, ImmutableMap.of("param1", "foo", "param2", 4)); assertEquals(response.getStatus(), Response.Status.ACCEPTED.getStatusCode()); String result = response.getEntity(String.class); assertEquals(result, "foo4"); } @Test(dependsOnMethods = "testListSensors") public void testTriggerSampleEffectorWithFormData() throws InterruptedException, IOException { MultivaluedMap<String, String> data = new MultivaluedMapImpl(); data.add("param1", "foo"); data.add("param2", "4"); ClientResponse response = client() .resource("/v1/applications/simple-app/entities/simple-ent/effectors/"+RestMockSimpleEntity.SAMPLE_EFFECTOR.getName()) .type(MediaType.APPLICATION_FORM_URLENCODED_TYPE) .post(ClientResponse.class, data); assertEquals(response.getStatus(), Response.Status.ACCEPTED.getStatusCode()); String result = response.getEntity(String.class); assertEquals(result, "foo4"); } @Test(dependsOnMethods = "testTriggerSampleEffector") public void testBatchSensorValues() { WebResource resource = client().resource("/v1/applications/simple-app/entities/simple-ent/sensors/current-state"); Map<String, Object> sensors = resource.get(new GenericType<Map<String, Object>>() {}); assertTrue(sensors.size() > 0); assertEquals(sensors.get(RestMockSimpleEntity.SAMPLE_SENSOR.getName()), "foo4"); } @Test(dependsOnMethods = "testBatchSensorValues") public void testReadEachSensor() { Set<SensorSummary> sensors = client().resource("/v1/applications/simple-app/entities/simple-ent/sensors") .get(new GenericType<Set<SensorSummary>>() {}); Map<String, String> readings = Maps.newHashMap(); for (SensorSummary sensor : sensors) { try { readings.put(sensor.getName(), client().resource(sensor.getLinks().get("self")).accept(MediaType.TEXT_PLAIN).get(String.class)); } catch (UniformInterfaceException uie) { if (uie.getResponse().getStatus() == 204) { // no content readings.put(sensor.getName(), null); } else { Exceptions.propagate(uie); } } } assertEquals(readings.get(RestMockSimpleEntity.SAMPLE_SENSOR.getName()), "foo4"); } @Test(dependsOnMethods = "testTriggerSampleEffector") public void testPolicyWhichCapitalizes() { String policiesEndpoint = "/v1/applications/simple-app/entities/simple-ent/policies"; Set<PolicySummary> policies = client().resource(policiesEndpoint).get(new GenericType<Set<PolicySummary>>(){}); assertEquals(policies.size(), 0); ClientResponse response = client().resource(policiesEndpoint) .queryParam("type", CapitalizePolicy.class.getCanonicalName()) .type(MediaType.APPLICATION_JSON_TYPE) .post(ClientResponse.class, Maps.newHashMap()); assertEquals(response.getStatus(), 200); PolicySummary policy = response.getEntity(PolicySummary.class); assertNotNull(policy.getId()); String newPolicyId = policy.getId(); log.info("POLICY CREATED: " + newPolicyId); policies = client().resource(policiesEndpoint).get(new GenericType<Set<PolicySummary>>() {}); assertEquals(policies.size(), 1); Lifecycle status = client().resource(policiesEndpoint + "/" + newPolicyId).get(Lifecycle.class); log.info("POLICY STATUS: " + status); response = client().resource(policiesEndpoint+"/"+newPolicyId+"/start") .post(ClientResponse.class); assertEquals(response.getStatus(), 204); status = client().resource(policiesEndpoint + "/" + newPolicyId).get(Lifecycle.class); assertEquals(status, Lifecycle.RUNNING); response = client().resource(policiesEndpoint+"/"+newPolicyId+"/stop") .post(ClientResponse.class); assertEquals(response.getStatus(), 204); status = client().resource(policiesEndpoint + "/" + newPolicyId).get(Lifecycle.class); assertEquals(status, Lifecycle.STOPPED); response = client().resource(policiesEndpoint+"/"+newPolicyId+"/destroy") .post(ClientResponse.class); assertEquals(response.getStatus(), 204); response = client().resource(policiesEndpoint+"/"+newPolicyId).get(ClientResponse.class); log.info("POLICY STATUS RESPONSE AFTER DESTROY: " + response.getStatus()); assertEquals(response.getStatus(), 404); policies = client().resource(policiesEndpoint).get(new GenericType<Set<PolicySummary>>() {}); assertEquals(0, policies.size()); } @SuppressWarnings({ "rawtypes" }) @Test(dependsOnMethods = "testDeployApplication") public void testLocatedLocation() { log.info("starting testLocatedLocations"); testListApplications(); LocationInternal l = (LocationInternal) getManagementContext().getApplications().iterator().next().getLocations().iterator().next(); if (l.config().getLocalRaw(LocationConfigKeys.LATITUDE).isAbsent()) { log.info("Supplying fake locations for localhost because could not be autodetected"); ((AbstractLocation) l).setHostGeoInfo(new HostGeoInfo("localhost", "localhost", 50, 0)); } Map result = client().resource("/v1/locations/usage/LocatedLocations") .get(Map.class); log.info("LOCATIONS: " + result); Assert.assertEquals(result.size(), 1); Map details = (Map) result.values().iterator().next(); assertEquals(details.get("leafEntityCount"), 2); } @Test(dependsOnMethods = {"testListEffectors", "testFetchApplicationsAndEntity", "testTriggerSampleEffector", "testListApplications","testReadEachSensor","testPolicyWhichCapitalizes","testLocatedLocation"}) public void testDeleteApplication() throws TimeoutException, InterruptedException { waitForPageFoundResponse("/v1/applications/simple-app", ApplicationSummary.class); Collection<Application> apps = getManagementContext().getApplications(); log.info("Deleting simple-app from " + apps); int size = apps.size(); ClientResponse response = client().resource("/v1/applications/simple-app") .delete(ClientResponse.class); assertEquals(response.getStatus(), Response.Status.ACCEPTED.getStatusCode()); TaskSummary task = response.getEntity(TaskSummary.class); assertTrue(task.getDescription().toLowerCase().contains("destroy"), task.getDescription()); assertTrue(task.getDescription().toLowerCase().contains("simple-app"), task.getDescription()); waitForPageNotFoundResponse("/v1/applications/simple-app", ApplicationSummary.class); log.info("App appears gone, apps are: " + getManagementContext().getApplications()); // more logging above, for failure in the check below Asserts.eventually( 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); HttpAsserts.assertHealthyStatusCode(response.getStatus()); 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); HttpAsserts.assertHealthyStatusCode(response2.getStatus()); 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); HttpAsserts.assertClientErrorStatusCode(response3.getStatus()); 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); } }