diff options
author | Jon Marius Venstad <jvenstad@yahoo-inc.com> | 2019-05-13 15:08:28 +0200 |
---|---|---|
committer | Jon Marius Venstad <jvenstad@yahoo-inc.com> | 2019-05-13 15:08:28 +0200 |
commit | 03770824cee298626766df71f19af998da71b810 (patch) | |
tree | f52d8421deca020a224566605004b3ec167166e0 | |
parent | 202ed3ce6c69a1cba13fee602e91a2ddfd8940a0 (diff) |
Encode path parts too
-rw-r--r-- | hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java | 56 | ||||
-rw-r--r-- | vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java | 22 |
2 files changed, 74 insertions, 4 deletions
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 15cb9aa01ef..9c563306cee 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 @@ -7,10 +7,12 @@ import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.security.SslContextBuilder; +import com.yahoo.slime.ArrayTraverser; import com.yahoo.slime.Cursor; import com.yahoo.slime.Inspector; import com.yahoo.slime.JsonDecoder; import com.yahoo.slime.JsonFormat; +import com.yahoo.slime.ObjectTraverser; import com.yahoo.slime.Slime; import java.io.ByteArrayInputStream; @@ -19,22 +21,27 @@ import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.net.URI; +import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Callable; import java.util.function.Supplier; +import java.util.stream.Stream; import static ai.vespa.hosted.api.Method.DELETE; import static ai.vespa.hosted.api.Method.GET; import static ai.vespa.hosted.api.Method.POST; -import static java.net.http.HttpRequest.BodyPublishers.ofByteArray; import static java.net.http.HttpRequest.BodyPublishers.ofInputStream; import static java.net.http.HttpResponse.BodyHandlers.ofByteArray; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.stream.Collectors.joining; /** * Talks to a remote controller over HTTP. Subclasses are responsible for adding authentication to the requests. @@ -105,6 +112,18 @@ public abstract class ControllerHttpClient { .field("compileVersion").asString(); } + /** 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)) + .timeout(Duration.ofSeconds(10)), + GET))); + } + + /** Returns the sorted list of log entries from the deployment job of the given ids. */ + public DeploymentLog deploymentLog(ApplicationId id, ZoneId zone, long run) { + return deploymentLog(id, zone, run, -1); + } + protected HttpRequest request(HttpRequest.Builder request, Method method, Supplier<InputStream> data) { return request.method(method.name(), ofInputStream(data)).build(); } @@ -146,7 +165,14 @@ public abstract class ControllerHttpClient { private URI deploymentJobPath(ApplicationId id, ZoneId zone) { return concatenated(instancePath(id), - "deploy", zone.environment().value() + "-" + zone.region().value().replaceAll("vaas-", "")); + "deploy", jobNameOf(zone)); + } + + private URI runPath(ApplicationId id, ZoneId zone, long run, long after) { + return withQuery(concatenated(instancePath(id), + "job", jobNameOf(zone), + "run", Long.toString(run)), + "after", Long.toString(after)); } private URI defaultRegionPath() { @@ -154,7 +180,17 @@ public abstract class ControllerHttpClient { } private static URI concatenated(URI base, String... parts) { - return base.resolve(String.join("/", parts) + "/"); + return base.resolve(Stream.of(parts).map(part -> URLEncoder.encode(part, UTF_8)).collect(joining("/")) + "/"); + } + + private static URI withQuery(URI base, String name, String value) { + return base.resolve( "?" + (base.getRawQuery() != null ? base.getRawQuery() + "&" : "") + + URLEncoder.encode(name, UTF_8) + "=" + URLEncoder.encode(value, UTF_8)); + } + + // TODO jvenstad: remove when vaas is no longer part of region names. + private static String jobNameOf(ZoneId zone) { + return zone.environment().value() + "-" + zone.region().value().replaceAll("vaas-", ""); } private HttpResponse<byte[]> send(HttpRequest request) { @@ -238,6 +274,20 @@ public abstract class ControllerHttpClient { rootObject.field("run").asLong()); } + private static DeploymentLog toDeploymentLog(HttpResponse<byte[]> response) { + Inspector rootObject = toInspector(response); + List<DeploymentLog.Entry> entries = new ArrayList<>(); + rootObject.field("log").traverse((ObjectTraverser) (__, entryArray) -> + entryArray.traverse((ArrayTraverser) (___, entryObject) -> { + entries.add(new DeploymentLog.Entry(Instant.ofEpochMilli(entryObject.field("at").asLong()), + entryObject.field("type").asString(), + entryObject.field("message").asString())); + })); + return new DeploymentLog(entries, + rootObject.field("active").asBool(), + rootObject.field("lastId").asLong()); + } + private static Slime toSlime(byte[] data) { return new JsonDecoder().decode(new Slime(), data); } 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 7028e41c2d7..503dcdea629 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 @@ -1,12 +1,15 @@ package ai.vespa.hosted.plugin; import ai.vespa.hosted.api.Deployment; +import ai.vespa.hosted.api.DeploymentLog; import ai.vespa.hosted.api.DeploymentResult; import com.yahoo.config.provision.zone.ZoneId; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import java.nio.file.Paths; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; /** * Deploys a Vespa application package to the hosted Vespa API. @@ -43,6 +46,9 @@ public class DeployMojo extends AbstractVespaMojo { @Parameter(property = "build") private Long build; + @Parameter(property = "follow", defaultValue = "true") + private boolean follow; + @Override protected void doExecute() { Deployment deployment = build == null @@ -54,7 +60,21 @@ public class DeployMojo extends AbstractVespaMojo { ZoneId zone = environment == null || region == null ? controller.devZone() : ZoneId.from(environment, region); DeploymentResult result = controller.deploy(deployment, id, zone); - System.out.println("Success: " + result.message()); + System.out.println(result.message()); + + if (follow) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss").withZone(ZoneOffset.UTC); + DeploymentLog log = controller.deploymentLog(id, zone, result.run()); + do { + for (DeploymentLog.Entry entry : log.entries()) + System.out.printf("[%10s%10s] %s\n", + entry.level().toUpperCase(), + formatter.format(entry.at()), + entry.message()); + log = controller.deploymentLog(id, zone, result.run(), log.last()); + } + while (log.isActive()); + } } } |