http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2aac052f/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceTest.java ---------------------------------------------------------------------- diff --cc usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceTest.java index 0000000,f33820d..a323496 mode 000000,100644..100644 --- 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 @@@ -1,0 -1,272 +1,273 @@@ + /* + * 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.apache.brooklyn.test.HttpTestUtils; + 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 org.apache.brooklyn.rest.api.SensorApi; + import org.apache.brooklyn.rest.domain.ApplicationSpec; + import org.apache.brooklyn.rest.domain.EntitySpec; + import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; + import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity; -import brooklyn.test.HttpTestUtils; ++import org.apache.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. + * <p> + * 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<Integer> SENSOR = Sensors.newIntegerSensor(SENSOR_NAME); + + EntityInternal entity; + + /** + * Sets up the application and entity. + * <p> + * 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<String, ?> currentState = response.getEntity(new GenericType<Map<String,?>>(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<String, ?> currentState = response.getEntity(new GenericType<Map<String,?>>(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/2aac052f/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceIntegrationTest.java ---------------------------------------------------------------------- diff --cc usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceIntegrationTest.java index 0000000,1b29fad..7f70971 mode 000000,100644..100644 --- 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 @@@ -1,0 -1,126 +1,127 @@@ + /* + * 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.brooklyn.test.HttpTestUtils; + 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 org.apache.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<String, String> 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.<String, String>of()); + HttpTestUtils.assertHealthyStatusCode(response.getResponseCode()); + return response.getContentAsString(); + } + + } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2aac052f/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerShutdownTest.java ---------------------------------------------------------------------- diff --cc usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ServerShutdownTest.java index 0000000,d38b380..a74f22a mode 000000,100644..100644 --- 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 @@@ -1,0 -1,187 +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.apache.brooklyn.test.EntityTestUtils; + 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<String, String> 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<TestApplication> appSpec = EntitySpec.create(TestApplication.class); + TestApplication app = emgr.createEntity(appSpec); + emgr.manage(app); + EntitySpec<StopLatchEntity> 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<Void> firstStop = app.invoke(Startable.STOP, ImmutableMap.<String, Object>of()); + Asserts.succeedsEventually(new Runnable() { + @Override + public void run() { + assertTrue(entity.isBlocked()); + } + }); + + final AtomicReference<Exception> shutdownError = new AtomicReference<>(); + // Can't use ExecutionContext as it will be stopped on shutdown + Thread shutdownThread = new Thread() { + @Override + public void run() { + try { + MultivaluedMap<String, String> 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(); + } + } + + }
