/* * 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.entity.proxy.nginx; import static org.apache.brooklyn.test.EntityTestUtils.assertAttributeEqualsEventually; import static org.apache.brooklyn.test.HttpTestUtils.assertHttpStatusCodeEquals; import static org.apache.brooklyn.test.HttpTestUtils.assertHttpStatusCodeEventuallyEquals; import static org.testng.Assert.assertEquals; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.api.entity.Group; import org.apache.brooklyn.api.location.LocationSpec; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; import org.apache.brooklyn.core.mgmt.rebind.RebindOptions; import org.apache.brooklyn.core.mgmt.rebind.RebindTestFixtureWithApp; import org.apache.brooklyn.entity.group.BasicGroup; import org.apache.brooklyn.entity.group.DynamicCluster; import org.apache.brooklyn.entity.proxy.nginx.NginxController; import org.apache.brooklyn.entity.proxy.nginx.UrlMapping; import org.apache.brooklyn.entity.proxy.nginx.UrlRewriteRule; import org.apache.brooklyn.entity.software.base.SoftwareProcess; import org.apache.brooklyn.entity.webapp.tomcat.Tomcat8Server; import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.test.EntityTestUtils; import org.apache.brooklyn.test.WebAppMonitor; import org.apache.brooklyn.test.support.TestResourceUnavailableException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; /** * Test the operation of the {@link NginxController} class. */ public class NginxRebindIntegrationTest extends RebindTestFixtureWithApp { private static final Logger LOG = LoggerFactory.getLogger(NginxRebindIntegrationTest.class); private LocalhostMachineProvisioningLocation localhostProvisioningLocation; private List<WebAppMonitor> webAppMonitors = new CopyOnWriteArrayList<WebAppMonitor>(); private ExecutorService executor; @Override protected boolean useLiveManagementContext() { // For Aled, the test failed without own ~/.brooklyn/brooklyn.properties. // Suspect that was caused by local environment, with custom brooklyn.ssh.config.scriptHeader // to set things like correct Java on path. return true; } @BeforeMethod(alwaysRun=true) public void setUp() throws Exception { super.setUp(); localhostProvisioningLocation = origManagementContext.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class)); executor = Executors.newCachedThreadPool(); } public String getTestWar() { TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/hello-world.war"); return "classpath://hello-world.war"; } @AfterMethod(alwaysRun=true) public void tearDown() throws Exception { for (WebAppMonitor monitor : webAppMonitors) { monitor.terminate(); } webAppMonitors.clear(); if (executor != null) executor.shutdownNow(); super.tearDown(); } private WebAppMonitor newWebAppMonitor(String url, int expectedResponseCode) { WebAppMonitor monitor = new WebAppMonitor(url) // .delayMillis(0) FIXME Re-enable to fast polling .expectedResponseCode(expectedResponseCode) .logFailures(LOG); webAppMonitors.add(monitor); executor.execute(monitor); return monitor; } /** * Test can rebind to the simplest possible nginx configuration (i.e. no server pool). */ @Test(groups = "Integration") public void testRebindsWithEmptyServerPool() throws Exception { // Set up nginx with a server pool DynamicCluster origServerPool = origApp.createAndManageChild(EntitySpec.create(DynamicCluster.class) .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(Tomcat8Server.class)) .configure("initialSize", 0)); NginxController origNginx = origApp.createAndManageChild(EntitySpec.create(NginxController.class) .configure("serverPool", origServerPool) .configure("domain", "localhost")); // Start the app, and ensure reachable; start polling the URL origApp.start(ImmutableList.of(localhostProvisioningLocation)); String rootUrl = origNginx.getAttribute(NginxController.ROOT_URL); int nginxPort = origNginx.getAttribute(NginxController.PROXY_HTTP_PORT); assertHttpStatusCodeEventuallyEquals(rootUrl, 404); WebAppMonitor monitor = newWebAppMonitor(rootUrl, 404); final String origConfigFile = origNginx.getConfigFile(); newApp = rebind(RebindOptions.create().terminateOrigManagementContext(true)); final NginxController newNginx = (NginxController) Iterables.find(newApp.getChildren(), Predicates.instanceOf(NginxController.class)); assertEquals(newNginx.getConfigFile(), origConfigFile); EntityTestUtils.assertAttributeEqualsEventually(newNginx, NginxController.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); assertEquals(newNginx.getAttribute(NginxController.PROXY_HTTP_PORT), (Integer)nginxPort); assertEquals(newNginx.getAttribute(NginxController.ROOT_URL), rootUrl); assertEquals(newNginx.getAttribute(NginxController.PROXY_HTTP_PORT), origNginx.getAttribute(NginxController.PROXY_HTTP_PORT)); assertEquals(newNginx.getConfig(NginxController.STICKY), origNginx.getConfig(NginxController.STICKY)); assertAttributeEqualsEventually(newNginx, SoftwareProcess.SERVICE_UP, true); assertHttpStatusCodeEventuallyEquals(rootUrl, 404); assertEquals(monitor.getFailures(), 0); } /* Exception java.lang.NoClassDefFoundError Message: org/apache/brooklyn/test/HttpTestUtils$3 Stacktrace: at org.apache.brooklyn.test.HttpTestUtils.assertHttpStatusCodeEventuallyEquals(HttpTestUtils.java:208) at org.apache.brooklyn.test.HttpTestUtils.assertHttpStatusCodeEventuallyEquals(HttpTestUtils.java:204) at org.apache.brooklyn.entity.proxy.nginx.NginxRebindIntegrationTest.testRebindsWithoutLosingServerPool(NginxRebindIntegrationTest.java:178) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84) at org.testng.internal.Invoker.invokeMethod(Invoker.java:714) at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901) at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231) at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127) at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111) at org.testng.TestRunner.privateRun(TestRunner.java:767) at org.testng.TestRunner.run(TestRunner.java:617) at org.testng.SuiteRunner.runTest(SuiteRunner.java:348) at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:343) at org.testng.SuiteRunner.privateRun(SuiteRunner.java:305) at org.testng.SuiteRunner.run(SuiteRunner.java:254) at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52) at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86) at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224) at org.testng.TestNG.runSuitesLocally(TestNG.java:1149) at org.testng.TestNG.run(TestNG.java:1057) at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:115) at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.executeMulti(TestNGDirectoryTestSuite.java:205) at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.execute(TestNGDirectoryTestSuite.java:108) at org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:111) at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:203) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:155) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:103) */ /** * Test can rebind with an active server pool. */ @Test(groups = {"Integration","Broken"}) public void testRebindsWithoutLosingServerPool() throws Exception { // Set up nginx with a server pool DynamicCluster origServerPool = origApp.createAndManageChild(EntitySpec.create(DynamicCluster.class) .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(Tomcat8Server.class).configure("war", getTestWar())) .configure("initialSize", 1)); NginxController origNginx = origApp.createAndManageChild(EntitySpec.create(NginxController.class) .configure("serverPool", origServerPool) .configure("domain", "localhost")); // Start the app, and ensure reachable; start polling the URL origApp.start(ImmutableList.of(localhostProvisioningLocation)); String rootUrl = origNginx.getAttribute(NginxController.ROOT_URL); Tomcat8Server origServer = (Tomcat8Server) Iterables.getOnlyElement(origServerPool.getMembers()); assertEquals(origNginx.getAttribute(NginxController.SERVER_POOL_TARGETS).keySet(), ImmutableSet.of(origServer)); assertHttpStatusCodeEventuallyEquals(rootUrl, 200); WebAppMonitor monitor = newWebAppMonitor(rootUrl, 200); final String origConfigFile = origNginx.getConfigFile(); // Rebind newApp = rebind(RebindOptions.create().terminateOrigManagementContext(true)); ManagementContext newManagementContext = newApp.getManagementContext(); final NginxController newNginx = (NginxController) Iterables.find(newApp.getChildren(), Predicates.instanceOf(NginxController.class)); final DynamicCluster newServerPool = (DynamicCluster) newManagementContext.getEntityManager().getEntity(origServerPool.getId()); final Tomcat8Server newServer = (Tomcat8Server) Iterables.getOnlyElement(newServerPool.getMembers()); // Expect continually to have same nginx members; should not lose them temporarily! Asserts.succeedsContinually(new Runnable() { public void run() { Map<Entity, String> newNginxMemebers = newNginx.getAttribute(NginxController.SERVER_POOL_TARGETS); assertEquals(newNginxMemebers.keySet(), ImmutableSet.of(newServer)); }}); assertAttributeEqualsEventually(newNginx, SoftwareProcess.SERVICE_UP, true); assertHttpStatusCodeEventuallyEquals(rootUrl, 200); assertEquals(newNginx.getConfigFile(), origConfigFile); // Check that an update doesn't break things newNginx.update(); assertHttpStatusCodeEquals(rootUrl, 200); // Resize new cluster, and confirm change takes affect. // - Increase size // - wait for nginx to definitely be updates (TODO nicer way to wait for updated?) // - terminate old server // - confirm can still route messages newServerPool.resize(2); Thread.sleep(10*1000); newServer.stop(); assertHttpStatusCodeEventuallyEquals(rootUrl, 200); // Check that URLs have been constantly reachable assertEquals(monitor.getFailures(), 0); } /* Exception java.lang.NoClassDefFoundError Message: org/apache/brooklyn/test/HttpTestUtils$3 Stacktrace: at org.apache.brooklyn.test.HttpTestUtils.assertHttpStatusCodeEventuallyEquals(HttpTestUtils.java:208) at org.apache.brooklyn.test.HttpTestUtils.assertHttpStatusCodeEventuallyEquals(HttpTestUtils.java:204) at org.apache.brooklyn.entity.proxy.nginx.NginxRebindIntegrationTest.testRebindsWithoutLosingUrlMappings(NginxRebindIntegrationTest.java:254) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84) at org.testng.internal.Invoker.invokeMethod(Invoker.java:714) at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901) at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231) at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127) at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111) at org.testng.TestRunner.privateRun(TestRunner.java:767) at org.testng.TestRunner.run(TestRunner.java:617) at org.testng.SuiteRunner.runTest(SuiteRunner.java:348) at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:343) at org.testng.SuiteRunner.privateRun(SuiteRunner.java:305) at org.testng.SuiteRunner.run(SuiteRunner.java:254) at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52) at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86) at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224) at org.testng.TestNG.runSuitesLocally(TestNG.java:1149) at org.testng.TestNG.run(TestNG.java:1057) at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:115) at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.executeMulti(TestNGDirectoryTestSuite.java:205) at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.execute(TestNGDirectoryTestSuite.java:108) at org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:111) at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:203) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:155) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:103) */ /** * Test can rebind to the with server pool and URL remappings. * NOTE: This requires a redirection from localhost1 to 127.0.0.1 in your /etc/hosts file */ @Test(groups = {"Integration","Broken"}) public void testRebindsWithoutLosingUrlMappings() throws Exception { // Set up nginx with a url-mapping Group origUrlMappingsGroup = origApp.createAndManageChild(EntitySpec.create(BasicGroup.class) .configure("childrenAsMembers", true)); DynamicCluster origServerPool = origApp.createAndManageChild(EntitySpec.create(DynamicCluster.class) .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(Tomcat8Server.class).configure("war", getTestWar())) .configure("initialSize", 1)); UrlMapping origMapping = origUrlMappingsGroup.addChild(EntitySpec.create(UrlMapping.class) .configure("domain", "localhost1") .configure("target", origServerPool) .configure("rewrites", ImmutableList.of(new UrlRewriteRule("/foo/(.*)", "/$1")))); NginxController origNginx = origApp.createAndManageChild(EntitySpec.create(NginxController.class) .configure("domain", "localhost") .configure("urlMappings", origUrlMappingsGroup)); // Start the app, and ensure reachable; start polling the URL origApp.start(ImmutableList.of(localhostProvisioningLocation)); String mappingGroupUrl = "http://localhost1:"+origNginx.getAttribute(NginxController.PROXY_HTTP_PORT)+"/foo/"; assertHttpStatusCodeEventuallyEquals(mappingGroupUrl, 200); WebAppMonitor monitor = newWebAppMonitor(mappingGroupUrl, 200); final String origConfigFile = origNginx.getConfigFile(); // Create a rebinding newApp = rebind(RebindOptions.create().terminateOrigManagementContext(true)); ManagementContext newManagementContext = newApp.getManagementContext(); final NginxController newNginx = (NginxController) Iterables.find(newApp.getChildren(), Predicates.instanceOf(NginxController.class)); DynamicCluster newServerPool = (DynamicCluster) newManagementContext.getEntityManager().getEntity(origServerPool.getId()); Tomcat8Server newServer = (Tomcat8Server) Iterables.getOnlyElement(newServerPool.getMembers()); assertAttributeEqualsEventually(newNginx, SoftwareProcess.SERVICE_UP, true); assertHttpStatusCodeEventuallyEquals(mappingGroupUrl, 200); assertEquals(newNginx.getConfigFile(), origConfigFile); // Check that an update doesn't break things newNginx.update(); assertHttpStatusCodeEquals(mappingGroupUrl, 200); // Resize new cluster, and confirm change takes affect. // - Increase size // - wait for nginx to definitely be updates (TODO nicer way to wait for updated?) // - terminate old server // - confirm can still route messages newServerPool.resize(2); Thread.sleep(10*1000); newServer.stop(); assertHttpStatusCodeEquals(mappingGroupUrl, 200); // Check that URLs have been constantly reachable assertEquals(monitor.getFailures(), 0); } }