/*
* Copyright 2016 ThoughtWorks, Inc.
*
* Licensed 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 com.thoughtworks.go.agent.testhelper;
import org.apache.commons.io.FileUtils;
import org.apache.poi.util.IOUtils;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.EnumSet;
import java.util.Properties;
import static com.thoughtworks.go.agent.testhelper.FakeBootstrapperServer.TestResource.*;
import static com.thoughtworks.go.util.FileDigester.md5DigestOfStream;
public class FakeBootstrapperServer extends BlockJUnit4ClassRunner {
public enum TestResource {
TEST_AGENT(Resource.newClassPathResource("testdata/gen/test-agent.jar")),
TEST_AGENT_LAUNCHER(Resource.newClassPathResource("testdata/gen/agent-launcher.jar")),
TEST_AGENT_PLUGINS(Resource.newClassPathResource("testdata/agent-plugins.zip")),
TEST_TFS_IMPL(Resource.newClassPathResource("testdata/gen/tfs-impl-14.jar")),;
private final Resource source;
TestResource(Resource source) {
this.source = source;
}
public String getMd5() throws IOException {
try (InputStream in = source.getInputStream()) {
return md5DigestOfStream(in);
}
}
public void copyTo(OutputStream outputStream) throws IOException {
try (InputStream in = source.getInputStream()) {
IOUtils.copy(in, outputStream);
}
}
// Because the resource can be a jarresource, which extracts to dir instead of a simple copy.
public void copyTo(File output) throws IOException {
try (InputStream in = source.getInputStream()) {
FileUtils.copyToFile(in, output);
}
}
}
private Server server;
public FakeBootstrapperServer(Class<?> testClass) throws InitializationError {
super(testClass);
}
public void run(RunNotifier runNotifier) {
try {
// could be smarter if this is too slow, start only if not started already
// shut down on JVM shut down instead
start();
} catch (Exception e) {
runNotifier.fireTestFailure(new Failure(getDescription(), e));
}
try {
super.run(runNotifier);
} finally {
try {
stop();
} catch (Exception e) {
runNotifier.fireTestFailure(new Failure(getDescription(), e));
}
}
}
public void start() throws Exception {
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(9090);
server.addConnector(connector);
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setCertAlias("cruise");
sslContextFactory.setKeyStoreResource(Resource.newClassPathResource("testdata/fake-server-keystore"));
sslContextFactory.setKeyStorePassword("serverKeystorepa55w0rd");
ServerConnector secureConnnector = new ServerConnector(server,
new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
new HttpConnectionFactory(new HttpConfiguration())
);
secureConnnector.setPort(9091);
server.addConnector(secureConnnector);
WebAppContext wac = new WebAppContext(".", "/go");
ServletHolder holder = new ServletHolder();
holder.setServlet(new HttpServlet() {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getOutputStream().println("Hello");
}
});
wac.addServlet(holder, "/hello");
addFakeAgentBinaryServlet(wac, "/admin/agent", TEST_AGENT);
addFakeAgentBinaryServlet(wac, "/admin/agent-launcher.jar", TEST_AGENT_LAUNCHER);
addFakeAgentBinaryServlet(wac, "/admin/agent-plugins.zip", TEST_AGENT_PLUGINS);
addFakeAgentBinaryServlet(wac, "/admin/tfs-impl.jar", TEST_TFS_IMPL);
addlatestAgentStatusCall(wac);
addDefaultServlet(wac);
server.setHandler(wac);
server.setStopAtShutdown(true);
server.start();
}
public static final class AgentStatusApi extends HttpServlet {
public static String status = "disabled";
public static Properties pluginProps = new Properties();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Plugins-Status", status);
pluginProps.setProperty("Active Mock Bundle 1", "1.1.1");
pluginProps.setProperty("Active Mock Bundle 2", "2.2.2");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
pluginProps.store(baos, "Go Plugins for Testing");
resp.getOutputStream().write(baos.toByteArray());
baos.close();
}
}
private void addlatestAgentStatusCall(WebAppContext wac) {
wac.addServlet(AgentStatusApi.class, "/admin/latest-agent.status");
}
public static final class BreakpointFriendlyFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String requestURI = ((HttpServletRequest) servletRequest).getRequestURI();
filterChain.doFilter(servletRequest, servletResponse);
}
public void destroy() {
}
}
private void addDefaultServlet(WebAppContext wac) {
wac.addFilter(BreakpointFriendlyFilter.class, "*", EnumSet.of(DispatcherType.REQUEST));
}
private static void addFakeAgentBinaryServlet(WebAppContext wac, final String pathSpec, final TestResource resource) {
ServletHolder holder = new ServletHolder();
holder.setServlet(new AgentBinariesServlet(resource));
wac.addServlet(holder, pathSpec);
}
public void stop() throws Exception {
server.stop();
server.join();
}
}