aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ChangeRequestSerializer.java
diff options
context:
space:
mode:
authorOla Aunrønning <olaa@verizonmedia.com>2021-04-12 15:43:52 +0200
committerOla Aunrønning <olaa@verizonmedia.com>2021-04-12 15:43:52 +0200
commit5eed21519359724944bd886b25afbf6699876820 (patch)
tree59f56943273908578abc33bbba1eafc3a80cd57f /controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ChangeRequestSerializer.java
parent64d09cf6b81565e82988507e2112d791e0fba33f (diff)
Persist vcmrs
Diffstat (limited to 'controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ChangeRequestSerializer.java')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ChangeRequestSerializer.java150
1 files changed, 150 insertions, 0 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ChangeRequestSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ChangeRequestSerializer.java
new file mode 100644
index 00000000000..407eb5ad5ab
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ChangeRequestSerializer.java
@@ -0,0 +1,150 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.persistence;
+
+import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.slime.ArrayTraverser;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Inspector;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequest;
+import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequestSource;
+import com.yahoo.vespa.hosted.controller.api.integration.vcmr.HostAction;
+import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VespaChangeRequest;
+
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author olaa
+ */
+public class ChangeRequestSerializer {
+
+ // WARNING: Since there are multiple servers in a ZooKeeper cluster and they upgrade one by one
+ // (and rewrite all nodes on startup), changes to the serialized format must be made
+ // such that what is serialized on version N+1 can be read by version N:
+ // - ADDING FIELDS: Always ok
+ // - REMOVING FIELDS: Stop reading the field first. Stop writing it on a later version.
+ // - CHANGING THE FORMAT OF A FIELD: Don't do it bro.
+
+ private static final String ID_FIELD = "id";
+ private static final String SOURCE_FIELD = "source";
+ private static final String SOURCE_SYSTEM_FIELD = "system";
+ private static final String STATUS_FIELD = "status";
+ private static final String URL_FIELD = "url";
+ private static final String ZONE_FIELD = "zoneId";
+ private static final String START_TIME_FIELD = "plannedStartTime";
+ private static final String END_TIME_FIELD = "plannedEndTime";
+ private static final String APPROVAL_FIELD = "approval";
+ private static final String IMPACT_FIELD = "impact";
+ private static final String IMPACTED_HOSTS_FIELD = "impactedHosts";
+ private static final String IMPACTED_SWITCHES_FIELD = "impactedSwitches";
+ private static final String ACTION_PLAN_FIELD = "actionPlan";
+ private static final String HOST_FIELD = "hostname";
+ private static final String ACTION_STATE_FIELD = "state";
+ private static final String LAST_UPDATED_FIELD = "lastUpdated";
+ private static final String HOSTS_FIELD = "hosts";
+
+
+ public static VespaChangeRequest fromSlime(Slime slime) {
+ var inspector = slime.get();
+ var id = inspector.field(ID_FIELD).asString();
+ var zoneId = ZoneId.from(inspector.field(ZONE_FIELD).asString());
+ var changeRequestSource = readChangeRequestSource(inspector.field(SOURCE_FIELD));
+ var actionPlan = readHostActionPlan(inspector.field(ACTION_PLAN_FIELD));
+ var status = VespaChangeRequest.Status.valueOf(inspector.field(STATUS_FIELD).asString());
+ var impact = ChangeRequest.Impact.valueOf(inspector.field(IMPACT_FIELD).asString());
+ var approval = ChangeRequest.Approval.valueOf(inspector.field(APPROVAL_FIELD).asString());
+
+ var impactedHosts = new ArrayList<String>();
+ inspector.field(IMPACTED_HOSTS_FIELD)
+ .traverse((ArrayTraverser) (i, hostname) -> impactedHosts.add(hostname.asString()));
+ var impactedSwitches = new ArrayList<String>();
+ inspector.field(IMPACTED_SWITCHES_FIELD)
+ .traverse((ArrayTraverser) (i, switchName) -> impactedSwitches.add(switchName.asString()));
+
+ return new VespaChangeRequest(
+ id,
+ changeRequestSource,
+ impactedSwitches,
+ impactedHosts,
+ approval,
+ impact,
+ status,
+ actionPlan,
+ zoneId);
+ }
+
+ public static Slime toSlime(VespaChangeRequest changeRequest) {
+ var slime = new Slime();
+ writeChangeRequest(slime.setObject(), changeRequest);
+ return slime;
+ }
+
+ public static void writeChangeRequest(Cursor cursor, VespaChangeRequest changeRequest) {
+ cursor.setString(ID_FIELD, changeRequest.getId());
+ cursor.setString(STATUS_FIELD, changeRequest.getStatus().name());
+ cursor.setString(IMPACT_FIELD, changeRequest.getImpact().name());
+ cursor.setString(APPROVAL_FIELD, changeRequest.getApproval().name());
+ cursor.setString(ZONE_FIELD, changeRequest.getZoneId().value());
+ writeChangeRequestSource(cursor.setObject(SOURCE_FIELD), changeRequest.getChangeRequestSource());
+ writeActionPlan(cursor.setObject(ACTION_PLAN_FIELD), changeRequest);
+
+ var impactedHosts = cursor.setArray(IMPACTED_HOSTS_FIELD);
+ changeRequest.getImpactedHosts().forEach(impactedHosts::addString);
+ var impactedSwitches = cursor.setArray(IMPACTED_SWITCHES_FIELD);
+ changeRequest.getImpactedSwitches().forEach(impactedSwitches::addString);
+ }
+
+ private static void writeActionPlan(Cursor cursor, VespaChangeRequest changeRequest) {
+ var hostsCursor = cursor.setArray(HOSTS_FIELD);
+
+ changeRequest.getHostActionPlan().forEach(action -> {
+ var actionCursor = hostsCursor.addObject();
+ actionCursor.setString(HOST_FIELD, action.getHostname());
+ actionCursor.setString(ACTION_STATE_FIELD, action.getState().name());
+ actionCursor.setString(LAST_UPDATED_FIELD, action.getLastUpdated().toString());
+ });
+
+ // TODO: Add action plan per application
+ }
+
+ private static void writeChangeRequestSource(Cursor cursor, ChangeRequestSource source) {
+ cursor.setString(SOURCE_SYSTEM_FIELD, source.getSystem());
+ cursor.setString(ID_FIELD, source.getId());
+ cursor.setString(URL_FIELD, source.getUrl());
+ cursor.setString(START_TIME_FIELD, source.getPlannedStartTime().toString());
+ cursor.setString(END_TIME_FIELD, source.getPlannedEndTime().toString());
+ cursor.setString(STATUS_FIELD, source.getStatus().name());
+ }
+
+ private static ChangeRequestSource readChangeRequestSource(Inspector inspector) {
+ return new ChangeRequestSource(
+ inspector.field(SOURCE_SYSTEM_FIELD).asString(),
+ inspector.field(ID_FIELD).asString(),
+ inspector.field(URL_FIELD).asString(),
+ ChangeRequestSource.Status.valueOf(inspector.field(STATUS_FIELD).asString()),
+ ZonedDateTime.parse(inspector.field(START_TIME_FIELD).asString()),
+ ZonedDateTime.parse(inspector.field(END_TIME_FIELD).asString())
+ );
+ }
+
+ private static List<HostAction> readHostActionPlan(Inspector inspector) {
+ if (!inspector.valid())
+ return List.of();
+
+ var actionPlan = new ArrayList<HostAction>();
+ inspector.field(HOSTS_FIELD).traverse((ArrayTraverser) (index, hostObject) ->
+ actionPlan.add(
+ new HostAction(
+ hostObject.field(HOST_FIELD).asString(),
+ HostAction.State.valueOf(hostObject.field(ACTION_STATE_FIELD).asString()),
+ Instant.parse(hostObject.field(LAST_UPDATED_FIELD).asString())
+ )
+ )
+ );
+ return actionPlan;
+ }
+
+}