Integration tests for resource accessibility with regard to the HA state
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/5a757750 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/5a757750 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/5a757750 Branch: refs/heads/master Commit: 5a757750783b1f6f79068e8d6f6ebb7d1121ee83 Parents: 86cf35f Author: Svetoslav Neykov <[email protected]> Authored: Fri Mar 6 14:02:31 2015 +0200 Committer: Svetoslav Neykov <[email protected]> Committed: Thu Mar 19 16:01:29 2015 +0200 ---------------------------------------------------------------------- .../brooklyn/rest/BrooklynRestApiLauncher.java | 12 +- .../test/java/brooklyn/rest/HaHotCheckTest.java | 82 +++++++ .../brooklyn/rest/HaMasterCheckFilterTest.java | 221 +++++++++++++++++++ .../brooklyn/rest/resources/HaHotCheckTest.java | 82 ------- 4 files changed, 312 insertions(+), 85 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5a757750/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java index 5baf2d2..cb7b29d 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java @@ -104,6 +104,7 @@ public class BrooklynRestApiLauncher { private ManagementContext mgmt; private ContextHandler customContext; private boolean deployJsgui = true; + private boolean disableHighAvailability = true; protected BrooklynRestApiLauncher() {} @@ -151,6 +152,11 @@ public class BrooklynRestApiLauncher { this.deployJsgui = false; return this; } + + public BrooklynRestApiLauncher disableHighAvailability(boolean value) { + this.disableHighAvailability = value; + return this; + } public Server start() { if (this.mgmt == null) { @@ -198,7 +204,7 @@ public class BrooklynRestApiLauncher { ((LocalManagementContext) mgmt).setBaseClassPathForScanning(ClasspathHelper.forJavaClassPath()); } - return startServer(mgmt, context, summary); + return startServer(mgmt, context, summary, disableHighAvailability); } private ContextHandler filterContextHandler(ManagementContext mgmt) { @@ -254,7 +260,7 @@ public class BrooklynRestApiLauncher { /** starts a server, on all NICs if security is configured, * otherwise (no security) only on loopback interface */ - public static Server startServer(ManagementContext mgmt, ContextHandler context, String summary) { + public static Server startServer(ManagementContext mgmt, ContextHandler context, String summary, boolean disableHighAvailability) { // TODO this repeats code in BrooklynLauncher / WebServer. should merge the two paths. boolean secure = mgmt != null && !BrooklynWebConfig.hasNoSecurityOptions(mgmt.getConfig()); if (secure) { @@ -266,7 +272,7 @@ public class BrooklynRestApiLauncher { ((BrooklynProperties)mgmt.getConfig()).put(BrooklynWebConfig.SECURITY_PROVIDER_CLASSNAME, AnyoneSecurityProvider.class.getName()); } } - if (mgmt != null) + if (mgmt != null && disableHighAvailability) mgmt.getHighAvailabilityManager().disabled(); InetSocketAddress bindLocation = new InetSocketAddress( secure ? Networking.ANY_NIC : Networking.LOOPBACK, http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5a757750/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java b/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java new file mode 100644 index 0000000..224cda6 --- /dev/null +++ b/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java @@ -0,0 +1,82 @@ +/* + * 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 brooklyn.rest; + +import static org.testng.Assert.assertEquals; + +import javax.ws.rs.core.MediaType; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.management.ha.HighAvailabilityManager; +import brooklyn.management.ha.ManagementNodeState; +import brooklyn.rest.filter.HaHotCheckResourceFilter; +import brooklyn.rest.testing.BrooklynRestResourceTest; +import brooklyn.rest.testing.mocks.ManagementContextMock; +import brooklyn.rest.util.HaHotStateCheckClassResource; +import brooklyn.rest.util.HaHotStateCheckResource; +import brooklyn.rest.util.ManagementContextProvider; + +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.core.ResourceConfig; + +public class HaHotCheckTest extends BrooklynRestResourceTest { + + private ManagementContextMock mgmtMock; + + @Override + @BeforeMethod(alwaysRun = true) + public void setUp() throws Exception { + mgmtMock = new ManagementContextMock(); + super.setUp(); + } + + @Override + protected void addBrooklynResources() { + config.getSingletons().add(new ManagementContextProvider(mgmtMock)); + config.getProperties().put(ResourceConfig.PROPERTY_RESOURCE_FILTER_FACTORIES, HaHotCheckResourceFilter.class.getName()); + addResource(new HaHotStateCheckResource()); + addResource(new HaHotStateCheckClassResource()); + } + + @Test + public void testHaCheck() { + HighAvailabilityManager ha = mgmtMock.getHighAvailabilityManager(); + assertEquals(ha.getNodeState(), ManagementNodeState.MASTER); + testResourceFetch("/ha/method/ok", 200); + testResourceFetch("/ha/method/fail", 200); + testResourceFetch("/ha/class/fail", 200); + + mgmtMock.setState(ManagementNodeState.STANDBY); + assertEquals(ha.getNodeState(), ManagementNodeState.STANDBY); + + testResourceFetch("/ha/method/ok", 200); + testResourceFetch("/ha/method/fail", 403); + testResourceFetch("/ha/class/fail", 403); + } + + private void testResourceFetch(String resource, int code) { + ClientResponse response = client().resource(resource) + .accept(MediaType.APPLICATION_JSON_TYPE) + .get(ClientResponse.class); + assertEquals(response.getStatus(), code); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5a757750/usage/rest-server/src/test/java/brooklyn/rest/HaMasterCheckFilterTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/brooklyn/rest/HaMasterCheckFilterTest.java b/usage/rest-server/src/test/java/brooklyn/rest/HaMasterCheckFilterTest.java new file mode 100644 index 0000000..55452c8 --- /dev/null +++ b/usage/rest-server/src/test/java/brooklyn/rest/HaMasterCheckFilterTest.java @@ -0,0 +1,221 @@ +/* + * 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 brooklyn.rest; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import io.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer; + +import java.io.File; +import java.net.URI; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; + +import org.apache.http.client.HttpClient; +import org.eclipse.jetty.server.Server; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +import brooklyn.entity.Entity; +import brooklyn.entity.basic.BasicApplication; +import brooklyn.entity.basic.Entities; +import brooklyn.entity.proxying.EntitySpec; +import brooklyn.entity.rebind.RebindTestUtils; +import brooklyn.management.EntityManager; +import brooklyn.management.ManagementContext; +import brooklyn.management.ha.HighAvailabilityMode; +import brooklyn.management.ha.ManagementNodeState; +import brooklyn.rest.security.provider.AnyoneSecurityProvider; +import brooklyn.test.Asserts; +import brooklyn.util.http.HttpTool; +import brooklyn.util.http.HttpToolResponse; +import brooklyn.util.os.Os; +import brooklyn.util.time.Duration; + +import com.google.common.base.Predicates; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMap; + +public class HaMasterCheckFilterTest extends BrooklynRestApiLauncherTestFixture { + private static final Logger LOG = LoggerFactory.getLogger(HaMasterCheckFilterTest.class); + private static final Duration TIMEOUT = Duration.THIRTY_SECONDS; + + private File mementoDir; + private ManagementContext writeMgmt; + private ManagementContext readMgmt; + private String appId; + private Server server; + private HttpClient client; + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + server.stop(); + Entities.destroyAll(writeMgmt); + Entities.destroyAll(readMgmt); + Os.deleteRecursively(mementoDir); + } + + @Test(groups = "Integration") + public void testEntitiesExistOnMasterPromotion() throws Exception { + initHaCluster(HighAvailabilityMode.AUTO, HighAvailabilityMode.AUTO); + stopWriteNode(); + assertEntityExists(new ReturnCodeCheck()); + assertReadIsMaster(); + } + + @Test(groups = "Integration") + public void testEntitiesExistOnHotStandbyAndPromotion() throws Exception { + initHaCluster(HighAvailabilityMode.AUTO, HighAvailabilityMode.HOT_STANDBY); + assertEntityExists(new ReturnCodeCheck()); + stopWriteNode(); + assertEntityExists(new ReturnCodeAndNodeState()); + assertReadIsMaster(); + } + + @Test(groups = "Integration") + public void testEntitiesExistOnHotBackup() throws Exception { + initHaCluster(HighAvailabilityMode.AUTO, HighAvailabilityMode.HOT_BACKUP); + Asserts.continually( + ImmutableMap.<String,Object>of( + "timeout", Duration.THIRTY_SECONDS, + "period", Duration.ZERO), + new ReturnCodeSupplier(), + Predicates.or(Predicates.equalTo(200), Predicates.equalTo(403))); + } + + private HttpClient getClient(Server server) { + HttpClient client = HttpTool.httpClientBuilder() + .uri(getBaseUri(server)) + .build(); + return client; + } + + private int getAppResponseCode() { + HttpToolResponse response = HttpTool.httpGet( + client, URI.create(getBaseUri(server) + "/v1/applications/" + appId), + ImmutableMap.<String,String>of()); + return response.getResponseCode(); + } + + private String createApp(ManagementContext mgmt) { + EntityManager entityMgr = mgmt.getEntityManager(); + Entity app = entityMgr.createEntity(EntitySpec.create(BasicApplication.class)); + entityMgr.manage(app); + return app.getId(); + } + + private ManagementContext createManagementContext(File mementoDir, HighAvailabilityMode mode) { + ManagementContext mgmt = RebindTestUtils.managementContextBuilder(mementoDir, getClass().getClassLoader()) + .persistPeriodMillis(1) + .forLive(false) + .emptyCatalog(true) + .buildUnstarted(); + + mgmt.getHighAvailabilityManager().start(mode); + + new BrooklynCampPlatformLauncherNoServer() + .useManagementContext(mgmt) + .launch(); + + return mgmt; + } + + private void initHaCluster(HighAvailabilityMode writeMode, HighAvailabilityMode readMode) throws InterruptedException, TimeoutException { + mementoDir = Os.newTempDir(getClass()); + + writeMgmt = createManagementContext(mementoDir, writeMode); + appId = createApp(writeMgmt); + writeMgmt.getRebindManager().getPersister().waitForWritesCompleted(TIMEOUT); + + readMgmt = createManagementContext(mementoDir, readMode); + + server = useServerForTest(BrooklynRestApiLauncher.launcher() + .managementContext(readMgmt) + .securityProvider(AnyoneSecurityProvider.class) + .forceUseOfDefaultCatalogWithJavaClassPath(true) + .withoutJsgui() + .disableHighAvailability(false) + .start()); + client = getClient(server); + } + + private void assertEntityExists(Callable<Boolean> c) { + assertTrue(Asserts.succeedsEventually(c), "Unexpected code returned"); + } + + private void assertReadIsMaster() { +// Asserts.eventually(new NodeStateSupplier(readMgmt), Predicates.equalTo(ManagementNodeState.MASTER)); + assertEquals(readMgmt.getHighAvailabilityManager().getNodeState(), ManagementNodeState.MASTER); + } + + private void stopWriteNode() { + writeMgmt.getHighAvailabilityManager().stop(); + } + + private class ReturnCodeCheck implements Callable<Boolean> { + @Override + public Boolean call() { + int retCode = getAppResponseCode(); + if (retCode == 200) { + return true; + } else if (retCode == 403) { + throw new RuntimeException("Not ready, response " + retCode); + } else { + LOG.error("Unexpected return code " + retCode); + return false; + } + } + } + + private class ReturnCodeAndNodeState extends ReturnCodeCheck { + @Override + public Boolean call() { + Boolean ret = super.call(); + if (ret) { + ManagementNodeState state = readMgmt.getHighAvailabilityManager().getNodeState(); + if (state != ManagementNodeState.MASTER) { + throw new RuntimeException("Not master yet " + state); + } + } + return ret; + } + } + + private class ReturnCodeSupplier implements Supplier<Integer> { + @Override + public Integer get() { + return getAppResponseCode(); + } + } + + private static class NodeStateSupplier implements Supplier<ManagementNodeState> { + private ManagementContext node; + public NodeStateSupplier(ManagementContext node) { + this.node = node; + } + + @Override + public ManagementNodeState get() { + return node.getHighAvailabilityManager().getNodeState(); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5a757750/usage/rest-server/src/test/java/brooklyn/rest/resources/HaHotCheckTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/HaHotCheckTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/HaHotCheckTest.java deleted file mode 100644 index b8e54ef..0000000 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/HaHotCheckTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 brooklyn.rest.resources; - -import static org.testng.Assert.assertEquals; - -import javax.ws.rs.core.MediaType; - -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import brooklyn.management.ha.HighAvailabilityManager; -import brooklyn.management.ha.ManagementNodeState; -import brooklyn.rest.filter.HaHotCheckResourceFilter; -import brooklyn.rest.testing.BrooklynRestResourceTest; -import brooklyn.rest.testing.mocks.ManagementContextMock; -import brooklyn.rest.util.HaHotStateCheckClassResource; -import brooklyn.rest.util.HaHotStateCheckResource; -import brooklyn.rest.util.ManagementContextProvider; - -import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.core.ResourceConfig; - -public class HaHotCheckTest extends BrooklynRestResourceTest { - - private ManagementContextMock mgmtMock; - - @Override - @BeforeMethod(alwaysRun = true) - public void setUp() throws Exception { - mgmtMock = new ManagementContextMock(); - super.setUp(); - } - - @Override - protected void addBrooklynResources() { - config.getSingletons().add(new ManagementContextProvider(mgmtMock)); - config.getProperties().put(ResourceConfig.PROPERTY_RESOURCE_FILTER_FACTORIES, HaHotCheckResourceFilter.class.getName()); - addResource(new HaHotStateCheckResource()); - addResource(new HaHotStateCheckClassResource()); - } - - @Test - public void testHaCheck() throws Exception { - HighAvailabilityManager ha = mgmtMock.getHighAvailabilityManager(); - assertEquals(ha.getNodeState(), ManagementNodeState.MASTER); - testResourceFetch("/ha/method/ok", 200); - testResourceFetch("/ha/method/fail", 200); - testResourceFetch("/ha/class/fail", 200); - - mgmtMock.setState(ManagementNodeState.STANDBY); - assertEquals(ha.getNodeState(), ManagementNodeState.STANDBY); - - testResourceFetch("/ha/method/ok", 200); - testResourceFetch("/ha/method/fail", 403); - testResourceFetch("/ha/class/fail", 403); - } - - private void testResourceFetch(String resource, int code) { - ClientResponse response = client().resource(resource) - .accept(MediaType.APPLICATION_JSON_TYPE) - .get(ClientResponse.class); - assertEquals(response.getStatus(), code); - } - -}
