summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/Authenticator.java23
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java43
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/Properties.java55
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/TestConfig.java70
-rw-r--r--tenant-auth/src/main/java/ai/vespa/hosted/auth/Authenticator.java38
-rw-r--r--tenant-cd/src/main/java/ai/vespa/hosted/cd/TestConfig.java101
-rw-r--r--tenant-cd/src/main/java/ai/vespa/hosted/cd/TestRuntime.java77
-rw-r--r--tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpDeployment.java15
-rw-r--r--tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpEndpoint.java8
-rw-r--r--vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java7
10 files changed, 296 insertions, 141 deletions
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/Authenticator.java b/hosted-api/src/main/java/ai/vespa/hosted/api/Authenticator.java
new file mode 100644
index 00000000000..acd8a215e7f
--- /dev/null
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/Authenticator.java
@@ -0,0 +1,23 @@
+package ai.vespa.hosted.api;
+
+import javax.net.ssl.SSLContext;
+import java.net.http.HttpRequest;
+import java.util.Optional;
+
+/**
+ * Adds environment dependent authentication to HTTP request against hosted Vespa API and deployments.
+ *
+ * @author jonmv
+ */
+public interface Authenticator {
+
+ /** Returns an SSLContext which provides authentication against a Vespa endpoint. */
+ SSLContext sslContext();
+
+ /** Adds necessary authentication to the given HTTP request builder, to pass the data plane of a Vespa endpoint. */
+ HttpRequest.Builder authenticated(HttpRequest.Builder request);
+
+ /** Returns a client authenticated to talk to the hosted Vespa API. */
+ ControllerHttpClient controller();
+
+}
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
index d59eb166e2b..153a03e56d2 100644
--- a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
@@ -104,14 +104,14 @@ public abstract class ControllerHttpClient {
/** Deactivates the deployment of the given application in the given zone. */
public String deactivate(ApplicationId id, ZoneId zone) {
- return asText(send(request(HttpRequest.newBuilder(deploymentPath(id, zone))
- .timeout(Duration.ofSeconds(10)),
- DELETE)));
+ return asString(send(request(HttpRequest.newBuilder(deploymentPath(id, zone))
+ .timeout(Duration.ofSeconds(10)),
+ DELETE)));
}
- /** Returns the default {@link Environment#dev} {@link ZoneId}, to use for development deployments. */
- public ZoneId devZone() {
- Inspector rootObject = toInspector(send(request(HttpRequest.newBuilder(defaultRegionPath())
+ /** Returns the default {@link ZoneId} for the given environment, if any. */
+ public ZoneId defaultZone(Environment environment) {
+ Inspector rootObject = toInspector(send(request(HttpRequest.newBuilder(defaultRegionPath(environment))
.timeout(Duration.ofSeconds(10)),
GET)));
return ZoneId.from("dev", rootObject.field("name").asString());
@@ -125,6 +125,11 @@ public abstract class ControllerHttpClient {
.field("compileVersion").asString();
}
+ /** Returns the test config for functional and verification tests of the indicated Vespa deployment. */
+ public TestConfig testConfig(ApplicationId id, ZoneId zone) {
+ return TestConfig.fromJson(asBytes(send(request(HttpRequest.newBuilder(testConfigPath(id, zone)), GET))));
+ }
+
/** Returns the sorted list of log entries after the given after from the deployment job of the given ids. */
public DeploymentLog deploymentLog(ApplicationId id, ZoneId zone, long run, long after) {
return toDeploymentLog(send(request(HttpRequest.newBuilder(runPath(id, zone, run, after))
@@ -137,6 +142,7 @@ public abstract class ControllerHttpClient {
return deploymentLog(id, zone, run, -1);
}
+ /** Returns an authenticated request from the given input. Override this for, e.g., request signing. */
protected HttpRequest request(HttpRequest.Builder request, Method method, Supplier<InputStream> data) {
return request.method(method.name(), ofInputStream(data)).build();
}
@@ -181,15 +187,22 @@ public abstract class ControllerHttpClient {
"deploy", jobNameOf(zone));
}
+ private URI jobPath(ApplicationId id, ZoneId zone) {
+ return concatenated(instancePath(id), "job", jobNameOf(zone));
+ }
+
+ private URI testConfigPath(ApplicationId id, ZoneId zone) {
+ return concatenated(instancePath(id), "test-config");
+ }
+
private URI runPath(ApplicationId id, ZoneId zone, long run, long after) {
- return withQuery(concatenated(instancePath(id),
- "job", jobNameOf(zone),
+ return withQuery(concatenated(jobPath(id, zone),
"run", Long.toString(run)),
"after", Long.toString(after));
}
- private URI defaultRegionPath() {
- return concatenated(endpoint, "zone", "v1", "environment", Environment.dev.value(), "default");
+ private URI defaultRegionPath(Environment environment) {
+ return concatenated(endpoint, "zone", "v1", "environment", environment.value(), "default");
}
private static URI concatenated(URI base, String... parts) {
@@ -247,9 +260,15 @@ public abstract class ControllerHttpClient {
return streamer;
}
- private static String asText(HttpResponse<byte[]> response) {
+ /** Returns the response body as a String, or throws if the status code is non-2XX. */
+ private static String asString(HttpResponse<byte[]> response) {
+ return new String(asBytes(response), UTF_8);
+ }
+
+ /** Returns the response body as a byte array, or throws if the status code is non-2XX. */
+ private static byte[] asBytes(HttpResponse<byte[]> response) {
toInspector(response);
- return new String(response.body(), UTF_8);
+ return response.body();
}
/** Returns an {@link Inspector} for the assumed JSON formatted response, or throws if the status code is non-2XX. */
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/Properties.java b/hosted-api/src/main/java/ai/vespa/hosted/api/Properties.java
new file mode 100644
index 00000000000..61893a30e7e
--- /dev/null
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/Properties.java
@@ -0,0 +1,55 @@
+package ai.vespa.hosted.api;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.RegionName;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Optional;
+
+/**
+ * Utilities and common definitions of system properties defining a Vespa application project.
+ *
+ * @author jonmv
+ */
+public class Properties {
+
+ public static ApplicationId application() {
+ return ApplicationId.from(requireNonBlankProperty("tenant"),
+ requireNonBlankProperty("application"),
+ getNonBlankProperty("instance").orElse("default"));
+ }
+
+ public static Optional<Environment> environment() {
+ return getNonBlankProperty("environment").map(Environment::from);
+ }
+
+ public static Optional<RegionName> region() {
+ return getNonBlankProperty("region").map(RegionName::from);
+ }
+
+ public static URI endpoint() {
+ return URI.create(requireNonBlankProperty("endpoint"));
+ }
+
+ public static Path privateKeyFile() {
+ return Paths.get(requireNonBlankProperty("privateKeyFile"));
+ }
+
+ public static Path certificateFile() {
+ return Paths.get(requireNonBlankProperty("certificateFile"));
+ }
+
+ /** Returns the system property with the given name if it is set, or empty. */
+ public static Optional<String> getNonBlankProperty(String name) {
+ return Optional.ofNullable(System.getProperty(name)).filter(value -> ! value.isBlank());
+ }
+
+ /** Returns the system property with the given name if it is set, or throws. */
+ public static String requireNonBlankProperty(String name) {
+ return getNonBlankProperty(name).orElseThrow(() -> new IllegalStateException("Missing required property '" + name + "'"));
+ }
+
+}
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/TestConfig.java b/hosted-api/src/main/java/ai/vespa/hosted/api/TestConfig.java
new file mode 100644
index 00000000000..a0c679f312e
--- /dev/null
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/TestConfig.java
@@ -0,0 +1,70 @@
+package ai.vespa.hosted.api;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.slime.Inspector;
+import com.yahoo.slime.JsonDecoder;
+import com.yahoo.slime.ObjectTraverser;
+import com.yahoo.slime.Slime;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Config required to run a functional or verification test of a Vespa deployment.
+ *
+ * @author jvenstad
+ */
+public class TestConfig {
+
+ private final ApplicationId application;
+ private final ZoneId zone;
+ private final SystemName system;
+ private final Map<ZoneId, Map<String, URI>> deployments;
+
+ public TestConfig(ApplicationId application, ZoneId zone, SystemName system, Map<ZoneId, Map<String, URI>> deployments) {
+ if ( ! deployments.containsKey(zone))
+ throw new IllegalArgumentException("Config must contain a deployment for its zone, but only does for " + deployments.keySet());
+ this.application = requireNonNull(application);
+ this.zone = requireNonNull(zone);
+ this.system = requireNonNull(system);
+ this.deployments = deployments.entrySet().stream()
+ .collect(Collectors.toUnmodifiableMap(entry -> entry.getKey(),
+ entry -> Map.copyOf(entry.getValue())));
+ }
+
+ public static TestConfig fromJson(byte[] jsonBytes) {
+ Inspector config = new JsonDecoder().decode(new Slime(), jsonBytes).get();
+ ApplicationId application = ApplicationId.fromSerializedForm(config.field("application").asString());
+ ZoneId zone = ZoneId.from(config.field("zone").asString());
+ SystemName system = SystemName.from(config.field("system").asString());
+ Map<ZoneId, Map<String, URI>> deployments = new HashMap<>();
+ config.field("clusterEndpoints").traverse((ObjectTraverser) (zoneId, endpointsObject) -> {
+ Map<String, URI> endpoints = new HashMap<>();
+ endpointsObject.traverse((ObjectTraverser) (cluster, uri) -> endpoints.put(cluster, URI.create(uri.asString())));
+ deployments.put(ZoneId.from(zoneId), endpoints);
+ });
+ return new TestConfig(application, zone, system, deployments);
+ }
+
+ /** Returns the full id of the application to test. */
+ public ApplicationId application() { return application; }
+
+ /** Returns the zone of the deployment to test. */
+ public ZoneId zone() { return zone; }
+
+ /** Returns an immutable view of deployments, per zone, of the application to test. */
+ public Map<ZoneId, Map<String, URI>> allDeployments() { return deployments; }
+
+ /** Returns the deployment to test in this test runtime. */
+ public Map<String, URI> deploymentToTest() { return deployments.get(zone); }
+
+ /** Returns the hosted Vespa system this is run against. */
+ public SystemName system() { return system; }
+
+}
diff --git a/tenant-auth/src/main/java/ai/vespa/hosted/auth/Authenticator.java b/tenant-auth/src/main/java/ai/vespa/hosted/auth/Authenticator.java
index 33c1d09c828..f2de1f1e210 100644
--- a/tenant-auth/src/main/java/ai/vespa/hosted/auth/Authenticator.java
+++ b/tenant-auth/src/main/java/ai/vespa/hosted/auth/Authenticator.java
@@ -1,6 +1,7 @@
package ai.vespa.hosted.auth;
import ai.vespa.hosted.api.ControllerHttpClient;
+import ai.vespa.hosted.api.Properties;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.security.KeyUtils;
import com.yahoo.security.SslContextBuilder;
@@ -20,17 +21,21 @@ import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.Optional;
+import static ai.vespa.hosted.api.Properties.getNonBlankProperty;
+import static ai.vespa.hosted.api.Properties.requireNonBlankProperty;
+
/**
- * Authenticates {@link HttpRequest}s against a hosted Vespa application based on mutual TLS.
+ * Authenticates against the hosted Vespa API using private key signatures, and against Vespa applications using mutual TLS.
*
* @author jonmv
*/
-public class Authenticator {
+public class Authenticator implements ai.vespa.hosted.api.Authenticator {
- /** Returns an SSLContext which provides authentication against a Vespa endpoint.
- *
+ /**
* If {@code System.getProperty("vespa.test.credentials.root")} is set, key and certificate files
- * "key" and "cert" in that directory are used; otherwise, the system default SSLContext is returned. */
+ * "key" and "cert" in that directory are used; otherwise, the system default SSLContext is returned.
+ */
+ @Override
public SSLContext sslContext() {
try {
Optional<String> credentialsRootProperty = getNonBlankProperty("vespa.test.credentials.root");
@@ -57,28 +62,17 @@ public class Authenticator {
}
}
- /** Adds necessary authentication to the given HTTP request builder, to be verified by a Vespa endpoint. */
+ @Override
public HttpRequest.Builder authenticated(HttpRequest.Builder request) {
return request;
}
- /** Returns an authenticated controller client. */
+ /** Returns an authenticating controller client, using the (overridable) project properties of this Vespa application. */
+ @Override
public ControllerHttpClient controller() {
- ApplicationId id = ApplicationId.from(requireNonBlankProperty("tenant"),
- requireNonBlankProperty("application"),
- getNonBlankProperty("instance").orElse("default"));
- URI endpoint = URI.create(requireNonBlankProperty("endpoint"));
- Path privateKeyFile = Paths.get(requireNonBlankProperty("privateKeyFile"));
-
- return ControllerHttpClient.withSignatureKey(endpoint, privateKeyFile, id);
- }
-
- static Optional<String> getNonBlankProperty(String name) {
- return Optional.ofNullable(System.getProperty(name)).filter(value -> ! value.isBlank());
- }
-
- static String requireNonBlankProperty(String name) {
- return getNonBlankProperty(name).orElseThrow(() -> new IllegalStateException("Missing required property '" + name + "'"));
+ return ControllerHttpClient.withSignatureKey(Properties.endpoint(),
+ Properties.privateKeyFile(),
+ Properties.application());
}
}
diff --git a/tenant-cd/src/main/java/ai/vespa/hosted/cd/TestConfig.java b/tenant-cd/src/main/java/ai/vespa/hosted/cd/TestConfig.java
deleted file mode 100644
index e441254cff7..00000000000
--- a/tenant-cd/src/main/java/ai/vespa/hosted/cd/TestConfig.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package ai.vespa.hosted.cd;
-
-import ai.vespa.hosted.api.ControllerHttpClient;
-import ai.vespa.hosted.auth.Authenticator;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.slime.ArrayTraverser;
-import com.yahoo.slime.Inspector;
-import com.yahoo.slime.JsonDecoder;
-import com.yahoo.slime.ObjectTraverser;
-import com.yahoo.config.provision.zone.ZoneId;
-import com.yahoo.slime.Slime;
-
-import java.net.URI;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * The place to obtain environment-dependent configuration for the current test run.
- *
- * If the system property 'vespa.test.config' is set, this class attempts to parse config
- * from a JSON file at that location -- otherwise, attempts to access the config will return null.
- *
- * @author jvenstad
- */
-public class TestConfig {
-
- private static TestConfig theConfig;
-
- private final ApplicationId application;
- private final ZoneId zone;
- private final SystemName system;
- private final Map<ZoneId, Deployment> deployments;
-
- private TestConfig(ApplicationId application, ZoneId zone, SystemName system, Map<ZoneId, Deployment> deployments) {
- this.application = application;
- this.zone = zone;
- this.system = system;
- this.deployments = Map.copyOf(deployments);
- }
-
- /** Returns the config for this test, or null if it has not been provided. */
- public static synchronized TestConfig get() {
- if (theConfig == null) {
- String configPath = System.getProperty("vespa.test.config");
- theConfig = configPath != null ? fromFile(configPath) : fromController();
- }
- return theConfig;
- }
-
- /** Returns the full id of the application to be tested. */
- public ApplicationId application() { return application; }
-
- /** Returns the zone of the deployment to test. */
- public ZoneId zone() { return zone; }
-
- /** Returns an immutable view of all configured endpoints for each zone of the application to test. */
- public Map<ZoneId, Deployment> allDeployments() { return deployments; }
-
- /** Returns the deployment to test in this test runtime. */
- public Deployment deploymentToTest() { return deployments.get(zone); }
-
- /** Returns the system this is run against. */
- public SystemName system() { return system; }
-
- static TestConfig fromFile(String path) {
- if (path == null)
- return null;
-
- try {
- return fromJson(Files.readAllBytes(Paths.get(path)));
- }
- catch (Exception e) {
- throw new IllegalArgumentException("Failed reading config from '" + path + "'!", e);
- }
- }
-
- static TestConfig fromController() {
- ControllerHttpClient controller = new Authenticator().controller();
- return null;
- }
-
- static TestConfig fromJson(byte[] jsonBytes) {
- Inspector config = new JsonDecoder().decode(new Slime(), jsonBytes).get();
- ApplicationId application = ApplicationId.fromSerializedForm(config.field("application").asString());
- ZoneId zone = ZoneId.from(config.field("zone").asString());
- SystemName system = SystemName.from(config.field("system").asString());
- Map<ZoneId, Deployment> endpoints = new HashMap<>();
- config.field("endpoints").traverse((ObjectTraverser) (zoneId, endpointArray) -> {
- List<URI> uris = new ArrayList<>();
- endpointArray.traverse((ArrayTraverser) (__, uri) -> uris.add(URI.create(uri.asString())));
- endpoints.put(ZoneId.from(zoneId), null); // TODO jvenstad
- });
- return new TestConfig(application, zone, system, endpoints);
- }
-
-}
diff --git a/tenant-cd/src/main/java/ai/vespa/hosted/cd/TestRuntime.java b/tenant-cd/src/main/java/ai/vespa/hosted/cd/TestRuntime.java
new file mode 100644
index 00000000000..fa09d7037c9
--- /dev/null
+++ b/tenant-cd/src/main/java/ai/vespa/hosted/cd/TestRuntime.java
@@ -0,0 +1,77 @@
+package ai.vespa.hosted.cd;
+
+import ai.vespa.hosted.api.Authenticator;
+import ai.vespa.hosted.api.ControllerHttpClient;
+import ai.vespa.hosted.api.Properties;
+import ai.vespa.hosted.api.TestConfig;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.RegionName;
+import com.yahoo.config.provision.zone.ZoneId;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import static ai.vespa.hosted.api.TestConfig.fromJson;
+
+/**
+ * The place to obtain environment-dependent configuration for test of a Vespa deployment.
+ *
+ * @author jvenstad
+ */
+public class TestRuntime {
+
+ private static TestRuntime theRuntime;
+
+ private final TestConfig config;
+ private final Authenticator authenticator;
+
+ private TestRuntime(TestConfig config, Authenticator authenticator) {
+ this.config = config;
+ this.authenticator = authenticator;
+ }
+
+ /**
+ * Returns the config for this test, or null if it has not been provided.
+ *
+ * If the system property {@code "vespa.test.config"} is set (to a file path), a file at that location
+ * is attempted read, and config parsed from it.
+ * Otherwise, config is fetched over HTTP from the hosted Vespa API, assuming the deployment indicated
+ * by the optional {@code "environment"} and {@code "region"} system properties exists.
+ * When environment is not specified, it defaults to {@link Environment#dev},
+ * while region must be set unless the environment is {@link Environment#dev} or {@link Environment#perf}.
+ */
+ public static synchronized TestRuntime get() {
+ if (theRuntime == null) {
+ String configPath = System.getProperty("vespa.test.config");
+ Authenticator authenticator = new ai.vespa.hosted.auth.Authenticator();
+ theRuntime = new TestRuntime(configPath != null ? fromFile(configPath) : fromController(authenticator),
+ authenticator);
+ }
+ return theRuntime;
+ }
+
+ /** Returns a copy of this runtime, with the given authenticator. */
+ public TestRuntime with(Authenticator authenticator) {
+ return new TestRuntime(config, authenticator);
+ }
+
+ 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(Authenticator authenticator) {
+ ControllerHttpClient controller = authenticator.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/tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpDeployment.java b/tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpDeployment.java
index 8eebe04ebef..6234b54c0a1 100644
--- a/tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpDeployment.java
+++ b/tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpDeployment.java
@@ -1,9 +1,13 @@
package ai.vespa.hosted.cd.http;
+import ai.vespa.hosted.api.Authenticator;
import ai.vespa.hosted.cd.Deployment;
import ai.vespa.hosted.cd.Endpoint;
import ai.vespa.hosted.cd.TestDeployment;
-import ai.vespa.hosted.cd.TestEndpoint;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.stream.Collectors;
/**
* A remote deployment of a Vespa application, reachable over HTTP. Contains {@link HttpEndpoint}s.
@@ -12,6 +16,15 @@ import ai.vespa.hosted.cd.TestEndpoint;
*/
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, Authenticator authenticator) {
+ this.endpoints = endpoints.entrySet().stream()
+ .collect(Collectors.toUnmodifiableMap(entry -> entry.getKey(),
+ entry -> new HttpEndpoint(entry.getValue(), authenticator)));
+ }
+
@Override
public Endpoint endpoint() {
return null;
diff --git a/tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpEndpoint.java b/tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpEndpoint.java
index 4fafa65773d..798eb1e692b 100644
--- a/tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpEndpoint.java
+++ b/tenant-cd/src/main/java/ai/vespa/hosted/cd/http/HttpEndpoint.java
@@ -1,6 +1,6 @@
package ai.vespa.hosted.cd.http;
-import ai.vespa.hosted.auth.Authenticator;
+import ai.vespa.hosted.api.Authenticator;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.JsonDecoder;
import com.yahoo.slime.Slime;
@@ -34,11 +34,11 @@ public class HttpEndpoint implements TestEndpoint {
private final URI endpoint;
private final HttpClient client;
- private final Authenticator authenticator;
+ private final ai.vespa.hosted.api.Authenticator authenticator;
- public HttpEndpoint(URI endpoint) {
+ public HttpEndpoint(URI endpoint, Authenticator authenticator) {
this.endpoint = requireNonNull(endpoint);
- this.authenticator = new Authenticator();
+ this.authenticator = requireNonNull(authenticator);
this.client = HttpClient.newBuilder()
.sslContext(authenticator.sslContext())
.connectTimeout(Duration.ofSeconds(5))
diff --git a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java
index 32ff03ae202..d62ccb1bba4 100644
--- a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java
+++ b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java
@@ -4,6 +4,7 @@ import ai.vespa.hosted.api.Deployment;
import ai.vespa.hosted.api.DeploymentLog;
import ai.vespa.hosted.api.DeploymentResult;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.zone.ZoneId;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
@@ -41,7 +42,11 @@ public class DeployMojo extends AbstractVespaMojo {
projectPathOf("target", "application.zip"))));
if (vespaVersion != null) deployment = deployment.atVersion(vespaVersion);
- ZoneId zone = environment == null || region == null ? controller.devZone() : ZoneId.from(environment, region);
+ ZoneId zone = region == null
+ ? controller.defaultZone(environment == null
+ ? Environment.dev
+ : Environment.from(environment))
+ : ZoneId.from(environment, region);
DeploymentResult result = controller.deploy(deployment, id, zone);
getLog().info(result.message());