http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java new file mode 100644 index 0000000..21fd246 --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java @@ -0,0 +1,444 @@ +/* + * 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 org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.api.location.NoMachinesAvailableException; +import org.apache.brooklyn.core.mgmt.internal.LocalUsageManager; +import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; +import org.apache.brooklyn.core.test.entity.TestApplication; +import org.apache.brooklyn.entity.software.base.SoftwareProcessEntityTest; +import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation; +import org.apache.brooklyn.location.ssh.SshMachineLocation; +import org.apache.brooklyn.rest.domain.ApplicationSpec; +import org.apache.brooklyn.rest.domain.Status; +import org.apache.brooklyn.rest.domain.TaskSummary; +import org.apache.brooklyn.rest.domain.UsageStatistic; +import org.apache.brooklyn.rest.domain.UsageStatistics; +import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; +import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity; +import org.apache.brooklyn.util.repeat.Repeater; +import org.apache.brooklyn.util.time.Time; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import javax.ws.rs.core.GenericType; + +@Test( // by using a different suite name we disallow interleaving other tests between the methods of this test class, which wrecks the test fixtures + suiteName = "UsageResourceTest") +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 org.apache.brooklyn.rest.domain.EntitySpec("simple-ent", RestMockSimpleEntity.class.getName()))). + locations(ImmutableSet.of("localhost")). + build(); + + @BeforeMethod(alwaysRun=true) + public void setUp() { + ((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 + Response response = client().path("/usage/applications").get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + Iterable<UsageStatistics> usages = response.readEntity(new GenericType<List<UsageStatistics>>() {}); + 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().path("/usage/applications").query("start", 0).query("end", preStart.getTime().getTime()-1).get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usages = response.readEntity(new GenericType<List<UsageStatistics>>() {}); + 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().path("/usage/applications").query("start", afterPostStart.getTime().getTime()).query("end", afterPostStart.getTime().getTime()).get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usages = response.readEntity(new GenericType<List<UsageStatistics>>() {}); + 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().path("/usage/applications").get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usages = response.readEntity(new GenericType<List<UsageStatistics>>() {}); + 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().path("/usage/applications").query("start", afterPostDelete).get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usages = response.readEntity(new GenericType<List<UsageStatistics>>() {}); + assertTrue(Iterables.isEmpty(usages), "usages="+usages); + } + + @Test + public void testGetApplicationUsagesForNonExistantApp() throws Exception { + Response response = client().path("/usage/applications/wrongid").get(); + 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 + Response response = client().path("/usage/applications/" + appId).get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + UsageStatistics usage = response.readEntity(new GenericType<UsageStatistics>() {}); + assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart); + + // Time-constrained requests + response = client().path("/usage/applications/" + appId).query("start", "1970-01-01T00:00:00-0100").get(); + usage = response.readEntity(new GenericType<UsageStatistics>() {}); + assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart); + + response = client().path("/usage/applications/" + appId).query("start", "9999-01-01T00:00:00+0100").get(); + assertTrue(response.getStatus() >= 400, "end defaults to NOW, so future start should fail, instead got code "+response.getStatus()); + + response = client().path("/usage/applications/" + appId).query("start", "9999-01-01T00:00:00%2B0100").query("end", "9999-01-02T00:00:00%2B0100").get(); + usage = response.readEntity(new GenericType<UsageStatistics>() {}); + assertTrue(usage.getStatistics().isEmpty()); + + response = client().path("/usage/applications/" + appId).query("end", "9999-01-01T00:00:00+0100").get(); + usage = response.readEntity(new GenericType<UsageStatistics>() {}); + assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart); + + response = client().path("/usage/applications/" + appId).query("start", "9999-01-01T00:00:00+0100").query("end", "9999-02-01T00:00:00+0100").get(); + usage = response.readEntity(new GenericType<UsageStatistics>() {}); + assertTrue(usage.getStatistics().isEmpty()); + + response = client().path("/usage/applications/" + appId).query("start", "1970-01-01T00:00:00-0100").query("end", "9999-01-01T00:00:00+0100").get(); + usage = response.readEntity(new GenericType<UsageStatistics>() {}); + assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart); + + response = client().path("/usage/applications/" + appId).query("end", "1970-01-01T00:00:00-0100").get(); + usage = response.readEntity(new GenericType<UsageStatistics>() {}); + 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().path("/usage/applications/" + appId).get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usage = response.readEntity(new GenericType<UsageStatistics>() {}); + 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().path("/usage/applications/" + appId).query("start", afterPostDelete).get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usage = response.readEntity(new GenericType<UsageStatistics>() {}); + assertTrue(usage.getStatistics().isEmpty(), "usages="+usage); + } + + @Test + public void testGetMachineUsagesForNonExistantMachine() throws Exception { + Response response = client().path("/usage/machines/wrongid").get(); + assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode()); + } + + @Test + public void testGetMachineUsagesInitiallyEmpty() throws Exception { + // All machines: empty + Response response = client().path("/usage/machines").get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + Iterable<UsageStatistics> usages = response.readEntity(new GenericType<List<UsageStatistics>>() {}); + assertTrue(Iterables.isEmpty(usages)); + + // Specific machine that does not exist: get 404 + response = client().path("/usage/machines/machineIdThatDoesNotExist").get(); + 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 = getManagementContext().getEntityManager().createEntity(EntitySpec.create(TestApplication.class)); + SoftwareProcessEntityTest.MyService entity = app.createAndManageChild(org.apache.brooklyn.api.entity.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 + Response response = client().path("/usage/machines").get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + Iterable<UsageStatistics> usages = response.readEntity(new GenericType<List<UsageStatistics>>() {}); + UsageStatistics usage = Iterables.getOnlyElement(usages); + assertMachineUsage(usage, app.getId(), machine.getId(), ImmutableList.of(Status.ACCEPTED), roundDown(preStart), postStart); + + // Specific machine + response = client().path("/usage/machines/"+machine.getId()).get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usage = response.readEntity(new GenericType<UsageStatistics>() {}); + 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 = getManagementContext().getEntityManager().createEntity(EntitySpec.create(TestApplication.class)); + SoftwareProcessEntityTest.MyService entity = app.createAndManageChild(org.apache.brooklyn.api.entity.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 + Response response = client().path("/usage/machines").query("application", appId).get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + Iterable<UsageStatistics> usages = response.readEntity(new GenericType<List<UsageStatistics>>() {}); + 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().path("/usage/machines").query("application", appId).get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usages = response.readEntity(new GenericType<List<UsageStatistics>>() {}); + 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().path("/usage/applications").query("start", futureTime).get(); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + usages = response.readEntity(new GenericType<List<UsageStatistics>>() {}); + assertTrue(Iterables.isEmpty(usages), "usages="+usages); + } + + private String createApp(ApplicationSpec spec) { + Response response = clientDeploy(spec); + assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode()); + TaskSummary createTask = response.readEntity(TaskSummary.class); + waitForTask(createTask.getId()); + return createTask.getEntityId(); + } + + private void deleteApp(String appId) { + Response response = client().path("/applications/"+appId) + .delete(); + assertEquals(response.getStatus(), Response.Status.ACCEPTED.getStatusCode()); + TaskSummary deletionTask = response.readEntity(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<Boolean>() { + @Override public Boolean call() { + Response response = client().path("/activities/"+taskId).get(); + if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) { + return true; + } + TaskSummary summary = response.readEntity(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<Status> states, Calendar pre, Calendar post) throws Exception { + assertUsage(usage.getStatistics(), appId, machineId, states, pre, post, false); + } + + private void assertMachineUsage(Iterable<UsageStatistic> usages, String appId, String machineId, List<Status> states, Calendar pre, Calendar post) throws Exception { + assertUsage(usages, appId, machineId, states, pre, post, false); + } + + private void assertAppUsage(UsageStatistics usage, String appId, List<Status> states, Calendar pre, Calendar post) throws Exception { + assertUsage(usage.getStatistics(), appId, appId, states, pre, post, false); + } + + private void assertAppUsage(Iterable<UsageStatistic> usages, String appId, List<Status> states, Calendar pre, Calendar post) throws Exception { + assertUsage(usages, appId, appId, states, pre, post, false); + } + + private void assertUsage(Iterable<UsageStatistic> usages, String appId, String id, List<Status> 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 { + @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/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/VersionResourceTest.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/VersionResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/VersionResourceTest.java new file mode 100644 index 0000000..fad05cc --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/VersionResourceTest.java @@ -0,0 +1,46 @@ +/* + * 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; + +@Test(singleThreaded = true, + // by using a different suite name we disallow interleaving other tests between the methods of this test class, which wrecks the test fixtures + suiteName = "VersionResourceTest") +public class VersionResourceTest extends BrooklynRestResourceTest { + + @Test + public void testGetVersion() { + Response response = client().path("/version") + .get(); + + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + String version = response.readEntity(String.class); + + assertTrue(version.matches("^\\d+\\.\\d+\\.\\d+.*")); + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/security/PasswordHasherTest.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/security/PasswordHasherTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/security/PasswordHasherTest.java new file mode 100644 index 0000000..575d6a4 --- /dev/null +++ b/rest/rest-resources/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"); + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/test/config/render/TestRendererHints.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/test/config/render/TestRendererHints.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/test/config/render/TestRendererHints.java new file mode 100644 index 0000000..91294db --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/test/config/render/TestRendererHints.java @@ -0,0 +1,36 @@ +/* + * 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.test.config.render; + +import org.apache.brooklyn.core.config.render.RendererHints; + +/** Methods used when testing the {@link RendererHints} regiostry. */ +public class TestRendererHints { + + /** Clear the registry. + * + * MUST be used by a single test only. + * TestNG interleaves the tests (sequentially) which results in tearDown + * executing in the middle of another class' tests. Only one tearDown may + * call this method. + **/ + public static void clearRegistry() { + RendererHints.getRegistry().clear(); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/test/entity/brooklynnode/DeployBlueprintTest.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/test/entity/brooklynnode/DeployBlueprintTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/test/entity/brooklynnode/DeployBlueprintTest.java new file mode 100644 index 0000000..0113d39 --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/test/entity/brooklynnode/DeployBlueprintTest.java @@ -0,0 +1,97 @@ +/* + * 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.test.entity.brooklynnode; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; +import java.util.List; +import java.util.Map; + +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.mgmt.EntityManager; +import org.apache.brooklyn.entity.brooklynnode.BrooklynNode; +import org.apache.brooklyn.entity.brooklynnode.BrooklynNode.DeployBlueprintEffector; +import org.apache.brooklyn.entity.stock.BasicApplication; +import org.apache.brooklyn.feed.http.JsonFunctions; +import org.apache.brooklyn.test.HttpTestUtils; +import org.apache.brooklyn.util.guava.Functionals; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; + +public class DeployBlueprintTest extends BrooklynRestResourceTest { + + @Override + protected boolean useLocalScannedCatalog() { + return true; + } + + @Override + protected String getEndpointAddress() { + return ENDPOINT_ADDRESS_HTTP + "v1"; + } + + private static final Logger log = LoggerFactory.getLogger(DeployBlueprintTest.class); +// +// Server server; +// +// @BeforeMethod(alwaysRun=true) +// public void setUp() throws Exception { +// server = newServer(); +// useServerForTest(server); +// } +// + @Test + public void testStartsAppViaEffector() throws Exception { + URI webConsoleUri = URI.create(ENDPOINT_ADDRESS_HTTP); // BrooklynNode will append "/v1" to it + + EntitySpec<BrooklynNode> spec = EntitySpec.create(BrooklynNode.class); + EntityManager mgr = getManagementContext().getEntityManager(); // getManagementContextFromJettyServerAttributes(server).getEntityManager(); + BrooklynNode node = mgr.createEntity(spec); + node.sensors().set(BrooklynNode.WEB_CONSOLE_URI, webConsoleUri); + mgr.manage(node); + Map<String, String> params = ImmutableMap.of(DeployBlueprintEffector.BLUEPRINT_CAMP_PLAN.getName(), "{ services: [ serviceType: \"java:"+BasicApplication.class.getName()+"\" ] }"); + String id = node.invoke(BrooklynNode.DEPLOY_BLUEPRINT, params).getUnchecked(); + + log.info("got: "+id); + + String apps = HttpTestUtils.getContent(getEndpointAddress() + "/applications"); + List<String> appType = parseJsonList(apps, ImmutableList.of("spec", "type"), String.class); + assertEquals(appType, ImmutableList.of(BasicApplication.class.getName())); + + String status = HttpTestUtils.getContent(getEndpointAddress()+"/applications/"+id+"/entities/"+id+"/sensors/service.status"); + log.info("STATUS: "+status); + } + + private <T> List<T> parseJsonList(String json, List<String> elements, Class<T> clazz) { + Function<String, List<T>> func = Functionals.chain( + JsonFunctions.asJson(), + JsonFunctions.forEach(Functionals.chain( + JsonFunctions.walk(elements), + JsonFunctions.cast(clazz)))); + return func.apply(json); + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java new file mode 100644 index 0000000..853afb0 --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java @@ -0,0 +1,223 @@ +/* + * 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.testing; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.brooklyn.api.location.LocationRegistry; +import org.apache.brooklyn.api.mgmt.ManagementContext; +import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.internal.BrooklynProperties; +import org.apache.brooklyn.core.location.BasicLocationRegistry; +import org.apache.brooklyn.core.mgmt.ManagementContextInjectable; +import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext; +import org.apache.brooklyn.core.server.BrooklynServerConfig; +import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; +import org.apache.brooklyn.rest.BrooklynRestApi; +import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils; +import org.apache.brooklyn.rest.util.ManagementContextProvider; +import org.apache.brooklyn.rest.util.ShutdownHandlerProvider; +import org.apache.brooklyn.rest.util.TestShutdownHandler; +import org.apache.brooklyn.rest.util.json.BrooklynJacksonJsonProvider; +import org.apache.cxf.jaxrs.client.JAXRSClientFactory; +import org.reflections.util.ClasspathHelper; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public abstract class BrooklynRestApiTest { + + public static final String SCANNING_CATALOG_BOM_URL = "classpath://brooklyn/scanning.catalog.bom"; + + protected ManagementContext manager; + + + protected TestShutdownHandler shutdownListener = createShutdownHandler(); + protected final static String ENDPOINT_ADDRESS_LOCAL = "local://"; + protected final static String ENDPOINT_ADDRESS_HTTP = "http://localhost:9998/"; + + protected Set<Class<?>> resourceClasses; + protected Set<Object> resourceBeans; + + @BeforeClass(alwaysRun = true) + public void setUpClass() throws Exception { + if (!isMethodInit()) { + initClass(); + } + } + + @AfterClass(alwaysRun = true) + public void tearDownClass() throws Exception { + if (!isMethodInit()) { + destroyClass(); + } + } + + @BeforeMethod(alwaysRun = true) + public void setUpMethod() throws Exception { + if (isMethodInit()) { + initClass(); + } + initMethod(); + } + + @AfterMethod(alwaysRun = true) + public void tearDownMethod() throws Exception { + if (isMethodInit()) { + destroyClass(); + } + destroyMethod(); + } + + protected void initClass() throws Exception { + resourceClasses = new HashSet<>(); + resourceBeans = new HashSet<>(); + } + + protected void destroyClass() throws Exception { + destroyManagementContext(); + resourceClasses = null; + resourceBeans = null; + } + + protected void initMethod() throws Exception { + resetShutdownListener(); + } + + protected void destroyMethod() throws Exception { + } + + /** + * @return true to start/destroy the test environment for each method. + * Returns false by default to speed up testing. + */ + protected boolean isMethodInit() { + return false; + } + + protected void resetShutdownListener() { + shutdownListener.reset(); + } + + protected void destroyManagementContext() { + if (manager!=null) { + Entities.destroyAll(manager); + resourceClasses = null; + resourceBeans = null; + manager = null; + } + } + + protected boolean useLocalScannedCatalog() { + return false; + } + + private TestShutdownHandler createShutdownHandler() { + return new TestShutdownHandler(); + } + + protected synchronized ManagementContext getManagementContext() { + if (manager==null) { + if (useLocalScannedCatalog()) { + manager = new LocalManagementContext(); + forceUseOfDefaultCatalogWithJavaClassPath(); + } else { + manager = new LocalManagementContextForTests(); + } + manager.getHighAvailabilityManager().disabled(); + BasicLocationRegistry.setupLocationRegistryForTesting(manager); + + new BrooklynCampPlatformLauncherNoServer() + .useManagementContext(manager) + .launch(); + } + return manager; + } + + protected String getEndpointAddress() { + return ENDPOINT_ADDRESS_HTTP; + } + + protected ObjectMapper mapper() { + return BrooklynJacksonJsonProvider.findSharedObjectMapper(null, getManagementContext()); + } + + public LocationRegistry getLocationRegistry() { + return new BrooklynRestResourceUtils(getManagementContext()).getLocationRegistry(); + } + + protected final void addResource(Object resource) { + if (resource instanceof Class) { + resourceClasses.add((Class<?>)resource); + } else { + resourceBeans.add(resource); + } + if (resource instanceof ManagementContextInjectable) { + ((ManagementContextInjectable)resource).setManagementContext(getManagementContext()); + } + } + + protected final void addProvider(Class<?> provider) { + addResource(provider); + } + + protected void addDefaultResources() { + addResource(new ShutdownHandlerProvider(shutdownListener)); + addResource(new ManagementContextProvider(getManagementContext())); + } + + + /** intended for overriding if you only want certain resources added, or additional ones added */ + protected void addBrooklynResources() { + for (Object r: BrooklynRestApi.getBrooklynRestResources()) + addResource(r); + } + + protected final void setUpResources() { + addDefaultResources(); + addBrooklynResources(); + for (Object r: BrooklynRestApi.getMiscResources()) + addResource(r); + } + + public <T> T resource(Class<T> clazz) { + return JAXRSClientFactory.create(getEndpointAddress(), clazz); + } + + public <T> T resource(String uri, Class<T> clazz) { + return JAXRSClientFactory.create(getEndpointAddress() + uri, clazz); + } + + private void forceUseOfDefaultCatalogWithJavaClassPath() { + // don't use any catalog.xml which is set + ((BrooklynProperties)manager.getConfig()).put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, SCANNING_CATALOG_BOM_URL); + // sets URLs for a surefire + ((LocalManagementContext)manager).setBaseClassPathForScanning(ClasspathHelper.forJavaClassPath()); + // this also works +// ((LocalManagementContext)manager).setBaseClassPathForScanning(ClasspathHelper.forPackage("brooklyn")); + // but this (near-default behaviour) does not +// ((LocalManagementContext)manager).setBaseClassLoader(getClass().getClassLoader()); + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java new file mode 100644 index 0000000..505bb58 --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java @@ -0,0 +1,212 @@ +/* + * 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.testing; + +import static org.testng.Assert.assertTrue; + +import java.io.IOException; +import java.net.URI; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.brooklyn.api.entity.Application; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.rest.domain.ApplicationSpec; +import org.apache.brooklyn.rest.domain.ApplicationSummary; +import org.apache.brooklyn.rest.domain.Status; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.repeat.Repeater; +import org.apache.brooklyn.util.time.Duration; +import org.apache.cxf.endpoint.Server; +import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; +import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.jaxrs.utils.ResourceUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; + +public abstract class BrooklynRestResourceTest extends BrooklynRestApiTest { + + private static final Logger log = LoggerFactory.getLogger(BrooklynRestResourceTest.class); + + private static Server server; + protected List<?> clientProviders; + + class DefaultTestApp extends javax.ws.rs.core.Application { + @Override + public Set<Class<?>> getClasses() { + return resourceClasses; + } + + @Override + public Set<Object> getSingletons() { + return resourceBeans; + } + + }; + + @Override + public void initClass() throws Exception { + super.initClass(); + startServer(); + } + + @Override + public void destroyClass() throws Exception { + stopServer(); + super.destroyClass(); + } + + protected synchronized void startServer() throws Exception { + if (server == null) { + setUpResources(); + JAXRSServerFactoryBean sf = ResourceUtils.createApplication(createRestApp(), true); + if (clientProviders == null) { + clientProviders = sf.getProviders(); + } + configureCXF(sf); + sf.setAddress(getEndpointAddress()); + sf.setFeatures(ImmutableList.of(new org.apache.cxf.feature.LoggingFeature())); + server = sf.create(); + } + } + + private javax.ws.rs.core.Application createRestApp() { + return new DefaultTestApp(); + } + + /** Allows subclasses to customize the CXF server bean. */ + protected void configureCXF(JAXRSServerFactoryBean sf) { + } + + public synchronized void stopServer() throws Exception { + if (server != null) { + server.stop(); + server.destroy(); + server = null; + } + } + + + protected Response clientDeploy(ApplicationSpec spec) { + try { + // dropwizard TestClient won't skip deserialization of trivial things like string and byte[] and inputstream + // if we pass in an object it serializes, so we have to serialize things ourselves + return client().path("/applications") + .post(Entity.entity(new ObjectMapper().writer().writeValueAsBytes(spec), MediaType.APPLICATION_OCTET_STREAM)); + } catch (Exception e) { + throw Exceptions.propagate(e); + } + } + + protected void waitForApplicationToBeRunning(final URI applicationRef) { + waitForApplicationToBeRunning(applicationRef, Duration.minutes(3)); + } + protected void waitForApplicationToBeRunning(final URI applicationRef, Duration timeout) { + if (applicationRef==null) + throw new NullPointerException("No application URI available (consider using BrooklynRestResourceTest.clientDeploy)"); + + boolean started = Repeater.create("Wait for application startup") + .until(new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + Status status = getApplicationStatus(applicationRef); + if (status == Status.ERROR) { + Assert.fail("Application failed with ERROR"); + } + return status == Status.RUNNING; + } + }) + .backoffTo(Duration.ONE_SECOND) + .limitTimeTo(timeout) + .run(); + + if (!started) { + log.warn("Did not start application "+applicationRef+":"); + Collection<Application> apps = getManagementContext().getApplications(); + for (Application app: apps) + Entities.dumpInfo(app); + } + assertTrue(started); + } + + protected Status getApplicationStatus(URI uri) { + return client().path(uri).get(ApplicationSummary.class).getStatus(); + } + + protected void waitForPageFoundResponse(final String resource, final Class<?> clazz) { + boolean found = Repeater.create("Wait for page found") + .until(new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + try { + client().path(resource).get(clazz); + return true; + } catch (WebApplicationException e) { + return false; + } + } + }) + .every(1, TimeUnit.SECONDS) + .limitTimeTo(30, TimeUnit.SECONDS) + .run(); + assertTrue(found); + } + + protected void waitForPageNotFoundResponse(final String resource, final Class<?> clazz) { + boolean success = Repeater.create("Wait for page not found") + .until(new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + try { + client().path(resource).get(clazz); + return false; + } catch (WebApplicationException e) { + return e.getResponse().getStatus() == 404; + } + } + }) + .every(1, TimeUnit.SECONDS) + .limitTimeTo(30, TimeUnit.SECONDS) + .run(); + assertTrue(success); + } + + protected static Entity<byte[]> toJsonEntity(Object obj) throws IOException { + // TODO: figure out how to have CXF actually send empty maps instead of replacing them with nulls without this workaround + // see cxf's AbstractClient.checkIfBodyEmpty + return Entity.entity(new ObjectMapper().writer().writeValueAsBytes(obj), MediaType.APPLICATION_JSON); + } + + public WebClient client() { + return WebClient.create(getEndpointAddress(), clientProviders); + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java new file mode 100644 index 0000000..7d80a6f --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java @@ -0,0 +1,33 @@ +/* + * 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.testing.mocks; + +import org.apache.brooklyn.api.entity.EntityLocal; +import org.apache.brooklyn.core.policy.AbstractPolicy; + +@SuppressWarnings("deprecation") +public class CapitalizePolicy extends AbstractPolicy { + + @Override + public void setEntity(EntityLocal entity) { + super.setEntity(entity); + // TODO subscribe to foo and emit an enriched sensor on different channel which is capitalized + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroup.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroup.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroup.java new file mode 100644 index 0000000..707c4a3 --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroup.java @@ -0,0 +1,27 @@ +/* + * 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.testing.mocks; + +import org.apache.brooklyn.api.entity.Group; +import org.apache.brooklyn.api.entity.ImplementedBy; + +@ImplementedBy(EverythingGroupImpl.class) +public interface EverythingGroup extends Group { + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroupImpl.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroupImpl.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroupImpl.java new file mode 100644 index 0000000..8b2c98b --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroupImpl.java @@ -0,0 +1,32 @@ +/* + * 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.testing.mocks; + +import org.apache.brooklyn.entity.group.DynamicGroupImpl; + +import com.google.common.base.Predicates; + +public class EverythingGroupImpl extends DynamicGroupImpl implements EverythingGroup { + + public EverythingGroupImpl() { + super(); + config().set(ENTITY_FILTER, Predicates.alwaysTrue()); + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroup.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroup.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroup.java new file mode 100644 index 0000000..f9a2e21 --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroup.java @@ -0,0 +1,30 @@ +/* + * 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.testing.mocks; + +import org.apache.brooklyn.api.entity.Group; +import org.apache.brooklyn.api.entity.ImplementedBy; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; + +@ImplementedBy(NameMatcherGroupImpl.class) +public interface NameMatcherGroup extends Group { + + public static final ConfigKey<String> NAME_REGEX = ConfigKeys.newStringConfigKey("namematchergroup.regex"); +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroupImpl.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroupImpl.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroupImpl.java new file mode 100644 index 0000000..bec416f --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroupImpl.java @@ -0,0 +1,33 @@ +/* + * 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.testing.mocks; + +import org.apache.brooklyn.core.entity.EntityPredicates; +import org.apache.brooklyn.entity.group.DynamicGroupImpl; +import org.apache.brooklyn.util.text.StringPredicates; + +public class NameMatcherGroupImpl extends DynamicGroupImpl implements NameMatcherGroup { + + @Override + public void init() { + super.init(); + config().set(ENTITY_FILTER, EntityPredicates.displayNameSatisfies(StringPredicates.matchesRegex(getConfig(NAME_REGEX)))); + rescanEntities(); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockApp.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockApp.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockApp.java new file mode 100644 index 0000000..6d92e65 --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockApp.java @@ -0,0 +1,24 @@ +/* + * 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.testing.mocks; + +import org.apache.brooklyn.core.entity.AbstractApplication; + +public class RestMockApp extends AbstractApplication { +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockAppBuilder.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockAppBuilder.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockAppBuilder.java new file mode 100644 index 0000000..1ca10bd --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockAppBuilder.java @@ -0,0 +1,39 @@ +/* + * 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.testing.mocks; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.core.entity.StartableApplication; +import org.apache.brooklyn.core.entity.factory.ApplicationBuilder; +import org.apache.brooklyn.util.javalang.Reflections; + +public class RestMockAppBuilder extends ApplicationBuilder { + + public RestMockAppBuilder() { + super(EntitySpec.create(StartableApplication.class).impl(RestMockApp.class)); + } + + @Override + protected void doBuild() { + addChild(EntitySpec.create(Entity.class).impl(RestMockSimpleEntity.class) + .additionalInterfaces(Reflections.getAllInterfaces(RestMockSimpleEntity.class)) + .displayName("child1")); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java new file mode 100644 index 0000000..58d24aa --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java @@ -0,0 +1,103 @@ +/* + * 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.testing.mocks; + +import java.util.Map; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.EntityLocal; +import org.apache.brooklyn.api.sensor.AttributeSensor; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.annotation.Effector; +import org.apache.brooklyn.core.annotation.EffectorParam; +import org.apache.brooklyn.core.config.BasicConfigKey; +import org.apache.brooklyn.core.effector.MethodEffector; +import org.apache.brooklyn.core.sensor.BasicAttributeSensor; +import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver; +import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.brooklyn.location.ssh.SshMachineLocation; +import org.apache.brooklyn.util.core.flags.SetFromFlag; + +public class RestMockSimpleEntity extends SoftwareProcessImpl { + + private static final Logger log = LoggerFactory.getLogger(RestMockSimpleEntity.class); + + public RestMockSimpleEntity() { + super(); + } + + public RestMockSimpleEntity(Entity parent) { + super(parent); + } + + public RestMockSimpleEntity(@SuppressWarnings("rawtypes") Map flags, Entity parent) { + super(flags, parent); + } + + public RestMockSimpleEntity(@SuppressWarnings("rawtypes") Map flags) { + super(flags); + } + + @Override + protected void connectSensors() { + super.connectSensors(); + connectServiceUpIsRunning(); + } + + @SetFromFlag("sampleConfig") + public static final ConfigKey<String> SAMPLE_CONFIG = new BasicConfigKey<String>( + String.class, "brooklyn.rest.mock.sample.config", "Mock sample config", "DEFAULT_VALUE"); + + public static final AttributeSensor<String> SAMPLE_SENSOR = new BasicAttributeSensor<String>( + String.class, "brooklyn.rest.mock.sample.sensor", "Mock sample sensor"); + + public static final MethodEffector<String> SAMPLE_EFFECTOR = new MethodEffector<String>(RestMockSimpleEntity.class, "sampleEffector"); + + @Effector + public String sampleEffector(@EffectorParam(name="param1", description="param one") String param1, + @EffectorParam(name="param2") Integer param2) { + log.info("Invoked sampleEffector("+param1+","+param2+")"); + String result = ""+param1+param2; + sensors().set(SAMPLE_SENSOR, result); + return result; + } + + @SuppressWarnings("rawtypes") + @Override + public Class getDriverInterface() { + return MockSshDriver.class; + } + + public static class MockSshDriver extends AbstractSoftwareProcessSshDriver { + public MockSshDriver(EntityLocal entity, SshMachineLocation machine) { + super(entity, machine); + } + public boolean isRunning() { return true; } + public void stop() {} + public void kill() {} + public void install() {} + public void customize() {} + public void launch() {} + public void setup() { } + public void copyInstallResources() { } + public void copyRuntimeResources() { } + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java new file mode 100644 index 0000000..e15cdd1 --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java @@ -0,0 +1,64 @@ +/* + * 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.testing.mocks; + +import java.util.Map; + +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.BasicConfigKey; +import org.apache.brooklyn.core.policy.AbstractPolicy; +import org.apache.brooklyn.util.core.flags.SetFromFlag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RestMockSimplePolicy extends AbstractPolicy { + + @SuppressWarnings("unused") + private static final Logger log = LoggerFactory.getLogger(RestMockSimplePolicy.class); + + public RestMockSimplePolicy() { + super(); + } + + @SuppressWarnings("rawtypes") + public RestMockSimplePolicy(Map flags) { + super(flags); + } + + @SetFromFlag("sampleConfig") + public static final ConfigKey<String> SAMPLE_CONFIG = BasicConfigKey.builder(String.class) + .name("brooklyn.rest.mock.sample.config") + .description("Mock sample config") + .defaultValue("DEFAULT_VALUE") + .reconfigurable(true) + .build(); + + @SetFromFlag + public static final ConfigKey<Integer> INTEGER_CONFIG = BasicConfigKey.builder(Integer.class) + .name("brooklyn.rest.mock.sample.integer") + .description("Mock integer config") + .defaultValue(1) + .reconfigurable(true) + .build(); + + @Override + protected <T> void doReconfigureConfig(ConfigKey<T> key, T val) { + // no-op + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java new file mode 100644 index 0000000..48908e3 --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java @@ -0,0 +1,213 @@ +/* + * 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.util; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.util.Map; + +import org.apache.brooklyn.api.catalog.Catalog; +import org.apache.brooklyn.api.entity.Application; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.policy.Policy; +import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder; +import org.apache.brooklyn.core.catalog.internal.CatalogTemplateItemDto; +import org.apache.brooklyn.core.catalog.internal.CatalogUtils; +import org.apache.brooklyn.core.entity.AbstractApplication; +import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext; +import org.apache.brooklyn.core.objs.proxy.EntityProxy; +import org.apache.brooklyn.core.policy.AbstractPolicy; +import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; +import org.apache.brooklyn.core.test.entity.TestApplication; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.apache.brooklyn.entity.stock.BasicEntity; +import org.apache.brooklyn.rest.domain.ApplicationSpec; +import org.apache.brooklyn.rest.domain.EntitySpec; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; + +public class BrooklynRestResourceUtilsTest { + + private LocalManagementContext managementContext; + private BrooklynRestResourceUtils util; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + managementContext = LocalManagementContextForTests.newInstance(); + util = new BrooklynRestResourceUtils(managementContext); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + if (managementContext != null) managementContext.terminate(); + } + + @Test + public void testCreateAppFromImplClass() { + ApplicationSpec spec = ApplicationSpec.builder() + .name("myname") + .type(SampleNoOpApplication.class.getName()) + .locations(ImmutableSet.of("localhost")) + .build(); + Application app = util.create(spec); + + assertEquals(ImmutableList.copyOf(managementContext.getApplications()), ImmutableList.of(app)); + assertEquals(app.getDisplayName(), "myname"); + assertTrue(app instanceof EntityProxy); + assertTrue(app instanceof MyInterface); + assertFalse(app instanceof SampleNoOpApplication); + } + + @Test + public void testCreateAppFromCatalogByType() { + createAppFromCatalog(SampleNoOpApplication.class.getName()); + } + + @Test + public void testCreateAppFromCatalogByName() { + createAppFromCatalog("app.noop"); + } + + @Test + public void testCreateAppFromCatalogById() { + createAppFromCatalog("app.noop:0.0.1"); + } + + @Test + @SuppressWarnings("deprecation") + public void testCreateAppFromCatalogByTypeMultipleItems() { + CatalogTemplateItemDto item = CatalogItemBuilder.newTemplate("app.noop", "0.0.2-SNAPSHOT") + .javaType(SampleNoOpApplication.class.getName()) + .build(); + managementContext.getCatalog().addItem(item); + createAppFromCatalog(SampleNoOpApplication.class.getName()); + } + + @SuppressWarnings("deprecation") + private void createAppFromCatalog(String type) { + CatalogTemplateItemDto item = CatalogItemBuilder.newTemplate("app.noop", "0.0.1") + .javaType(SampleNoOpApplication.class.getName()) + .build(); + managementContext.getCatalog().addItem(item); + + ApplicationSpec spec = ApplicationSpec.builder() + .name("myname") + .type(type) + .locations(ImmutableSet.of("localhost")) + .build(); + Application app = util.create(spec); + + assertEquals(app.getCatalogItemId(), "app.noop:0.0.1"); + } + + @Test + public void testEntityAppFromCatalogByType() { + createEntityFromCatalog(BasicEntity.class.getName()); + } + + @Test + public void testEntityAppFromCatalogByName() { + createEntityFromCatalog("app.basic"); + } + + @Test + public void testEntityAppFromCatalogById() { + createEntityFromCatalog("app.basic:0.0.1"); + } + + @Test + @SuppressWarnings("deprecation") + public void testEntityAppFromCatalogByTypeMultipleItems() { + CatalogTemplateItemDto item = CatalogItemBuilder.newTemplate("app.basic", "0.0.2-SNAPSHOT") + .javaType(SampleNoOpApplication.class.getName()) + .build(); + managementContext.getCatalog().addItem(item); + createEntityFromCatalog(BasicEntity.class.getName()); + } + + @SuppressWarnings("deprecation") + private void createEntityFromCatalog(String type) { + String symbolicName = "app.basic"; + String version = "0.0.1"; + CatalogTemplateItemDto item = CatalogItemBuilder.newTemplate(symbolicName, version) + .javaType(BasicEntity.class.getName()) + .build(); + managementContext.getCatalog().addItem(item); + + ApplicationSpec spec = ApplicationSpec.builder() + .name("myname") + .entities(ImmutableSet.of(new EntitySpec(type))) + .locations(ImmutableSet.of("localhost")) + .build(); + Application app = util.create(spec); + + Entity entity = Iterables.getOnlyElement(app.getChildren()); + assertEquals(entity.getCatalogItemId(), CatalogUtils.getVersionedId(symbolicName, version)); + } + + @Test + public void testNestedApplications() { + // hierarchy is: app -> subapp -> subentity (where subentity has a policy) + + Application app = managementContext.getEntityManager().createEntity(org.apache.brooklyn.api.entity.EntitySpec.create(TestApplication.class) + .displayName("app") + .child(org.apache.brooklyn.api.entity.EntitySpec.create(TestApplication.class) + .displayName("subapp") + .child(org.apache.brooklyn.api.entity.EntitySpec.create(TestEntity.class) + .displayName("subentity") + .policy(org.apache.brooklyn.api.policy.PolicySpec.create(MyPolicy.class) + .displayName("mypolicy"))))); + + Application subapp = (Application) Iterables.getOnlyElement(app.getChildren()); + TestEntity subentity = (TestEntity) Iterables.getOnlyElement(subapp.getChildren()); + + Entity subappRetrieved = util.getEntity(app.getId(), subapp.getId()); + assertEquals(subappRetrieved.getDisplayName(), "subapp"); + + Entity subentityRetrieved = util.getEntity(app.getId(), subentity.getId()); + assertEquals(subentityRetrieved.getDisplayName(), "subentity"); + + Policy subappPolicy = util.getPolicy(app.getId(), subentity.getId(), "mypolicy"); + assertEquals(subappPolicy.getDisplayName(), "mypolicy"); + } + + public interface MyInterface { + } + + @Catalog(name="Sample No-Op Application", + description="Application which does nothing, included only as part of the test cases.", + iconUrl="") + public static class SampleNoOpApplication extends AbstractApplication implements MyInterface { + } + + public static class MyPolicy extends AbstractPolicy { + public MyPolicy() { + } + public MyPolicy(Map<String, ?> flags) { + super(flags); + } + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/EntityLocationUtilsTest.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/EntityLocationUtilsTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/EntityLocationUtilsTest.java new file mode 100644 index 0000000..f0c65e4 --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/EntityLocationUtilsTest.java @@ -0,0 +1,72 @@ +/* + * 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.util; + +import static org.testng.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.location.AbstractLocation; +import org.apache.brooklyn.core.location.geo.HostGeoInfo; +import org.apache.brooklyn.core.location.internal.LocationInternal; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.entity.software.base.SoftwareProcess; +import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity; + +import com.google.common.collect.ImmutableList; + +public class EntityLocationUtilsTest extends BrooklynAppUnitTestSupport { + + private static final Logger log = LoggerFactory.getLogger(EntityLocationUtilsTest.class); + + private Location loc; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + loc = mgmt.getLocationRegistry().resolve("localhost"); + ((AbstractLocation)loc).setHostGeoInfo(new HostGeoInfo("localhost", "localhost", 50, 0)); + } + + @Test + public void testCount() { + @SuppressWarnings("unused") + SoftwareProcess r1 = app.createAndManageChild(EntitySpec.create(SoftwareProcess.class, RestMockSimpleEntity.class)); + SoftwareProcess r2 = app.createAndManageChild(EntitySpec.create(SoftwareProcess.class, RestMockSimpleEntity.class)); + Entities.start(app, Arrays.<Location>asList(loc)); + + Entities.dumpInfo(app); + + log.info("r2loc: "+r2.getLocations()); + log.info("props: "+((LocationInternal)r2.getLocations().iterator().next()).config().getBag().getAllConfig()); + + Map<Location, Integer> counts = new EntityLocationUtils(mgmt).countLeafEntitiesByLocatedLocations(); + log.info("count: "+counts); + assertEquals(ImmutableList.copyOf(counts.values()), ImmutableList.of(2), "counts="+counts); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/HaHotStateCheckClassResource.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/HaHotStateCheckClassResource.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/HaHotStateCheckClassResource.java new file mode 100644 index 0000000..80e9c46 --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/util/HaHotStateCheckClassResource.java @@ -0,0 +1,38 @@ +/* + * 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.util; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.apache.brooklyn.rest.filter.HaHotStateRequired; + +@Path("/ha/class") +@Produces(MediaType.APPLICATION_JSON) +@HaHotStateRequired +public class HaHotStateCheckClassResource { + + @GET + @Path("fail") + public String fail() { + return "FAIL"; + } +}