diff --git a/plugins/discovery-ec2/qa/amazon-ec2/build.gradle b/plugins/discovery-ec2/qa/amazon-ec2/build.gradle index 2e1328d172194..90fac9e80cd78 100644 --- a/plugins/discovery-ec2/qa/amazon-ec2/build.gradle +++ b/plugins/discovery-ec2/qa/amazon-ec2/build.gradle @@ -28,12 +28,6 @@ dependencies { testCompile project(path: ':plugins:discovery-ec2', configuration: 'runtime') } -forbiddenApisTest { - // we are using jdk-internal instead of jdk-non-portable to allow for com.sun.net.httpserver.* usage - bundledSignatures -= 'jdk-non-portable' - bundledSignatures += 'jdk-internal' -} - final int ec2NumberOfNodes = 3 File ec2DiscoveryFile = new File(project.buildDir, 'generated-resources/nodes.uri') @@ -69,7 +63,7 @@ integTestCluster { ec2DiscoveryFile.setText(integTest.nodes.collect { n -> "${n.transportUri()}" }.join('\n'), 'UTF-8') File tmpFile = new File(node.cwd, 'wait.success') - ant.get(src: "http://${node.httpUri()}/_cluster/health?wait_for_nodes=>=${ec2NumberOfNodes}&wait_for_status=yellow", + ant.get(src: "http://${node.httpUri()}/", dest: tmpFile.toString(), ignoreerrors: true, retries: 10) diff --git a/plugins/discovery-ec2/qa/amazon-ec2/src/test/java/org/elasticsearch/discovery/ec2/AmazonEC2Fixture.java b/plugins/discovery-ec2/qa/amazon-ec2/src/test/java/org/elasticsearch/discovery/ec2/AmazonEC2Fixture.java index 7382d45e84fe1..0cf4cbdeadb34 100644 --- a/plugins/discovery-ec2/qa/amazon-ec2/src/test/java/org/elasticsearch/discovery/ec2/AmazonEC2Fixture.java +++ b/plugins/discovery-ec2/qa/amazon-ec2/src/test/java/org/elasticsearch/discovery/ec2/AmazonEC2Fixture.java @@ -18,264 +18,177 @@ */ package org.elasticsearch.discovery.ec2; -import com.amazonaws.util.IOUtils; -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import org.elasticsearch.common.SuppressForbidden; -import org.elasticsearch.mocksocket.MockHttpServer; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.test.fixture.AbstractHttpFixture; import javax.xml.XMLConstants; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamWriter; import java.io.IOException; import java.io.StringWriter; -import java.lang.management.ManagementFactory; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.List; +import java.util.Objects; import java.util.UUID; -import java.util.function.Predicate; -import static java.util.Collections.singleton; -import static java.util.Collections.singletonList; +import static java.nio.charset.StandardCharsets.UTF_8; /** * {@link AmazonEC2Fixture} is a fixture that emulates an AWS EC2 service. - *

- * It starts an asynchronous socket server that binds to a random local port. */ -public class AmazonEC2Fixture { +public class AmazonEC2Fixture extends AbstractHttpFixture { + + private final Path nodes; + + private AmazonEC2Fixture(final String workingDir, final String nodesUriPath) { + super(workingDir); + this.nodes = toPath(Objects.requireNonNull(nodesUriPath)); + } public static void main(String[] args) throws Exception { if (args == null || args.length != 2) { throw new IllegalArgumentException("AmazonEC2Fixture "); } - final InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); - final HttpServer httpServer = MockHttpServer.createHttp(socketAddress, 0); - - try { - final Path workingDirectory = toPath(args[0]); - /// Writes the PID of the current Java process in a `pid` file located in the working directory - writeFile(workingDirectory, "pid", ManagementFactory.getRuntimeMXBean().getName().split("@")[0]); - - final String addressAndPort = addressToString(httpServer.getAddress()); - // Writes the address and port of the http server in a `ports` file located in the working directory - writeFile(workingDirectory, "ports", addressAndPort); - - httpServer.createContext("/", new ResponseHandler(toPath(args[1]))); - httpServer.start(); - - // Wait to be killed - Thread.sleep(Long.MAX_VALUE); - - } finally { - httpServer.stop(0); - } - } - - @SuppressForbidden(reason = "Paths#get is fine - we don't have environment here") - private static Path toPath(final String dir) { - return Paths.get(dir); - } - - private static void writeFile(final Path dir, final String fileName, final String content) throws IOException { - final Path tempPidFile = Files.createTempFile(dir, null, null); - Files.write(tempPidFile, singleton(content)); - Files.move(tempPidFile, dir.resolve(fileName), StandardCopyOption.ATOMIC_MOVE); - } - - private static String addressToString(final SocketAddress address) { - final InetSocketAddress inetSocketAddress = (InetSocketAddress) address; - if (inetSocketAddress.getAddress() instanceof Inet6Address) { - return "[" + inetSocketAddress.getHostString() + "]:" + inetSocketAddress.getPort(); - } else { - return inetSocketAddress.getHostString() + ":" + inetSocketAddress.getPort(); - } + final AmazonEC2Fixture fixture = new AmazonEC2Fixture(args[0], args[1]); + fixture.listen(); } - static class ResponseHandler implements HttpHandler { - - private final Path discoveryPath; - - ResponseHandler(final Path discoveryPath) { - this.discoveryPath = discoveryPath; - } - - @Override - public void handle(HttpExchange exchange) throws IOException { - RestStatus responseStatus = RestStatus.INTERNAL_SERVER_ERROR; - String responseBody = null; - String responseContentType = "text/plain"; - - final String path = exchange.getRequestURI().getRawPath(); - if ("/".equals(path)) { - final String method = exchange.getRequestMethod(); - final Headers headers = exchange.getRequestHeaders(); - - if ("GET".equals(method) && matchingHeader(headers, "User-agent", v -> v.startsWith("Apache Ant"))) { - // Replies to the fixture's waiting condition - responseStatus = RestStatus.OK; - responseBody = "AmazonEC2Fixture"; - - } else if ("POST".equals(method) && matchingHeader(headers, "User-agent", v -> v.startsWith("aws-sdk-java"))) { - // Simulate an EC2 DescribeInstancesResponse - responseStatus = RestStatus.OK; - responseContentType = "text/xml; charset=UTF-8"; - - for (NameValuePair parse : URLEncodedUtils.parse(IOUtils.toString(exchange.getRequestBody()), StandardCharsets.UTF_8)) { - if ("Action".equals(parse.getName())) { - responseBody = generateDescribeInstancesResponse(); - break; - } - } - } - } - - final byte[] response = responseBody != null ? responseBody.getBytes(StandardCharsets.UTF_8) : new byte[0]; - exchange.sendResponseHeaders(responseStatus.getStatus(), response.length); - exchange.getResponseHeaders().put("Content-Type", singletonList(responseContentType)); - if (response.length > 0) { - exchange.getResponseBody().write(response); - } - exchange.close(); - } - - /** Checks if the given {@link Headers} contains a header with a given name which has a value that matches a predicate **/ - private boolean matchingHeader(final Headers headers, final String headerName, final Predicate predicate) { - if (headers != null && headers.isEmpty() == false) { - final List values = headers.get(headerName); - if (values != null) { - for (String value : values) { - if (predicate.test(value)) { - return true; - } + @Override + protected Response handle(final Request request) throws IOException { + if ("/".equals(request.getPath()) && ("POST".equals(request.getMethod()))) { + final String userAgent = request.getHeader("User-Agent"); + if (userAgent != null && userAgent.startsWith("aws-sdk-java")) { + // Simulate an EC2 DescribeInstancesResponse + byte[] responseBody = EMPTY_BYTE; + for (NameValuePair parse : URLEncodedUtils.parse(new String(request.getBody(), UTF_8), UTF_8)) { + if ("Action".equals(parse.getName())) { + responseBody = generateDescribeInstancesResponse(); + break; } } + return new Response(RestStatus.OK.getStatus(), contentType("text/xml; charset=UTF-8"), responseBody); } - return false; } + return null; + } - /** - * Generates a XML response that describe the EC2 instances - */ - private String generateDescribeInstancesResponse() { - final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory(); - xmlOutputFactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); - - final StringWriter out = new StringWriter(); - XMLStreamWriter sw; - try { - sw = xmlOutputFactory.createXMLStreamWriter(out); - sw.writeStartDocument(); + /** + * Generates a XML response that describe the EC2 instances + */ + private byte[] generateDescribeInstancesResponse() { + final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory(); + xmlOutputFactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); - String namespace = "http://ec2.amazonaws.com/doc/2013-02-01/"; - sw.setDefaultNamespace(namespace); - sw.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, "DescribeInstancesResponse", namespace); + final StringWriter out = new StringWriter(); + XMLStreamWriter sw; + try { + sw = xmlOutputFactory.createXMLStreamWriter(out); + sw.writeStartDocument(); + + String namespace = "http://ec2.amazonaws.com/doc/2013-02-01/"; + sw.setDefaultNamespace(namespace); + sw.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, "DescribeInstancesResponse", namespace); + { + sw.writeStartElement("requestId"); + sw.writeCharacters(UUID.randomUUID().toString()); + sw.writeEndElement(); + + sw.writeStartElement("reservationSet"); { - sw.writeStartElement("requestId"); - sw.writeCharacters(UUID.randomUUID().toString()); - sw.writeEndElement(); + if (Files.exists(nodes)) { + for (String address : Files.readAllLines(nodes)) { - sw.writeStartElement("reservationSet"); - { - if (Files.exists(discoveryPath)) { - for (String address : Files.readAllLines(discoveryPath)) { + sw.writeStartElement("item"); + { + sw.writeStartElement("reservationId"); + sw.writeCharacters(UUID.randomUUID().toString()); + sw.writeEndElement(); - sw.writeStartElement("item"); + sw.writeStartElement("instancesSet"); { - sw.writeStartElement("reservationId"); - sw.writeCharacters(UUID.randomUUID().toString()); - sw.writeEndElement(); - - sw.writeStartElement("instancesSet"); + sw.writeStartElement("item"); { - sw.writeStartElement("item"); - { - sw.writeStartElement("instanceId"); - sw.writeCharacters(UUID.randomUUID().toString()); - sw.writeEndElement(); - - sw.writeStartElement("imageId"); - sw.writeCharacters(UUID.randomUUID().toString()); - sw.writeEndElement(); - - sw.writeStartElement("instanceState"); - { - sw.writeStartElement("code"); - sw.writeCharacters("16"); - sw.writeEndElement(); + sw.writeStartElement("instanceId"); + sw.writeCharacters(UUID.randomUUID().toString()); + sw.writeEndElement(); - sw.writeStartElement("name"); - sw.writeCharacters("running"); - sw.writeEndElement(); - } - sw.writeEndElement(); + sw.writeStartElement("imageId"); + sw.writeCharacters(UUID.randomUUID().toString()); + sw.writeEndElement(); - sw.writeStartElement("privateDnsName"); - sw.writeCharacters(address); + sw.writeStartElement("instanceState"); + { + sw.writeStartElement("code"); + sw.writeCharacters("16"); sw.writeEndElement(); - sw.writeStartElement("dnsName"); - sw.writeCharacters(address); + sw.writeStartElement("name"); + sw.writeCharacters("running"); sw.writeEndElement(); + } + sw.writeEndElement(); - sw.writeStartElement("instanceType"); - sw.writeCharacters("m1.medium"); - sw.writeEndElement(); + sw.writeStartElement("privateDnsName"); + sw.writeCharacters(address); + sw.writeEndElement(); - sw.writeStartElement("placement"); - { - sw.writeStartElement("availabilityZone"); - sw.writeCharacters("use-east-1e"); - sw.writeEndElement(); + sw.writeStartElement("dnsName"); + sw.writeCharacters(address); + sw.writeEndElement(); - sw.writeEmptyElement("groupName"); + sw.writeStartElement("instanceType"); + sw.writeCharacters("m1.medium"); + sw.writeEndElement(); - sw.writeStartElement("tenancy"); - sw.writeCharacters("default"); - sw.writeEndElement(); - } + sw.writeStartElement("placement"); + { + sw.writeStartElement("availabilityZone"); + sw.writeCharacters("use-east-1e"); sw.writeEndElement(); - sw.writeStartElement("privateIpAddress"); - sw.writeCharacters(address); - sw.writeEndElement(); + sw.writeEmptyElement("groupName"); - sw.writeStartElement("ipAddress"); - sw.writeCharacters(address); + sw.writeStartElement("tenancy"); + sw.writeCharacters("default"); sw.writeEndElement(); } sw.writeEndElement(); + + sw.writeStartElement("privateIpAddress"); + sw.writeCharacters(address); + sw.writeEndElement(); + + sw.writeStartElement("ipAddress"); + sw.writeCharacters(address); + sw.writeEndElement(); } sw.writeEndElement(); } sw.writeEndElement(); } + sw.writeEndElement(); } - sw.writeEndElement(); } sw.writeEndElement(); - - sw.writeEndDocument(); - sw.flush(); } - } catch (Exception e) { - throw new RuntimeException(e); + sw.writeEndElement(); + + sw.writeEndDocument(); + sw.flush(); } - return out.toString(); + } catch (Exception e) { + throw new RuntimeException(e); } + return out.toString().getBytes(UTF_8); + } + + @SuppressForbidden(reason = "Paths#get is fine - we don't have environment here") + private static Path toPath(final String dir) { + return Paths.get(dir); } } diff --git a/plugins/discovery-ec2/qa/amazon-ec2/src/test/resources/rest-api-spec/test/discovery_ec2/10_basic.yml b/plugins/discovery-ec2/qa/amazon-ec2/src/test/resources/rest-api-spec/test/discovery_ec2/10_basic.yml index f9d42eee088a3..682327b72dd9e 100644 --- a/plugins/discovery-ec2/qa/amazon-ec2/src/test/resources/rest-api-spec/test/discovery_ec2/10_basic.yml +++ b/plugins/discovery-ec2/qa/amazon-ec2/src/test/resources/rest-api-spec/test/discovery_ec2/10_basic.yml @@ -1,4 +1,10 @@ # Integration tests for discovery-ec2 +setup: + - do: + cluster.health: + wait_for_status: green + wait_for_nodes: ${expected_nodes} + --- "All nodes are correctly discovered":