summaryrefslogtreecommitdiffstats
path: root/cloud-tenant-cd
diff options
context:
space:
mode:
authorMorten Tokle <mortent@verizonmedia.com>2020-06-10 14:28:15 +0200
committerMorten Tokle <mortent@verizonmedia.com>2020-06-15 12:34:46 +0200
commita19da10e4ab5997e3398754cdffc4e948f5ede60 (patch)
tree84e894cf0b3194ec6e8eff73c820e63610ccf16e /cloud-tenant-cd
parent5a5221dd531c1a927685a5d18f2fb9eb3e2a474c (diff)
Rename tenant-cd-impl -> cloud-tenant-cd
Diffstat (limited to 'cloud-tenant-cd')
-rw-r--r--cloud-tenant-cd/pom.xml56
-rw-r--r--cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/VespaTestRuntime.java62
-rw-r--r--cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpDeployment.java37
-rw-r--r--cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpEndpoint.java68
-rw-r--r--cloud-tenant-cd/src/main/resources/META-INF/services/ai.vespa.hosted.cd.TestRuntime2
5 files changed, 225 insertions, 0 deletions
diff --git a/cloud-tenant-cd/pom.xml b/cloud-tenant-cd/pom.xml
new file mode 100644
index 00000000000..1ba509d1a11
--- /dev/null
+++ b/cloud-tenant-cd/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>cloud-tenant-cd</artifactId>
+ <name>Vespa Cloud tenant CD implementation</name>
+ <description>Test library implementation for Vespa Cloud applications.</description>
+ <url>https://github.com/vespa-engine</url>
+ <packaging>jar</packaging>
+
+ <parent>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>parent</artifactId>
+ <version>7-SNAPSHOT</version>
+ <relativePath>../parent</relativePath>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>tenant-cd</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>security-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>vespajlib</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>config-provisioning</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>tenant-auth</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>hosted-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+</project> \ No newline at end of file
diff --git a/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/VespaTestRuntime.java b/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/VespaTestRuntime.java
new file mode 100644
index 00000000000..f77716064e5
--- /dev/null
+++ b/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/VespaTestRuntime.java
@@ -0,0 +1,62 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.hosted.cd;
+
+import ai.vespa.hosted.api.ControllerHttpClient;
+import ai.vespa.hosted.api.Properties;
+import ai.vespa.hosted.api.TestConfig;
+import ai.vespa.hosted.cd.http.HttpDeployment;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.zone.ZoneId;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+/**
+ * @author mortent
+ */
+public class VespaTestRuntime implements TestRuntime {
+ private final TestConfig config;
+ private final Deployment deploymentToTest;
+
+ public VespaTestRuntime() {
+ String configPath = System.getProperty("vespa.test.config");
+ TestConfig config = configPath != null ? fromFile(configPath) : fromController();
+ this.config = config;
+ this.deploymentToTest = new HttpDeployment(config.deployments().get(config.zone()), new ai.vespa.hosted.auth.EndpointAuthenticator(config.system()));
+ }
+
+// In use ?
+// /** Returns a copy of this runtime, with the given endpoint authenticator. */
+// public TestRuntime with(EndpointAuthenticator authenticator) {
+// return new TestRuntime(config, authenticator);
+// }
+
+ /** Returns the full id of the application this is testing. */
+ public ApplicationId application() { return config.application(); }
+
+ /** Returns the zone of the deployment this is testing. */
+ public ZoneId zone() { return config.zone(); }
+
+ /** Returns the deployment this is testing. */
+ @Override
+ public Deployment deploymentToTest() { return deploymentToTest; }
+
+ private static TestConfig fromFile(String path) {
+ try {
+ return TestConfig.fromJson(Files.readAllBytes(Paths.get(path)));
+ }
+ catch (Exception e) {
+ throw new IllegalArgumentException("Failed reading config from '" + path + "'!", e);
+ }
+ }
+
+ private static TestConfig fromController() {
+ ControllerHttpClient controller = new ai.vespa.hosted.auth.ApiAuthenticator().controller();
+ ApplicationId id = Properties.application();
+ Environment environment = Properties.environment().orElse(Environment.dev);
+ ZoneId zone = Properties.region().map(region -> ZoneId.from(environment, region))
+ .orElseGet(() -> controller.defaultZone(environment));
+ return controller.testConfig(id, zone);
+ }
+}
diff --git a/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpDeployment.java b/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpDeployment.java
new file mode 100644
index 00000000000..80d5416ab34
--- /dev/null
+++ b/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpDeployment.java
@@ -0,0 +1,37 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.hosted.cd.http;
+
+import ai.vespa.hosted.api.EndpointAuthenticator;
+import ai.vespa.hosted.cd.Deployment;
+import ai.vespa.hosted.cd.Endpoint;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.stream.Collectors;
+
+/**
+ * A remote deployment of a Vespa application, reachable over HTTP. Contains {@link HttpEndpoint}s.
+ *
+ * @author jonmv
+ */
+public class HttpDeployment implements Deployment {
+
+ private final Map<String, HttpEndpoint> endpoints;
+
+ /** Creates a representation of the given deployment endpoints, using the authenticator for data plane access. */
+ public HttpDeployment(Map<String, URI> endpoints, EndpointAuthenticator authenticator) {
+ this.endpoints = endpoints.entrySet().stream()
+ .collect(Collectors.toUnmodifiableMap(entry -> entry.getKey(),
+ entry -> new HttpEndpoint(entry.getValue(), authenticator)));
+ }
+
+ @Override
+ public Endpoint endpoint(String id) {
+ if ( ! endpoints.containsKey(id))
+ throw new NoSuchElementException("No cluster with id '" + id + "'");
+
+ return endpoints.get(id);
+ }
+
+}
diff --git a/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpEndpoint.java b/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpEndpoint.java
new file mode 100644
index 00000000000..a803fc3e0e2
--- /dev/null
+++ b/cloud-tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpEndpoint.java
@@ -0,0 +1,68 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.hosted.cd.http;
+
+import ai.vespa.hosted.api.EndpointAuthenticator;
+import ai.vespa.hosted.cd.Endpoint;
+
+import javax.net.ssl.SSLParameters;
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static java.net.URLEncoder.encode;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A remote endpoint in a {@link HttpDeployment} of a Vespa application, reachable over HTTP.
+ *
+ * @author jonmv
+ */
+public class HttpEndpoint implements Endpoint {
+
+ private final URI endpoint;
+ private final HttpClient client;
+ private final EndpointAuthenticator authenticator;
+
+ public HttpEndpoint(URI endpoint, EndpointAuthenticator authenticator) {
+ this.endpoint = requireNonNull(endpoint);
+ this.authenticator = requireNonNull(authenticator);
+ SSLParameters sslParameters = new SSLParameters();
+ sslParameters.setProtocols(new String[] {"TLSv1.2" });
+ this.client = HttpClient.newBuilder()
+ .sslContext(authenticator.sslContext())
+ .connectTimeout(Duration.ofSeconds(5))
+ .version(HttpClient.Version.HTTP_1_1)
+ .sslParameters(sslParameters)
+ .build();
+ }
+
+ @Override
+ public URI uri() {
+ return endpoint;
+ }
+
+ @Override
+ public <T> HttpResponse<T> send(HttpRequest.Builder request, HttpResponse.BodyHandler<T> handler) {
+ try {
+ return client.send(authenticator.authenticated(request).build(), handler);
+ }
+ catch (IOException | InterruptedException e) {
+ throw new RuntimeException(request.build() + " failed: " + e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public HttpRequest.Builder request(String path, Map<String, String> properties) {
+ return HttpRequest.newBuilder(endpoint.resolve(path +
+ properties.entrySet().stream()
+ .map(entry -> encode(entry.getKey(), UTF_8) + "=" + encode(entry.getValue(), UTF_8))
+ .collect(Collectors.joining("&", path.contains("?") ? "&" : "?", ""))));
+ }
+
+}
diff --git a/cloud-tenant-cd/src/main/resources/META-INF/services/ai.vespa.hosted.cd.TestRuntime b/cloud-tenant-cd/src/main/resources/META-INF/services/ai.vespa.hosted.cd.TestRuntime
new file mode 100644
index 00000000000..695fe363e4e
--- /dev/null
+++ b/cloud-tenant-cd/src/main/resources/META-INF/services/ai.vespa.hosted.cd.TestRuntime
@@ -0,0 +1,2 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+ai.vespa.hosted.cd.VespaTestRuntime \ No newline at end of file