Return-Path: X-Original-To: apmail-brooklyn-commits-archive@minotaur.apache.org Delivered-To: apmail-brooklyn-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id A21D118A80 for ; Sun, 9 Aug 2015 03:06:19 +0000 (UTC) Received: (qmail 5116 invoked by uid 500); 9 Aug 2015 03:06:19 -0000 Delivered-To: apmail-brooklyn-commits-archive@brooklyn.apache.org Received: (qmail 5094 invoked by uid 500); 9 Aug 2015 03:06:19 -0000 Mailing-List: contact commits-help@brooklyn.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@brooklyn.incubator.apache.org Delivered-To: mailing list commits@brooklyn.incubator.apache.org Received: (qmail 5085 invoked by uid 99); 9 Aug 2015 03:06:19 -0000 Received: from Unknown (HELO spamd3-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 09 Aug 2015 03:06:19 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd3-us-west.apache.org (ASF Mail Server at spamd3-us-west.apache.org) with ESMTP id CB88F19F6FB for ; Sun, 9 Aug 2015 03:06:13 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd3-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.791 X-Spam-Level: * X-Spam-Status: No, score=1.791 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, T_RP_MATCHES_RCVD=-0.01, URIBL_BLOCKED=0.001] autolearn=disabled Received: from mx1-us-west.apache.org ([10.40.0.8]) by localhost (spamd3-us-west.apache.org [10.40.0.10]) (amavisd-new, port 10024) with ESMTP id CT09lB8VYKPQ for ; Sun, 9 Aug 2015 03:06:01 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-us-west.apache.org (ASF Mail Server at mx1-us-west.apache.org) with SMTP id 95DB531C07 for ; Sun, 9 Aug 2015 02:55:09 +0000 (UTC) Received: (qmail 95356 invoked by uid 99); 9 Aug 2015 02:55:09 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 09 Aug 2015 02:55:09 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id EA841E1126; Sun, 9 Aug 2015 02:55:08 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: hadrian@apache.org To: commits@brooklyn.incubator.apache.org Date: Sun, 09 Aug 2015 02:55:09 -0000 Message-Id: In-Reply-To: <419f48f7be504a82be5b32cbfc6a0ba1@git.apache.org> References: <419f48f7be504a82be5b32cbfc6a0ba1@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [02/28] incubator-brooklyn git commit: brooklyn-rest-server: add org.apache package prefix http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/LocationResourceTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/LocationResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/LocationResourceTest.java new file mode 100644 index 0000000..5109fe9 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/LocationResourceTest.java @@ -0,0 +1,189 @@ +/* + * 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 org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.net.URI; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.google.api.client.repackaged.com.google.common.base.Joiner; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.GenericType; + +import brooklyn.location.LocationSpec; +import brooklyn.location.basic.SimulatedLocation; +import brooklyn.location.jclouds.JcloudsLocation; +import brooklyn.rest.domain.CatalogLocationSummary; +import brooklyn.rest.domain.LocationSummary; +import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; +import brooklyn.test.Asserts; + +@Test(singleThreaded = true) +public class LocationResourceTest extends BrooklynRestResourceTest { + + private static final Logger log = LoggerFactory.getLogger(LocationResourceTest.class); + private String legacyLocationName = "my-jungle-legacy"; + private String legacyLocationVersion = "0.0.0.SNAPSHOT"; + + private String locationName = "my-jungle"; + private String locationVersion = "0.1.2"; + + @Test + @Deprecated + public void testAddLegacyLocationDefinition() { + Map expectedConfig = ImmutableMap.of( + "identity", "bob", + "credential", "CR3dential"); + ClientResponse response = client().resource("/v1/locations") + .type(MediaType.APPLICATION_JSON_TYPE) + .post(ClientResponse.class, new brooklyn.rest.domain.LocationSpec(legacyLocationName, "aws-ec2:us-east-1", expectedConfig)); + + URI addedLegacyLocationUri = response.getLocation(); + log.info("added legacy, at: " + addedLegacyLocationUri); + LocationSummary location = client().resource(response.getLocation()).get(LocationSummary.class); + log.info(" contents: " + location); + assertEquals(location.getSpec(), "brooklyn.catalog:"+legacyLocationName+":"+legacyLocationVersion); + assertTrue(addedLegacyLocationUri.toString().startsWith("/v1/locations/")); + + JcloudsLocation l = (JcloudsLocation) getManagementContext().getLocationRegistry().resolve(legacyLocationName); + Assert.assertEquals(l.getProvider(), "aws-ec2"); + Assert.assertEquals(l.getRegion(), "us-east-1"); + Assert.assertEquals(l.getIdentity(), "bob"); + Assert.assertEquals(l.getCredential(), "CR3dential"); + } + + @SuppressWarnings("deprecation") + @Test + public void testAddNewLocationDefinition() { + String yaml = Joiner.on("\n").join(ImmutableList.of( + "brooklyn.catalog:", + " symbolicName: "+locationName, + " version: " + locationVersion, + "", + "brooklyn.locations:", + "- type: "+"aws-ec2:us-east-1", + " brooklyn.config:", + " identity: bob", + " credential: CR3dential")); + + + ClientResponse response = client().resource("/v1/catalog") + .post(ClientResponse.class, yaml); + + assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode()); + + + URI addedCatalogItemUri = response.getLocation(); + log.info("added, at: " + addedCatalogItemUri); + + // Ensure location definition exists + CatalogLocationSummary locationItem = client().resource("/v1/catalog/locations/"+locationName + "/" + locationVersion) + .get(CatalogLocationSummary.class); + log.info(" item: " + locationItem); + LocationSummary locationSummary = client().resource(URI.create("/v1/locations/"+locationName+"/")).get(LocationSummary.class); + log.info(" summary: " + locationSummary); + Assert.assertEquals(locationSummary.getSpec(), "brooklyn.catalog:"+locationName+":"+locationVersion); + + // Ensure location is usable - can instantiate, and has right config + JcloudsLocation l = (JcloudsLocation) getManagementContext().getLocationRegistry().resolve(locationName); + Assert.assertEquals(l.getProvider(), "aws-ec2"); + Assert.assertEquals(l.getRegion(), "us-east-1"); + Assert.assertEquals(l.getIdentity(), "bob"); + Assert.assertEquals(l.getCredential(), "CR3dential"); + } + + @SuppressWarnings("deprecation") + @Test(dependsOnMethods = { "testAddNewLocationDefinition" }) + public void testListAllLocationDefinitions() { + Set locations = client().resource("/v1/locations") + .get(new GenericType>() {}); + Iterable matching = Iterables.filter(locations, new Predicate() { + @Override + public boolean apply(@Nullable LocationSummary l) { + return locationName.equals(l.getName()); + } + }); + LocationSummary location = Iterables.getOnlyElement(matching); + + URI expectedLocationUri = URI.create("/v1/locations/"+locationName); + Assert.assertEquals(location.getSpec(), "brooklyn.catalog:"+locationName+":"+locationVersion); + Assert.assertEquals(location.getLinks().get("self"), expectedLocationUri); + } + + @SuppressWarnings("deprecation") + @Test(dependsOnMethods = { "testListAllLocationDefinitions" }) + public void testGetSpecificLocation() { + URI expectedLocationUri = URI.create("/v1/locations/"+locationName); + LocationSummary location = client().resource(expectedLocationUri).get(LocationSummary.class); + assertEquals(location.getSpec(), "brooklyn.catalog:"+locationName+":"+locationVersion); + } + + @SuppressWarnings("deprecation") + @Test + public void testGetLocationConfig() { + SimulatedLocation parentLoc = (SimulatedLocation) getManagementContext().getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class) + .configure("myParentKey", "myParentVal")); + SimulatedLocation loc = (SimulatedLocation) getManagementContext().getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class) + .parent(parentLoc) + .configure("mykey", "myval") + .configure("password", "mypassword")); + + // "full" means including-inherited, filtered to exclude secrets + URI uriFull = URI.create("/v1/locations/"+loc.getId()+"?full=true"); + LocationSummary summaryFull = client().resource(uriFull).get(LocationSummary.class); + assertEquals(summaryFull.getConfig(), ImmutableMap.of("mykey", "myval", "myParentKey", "myParentVal"), "conf="+summaryFull.getConfig()); + + // Default is local-only, filtered to exclude secrets + URI uriDefault = URI.create("/v1/locations/"+loc.getId()); + LocationSummary summaryDefault = client().resource(uriDefault).get(LocationSummary.class); + assertEquals(summaryDefault.getConfig(), ImmutableMap.of("mykey", "myval"), "conf="+summaryDefault.getConfig()); + } + + @Test(dependsOnMethods = { "testAddLegacyLocationDefinition" }) + @Deprecated + public void testDeleteLocation() { + final int size = getLocationRegistry().getDefinedLocations().size(); + URI expectedLocationUri = URI.create("/v1/locations/"+legacyLocationName); + + ClientResponse response = client().resource(expectedLocationUri).delete(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode()); + Asserts.succeedsEventually(new Runnable() { + @Override + public void run() { + assertEquals(getLocationRegistry().getDefinedLocations().size(), size - 1); + } + }); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/PolicyResourceTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/PolicyResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/PolicyResourceTest.java new file mode 100644 index 0000000..5c6d6e0 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/PolicyResourceTest.java @@ -0,0 +1,145 @@ +/* + * 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 org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.fail; + +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import brooklyn.rest.domain.ApplicationSpec; +import brooklyn.rest.domain.EntitySpec; +import brooklyn.rest.domain.PolicyConfigSummary; +import brooklyn.rest.domain.PolicySummary; +import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; +import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity; +import org.apache.brooklyn.rest.testing.mocks.RestMockSimplePolicy; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.GenericType; + +@Test(singleThreaded = true) +public class PolicyResourceTest extends BrooklynRestResourceTest { + + @SuppressWarnings("unused") + private static final Logger log = LoggerFactory.getLogger(PolicyResourceTest.class); + + private static final String ENDPOINT = "/v1/applications/simple-app/entities/simple-ent/policies/"; + + private final ApplicationSpec simpleSpec = ApplicationSpec.builder().name("simple-app").entities( + ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName()))).locations( + ImmutableSet.of("localhost")).build(); + + private String policyId; + + @BeforeClass(alwaysRun = true) + @Override + public void setUp() throws Exception { + super.setUp(); + + ClientResponse aResponse = clientDeploy(simpleSpec); + waitForApplicationToBeRunning(aResponse.getLocation()); + + ClientResponse pResponse = client().resource(ENDPOINT) + .queryParam("type", RestMockSimplePolicy.class.getCanonicalName()) + .type(MediaType.APPLICATION_JSON_TYPE) + .post(ClientResponse.class, Maps.newHashMap()); + + PolicySummary response = pResponse.getEntity(PolicySummary.class); + assertNotNull(response.getId()); + policyId = response.getId(); + + } + + @Test + public void testListConfig() throws Exception { + Set config = client().resource(ENDPOINT + policyId + "/config") + .get(new GenericType>() {}); + + Set configNames = Sets.newLinkedHashSet(); + for (PolicyConfigSummary conf : config) { + configNames.add(conf.getName()); + } + + assertEquals(configNames, ImmutableSet.of( + RestMockSimplePolicy.SAMPLE_CONFIG.getName(), + RestMockSimplePolicy.INTEGER_CONFIG.getName())); + } + + @Test + public void testGetNonExistantConfigReturns404() throws Exception { + String invalidConfigName = "doesnotexist"; + try { + PolicyConfigSummary summary = client().resource(ENDPOINT + policyId + "/config/" + invalidConfigName) + .get(PolicyConfigSummary.class); + fail("Should have thrown 404, but got "+summary); + } catch (Exception e) { + if (!e.toString().contains("404")) throw e; + } + } + + @Test + public void testGetDefaultValue() throws Exception { + String configName = RestMockSimplePolicy.SAMPLE_CONFIG.getName(); + String expectedVal = RestMockSimplePolicy.SAMPLE_CONFIG.getDefaultValue(); + + String configVal = client().resource(ENDPOINT + policyId + "/config/" + configName) + .get(String.class); + assertEquals(configVal, expectedVal); + } + + @Test(dependsOnMethods = "testGetDefaultValue") + public void testReconfigureConfig() throws Exception { + String configName = RestMockSimplePolicy.SAMPLE_CONFIG.getName(); + + ClientResponse response = client().resource(ENDPOINT + policyId + "/config/" + configName + "/set") + .queryParam("value", "newval") + .post(ClientResponse.class); + + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + } + + @Test(dependsOnMethods = "testReconfigureConfig") + public void testGetConfigValue() throws Exception { + String configName = RestMockSimplePolicy.SAMPLE_CONFIG.getName(); + String expectedVal = "newval"; + + Map allState = client().resource(ENDPOINT + policyId + "/config/current-state") + .get(new GenericType>() {}); + assertEquals(allState, ImmutableMap.of(configName, expectedVal)); + + String configVal = client().resource(ENDPOINT + policyId + "/config/" + configName) + .get(String.class); + assertEquals(configVal, expectedVal); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ScriptResourceTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ScriptResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ScriptResourceTest.java new file mode 100644 index 0000000..63ede14 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ScriptResourceTest.java @@ -0,0 +1,53 @@ +/* + * 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 java.util.Collections; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import brooklyn.entity.basic.Entities; +import brooklyn.location.Location; +import brooklyn.management.ManagementContext; +import brooklyn.rest.domain.ScriptExecutionSummary; +import org.apache.brooklyn.rest.testing.mocks.RestMockApp; + +public class ScriptResourceTest { + + @Test + public void testGroovy() { + RestMockApp app = new RestMockApp(); + Entities.startManagement(app); + ManagementContext mgmt = app.getManagementContext(); + try { + + Entities.start(app, Collections.emptyList()); + + ScriptResource s = new ScriptResource(); + s.injectManagementContext(mgmt); + + ScriptExecutionSummary result = s.groovy(null, "def apps = []; mgmt.applications.each { println 'app:'+it; apps << it.id }; apps"); + Assert.assertEquals(Collections.singletonList(app.getId()).toString(), result.getResult()); + Assert.assertTrue(result.getStdout().contains("app:RestMockApp")); + + } finally { Entities.destroyAll(mgmt); } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java new file mode 100644 index 0000000..c61c327 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java @@ -0,0 +1,83 @@ +/* + * 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 java.net.URI; + +import org.apache.http.client.HttpClient; +import org.eclipse.jetty.server.Server; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import brooklyn.entity.Entity; +import brooklyn.entity.basic.BasicApplication; +import brooklyn.entity.basic.EntityInternal; +import brooklyn.entity.basic.EntityPredicates; +import brooklyn.entity.proxying.EntitySpec; +import brooklyn.management.ManagementContext; +import org.apache.brooklyn.rest.BrooklynRestApiLauncher; +import org.apache.brooklyn.rest.BrooklynRestApiLauncherTestFixture; +import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity; +import brooklyn.test.HttpTestUtils; +import brooklyn.test.entity.LocalManagementContextForTests; +import brooklyn.util.collections.MutableList; +import brooklyn.util.http.HttpTool; +import brooklyn.util.http.HttpToolResponse; +import brooklyn.util.net.Urls; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; + +public class SensorResourceIntegrationTest extends BrooklynRestApiLauncherTestFixture { + + private Server server; + private ManagementContext mgmt; + private BasicApplication app; + + @BeforeClass(alwaysRun = true) + protected void setUp() { + mgmt = LocalManagementContextForTests.newInstance(); + server = useServerForTest(BrooklynRestApiLauncher.launcher() + .managementContext(mgmt) + .withoutJsgui() + .start()); + app = mgmt.getEntityManager().createEntity(EntitySpec.create(BasicApplication.class).displayName("simple-app") + .child(EntitySpec.create(Entity.class, RestMockSimpleEntity.class).displayName("simple-ent"))); + mgmt.getEntityManager().manage(app); + app.start(MutableList.of(mgmt.getLocationRegistry().resolve("localhost"))); + } + + // marked integration because of time + @Test(groups = "Integration") + public void testSensorBytes() throws Exception { + EntityInternal entity = (EntityInternal) Iterables.find(mgmt.getEntityManager().getEntities(), EntityPredicates.displayNameEqualTo("simple-ent")); + SensorResourceTest.addAmphibianSensor(entity); + + String baseUri = getBaseUri(server); + URI url = URI.create(Urls.mergePaths(baseUri, SensorResourceTest.SENSORS_ENDPOINT, SensorResourceTest.SENSOR_NAME)); + + // Uses explicit "application/json" because failed on jenkins as though "text/plain" was the default on Ubuntu jenkins! + HttpClient client = HttpTool.httpClientBuilder().uri(baseUri).build(); + HttpToolResponse response = HttpTool.httpGet(client, url, ImmutableMap.of("Accept", "application/json")); + HttpTestUtils.assertHealthyStatusCode(response.getResponseCode()); + Assert.assertEquals(response.getContentAsString(), "\"12345 frogs\""); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceTest.java new file mode 100644 index 0000000..0cda8ad --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceTest.java @@ -0,0 +1,272 @@ +/* + * 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 org.testng.Assert.assertEquals; + +import java.util.Map; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import brooklyn.config.render.RendererHints; +import brooklyn.config.render.TestRendererHints; +import brooklyn.entity.basic.EntityInternal; +import brooklyn.entity.basic.EntityPredicates; +import brooklyn.event.AttributeSensor; +import brooklyn.event.basic.Sensors; +import brooklyn.rest.api.SensorApi; +import brooklyn.rest.domain.ApplicationSpec; +import brooklyn.rest.domain.EntitySpec; +import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; +import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity; +import brooklyn.test.HttpTestUtils; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.stream.Streams; +import brooklyn.util.text.StringFunctions; + +import com.google.common.base.Functions; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.GenericType; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.WebResource.Builder; + +/** + * Test the {@link SensorApi} implementation. + *

+ * Check that {@link SensorResource} correctly renders {@link AttributeSensor} + * values, including {@link RendererHints.DisplayValue} hints. + */ +@Test(singleThreaded = true) +public class SensorResourceTest extends BrooklynRestResourceTest { + + final static ApplicationSpec SIMPLE_SPEC = ApplicationSpec.builder() + .name("simple-app") + .entities(ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName()))) + .locations(ImmutableSet.of("localhost")) + .build(); + + static final String SENSORS_ENDPOINT = "/v1/applications/simple-app/entities/simple-ent/sensors"; + static final String SENSOR_NAME = "amphibian.count"; + static final AttributeSensor SENSOR = Sensors.newIntegerSensor(SENSOR_NAME); + + EntityInternal entity; + + /** + * Sets up the application and entity. + *

+ * Adds a sensor and sets its value to {@code 12345}. Configures a display value + * hint that appends {@code frogs} to the value of the sensor. + */ + @BeforeClass(alwaysRun = true) + @Override + public void setUp() throws Exception { + super.setUp(); + + // Deploy application + ClientResponse deploy = clientDeploy(SIMPLE_SPEC); + waitForApplicationToBeRunning(deploy.getLocation()); + + entity = (EntityInternal) Iterables.find(getManagementContext().getEntityManager().getEntities(), EntityPredicates.displayNameEqualTo("simple-ent")); + addAmphibianSensor(entity); + } + + static void addAmphibianSensor(EntityInternal entity) { + // Add new sensor + entity.getMutableEntityType().addSensor(SENSOR); + entity.setAttribute(SENSOR, 12345); + + // Register display value hint + RendererHints.register(SENSOR, RendererHints.displayValue(Functions.compose(StringFunctions.append(" frogs"), Functions.toStringFunction()))); + } + + @AfterClass(alwaysRun = true) + @Override + public void tearDown() throws Exception { + TestRendererHints.clearRegistry(); + super.tearDown(); + } + + /** Check default is to use display value hint. */ + @Test + public void testBatchSensorRead() throws Exception { + ClientResponse response = client().resource(SENSORS_ENDPOINT + "/current-state") + .accept(MediaType.APPLICATION_JSON) + .get(ClientResponse.class); + Map currentState = response.getEntity(new GenericType>(Map.class) {}); + + for (String sensor : currentState.keySet()) { + if (sensor.equals(SENSOR_NAME)) { + assertEquals(currentState.get(sensor), "12345 frogs"); + } + } + } + + /** Check setting {@code raw} to {@code true} ignores display value hint. */ + @Test(dependsOnMethods = "testBatchSensorRead") + public void testBatchSensorReadRaw() throws Exception { + ClientResponse response = client().resource(SENSORS_ENDPOINT + "/current-state") + .queryParam("raw", "true") + .accept(MediaType.APPLICATION_JSON) + .get(ClientResponse.class); + Map currentState = response.getEntity(new GenericType>(Map.class) {}); + + for (String sensor : currentState.keySet()) { + if (sensor.equals(SENSOR_NAME)) { + assertEquals(currentState.get(sensor), Integer.valueOf(12345)); + } + } + } + + protected ClientResponse doSensorTest(Boolean raw, MediaType acceptsType, Object expectedValue) { + return doSensorTestUntyped( + raw==null ? null : (""+raw).toLowerCase(), + acceptsType==null ? null : new String[] { acceptsType.getType() }, + expectedValue); + } + protected ClientResponse doSensorTestUntyped(String raw, String[] acceptsTypes, Object expectedValue) { + WebResource req = client().resource(SENSORS_ENDPOINT + "/" + SENSOR_NAME); + if (raw!=null) req = req.queryParam("raw", raw); + ClientResponse response; + if (acceptsTypes!=null) { + Builder rb = req.accept(acceptsTypes); + response = rb.get(ClientResponse.class); + } else { + response = req.get(ClientResponse.class); + } + if (expectedValue!=null) { + HttpTestUtils.assertHealthyStatusCode(response.getStatus()); + Object value = response.getEntity(expectedValue.getClass()); + assertEquals(value, expectedValue); + } + return response; + } + + /** + * Check we can get a sensor, explicitly requesting json; gives a string picking up the rendering hint. + * + * If no "Accepts" header is given, then we don't control whether json or plain text comes back. + * It is dependent on the method order, which is compiler-specific. + */ + @Test + public void testGetJson() throws Exception { + doSensorTest(null, MediaType.APPLICATION_JSON_TYPE, "\"12345 frogs\""); + } + + @Test + public void testGetJsonBytes() throws Exception { + ClientResponse response = doSensorTest(null, MediaType.APPLICATION_JSON_TYPE, null); + byte[] bytes = Streams.readFully(response.getEntityInputStream()); + // assert we have one set of surrounding quotes + assertEquals(bytes.length, 13); + } + + /** Check that plain returns a string without quotes, with the rendering hint */ + @Test + public void testGetPlain() throws Exception { + doSensorTest(null, MediaType.TEXT_PLAIN_TYPE, "12345 frogs"); + } + + /** + * Check that when we set {@code raw = true}, the result ignores the display value hint. + * + * If no "Accepts" header is given, then we don't control whether json or plain text comes back. + * It is dependent on the method order, which is compiler-specific. + */ + @Test + public void testGetRawJson() throws Exception { + doSensorTest(true, MediaType.APPLICATION_JSON_TYPE, 12345); + } + + /** As {@link #testGetRaw()} but with plain set, returns the number */ + @Test + public void testGetPlainRaw() throws Exception { + // have to pass a string because that's how PLAIN is deserialized + doSensorTest(true, MediaType.TEXT_PLAIN_TYPE, "12345"); + } + + /** Check explicitly setting {@code raw} to {@code false} is as before */ + @Test + public void testGetPlainRawFalse() throws Exception { + doSensorTest(false, MediaType.TEXT_PLAIN_TYPE, "12345 frogs"); + } + + /** Check empty vaue for {@code raw} will revert to using default. */ + @Test + public void testGetPlainRawEmpty() throws Exception { + doSensorTestUntyped("", new String[] { MediaType.TEXT_PLAIN }, "12345 frogs"); + } + + /** Check unparseable vaue for {@code raw} will revert to using default. */ + @Test + public void testGetPlainRawError() throws Exception { + doSensorTestUntyped("biscuits", new String[] { MediaType.TEXT_PLAIN }, "12345 frogs"); + } + + /** Check we can set a value */ + @Test + public void testSet() throws Exception { + try { + ClientResponse response = client().resource(SENSORS_ENDPOINT + "/" + SENSOR_NAME) + .type(MediaType.APPLICATION_JSON_TYPE) + .post(ClientResponse.class, 67890); + assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode()); + + assertEquals(entity.getAttribute(SENSOR), (Integer)67890); + + String value = client().resource(SENSORS_ENDPOINT + "/" + SENSOR_NAME).accept(MediaType.TEXT_PLAIN_TYPE).get(String.class); + assertEquals(value, "67890 frogs"); + + } finally { addAmphibianSensor(entity); } + } + + @Test + public void testSetFromMap() throws Exception { + try { + ClientResponse response = client().resource(SENSORS_ENDPOINT) + .type(MediaType.APPLICATION_JSON_TYPE) + .post(ClientResponse.class, MutableMap.of(SENSOR_NAME, 67890)); + assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode()); + + assertEquals(entity.getAttribute(SENSOR), (Integer)67890); + + } finally { addAmphibianSensor(entity); } + } + + /** Check we can delete a value */ + @Test + public void testDelete() throws Exception { + try { + ClientResponse response = client().resource(SENSORS_ENDPOINT + "/" + SENSOR_NAME) + .delete(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode()); + + String value = client().resource(SENSORS_ENDPOINT + "/" + SENSOR_NAME).accept(MediaType.TEXT_PLAIN_TYPE).get(String.class); + assertEquals(value, ""); + + } finally { addAmphibianSensor(entity); } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceIntegrationTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceIntegrationTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceIntegrationTest.java new file mode 100644 index 0000000..1b29fad --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceIntegrationTest.java @@ -0,0 +1,126 @@ +/* + * 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 brooklyn.util.http.HttpTool.httpClientBuilder; +import static org.testng.Assert.assertEquals; + +import java.net.URI; +import java.util.Collections; +import java.util.Map; + +import org.apache.http.HttpStatus; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.HttpClient; +import org.eclipse.jetty.server.Server; +import org.testng.annotations.Test; + +import brooklyn.config.BrooklynProperties; +import brooklyn.management.ManagementContext; +import brooklyn.management.internal.LocalManagementContext; +import brooklyn.management.internal.ManagementContextInternal; +import org.apache.brooklyn.rest.BrooklynRestApiLauncher; +import org.apache.brooklyn.rest.BrooklynRestApiLauncherTestFixture; +import org.apache.brooklyn.rest.security.provider.TestSecurityProvider; +import brooklyn.test.HttpTestUtils; +import brooklyn.util.http.HttpTool; +import brooklyn.util.http.HttpToolResponse; + +import com.google.common.collect.ImmutableMap; + +public class ServerResourceIntegrationTest extends BrooklynRestApiLauncherTestFixture { + + /** + * [sam] Other tests rely on brooklyn.properties not containing security properties so .. + * I think the best way to test this is to set a security provider, then reload properties + * and check no authentication is required. + * + * [aled] Changing this test so doesn't rely on brooklyn.properties having no security + * provider (that can lead to failures locally when running just this test). Asserts + */ + @Test(groups = "Integration") + public void testSecurityProviderUpdatesWhenPropertiesReloaded() { + BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty(); + brooklynProperties.put("brooklyn.webconsole.security.users", "admin"); + brooklynProperties.put("brooklyn.webconsole.security.user.admin.password", "mypassword"); + UsernamePasswordCredentials defaultCredential = new UsernamePasswordCredentials("admin", "mypassword"); + + ManagementContext mgmt = new LocalManagementContext(brooklynProperties); + + try { + Server server = useServerForTest(BrooklynRestApiLauncher.launcher() + .managementContext(mgmt) + .withoutJsgui() + .securityProvider(TestSecurityProvider.class) + .start()); + String baseUri = getBaseUri(server); + + HttpToolResponse response; + final URI uri = URI.create(getBaseUri() + "/v1/server/properties/reload"); + final Map args = Collections.emptyMap(); + + // Unauthorised when no credentials, and when default credentials. + response = HttpTool.httpPost(httpClientBuilder().uri(baseUri).build(), uri, args, args); + assertEquals(response.getResponseCode(), HttpStatus.SC_UNAUTHORIZED); + + response = HttpTool.httpPost(httpClientBuilder().uri(baseUri).credentials(defaultCredential).build(), + uri, args, args); + assertEquals(response.getResponseCode(), HttpStatus.SC_UNAUTHORIZED); + + // Accepts TestSecurityProvider credentials, and we reload. + response = HttpTool.httpPost(httpClientBuilder().uri(baseUri).credentials(TestSecurityProvider.CREDENTIAL).build(), + uri, args, args); + HttpTestUtils.assertHealthyStatusCode(response.getResponseCode()); + + // Has no gone back to credentials from brooklynProperties; TestSecurityProvider credentials no longer work + response = HttpTool.httpPost(httpClientBuilder().uri(baseUri).credentials(defaultCredential).build(), + uri, args, args); + HttpTestUtils.assertHealthyStatusCode(response.getResponseCode()); + + response = HttpTool.httpPost(httpClientBuilder().uri(baseUri).credentials(TestSecurityProvider.CREDENTIAL).build(), + uri, args, args); + assertEquals(response.getResponseCode(), HttpStatus.SC_UNAUTHORIZED); + + } finally { + ((ManagementContextInternal)mgmt).terminate(); + } + } + + @Test(groups = "Integration") + public void testGetUser() throws Exception { + Server server = useServerForTest(BrooklynRestApiLauncher.launcher() + .securityProvider(TestSecurityProvider.class) + .withoutJsgui() + .start()); + assertEquals(getServerUser(server), TestSecurityProvider.USER); + } + + private String getServerUser(Server server) throws Exception { + HttpClient client = httpClientBuilder() + .uri(getBaseUri(server)) + .credentials(TestSecurityProvider.CREDENTIAL) + .build(); + + HttpToolResponse response = HttpTool.httpGet(client, URI.create(getBaseUri(server) + "/v1/server/user"), + ImmutableMap.of()); + HttpTestUtils.assertHealthyStatusCode(response.getResponseCode()); + return response.getContentAsString(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java new file mode 100644 index 0000000..2d361f8 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java @@ -0,0 +1,176 @@ +/* + * 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 org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.ws.rs.core.MultivaluedMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.sun.jersey.api.client.UniformInterfaceException; +import com.sun.jersey.core.util.MultivaluedMapImpl; + +import brooklyn.BrooklynVersion; +import brooklyn.config.BrooklynProperties; +import brooklyn.entity.basic.EmptySoftwareProcess; +import brooklyn.entity.basic.EmptySoftwareProcessDriver; +import brooklyn.entity.basic.EmptySoftwareProcessImpl; +import brooklyn.entity.proxying.ImplementedBy; +import brooklyn.management.ManagementContext; +import brooklyn.management.internal.ManagementContextInternal; +import brooklyn.rest.domain.HighAvailabilitySummary; +import brooklyn.rest.domain.VersionSummary; +import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; +import brooklyn.test.Asserts; +import brooklyn.util.exceptions.Exceptions; + +@Test(singleThreaded = true) +public class ServerResourceTest extends BrooklynRestResourceTest { + + private static final Logger log = LoggerFactory.getLogger(ServerResourceTest.class); + + @Test + public void testGetVersion() throws Exception { + VersionSummary version = client().resource("/v1/server/version").get(VersionSummary.class); + assertEquals(version.getVersion(), BrooklynVersion.get()); + } + + @Test + public void testGetStatus() throws Exception { + String status = client().resource("/v1/server/status").get(String.class); + assertEquals(status, "MASTER"); + } + + @Test + public void testGetHighAvailability() throws Exception { + // Note by default management context from super is started without HA enabled. + // Therefore can only assert a minimal amount of stuff. + HighAvailabilitySummary summary = client().resource("/v1/server/highAvailability").get(HighAvailabilitySummary.class); + log.info("HA summary is: "+summary); + + String ownNodeId = getManagementContext().getManagementNodeId(); + assertEquals(summary.getOwnId(), ownNodeId); + assertEquals(summary.getMasterId(), ownNodeId); + assertEquals(summary.getNodes().keySet(), ImmutableSet.of(ownNodeId)); + assertEquals(summary.getNodes().get(ownNodeId).getNodeId(), ownNodeId); + assertEquals(summary.getNodes().get(ownNodeId).getStatus(), "MASTER"); + assertNotNull(summary.getNodes().get(ownNodeId).getLocalTimestamp()); + // remote will also be non-null if there is no remote backend (local is re-used) + assertNotNull(summary.getNodes().get(ownNodeId).getRemoteTimestamp()); + assertEquals(summary.getNodes().get(ownNodeId).getLocalTimestamp(), summary.getNodes().get(ownNodeId).getRemoteTimestamp()); + } + + @SuppressWarnings("serial") + @Test + public void testReloadsBrooklynProperties() throws Exception { + final AtomicInteger reloadCount = new AtomicInteger(); + getManagementContext().addPropertiesReloadListener(new ManagementContext.PropertiesReloadListener() { + @Override public void reloaded() { + reloadCount.incrementAndGet(); + }}); + client().resource("/v1/server/properties/reload").post(); + assertEquals(reloadCount.get(), 1); + } + + @Test + void testGetConfig() throws Exception { + ((ManagementContextInternal)getManagementContext()).getBrooklynProperties().put("foo.bar.baz", "quux"); + try { + assertEquals(client().resource("/v1/server/config/foo.bar.baz").get(String.class), "quux"); + } finally { + ((ManagementContextInternal)getManagementContext()).getBrooklynProperties().remove("foo.bar.baz"); + } + } + + @Test + void testGetMissingConfigThrowsException() throws Exception { + final String key = "foo.bar.baz"; + BrooklynProperties properties = ((ManagementContextInternal)getManagementContext()).getBrooklynProperties(); + Object existingValue = null; + boolean keyAlreadyPresent = false; + String response = null; + if (properties.containsKey(key)) { + existingValue = properties.remove(key); + keyAlreadyPresent = true; + } + try { + response = client().resource("/v1/server/config/" + key).get(String.class); + Asserts.fail("Expected call to /v1/server/config/" + key + " to fail with status 404, instead server returned " + response); + } catch (UniformInterfaceException e) { + assertEquals(e.getResponse().getStatus(), 204); + } finally { + if (keyAlreadyPresent) { + properties.put(key, existingValue); + } + } + } + + // Alternatively could reuse a blocking location, see brooklyn.entity.basic.SoftwareProcessEntityTest.ReleaseLatchLocation + @ImplementedBy(StopLatchEntityImpl.class) + public interface StopLatchEntity extends EmptySoftwareProcess { + public void unblock(); + public boolean isBlocked(); + } + + public static class StopLatchEntityImpl extends EmptySoftwareProcessImpl implements StopLatchEntity { + private CountDownLatch lock = new CountDownLatch(1); + private volatile boolean isBlocked; + + @Override + public void unblock() { + lock.countDown(); + } + + @Override + protected void postStop() { + super.preStop(); + try { + isBlocked = true; + lock.await(); + isBlocked = false; + } catch (InterruptedException e) { + throw Exceptions.propagate(e); + } + } + + @Override + public Class getDriverInterface() { + return EmptySoftwareProcessDriver.class; + } + + @Override + public boolean isBlocked() { + return isBlocked; + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerShutdownTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerShutdownTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerShutdownTest.java new file mode 100644 index 0000000..d38b380 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerShutdownTest.java @@ -0,0 +1,187 @@ +/* + * 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 org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import java.util.concurrent.atomic.AtomicReference; + +import javax.ws.rs.core.MultivaluedMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.sun.jersey.core.util.MultivaluedMapImpl; + +import brooklyn.entity.basic.Attributes; +import brooklyn.entity.basic.Entities; +import brooklyn.entity.basic.Lifecycle; +import brooklyn.entity.drivers.BasicEntityDriverManager; +import brooklyn.entity.drivers.ReflectiveEntityDriverFactory; +import brooklyn.entity.proxying.EntitySpec; +import brooklyn.entity.trait.Startable; +import brooklyn.management.EntityManager; +import brooklyn.management.Task; +import org.apache.brooklyn.rest.resources.ServerResourceTest.StopLatchEntity; +import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; +import brooklyn.test.Asserts; +import brooklyn.test.EntityTestUtils; +import brooklyn.test.entity.TestApplication; +import brooklyn.util.exceptions.Exceptions; + +public class ServerShutdownTest extends BrooklynRestResourceTest { + private static final Logger log = LoggerFactory.getLogger(ServerResourceTest.class); + + // Need to initialise the ManagementContext before each test as it is destroyed. + @Override + @BeforeClass(alwaysRun = true) + public void setUp() throws Exception { + } + + @Override + @AfterClass(alwaysRun = true) + public void tearDown() throws Exception { + } + + @Override + @BeforeMethod(alwaysRun = true) + public void setUpMethod() { + setUpJersey(); + super.setUpMethod(); + } + + @AfterMethod(alwaysRun = true) + public void tearDownMethod() { + tearDownJersey(); + destroyManagementContext(); + } + + @Test + public void testShutdown() throws Exception { + assertTrue(getManagementContext().isRunning()); + assertFalse(shutdownListener.isRequested()); + + MultivaluedMap formData = new MultivaluedMapImpl(); + formData.add("requestTimeout", "0"); + formData.add("delayForHttpReturn", "0"); + client().resource("/v1/server/shutdown").entity(formData).post(); + + Asserts.succeedsEventually(new Runnable() { + @Override + public void run() { + assertTrue(shutdownListener.isRequested()); + } + }); + Asserts.succeedsEventually(new Runnable() { + @Override public void run() { + assertFalse(getManagementContext().isRunning()); + }}); + } + + @Test + public void testStopAppThenShutdownAndStopAppsWaitsForFirstStop() throws InterruptedException { + ReflectiveEntityDriverFactory f = ((BasicEntityDriverManager)getManagementContext().getEntityDriverManager()).getReflectiveDriverFactory(); + f.addClassFullNameMapping("brooklyn.entity.basic.EmptySoftwareProcessDriver", "org.apache.brooklyn.rest.resources.ServerResourceTest$EmptySoftwareProcessTestDriver"); + + // Second stop on SoftwareProcess could return early, while the first stop is still in progress + // This causes the app to shutdown prematurely, leaking machines. + EntityManager emgr = getManagementContext().getEntityManager(); + EntitySpec appSpec = EntitySpec.create(TestApplication.class); + TestApplication app = emgr.createEntity(appSpec); + emgr.manage(app); + EntitySpec latchEntitySpec = EntitySpec.create(StopLatchEntity.class); + final StopLatchEntity entity = app.createAndManageChild(latchEntitySpec); + app.start(ImmutableSet.of(app.newLocalhostProvisioningLocation())); + EntityTestUtils.assertAttributeEquals(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + + try { + final Task firstStop = app.invoke(Startable.STOP, ImmutableMap.of()); + Asserts.succeedsEventually(new Runnable() { + @Override + public void run() { + assertTrue(entity.isBlocked()); + } + }); + + final AtomicReference shutdownError = new AtomicReference<>(); + // Can't use ExecutionContext as it will be stopped on shutdown + Thread shutdownThread = new Thread() { + @Override + public void run() { + try { + MultivaluedMap formData = new MultivaluedMapImpl(); + formData.add("stopAppsFirst", "true"); + formData.add("shutdownTimeout", "0"); + formData.add("requestTimeout", "0"); + formData.add("delayForHttpReturn", "0"); + client().resource("/v1/server/shutdown").entity(formData).post(); + } catch (Exception e) { + log.error("Shutdown request error", e); + shutdownError.set(e); + throw Exceptions.propagate(e); + } + } + }; + shutdownThread.start(); + + //shutdown must wait until the first stop completes (or time out) + Asserts.succeedsContinually(new Runnable() { + @Override + public void run() { + assertFalse(firstStop.isDone()); + assertEquals(getManagementContext().getApplications().size(), 1); + assertFalse(shutdownListener.isRequested()); + } + }); + + // NOTE test is not fully deterministic. Depending on thread scheduling this will + // execute before or after ServerResource.shutdown does the app stop loop. This + // means that the shutdown code might not see the app at all. In any case though + // the test must succeed. + entity.unblock(); + + Asserts.succeedsEventually(new Runnable() { + @Override + public void run() { + assertTrue(firstStop.isDone()); + assertTrue(shutdownListener.isRequested()); + assertFalse(getManagementContext().isRunning()); + } + }); + + shutdownThread.join(); + assertNull(shutdownError.get(), "Shutdown request error, logged above"); + } finally { + // Be sure we always unblock entity stop even in the case of an exception. + // In the success path the entity is already unblocked above. + entity.unblock(); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java new file mode 100644 index 0000000..98a1a52 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java @@ -0,0 +1,447 @@ +/* + * 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 org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.entity.basic.ApplicationBuilder; +import brooklyn.entity.basic.SoftwareProcessEntityTest; +import brooklyn.location.Location; +import brooklyn.location.LocationSpec; +import brooklyn.location.NoMachinesAvailableException; +import brooklyn.location.basic.LocalhostMachineProvisioningLocation; +import brooklyn.location.basic.SshMachineLocation; +import brooklyn.management.internal.LocalUsageManager; +import brooklyn.management.internal.ManagementContextInternal; +import brooklyn.rest.domain.ApplicationSpec; +import brooklyn.rest.domain.EntitySpec; +import brooklyn.rest.domain.Status; +import brooklyn.rest.domain.TaskSummary; +import brooklyn.rest.domain.UsageStatistic; +import brooklyn.rest.domain.UsageStatistics; +import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; +import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity; +import brooklyn.test.entity.TestApplication; +import brooklyn.util.repeat.Repeater; +import brooklyn.util.time.Time; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.GenericType; + +public class UsageResourceTest extends BrooklynRestResourceTest { + + @SuppressWarnings("unused") + private static final Logger LOG = LoggerFactory.getLogger(UsageResourceTest.class); + + private static final long TIMEOUT_MS = 10*1000; + + private Calendar testStartTime; + + private final ApplicationSpec simpleSpec = ApplicationSpec.builder().name("simple-app"). + entities(ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName()))). + locations(ImmutableSet.of("localhost")). + build(); + + @BeforeMethod(alwaysRun=true) + public void setUpMethod() { + ((ManagementContextInternal)getManagementContext()).getStorage().remove(LocalUsageManager.APPLICATION_USAGE_KEY); + ((ManagementContextInternal)getManagementContext()).getStorage().remove(LocalUsageManager.LOCATION_USAGE_KEY); + testStartTime = new GregorianCalendar(); + } + + @Test + public void testListApplicationUsages() throws Exception { + // Create an app + Calendar preStart = new GregorianCalendar(); + String appId = createApp(simpleSpec); + Calendar postStart = new GregorianCalendar(); + + // We will retrieve usage from one millisecond after start; this guarantees to not be + // told about both STARTING+RUNNING, which could otherwise happen if they are in the + // same milliscond. + Calendar afterPostStart = Time.newCalendarFromMillisSinceEpochUtc(postStart.getTime().getTime()+1); + + // Check that app's usage is returned + ClientResponse response = client().resource("/v1/usage/applications").get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + Iterable usages = response.getEntity(new GenericType>() {}); + UsageStatistics usage = Iterables.getOnlyElement(usages); + assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart); + + // check app ignored if endCalendar before app started + response = client().resource("/v1/usage/applications?start="+0+"&end="+(preStart.getTime().getTime()-1)).get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usages = response.getEntity(new GenericType>() {}); + assertTrue(Iterables.isEmpty(usages), "usages="+usages); + + // Wait, so that definitely asking about things that have happened (not things in the future, + // or events that are happening this exact same millisecond) + waitForFuture(afterPostStart.getTime().getTime()); + + // Check app start + end date truncated, even if running for longer (i.e. only tell us about this time window). + // Note that start==end means we get a snapshot of the apps in use at that exact time. + // + // The start/end times in UsageStatistic are in String format, and are rounded down to the nearest second. + // The comparison does use the milliseconds passed in the REST call though. + // The rounding down result should be the same as roundDown(afterPostStart), because that is the time-window + // we asked for. + response = client().resource("/v1/usage/applications?start="+afterPostStart.getTime().getTime()+"&end="+afterPostStart.getTime().getTime()).get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usages = response.getEntity(new GenericType>() {}); + usage = Iterables.getOnlyElement(usages); + assertAppUsage(usage, appId, ImmutableList.of(Status.RUNNING), roundDown(preStart), postStart); + assertAppUsageTimesTruncated(usage, roundDown(afterPostStart), roundDown(afterPostStart)); + + // Delete the app + Calendar preDelete = new GregorianCalendar(); + deleteApp(appId); + Calendar postDelete = new GregorianCalendar(); + + // Deleted app still returned, if in time range + response = client().resource("/v1/usage/applications").get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usages = response.getEntity(new GenericType>() {}); + usage = Iterables.getOnlyElement(usages); + assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING, Status.DESTROYED), roundDown(preStart), postDelete); + assertAppUsage(ImmutableList.copyOf(usage.getStatistics()).subList(2, 3), appId, ImmutableList.of(Status.DESTROYED), roundDown(preDelete), postDelete); + + long afterPostDelete = postDelete.getTime().getTime()+1; + waitForFuture(afterPostDelete); + + response = client().resource("/v1/usage/applications?start=" + afterPostDelete).get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usages = response.getEntity(new GenericType>() {}); + assertTrue(Iterables.isEmpty(usages), "usages="+usages); + } + + @Test + public void testGetApplicationUsagesForNonExistantApp() throws Exception { + ClientResponse response = client().resource("/v1/usage/applications/wrongid").get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode()); + } + + @Test + public void testGetApplicationUsage() throws Exception { + // Create an app + Calendar preStart = new GregorianCalendar(); + String appId = createApp(simpleSpec); + Calendar postStart = new GregorianCalendar(); + + // Normal request returns all + ClientResponse response = client().resource("/v1/usage/applications/" + appId).get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + UsageStatistics usage = response.getEntity(new GenericType() {}); + assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart); + + // Time-constrained requests + response = client().resource("/v1/usage/applications/" + appId + "?start=1970-01-01T00:00:00-0100").get(ClientResponse.class); + usage = response.getEntity(new GenericType() {}); + assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart); + + response = client().resource("/v1/usage/applications/" + appId + "?start=9999-01-01T00:00:00+0100").get(ClientResponse.class); + assertTrue(response.getStatus() >= 400, "end defaults to NOW, so future start should fail, instead got code "+response.getStatus()); + + response = client().resource("/v1/usage/applications/" + appId + "?start=9999-01-01T00:00:00%2B0100&end=9999-01-02T00:00:00%2B0100").get(ClientResponse.class); + usage = response.getEntity(new GenericType() {}); + assertTrue(usage.getStatistics().isEmpty()); + + response = client().resource("/v1/usage/applications/" + appId + "?end=9999-01-01T00:00:00+0100").get(ClientResponse.class); + usage = response.getEntity(new GenericType() {}); + assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart); + + response = client().resource("/v1/usage/applications/" + appId + "?start=9999-01-01T00:00:00+0100&end=9999-02-01T00:00:00+0100").get(ClientResponse.class); + usage = response.getEntity(new GenericType() {}); + assertTrue(usage.getStatistics().isEmpty()); + + response = client().resource("/v1/usage/applications/" + appId + "?start=1970-01-01T00:00:00-0100&end=9999-01-01T00:00:00+0100").get(ClientResponse.class); + usage = response.getEntity(new GenericType() {}); + assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart); + + response = client().resource("/v1/usage/applications/" + appId + "?end=1970-01-01T00:00:00-0100").get(ClientResponse.class); + usage = response.getEntity(new GenericType() {}); + assertTrue(usage.getStatistics().isEmpty()); + + // Delete the app + Calendar preDelete = new GregorianCalendar(); + deleteApp(appId); + Calendar postDelete = new GregorianCalendar(); + + // Deleted app still returned, if in time range + response = client().resource("/v1/usage/applications/" + appId).get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usage = response.getEntity(new GenericType() {}); + assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING, Status.DESTROYED), roundDown(preStart), postDelete); + assertAppUsage(ImmutableList.copyOf(usage.getStatistics()).subList(2, 3), appId, ImmutableList.of(Status.DESTROYED), roundDown(preDelete), postDelete); + + // Deleted app not returned if terminated before time range begins + long afterPostDelete = postDelete.getTime().getTime()+1; + waitForFuture(afterPostDelete); + response = client().resource("/v1/usage/applications/" + appId +"?start=" + afterPostDelete).get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usage = response.getEntity(new GenericType() {}); + assertTrue(usage.getStatistics().isEmpty(), "usages="+usage); + } + + @Test + public void testGetMachineUsagesForNonExistantMachine() throws Exception { + ClientResponse response = client().resource("/v1/usage/machines/wrongid").get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode()); + } + + @Test + public void testGetMachineUsagesInitiallyEmpty() throws Exception { + // All machines: empty + ClientResponse response = client().resource("/v1/usage/machines").get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + Iterable usages = response.getEntity(new GenericType>() {}); + assertTrue(Iterables.isEmpty(usages)); + + // Specific machine that does not exist: get 404 + response = client().resource("/v1/usage/machines/machineIdThatDoesNotExist").get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode()); + } + + @Test + public void testListAndGetMachineUsage() throws Exception { + Location location = getManagementContext().getLocationManager().createLocation(LocationSpec.create(DynamicLocalhostMachineProvisioningLocation.class)); + TestApplication app = ApplicationBuilder.newManagedApp(TestApplication.class, getManagementContext()); + SoftwareProcessEntityTest.MyService entity = app.createAndManageChild(brooklyn.entity.proxying.EntitySpec.create(SoftwareProcessEntityTest.MyService.class)); + + Calendar preStart = new GregorianCalendar(); + app.start(ImmutableList.of(location)); + Calendar postStart = new GregorianCalendar(); + Location machine = Iterables.getOnlyElement(entity.getLocations()); + + // All machines + ClientResponse response = client().resource("/v1/usage/machines").get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + Iterable usages = response.getEntity(new GenericType>() {}); + UsageStatistics usage = Iterables.getOnlyElement(usages); + assertMachineUsage(usage, app.getId(), machine.getId(), ImmutableList.of(Status.ACCEPTED), roundDown(preStart), postStart); + + // Specific machine + response = client().resource("/v1/usage/machines/"+machine.getId()).get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usage = response.getEntity(new GenericType() {}); + assertMachineUsage(usage, app.getId(), machine.getId(), ImmutableList.of(Status.ACCEPTED), roundDown(preStart), postStart); + } + + @Test + public void testListMachinesUsageForApp() throws Exception { + Location location = getManagementContext().getLocationManager().createLocation(LocationSpec.create(DynamicLocalhostMachineProvisioningLocation.class)); + TestApplication app = ApplicationBuilder.newManagedApp(TestApplication.class, getManagementContext()); + SoftwareProcessEntityTest.MyService entity = app.createAndManageChild(brooklyn.entity.proxying.EntitySpec.create(SoftwareProcessEntityTest.MyService.class)); + String appId = app.getId(); + + Calendar preStart = new GregorianCalendar(); + app.start(ImmutableList.of(location)); + Calendar postStart = new GregorianCalendar(); + Location machine = Iterables.getOnlyElement(entity.getLocations()); + + // For running machine + ClientResponse response = client().resource("/v1/usage/machines?application="+appId).get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + Iterable usages = response.getEntity(new GenericType>() {}); + UsageStatistics usage = Iterables.getOnlyElement(usages); + assertMachineUsage(usage, app.getId(), machine.getId(), ImmutableList.of(Status.ACCEPTED), roundDown(preStart), postStart); + + // Stop the machine + Calendar preStop = new GregorianCalendar(); + app.stop(); + Calendar postStop = new GregorianCalendar(); + + // Deleted machine still returned, if in time range + response = client().resource("/v1/usage/machines?application=" + appId).get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usages = response.getEntity(new GenericType>() {}); + usage = Iterables.getOnlyElement(usages); + assertMachineUsage(usage, app.getId(), machine.getId(), ImmutableList.of(Status.ACCEPTED, Status.DESTROYED), roundDown(preStart), postStop); + assertMachineUsage(ImmutableList.copyOf(usage.getStatistics()).subList(1,2), appId, machine.getId(), ImmutableList.of(Status.DESTROYED), roundDown(preStop), postStop); + + // Terminated machines ignored if terminated since start-time + long futureTime = postStop.getTime().getTime()+1; + waitForFuture(futureTime); + response = client().resource("/v1/usage/applications?start=" + futureTime).get(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usages = response.getEntity(new GenericType>() {}); + assertTrue(Iterables.isEmpty(usages), "usages="+usages); + } + + private String createApp(ApplicationSpec spec) { + ClientResponse response = clientDeploy(spec); + assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode()); + TaskSummary createTask = response.getEntity(TaskSummary.class); + waitForTask(createTask.getId()); + return createTask.getEntityId(); + } + + private void deleteApp(String appId) { + ClientResponse response = client().resource("/v1/applications/"+appId) + .delete(ClientResponse.class); + assertEquals(response.getStatus(), Response.Status.ACCEPTED.getStatusCode()); + TaskSummary deletionTask = response.getEntity(TaskSummary.class); + waitForTask(deletionTask.getId()); + } + + private void assertCalendarOrders(Object context, Calendar... Calendars) { + if (Calendars.length <= 1) return; + + long[] times = new long[Calendars.length]; + for (int i = 0; i < times.length; i++) { + times[i] = millisSinceStart(Calendars[i]); + } + String err = "context="+context+"; Calendars="+Arrays.toString(Calendars) + "; CalendarsSanitized="+Arrays.toString(times); + + Calendar Calendar = Calendars[0]; + for (int i = 1; i < Calendars.length; i++) { + assertTrue(Calendar.getTime().getTime() <= Calendars[i].getTime().getTime(), err); + } + } + + private void waitForTask(final String taskId) { + boolean success = Repeater.create() + .repeat(new Runnable() { public void run() {}}) + .until(new Callable() { + @Override public Boolean call() { + ClientResponse response = client().resource("/v1/activities/"+taskId).get(ClientResponse.class); + if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) { + return true; + } + TaskSummary summary = response.getEntity(TaskSummary.class); + return summary != null && summary.getEndTimeUtc() != null; + }}) + .every(10L, TimeUnit.MILLISECONDS) + .limitTimeTo(TIMEOUT_MS, TimeUnit.MILLISECONDS) + .run(); + assertTrue(success, "task "+taskId+" not finished"); + } + + private long millisSinceStart(Calendar time) { + return time.getTime().getTime() - testStartTime.getTime().getTime(); + } + + private Calendar roundDown(Calendar calendar) { + long time = calendar.getTime().getTime(); + long timeDown = ((long)(time / 1000)) * 1000; + return Time.newCalendarFromMillisSinceEpochUtc(timeDown); + } + + @SuppressWarnings("unused") + private Calendar roundUp(Calendar calendar) { + long time = calendar.getTime().getTime(); + long timeDown = ((long)(time / 1000)) * 1000; + long timeUp = (time == timeDown) ? time : timeDown + 1000; + return Time.newCalendarFromMillisSinceEpochUtc(timeUp); + } + + private void assertMachineUsage(UsageStatistics usage, String appId, String machineId, List states, Calendar pre, Calendar post) throws Exception { + assertUsage(usage.getStatistics(), appId, machineId, states, pre, post, false); + } + + private void assertMachineUsage(Iterable usages, String appId, String machineId, List states, Calendar pre, Calendar post) throws Exception { + assertUsage(usages, appId, machineId, states, pre, post, false); + } + + private void assertAppUsage(UsageStatistics usage, String appId, List states, Calendar pre, Calendar post) throws Exception { + assertUsage(usage.getStatistics(), appId, appId, states, pre, post, false); + } + + private void assertAppUsage(Iterable usages, String appId, List states, Calendar pre, Calendar post) throws Exception { + assertUsage(usages, appId, appId, states, pre, post, false); + } + + private void assertUsage(Iterable usages, String appId, String id, List states, Calendar pre, Calendar post, boolean allowGaps) throws Exception { + String errMsg = "usages="+usages; + Calendar now = new GregorianCalendar(); + Calendar lowerBound = pre; + Calendar strictStart = null; + + assertEquals(Iterables.size(usages), states.size(), errMsg); + for (int i = 0; i < Iterables.size(usages); i++) { + UsageStatistic usage = Iterables.get(usages, i); + Calendar usageStart = Time.parseCalendar(usage.getStart()); + Calendar usageEnd = Time.parseCalendar(usage.getEnd()); + assertEquals(usage.getId(), id, errMsg); + assertEquals(usage.getApplicationId(), appId, errMsg); + assertEquals(usage.getStatus(), states.get(i), errMsg); + assertCalendarOrders(usages, lowerBound, usageStart, post); + assertCalendarOrders(usages, usageEnd, now); + if (strictStart != null) { + assertEquals(usageStart, strictStart, errMsg); + } + if (!allowGaps) { + strictStart = usageEnd; + } + lowerBound = usageEnd; + } + } + + private void assertAppUsageTimesTruncated(UsageStatistics usages, Calendar strictStart, Calendar strictEnd) throws Exception { + String errMsg = "strictStart="+Time.makeDateString(strictStart)+"; strictEnd="+Time.makeDateString(strictEnd)+";usages="+usages; + Calendar usageStart = Time.parseCalendar(Iterables.getFirst(usages.getStatistics(), null).getStart()); + Calendar usageEnd = Time.parseCalendar(Iterables.getLast(usages.getStatistics()).getStart()); + // time zones might be different - so must convert to date + assertEquals(usageStart.getTime(), strictStart.getTime(), "usageStart="+Time.makeDateString(usageStart)+";"+errMsg); + assertEquals(usageEnd.getTime(), strictEnd.getTime(), errMsg); + } + + public static class DynamicLocalhostMachineProvisioningLocation extends LocalhostMachineProvisioningLocation { + private static final long serialVersionUID = 2163357613938738967L; + + @Override + public SshMachineLocation obtain(Map flags) throws NoMachinesAvailableException { + return super.obtain(flags); + } + + @Override + public void release(SshMachineLocation machine) { + super.release(machine); + super.machines.remove(machine); + getManagementContext().getLocationManager().unmanage(machine); + } + } + + private void waitForFuture(long futureTime) throws InterruptedException { + long now; + while ((now = System.currentTimeMillis()) < futureTime) { + Thread.sleep(futureTime - now); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/VersionResourceTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/VersionResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/VersionResourceTest.java new file mode 100644 index 0000000..e93dcfb --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/VersionResourceTest.java @@ -0,0 +1,50 @@ +/* + * 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 org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import javax.ws.rs.core.Response; + +import org.testng.annotations.Test; + +import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; + +import com.sun.jersey.api.client.ClientResponse; + +public class VersionResourceTest extends BrooklynRestResourceTest { + + @Test + public void testGetVersion() { + ClientResponse response = client().resource("/v1/version") + .get(ClientResponse.class); + + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + String version = response.getEntity(String.class); + + assertTrue(version.matches("^\\d+\\.\\d+\\.\\d+.*")); + } + + @SuppressWarnings("deprecation") + @Override + protected void addBrooklynResources() { + addResource(new VersionResource()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/security/PasswordHasherTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/security/PasswordHasherTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/security/PasswordHasherTest.java new file mode 100644 index 0000000..575d6a4 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/security/PasswordHasherTest.java @@ -0,0 +1,37 @@ +/* + * 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.security; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +public class PasswordHasherTest { + + @Test + public void testHashSha256() throws Exception { + // Note: expected hash values generated externally: + // echo -n mysaltmypassword | openssl dgst -sha256 + + assertEquals(PasswordHasher.sha256("mysalt", "mypassword"), "d02878b06efa88579cd84d9e50b211c0a7caa92cf243bad1622c66081f7e2692"); + assertEquals(PasswordHasher.sha256("", "mypassword"), "89e01536ac207279409d4de1e5253e01f4a1769e696db0d6062ca9b8f56767c8"); + assertEquals(PasswordHasher.sha256(null, "mypassword"), "89e01536ac207279409d4de1e5253e01f4a1769e696db0d6062ca9b8f56767c8"); + } + +}