Repository: ambari Updated Branches: refs/heads/trunk 975c0bc44 -> d49cc72e9
AMBARI-13752. Add a functional test to create a local ambari server and test it's status using an API call. (Nahappan Somasundaram via swagle) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/d49cc72e Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/d49cc72e Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/d49cc72e Branch: refs/heads/trunk Commit: d49cc72e92a72596571061622aacb3c5b5721200 Parents: 975c0bc Author: Siddharth Wagle <swa...@hortonworks.com> Authored: Tue Nov 10 21:13:38 2015 -0800 Committer: Siddharth Wagle <swa...@hortonworks.com> Committed: Tue Nov 10 21:13:38 2015 -0800 ---------------------------------------------------------------------- .../server/configuration/Configuration.java | 9 ++ .../ambari/server/controller/AmbariServer.java | 96 ++++++----- .../server/LocalAmbariServer.java | 120 ++++++++++++++ .../server/StartStopServerTest.java | 159 +++++++++++++++++++ 4 files changed, 343 insertions(+), 41 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/d49cc72e/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java index e5e2c90..12f6bca 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java @@ -100,6 +100,7 @@ public class Configuration { public static final String API_GZIP_COMPRESSION_ENABLED_KEY = "api.gzip.compression.enabled"; public static final String API_GZIP_MIN_COMPRESSION_SIZE_KEY = "api.gzip.compression.min.size"; public static final String AGENT_API_GZIP_COMPRESSION_ENABLED_KEY = "agent.api.gzip.compression.enabled"; + public static final String AGENT_USE_SSL = "agent.ssl"; public static final String SRVR_TWO_WAY_SSL_KEY = "security.server.two_way_ssl"; public static final String SRVR_TWO_WAY_SSL_PORT_KEY = "security.server.two_way_ssl.port"; public static final String SRVR_ONE_WAY_SSL_PORT_KEY = "security.server.one_way_ssl.port"; @@ -1137,6 +1138,14 @@ public class Configuration { } /** + * Check to see if the Agent should be authenticated via ssl or not + * @return false if not, true if ssl needs to be used. + */ + public boolean getAgentSSLAuthentication() { + return ("true".equals(properties.getProperty(AGENT_USE_SSL, "true"))); + } + + /** * Get the value that should be set for the <code>Strict-Transport-Security</code> HTTP response header for Ambari Server UI. * <p/> * By default this will be <code>max-age=31536000; includeSubDomains</code>. For example: http://git-wip-us.apache.org/repos/asf/ambari/blob/d49cc72e/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java index b59ae5d..71e8082 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java @@ -335,47 +335,54 @@ public class AmbariServer { // root.addFilter(new FilterHolder(springSecurityFilter), "/proxy/*", DISPATCHER_TYPES); } - - //Secured connector for 2-way auth - SslContextFactory contextFactoryTwoWay = new SslContextFactory(); - disableInsecureProtocols(contextFactoryTwoWay); - SslSelectChannelConnector sslConnectorTwoWay = new - SslSelectChannelConnector(contextFactoryTwoWay); - sslConnectorTwoWay.setPort(configs.getTwoWayAuthPort()); - Map<String, String> configsMap = configs.getConfigsMap(); - String keystore = configsMap.get(Configuration.SRVR_KSTR_DIR_KEY) + - File.separator + configsMap.get(Configuration.KSTR_NAME_KEY); - String truststore = configsMap.get(Configuration.SRVR_KSTR_DIR_KEY) + - File.separator + configsMap.get(Configuration.TSTR_NAME_KEY); - String srvrCrtPass = configsMap.get(Configuration.SRVR_CRT_PASS_KEY); - sslConnectorTwoWay.setKeystore(keystore); - sslConnectorTwoWay.setTruststore(truststore); - sslConnectorTwoWay.setPassword(srvrCrtPass); - sslConnectorTwoWay.setKeyPassword(srvrCrtPass); - sslConnectorTwoWay.setTrustPassword(srvrCrtPass); - sslConnectorTwoWay.setKeystoreType(configsMap.get(Configuration.KSTR_TYPE_KEY)); - sslConnectorTwoWay.setTruststoreType(configsMap.get(Configuration.TSTR_TYPE_KEY)); - sslConnectorTwoWay.setNeedClientAuth(configs.getTwoWaySsl()); - - //SSL Context Factory - SslContextFactory contextFactoryOneWay = new SslContextFactory(true); - contextFactoryOneWay.setKeyStorePath(keystore); - contextFactoryOneWay.setTrustStore(truststore); - contextFactoryOneWay.setKeyStorePassword(srvrCrtPass); - contextFactoryOneWay.setKeyManagerPassword(srvrCrtPass); - contextFactoryOneWay.setTrustStorePassword(srvrCrtPass); - contextFactoryOneWay.setKeyStoreType(configsMap.get(Configuration.KSTR_TYPE_KEY)); - contextFactoryOneWay.setTrustStoreType(configsMap.get(Configuration.TSTR_TYPE_KEY)); - contextFactoryOneWay.setNeedClientAuth(false); - disableInsecureProtocols(contextFactoryOneWay); - - //Secured connector for 1-way auth - SslSelectChannelConnector sslConnectorOneWay = new SslSelectChannelConnector(contextFactoryOneWay); - sslConnectorOneWay.setPort(configs.getOneWayAuthPort()); - sslConnectorOneWay.setAcceptors(2); - sslConnectorTwoWay.setAcceptors(2); - serverForAgent.setConnectors(new Connector[]{sslConnectorOneWay, sslConnectorTwoWay}); + + if (configs.getAgentSSLAuthentication()) { + //Secured connector for 2-way auth + SslContextFactory contextFactoryTwoWay = new SslContextFactory(); + disableInsecureProtocols(contextFactoryTwoWay); + SslSelectChannelConnector sslConnectorTwoWay = new + SslSelectChannelConnector(contextFactoryTwoWay); + sslConnectorTwoWay.setPort(configs.getTwoWayAuthPort()); + + String keystore = configsMap.get(Configuration.SRVR_KSTR_DIR_KEY) + + File.separator + configsMap.get(Configuration.KSTR_NAME_KEY); + String truststore = configsMap.get(Configuration.SRVR_KSTR_DIR_KEY) + + File.separator + configsMap.get(Configuration.TSTR_NAME_KEY); + String srvrCrtPass = configsMap.get(Configuration.SRVR_CRT_PASS_KEY); + sslConnectorTwoWay.setKeystore(keystore); + sslConnectorTwoWay.setTruststore(truststore); + sslConnectorTwoWay.setPassword(srvrCrtPass); + sslConnectorTwoWay.setKeyPassword(srvrCrtPass); + sslConnectorTwoWay.setTrustPassword(srvrCrtPass); + sslConnectorTwoWay.setKeystoreType(configsMap.get(Configuration.KSTR_TYPE_KEY)); + sslConnectorTwoWay.setTruststoreType(configsMap.get(Configuration.TSTR_TYPE_KEY)); + sslConnectorTwoWay.setNeedClientAuth(configs.getTwoWaySsl()); + + //SSL Context Factory + SslContextFactory contextFactoryOneWay = new SslContextFactory(true); + contextFactoryOneWay.setKeyStorePath(keystore); + contextFactoryOneWay.setTrustStore(truststore); + contextFactoryOneWay.setKeyStorePassword(srvrCrtPass); + contextFactoryOneWay.setKeyManagerPassword(srvrCrtPass); + contextFactoryOneWay.setTrustStorePassword(srvrCrtPass); + contextFactoryOneWay.setKeyStoreType(configsMap.get(Configuration.KSTR_TYPE_KEY)); + contextFactoryOneWay.setTrustStoreType(configsMap.get(Configuration.TSTR_TYPE_KEY)); + contextFactoryOneWay.setNeedClientAuth(false); + disableInsecureProtocols(contextFactoryOneWay); + + //Secured connector for 1-way auth + SslSelectChannelConnector sslConnectorOneWay = new SslSelectChannelConnector(contextFactoryOneWay); + sslConnectorOneWay.setPort(configs.getOneWayAuthPort()); + sslConnectorOneWay.setAcceptors(2); + sslConnectorTwoWay.setAcceptors(2); + serverForAgent.setConnectors(new Connector[]{sslConnectorOneWay, sslConnectorTwoWay}); + } else { + SelectChannelConnector agentConnector = new SelectChannelConnector(); + agentConnector.setPort(configs.getOneWayAuthPort()); + agentConnector.setMaxIdleTime(configs.getConnectionMaxIdleTime()); + serverForAgent.addConnector(agentConnector); + } ServletHolder sh = new ServletHolder(ServletContainer.class); sh.setInitParameter("com.sun.jersey.config.property.resourceConfigClass", @@ -713,11 +720,18 @@ public class AmbariServer { } /** + * Initialize the view registry singleton instance. + */ + public void initViewRegistry() { + ViewRegistry.initInstance(this.viewRegistry); + } + + /** * Sets up proxy authentication. This must be done before the server is * initialized since <code>AmbariMetaInfo</code> requires potential URL * lookups that may need the proxy. */ - static void setupProxyAuth() { + public static void setupProxyAuth() { final String proxyUser = System.getProperty("http.proxyUser"); final String proxyPass = System.getProperty("http.proxyPassword"); http://git-wip-us.apache.org/repos/asf/ambari/blob/d49cc72e/ambari-server/src/test/java/org/apache/ambari/server/functionaltests/server/LocalAmbariServer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/functionaltests/server/LocalAmbariServer.java b/ambari-server/src/test/java/org/apache/ambari/server/functionaltests/server/LocalAmbariServer.java new file mode 100644 index 0000000..ceadbc2 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/functionaltests/server/LocalAmbariServer.java @@ -0,0 +1,120 @@ +/* + * 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.ambari.server.functionaltests.server; + +import com.google.inject.persist.PersistService; +import org.apache.ambari.server.controller.AmbariServer; +import org.apache.ambari.server.orm.GuiceJpaInitializer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import com.google.inject.Guice; +import com.google.inject.Injector; +import org.apache.ambari.server.orm.InMemoryDefaultTestModule; +import org.apache.ambari.server.configuration.Configuration; + +/** +* Wrap AmbariServer as a testable unit. +*/ +public class LocalAmbariServer implements Runnable { + + private static Log LOG = LogFactory.getLog(AmbariServer.class); + + /** + * Actual ambari server instance. + */ + private AmbariServer ambariServer = null; + + private Injector injector = null; + + private InMemoryDefaultTestModule module = null; + + /** + * Default constructor using the default implementation of InMemoryDefaultTestModule. + */ + public LocalAmbariServer() { this.module = new InMemoryDefaultTestModule(); } + + /** + * Overloaded constructor for sub-classed InMemoryDefaultTestModule implementations. + * @param module + */ + public LocalAmbariServer(InMemoryDefaultTestModule module) { this.module = module; } + + /** + * Thread entry point. + */ + @Override + public void run(){ + try { + startServer(); + } + catch (Exception ex) { + LOG.info("Exception received ", ex); + throw new RuntimeException(ex); + } + } + + /** + * Configures the Guice injector to use the in-memory test DB + * and attempts to start an instance of AmbariServer. + * + * @throws Exception + */ + private void startServer() throws Exception { + /** + * Specify the temp folder as the source for certificate keys. + * Without this, the CertificateManager will generate the keys + * in the current folder (ambari-server) causing issues with + * rat check. + */ + String tmpDir = System.getProperty("java.io.tmpdir"); + this.module.getProperties().setProperty(Configuration.SRVR_KSTR_DIR_KEY, tmpDir); + injector = Guice.createInjector(this.module); + + try { + LOG.info("Attempting to start ambari server..."); + + AmbariServer.setupProxyAuth(); + injector.getInstance(GuiceJpaInitializer.class); + ambariServer = injector.getInstance(AmbariServer.class); + ambariServer.initViewRegistry(); + ambariServer.run(); + } catch (InterruptedException ex) { + LOG.info(ex); + } catch (Throwable t) { + LOG.error("Failed to run the Ambari Server", t); + stopServer(); + } + } + + /** + * Attempts to stop the test AmbariServer instance. + * @throws Exception + */ + public void stopServer() throws Exception { + LOG.info("Stopping ambari server..."); + + if (ambariServer != null) { + ambariServer.stop(); + } + + if (injector != null) { + injector.getInstance(PersistService.class).stop(); + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/d49cc72e/ambari-server/src/test/java/org/apache/ambari/server/functionaltests/server/StartStopServerTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/functionaltests/server/StartStopServerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/functionaltests/server/StartStopServerTest.java new file mode 100644 index 0000000..604ef9e --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/functionaltests/server/StartStopServerTest.java @@ -0,0 +1,159 @@ +/* + * 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.ambari.server.functionaltests.server; + +import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.orm.InMemoryDefaultTestModule; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Properties; + +import static org.junit.Assert.assertTrue; + +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.methods.GetMethod; + +import java.io.StringReader; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; +import com.google.gson.JsonObject; +import com.google.gson.JsonArray; + +import java.io.IOException; +import java.text.MessageFormat; + +import org.apache.http.HttpStatus; + +/** + * Simple test that starts the local ambari server, + * tests it's status and shuts down the server. + */ +public class StartStopServerTest { + /** + * Run the ambari server on a thread. + */ + private Thread serverThread = null; + + /** + * Instance of the local ambari server, which wraps the actual + * ambari server with test configuration. + */ + private LocalAmbariServer server = null; + + /** + * Server port + */ + private static int serverPort = 9995; + + /** + * Server URL + */ + private static String SERVER_URL_FORMAT = "http://localhost:%d"; + + /** + * Test URL for GETting the status of the ambari server + */ + private static String stacksPath = "/api/v1/stacks"; + + /** + * Start our local server on a thread so that it does not block. + * @throws Exception + */ + @Before + public void setup() throws Exception { + InMemoryDefaultTestModule testModule = new InMemoryDefaultTestModule(); + Properties properties = testModule.getProperties(); + properties.setProperty(Configuration.AGENT_USE_SSL, "false"); + properties.setProperty(Configuration.CLIENT_API_PORT_KEY, Integer.toString(serverPort)); + server = new LocalAmbariServer(testModule); + serverThread = new Thread(server); + serverThread.start(); + serverThread.join(20000); // Give a few seconds for the ambari server to start up + } + + /** + * Shut down our local server. + * @throws Exception + */ + @After + public void teardown() throws Exception { + if (serverThread != null) { + serverThread.interrupt(); + } + if (server != null) { + server.stopServer(); + } + } + + /** + * Waits for the ambari server to startup and then checks it's + * status by querying /api/v1/stacks (does not touch the DB) + */ + @Test + public void testServerStatus() throws IOException { + /** + * Query the ambari server for the list of stacks. + * A successful GET returns the list of stacks. + * We should get a json like: + * { + * "href" : "http://localhost:9995/api/v1/stacks", + * "items" : [ + * { + * "href" : "http://localhost:9995/api/v1/stacks/HDP", + * "Stacks" : { + * "stack_name" : "HDP" + * } + * } + * ] + * } + */ + + String stacksUrl = String.format(SERVER_URL_FORMAT, serverPort) + stacksPath; + HttpClient httpClient = new HttpClient(); + GetMethod getMethod = new GetMethod(stacksUrl); + + try { + int statusCode = httpClient.executeMethod(getMethod); + + assertTrue (statusCode == HttpStatus.SC_OK); // HTTP status code 200 + + String responseBody = getMethod.getResponseBodyAsString(); + + assertTrue(responseBody != null); // Make sure response body is valid + + JsonElement jsonElement = new JsonParser().parse(new JsonReader(new StringReader(responseBody))); + + assertTrue (jsonElement != null); // Response was a JSON string + + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + assertTrue (jsonObject.has("items")); // Should have "items" entry + + JsonArray stacksArray = jsonObject.get("items").getAsJsonArray(); + + assertTrue (stacksArray.size() > 0); // Should have at least one stack + + } finally { + getMethod.releaseConnection(); + } + } +}