summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java17
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunLog.java9
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java13
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java15
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java15
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java20
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/overview.json155
9 files changed, 200 insertions, 48 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
index c2dbc8bdc59..79bc72f950d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
@@ -482,16 +482,19 @@ public class ApplicationController {
/** Register a DNS name for rotation */
private void registerRotationInDns(Rotation rotation, String dnsName) {
try {
- Optional<Record> record = nameService.findRecord(Record.Type.CNAME, RecordName.from(dnsName));
+
RecordData rotationName = RecordData.fqdn(rotation.name());
- if (record.isPresent()) {
+ List<Record> records = nameService.findRecords(Record.Type.CNAME, RecordName.from(dnsName));
+ records.forEach(record -> {
// Ensure that the existing record points to the correct rotation
- if ( ! record.get().data().equals(rotationName)) {
- nameService.updateRecord(record.get().id(), rotationName);
- log.info("Updated mapping for record ID " + record.get().id().asString() + ": '" + dnsName
- + "' -> '" + rotation.name() + "'");
+ if ( ! record.data().equals(rotationName)) {
+ nameService.updateRecord(record.id(), rotationName);
+ log.info("Updated mapping for record ID " + record.id().asString() + ": '" + dnsName
+ + "' -> '" + rotation.name() + "'");
}
- } else {
+ });
+
+ if (records.isEmpty()) {
RecordId id = nameService.createCname(RecordName.from(dnsName), rotationName);
log.info("Registered mapping with record ID " + id.asString() + ": '" + dnsName + "' -> '"
+ rotation.name() + "'");
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunLog.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunLog.java
index 596249a762b..b5ee571a387 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunLog.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunLog.java
@@ -31,7 +31,10 @@ public class RunLog {
/** Creates a RunLog which contains a deep copy of the given log. */
public static RunLog of(Map<Step, List<LogEntry>> log) {
ImmutableMap.Builder<Step, List<LogEntry>> builder = ImmutableMap.builder();
- log.forEach((step, entries) -> builder.put(step, ImmutableList.copyOf(entries)));
+ log.forEach((step, entries) -> {
+ if ( ! entries.isEmpty())
+ builder.put(step, ImmutableList.copyOf(entries));
+ });
OptionalLong lastId = log.values().stream()
.flatMap(List::stream)
.mapToLong(LogEntry::id)
@@ -45,8 +48,8 @@ public class RunLog {
}
/** Returns the log entries for the given step, if any are recorded. */
- public Optional<List<LogEntry>> get(Step step) {
- return Optional.ofNullable(log.get(step));
+ public List<LogEntry> get(Step step) {
+ return log.getOrDefault(step, Collections.emptyList());
}
/** Returns the id of the last log entry in this, if it has any. */
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java
index 349f4390fd1..b2b69a81dbc 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java
@@ -37,13 +37,8 @@ public class ApplicationOwnershipConfirmer extends Maintainer {
@Override
protected void maintain() {
- try {
- confirmApplicationOwnerships();
- ensureConfirmationResponses();
- }
- catch (UncheckedIOException e) {
- log.log(Level.INFO, () -> "IO exception handling issues, will retry in " + maintenanceInterval() + ": '" + Exceptions.toMessageString(e));
- }
+ confirmApplicationOwnerships();
+ ensureConfirmationResponses();
}
/** File an ownership issue with the owners of all applications we know about. */
@@ -65,7 +60,7 @@ public class ApplicationOwnershipConfirmer extends Maintainer {
ourIssueId.ifPresent(issueId -> store(issueId, application.id()));
}
catch (RuntimeException e) { // Catch errors due to wrong data in the controller, or issues client timeout.
- log.log(Level.WARNING, "Exception caught when attempting to file an issue for " + application.id(), e);
+ log.log(Level.INFO, "Exception caught when attempting to file an issue for '" + application.id() + "': " + Exceptions.toMessageString(e));
}
});
@@ -84,7 +79,7 @@ public class ApplicationOwnershipConfirmer extends Maintainer {
ownershipIssues.ensureResponse(issueId, propertyId);
}
catch (RuntimeException e) {
- log.log(Level.WARNING, "Exception caught when attempting to escalate issue with id " + issueId, e);
+ log.log(Level.INFO, "Exception caught when attempting to escalate issue with id '" + issueId + "': " + Exceptions.toMessageString(e));
}
});
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java
index c9718af7dff..11e0ada6c36 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java
@@ -48,14 +48,9 @@ public class DeploymentIssueReporter extends Maintainer {
@Override
protected void maintain() {
- try {
- maintainDeploymentIssues(applications());
- maintainPlatformIssue(applications());
- escalateInactiveDeploymentIssues(applications());
- }
- catch (UncheckedIOException e) {
- log.log(Level.INFO, () -> "IO exception handling issues, will retry in " + maintenanceInterval() + ": '" + Exceptions.toMessageString(e));
- }
+ maintainDeploymentIssues(applications());
+ maintainPlatformIssue(applications());
+ escalateInactiveDeploymentIssues(applications());
}
/** Returns the applications to maintain issue status for. */
@@ -137,7 +132,7 @@ public class DeploymentIssueReporter extends Maintainer {
store(applicationId, issueId);
}
catch (RuntimeException e) { // Catch errors due to wrong data in the controller, or issues client timeout.
- log.log(Level.WARNING, "Exception caught when attempting to file an issue for " + applicationId, e);
+ log.log(Level.INFO, "Exception caught when attempting to file an issue for '" + applicationId + "': " + Exceptions.toMessageString(e));
}
}
@@ -153,7 +148,7 @@ public class DeploymentIssueReporter extends Maintainer {
deploymentIssues.escalateIfInactive(issueId, propertyId, maxInactivity);
}
catch (RuntimeException e) {
- log.log(Level.WARNING, "Exception caught when attempting to escalate issue with id " + issueId, e);
+ log.log(Level.INFO, "Exception caught when attempting to escalate issue with id '" + issueId + "': " + Exceptions.toMessageString(e));
}
}));
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
index df682490200..05c7bd5e11b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
@@ -276,7 +276,7 @@ class JobControllerApiHandlerHelper {
controller.jobController().runs(application.id(), type).values().stream()
.sorted(Comparator.comparing(run -> -run.id().number()))
.limit(Math.max(0, 10 - runs))
- .forEach(run -> runToSlime(runArray.addObject(), run, baseUriForJob, true));
+ .forEach(run -> runToSlime(runArray.addObject(), run, baseUriForJob));
jobObject.setString("url", baseUriForJob.toString());
}
@@ -301,7 +301,7 @@ class JobControllerApiHandlerHelper {
}
}
- private static void runToSlime(Cursor runObject, Run run, URI baseUriForJobType, boolean summarize) {
+ private static void runToSlime(Cursor runObject, Run run, URI baseUriForJobType) {
runObject.setLong("id", run.id().number());
runObject.setString("status", run.status().name());
runObject.setLong("start", run.start().toEpochMilli());
@@ -309,10 +309,8 @@ class JobControllerApiHandlerHelper {
versionsToSlime(runObject, run.versions());
- if ( ! summarize) {
- Cursor stepsObject = runObject.setObject("steps");
- run.steps().forEach((step, status) -> stepsObject.setString(step.name(), status.name()));
- }
+ Cursor stepsObject = runObject.setObject("steps");
+ run.steps().forEach((step, status) -> stepsObject.setString(step.name(), status.name()));
Cursor tasksObject = runObject.setObject("tasks");
taskStatus(deployReal, run).ifPresent(status -> tasksObject.setString("deploy", status));
taskStatus(Step.installReal, run).ifPresent(status -> tasksObject.setString("install", status));
@@ -333,7 +331,7 @@ class JobControllerApiHandlerHelper {
Slime slime = new Slime();
Cursor cursor = slime.setObject();
- runs.forEach((runid, run) -> runToSlime(cursor.setObject(Long.toString(runid.number())), run, baseUriForJobType, false));
+ runs.forEach((runid, run) -> runToSlime(cursor.setObject(Long.toString(runid.number())), run, baseUriForJobType));
return new SlimeJsonResponse(slime);
}
@@ -371,7 +369,8 @@ class JobControllerApiHandlerHelper {
Cursor logObject = detailsObject.setObject("log");
for (Step step : Step.values()) {
- runLog.get(step).ifPresent(entries -> toSlime(logObject.setArray(step.name()), entries));
+ if ( ! runLog.get(step).isEmpty())
+ toSlime(logObject.setArray(step.name()), runLog.get(step));
}
runLog.lastId().ifPresent(id -> detailsObject.setLong("lastId", id));
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
index f33f82b78e2..a61d6bee91a 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
@@ -257,7 +257,7 @@ public class InternalStepRunnerTest {
}
private void assertTestLogEntries(RunId id, Step step, LogEntry... entries) {
- assertEquals(Arrays.asList(entries), tester.jobs().details(id).get().get(step).get());
+ assertEquals(Arrays.asList(entries), tester.jobs().details(id).get().get(step));
}
@Test
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java
index 2409b973b4e..cf8962cc631 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java
@@ -43,23 +43,23 @@ public class BufferedLogStoreTest {
logs.append(id.application(), id.type(), Step.deployReal, Collections.singletonList(entry));
assertEquals(Arrays.asList(entry0),
- logs.readActive(id.application(), id.type(), -1).get(Step.deployReal).get());
+ logs.readActive(id.application(), id.type(), -1).get(Step.deployReal));
assertEquals(RunLog.empty(), logs.readActive(id.application(), id.type(), 0));
logs.append(id.application(), id.type(), Step.deployReal, Collections.singletonList(entry));
assertEquals(Arrays.asList(entry0, entry1),
- logs.readActive(id.application(), id.type(), -1).get(Step.deployReal).get());
+ logs.readActive(id.application(), id.type(), -1).get(Step.deployReal));
assertEquals(Arrays.asList(entry1),
- logs.readActive(id.application(), id.type(), 0).get(Step.deployReal).get());
+ logs.readActive(id.application(), id.type(), 0).get(Step.deployReal));
assertEquals(RunLog.empty(), logs.readActive(id.application(), id.type(), 1));
logs.append(id.application(), id.type(), Step.deployReal, Collections.singletonList(entry));
assertEquals(Arrays.asList(entry0, entry1, entry2),
- logs.readActive(id.application(), id.type(), -1).get(Step.deployReal).get());
+ logs.readActive(id.application(), id.type(), -1).get(Step.deployReal));
assertEquals(Arrays.asList(entry1, entry2),
- logs.readActive(id.application(), id.type(), 0).get(Step.deployReal).get());
+ logs.readActive(id.application(), id.type(), 0).get(Step.deployReal));
assertEquals(Arrays.asList(entry2),
- logs.readActive(id.application(), id.type(), 1).get(Step.deployReal).get());
+ logs.readActive(id.application(), id.type(), 1).get(Step.deployReal));
assertEquals(RunLog.empty(), logs.readActive(id.application(), id.type(), 2));
// We should now have two chunks, with two and one entries.
@@ -73,12 +73,12 @@ public class BufferedLogStoreTest {
assertEquals(RunLog.empty(), logs.readActive(id.application(), id.type(), -1));
assertEquals(Arrays.asList(entry0, entry1, entry2),
- logs.readFinished(id, -1).get().get(Step.deployReal).get());
+ logs.readFinished(id, -1).get().get(Step.deployReal));
assertEquals(Arrays.asList(entry1, entry2),
- logs.readFinished(id, 0).get().get(Step.deployReal).get());
+ logs.readFinished(id, 0).get().get(Step.deployReal));
assertEquals(Arrays.asList(entry2),
- logs.readFinished(id, 1).get().get(Step.deployReal).get());
- assertEquals(Collections.emptyList(), logs.readFinished(id, 2).get().get(Step.deployReal).get());
+ logs.readFinished(id, 1).get().get(Step.deployReal));
+ assertEquals(Collections.emptyList(), logs.readFinished(id, 2).get().get(Step.deployReal));
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
index 78cd9e9937a..7d379911238 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java
@@ -116,6 +116,8 @@ public class JobControllerApiHandlerHelperTest {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
response.render(baos);
+ System.err.println(baos);
+
JSONObject actualJSON = new JSONObject(new String(baos.toByteArray()));
JSONObject expectedJSON = new JSONObject(expected);
assertEquals(expectedJSON.toString(), actualJSON.toString());
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/overview.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/overview.json
index 4466872022e..c7580d869b0 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/overview.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/overview.json
@@ -109,6 +109,17 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployReal": "succeeded",
+ "installReal": "succeeded",
+ "deployTester": "succeeded",
+ "installTester": "succeeded",
+ "startTests": "succeeded",
+ "endTests": "succeeded",
+ "deactivateReal": "succeeded",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {
"deploy": "succeeded",
"install": "succeeded",
@@ -141,6 +152,17 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployReal": "succeeded",
+ "installReal": "succeeded",
+ "deployTester": "succeeded",
+ "installTester": "succeeded",
+ "startTests": "succeeded",
+ "endTests": "succeeded",
+ "deactivateReal": "succeeded",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {
"deploy": "succeeded",
"install": "succeeded",
@@ -163,6 +185,17 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployReal": "succeeded",
+ "installReal": "succeeded",
+ "deployTester": "succeeded",
+ "installTester": "succeeded",
+ "startTests": "succeeded",
+ "endTests": "succeeded",
+ "deactivateReal": "succeeded",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {
"deploy": "succeeded",
"install": "succeeded",
@@ -227,6 +260,19 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployInitialReal": "succeeded",
+ "installInitialReal": "failed",
+ "deployReal": "unfinished",
+ "installReal": "unfinished",
+ "deployTester": "succeeded",
+ "installTester": "unfinished",
+ "startTests": "unfinished",
+ "endTests": "unfinished",
+ "deactivateReal": "succeeded",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {},
"log": "https://some.url:43/root/staging-test/run/4"
},
@@ -255,6 +301,19 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployInitialReal": "succeeded",
+ "installInitialReal": "succeeded",
+ "deployReal": "succeeded",
+ "installReal": "succeeded",
+ "deployTester": "succeeded",
+ "installTester": "succeeded",
+ "startTests": "succeeded",
+ "endTests": "succeeded",
+ "deactivateReal": "succeeded",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {
"deploy": "succeeded",
"install": "succeeded",
@@ -287,6 +346,19 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployInitialReal": "succeeded",
+ "installInitialReal": "succeeded",
+ "deployReal": "succeeded",
+ "installReal": "succeeded",
+ "deployTester": "succeeded",
+ "installTester": "succeeded",
+ "startTests": "succeeded",
+ "endTests": "succeeded",
+ "deactivateReal": "succeeded",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {
"deploy": "succeeded",
"install": "succeeded",
@@ -309,6 +381,19 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployInitialReal": "succeeded",
+ "installInitialReal": "succeeded",
+ "deployReal": "succeeded",
+ "installReal": "succeeded",
+ "deployTester": "succeeded",
+ "installTester": "succeeded",
+ "startTests": "succeeded",
+ "endTests": "succeeded",
+ "deactivateReal": "succeeded",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {
"deploy": "succeeded",
"install": "succeeded",
@@ -345,6 +430,16 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployReal": "succeeded",
+ "installReal": "unfinished",
+ "deployTester": "succeeded",
+ "installTester": "unfinished",
+ "startTests": "unfinished",
+ "endTests": "unfinished",
+ "deactivateTester": "unfinished",
+ "report": "unfinished"
+ },
"tasks": {
"deploy": "succeeded",
"install": "running"
@@ -376,6 +471,16 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployReal": "succeeded",
+ "installReal": "succeeded",
+ "deployTester": "succeeded",
+ "installTester": "succeeded",
+ "startTests": "succeeded",
+ "endTests": "succeeded",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {
"deploy": "succeeded",
"install": "succeeded",
@@ -398,6 +503,16 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployReal": "succeeded",
+ "installReal": "succeeded",
+ "deployTester": "succeeded",
+ "installTester": "succeeded",
+ "startTests": "succeeded",
+ "endTests": "succeeded",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {
"deploy": "succeeded",
"install": "succeeded",
@@ -461,6 +576,16 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployReal": "succeeded",
+ "installReal": "succeeded",
+ "deployTester": "succeeded",
+ "installTester": "succeeded",
+ "startTests": "succeeded",
+ "endTests": "failed",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {
"deploy": "succeeded",
"install": "succeeded",
@@ -483,6 +608,16 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployReal": "succeeded",
+ "installReal": "succeeded",
+ "deployTester": "succeeded",
+ "installTester": "succeeded",
+ "startTests": "succeeded",
+ "endTests": "succeeded",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {
"deploy": "succeeded",
"install": "succeeded",
@@ -547,6 +682,16 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployReal": "failed",
+ "installReal": "unfinished",
+ "deployTester": "unfinished",
+ "installTester": "unfinished",
+ "startTests": "unfinished",
+ "endTests": "unfinished",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {
"deploy": "failed"
},
@@ -567,6 +712,16 @@
"gitCommit": "commit1"
}
},
+ "steps": {
+ "deployReal": "succeeded",
+ "installReal": "succeeded",
+ "deployTester": "succeeded",
+ "installTester": "succeeded",
+ "startTests": "succeeded",
+ "endTests": "succeeded",
+ "deactivateTester": "succeeded",
+ "report": "succeeded"
+ },
"tasks": {
"deploy": "succeeded",
"install": "succeeded",