summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorMorten Tokle <mortent@verizonmedia.com>2021-06-14 10:42:44 +0200
committerMorten Tokle <mortent@verizonmedia.com>2021-06-14 10:42:44 +0200
commitf3aa283ed994d663e7d363267eb8c2713412c618 (patch)
tree154f46752d092f85e8375969f317ca9e11e8a906 /controller-server
parentd796e0be74c7f340883e923bb284228a6842c2c1 (diff)
Include expired grants as history
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializer.java69
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java36
4 files changed, 75 insertions, 40 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
index 3736d18a01c..a90f10401aa 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java
@@ -632,7 +632,7 @@ public class CuratorDb {
/** Take lock before reading before writing */
public void writeSupportAccess(DeploymentId deploymentId, SupportAccess supportAccess) {
- curator.set(supportAccessPath(deploymentId), asJson(SupportAccessSerializer.toSlime(supportAccess, true, Optional.empty())));
+ curator.set(supportAccessPath(deploymentId), asJson(SupportAccessSerializer.toSlime(supportAccess)));
}
// -------------- Paths ---------------------------------------------------
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializer.java
index 74e2bfbb471..33596fce2bd 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializer.java
@@ -45,33 +45,65 @@ public class SupportAccessSerializer {
private static final String certificateFieldName = "certificate";
- public static Slime toSlime(SupportAccess supportAccess, boolean includeCertificates, Optional<Instant> withCurrentState) {
+ public static Slime toSlime(SupportAccess supportAccess) {
Slime slime = new Slime();
Cursor root = slime.setObject();
- withCurrentState.ifPresent(now -> {
- Cursor status = root.setObject(stateFieldName);
- SupportAccess.CurrentStatus currentState = supportAccess.currentStatus(now);
- status.setString(supportAccessFieldName, currentState.state().name());
- if (currentState.state() == SupportAccess.State.ALLOWED) {
- status.setString(untilFieldName, serializeInstant(currentState.allowedUntil().orElseThrow()));
- status.setString(byFieldName, currentState.allowedBy().orElseThrow());
- }
- }
- );
-
- Cursor history = root.setArray(historyFieldName);
- for (SupportAccessChange change : supportAccess.changeHistory()) {
- Cursor historyObject = history.addObject();
+ serializeHistoricEvents(root, supportAccess.changeHistory(), List.of());
+ serializeGrants(root, supportAccess.grantHistory(), true);
+
+ return slime;
+ }
+
+ public static Slime serializeCurrentState(SupportAccess supportAccess, Instant currentTime) {
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+
+ Cursor status = root.setObject(stateFieldName);
+ SupportAccess.CurrentStatus currentState = supportAccess.currentStatus(currentTime);
+ status.setString(supportAccessFieldName, currentState.state().name());
+ if (currentState.state() == SupportAccess.State.ALLOWED) {
+ status.setString(untilFieldName, serializeInstant(currentState.allowedUntil().orElseThrow()));
+ status.setString(byFieldName, currentState.allowedBy().orElseThrow());
+ }
+
+ List<SupportAccessGrant> inactiveGrants = supportAccess.grantHistory().stream()
+ .filter(grant -> currentTime.isAfter(grant.certificate().getNotAfter().toInstant()))
+ .collect(Collectors.toList());
+
+ serializeHistoricEvents(root, supportAccess.changeHistory(), inactiveGrants);
+
+ // Active grants should show up in the grant section
+ List<SupportAccessGrant> activeGrants = supportAccess.grantHistory().stream()
+ .filter(grant -> currentTime.isBefore(grant.certificate().getNotAfter().toInstant()))
+ .collect(Collectors.toList());
+ serializeGrants(root, activeGrants, false);
+ return slime;
+ }
+
+ private static void serializeHistoricEvents(Cursor root, List<SupportAccessChange> changeEvents, List<SupportAccessGrant> historicGrants) {
+ Cursor historyRoot = root.setArray(historyFieldName);
+ for (SupportAccessChange change : changeEvents) {
+ Cursor historyObject = historyRoot.addObject();
historyObject.setString(stateFieldName, change.accessAllowedUntil().isPresent() ? allowedStateName : disallowedStateName);
historyObject.setString(atFieldName, serializeInstant(change.changeTime()));
change.accessAllowedUntil().ifPresent(allowedUntil -> historyObject.setString(untilFieldName, serializeInstant(allowedUntil)));
historyObject.setString(byFieldName, change.madeBy());
}
- Cursor grants = root.setArray(grantFieldName);
- for (SupportAccessGrant grant : supportAccess.grantHistory()) {
- Cursor grantObject = grants.addObject();
+ for (SupportAccessGrant grant : historicGrants) {
+ Cursor historyObject = historyRoot.addObject();
+ historyObject.setString(stateFieldName, "grant");
+ historyObject.setString(atFieldName, serializeInstant(grant.certificate().getNotBefore().toInstant()));
+ historyObject.setString(untilFieldName, serializeInstant(grant.certificate().getNotAfter().toInstant()));
+ historyObject.setString(byFieldName, grant.requestor());
+ }
+ }
+
+ private static void serializeGrants(Cursor root, List<SupportAccessGrant> grants, boolean includeCertificates) {
+ Cursor grantsRoot = root.setArray(grantFieldName);
+ for (SupportAccessGrant grant : grants) {
+ Cursor grantObject = grantsRoot.addObject();
grantObject.setString(requestorFieldName, grant.requestor());
if (includeCertificates) {
grantObject.setString(certificateFieldName, X509CertificateUtils.toPem(grant.certificate()));
@@ -80,7 +112,6 @@ public class SupportAccessSerializer {
grantObject.setString(notAfterFieldName, serializeInstant(grant.certificate().getNotAfter().toInstant()));
}
- return slime;
}
private static String serializeInstant(Instant i) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index d1cbe8e14b4..017da94facc 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -31,7 +31,6 @@ import com.yahoo.restapi.Path;
import com.yahoo.restapi.ResourceResponse;
import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.security.KeyUtils;
-import com.yahoo.security.X509CertificateUtils;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.JsonParseException;
@@ -123,7 +122,6 @@ import java.net.URISyntaxException;
import java.security.DigestInputStream;
import java.security.Principal;
import java.security.PublicKey;
-import java.security.cert.X509Certificate;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
@@ -982,7 +980,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
private HttpResponse supportAccess(String tenantName, String applicationName, String instanceName, String environment, String region, Map<String, String> queryParameters) {
DeploymentId deployment = new DeploymentId(ApplicationId.from(tenantName, applicationName, instanceName), requireZone(environment, region));
SupportAccess supportAccess = controller.supportAccess().forDeployment(deployment);
- return new SlimeJsonResponse(SupportAccessSerializer.toSlime(supportAccess, false, Optional.of(controller.clock().instant())));
+ return new SlimeJsonResponse(SupportAccessSerializer.serializeCurrentState(supportAccess, controller.clock().instant()));
}
// TODO support access: only let tenants (not operators!) allow access
@@ -992,14 +990,14 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
Principal principal = requireUserPrincipal(request);
Instant now = controller.clock().instant();
SupportAccess allowed = controller.supportAccess().allow(deployment, now.plus(7, ChronoUnit.DAYS), principal.getName());
- return new SlimeJsonResponse(SupportAccessSerializer.toSlime(allowed, false, Optional.of(now)));
+ return new SlimeJsonResponse(SupportAccessSerializer.serializeCurrentState(allowed, now));
}
private HttpResponse disallowSupportAccess(String tenantName, String applicationName, String instanceName, String environment, String region, HttpRequest request) {
DeploymentId deployment = new DeploymentId(ApplicationId.from(tenantName, applicationName, instanceName), requireZone(environment, region));
Principal principal = requireUserPrincipal(request);
SupportAccess disallowed = controller.supportAccess().disallow(deployment, principal.getName());
- return new SlimeJsonResponse(SupportAccessSerializer.toSlime(disallowed, false, Optional.of(controller.clock().instant())));
+ return new SlimeJsonResponse(SupportAccessSerializer.serializeCurrentState(disallowed, controller.clock().instant()));
}
private HttpResponse metrics(String tenantName, String applicationName, String instanceName, String environment, String region) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java
index 2a43f8cc4f3..97cf53d7b89 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java
@@ -7,7 +7,7 @@ import com.yahoo.security.SignatureAlgorithm;
import com.yahoo.security.X509CertificateBuilder;
import com.yahoo.security.X509CertificateUtils;
import com.yahoo.slime.JsonFormat;
-import com.yahoo.slime.SlimeUtils;
+import com.yahoo.slime.Slime;
import com.yahoo.vespa.hosted.controller.support.access.SupportAccess;
import com.yahoo.vespa.hosted.controller.support.access.SupportAccessGrant;
import org.intellij.lang.annotations.Language;
@@ -21,7 +21,6 @@ import java.math.BigInteger;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
-import java.util.Optional;
import static org.junit.Assert.*;
@@ -80,7 +79,8 @@ public class SupportAccessSerializerTest {
@Test
public void serialize_default() {
- assertSerialized(SupportAccess.DISALLOWED_NO_HISTORY, true, Instant.EPOCH, "{\n" +
+ var slime = SupportAccessSerializer.serializeCurrentState(SupportAccess.DISALLOWED_NO_HISTORY, Instant.EPOCH);
+ assertSerialized(slime, "{\n" +
" \"state\": {\n" +
" \"supportAccess\": \"NOT_ALLOWED\"\n" +
" },\n" +
@@ -93,12 +93,14 @@ public class SupportAccessSerializerTest {
@Test
public void serialize_with_certificates() {
- assertSerialized(supportAccessExample, true, null, expectedWithCertificates);
+ var slime = SupportAccessSerializer.toSlime(supportAccessExample);
+ assertSerialized(slime, expectedWithCertificates);
}
@Test
public void serialize_with_status() {
- assertSerialized(supportAccessExample, false, hour(32),
+ var slime = SupportAccessSerializer.serializeCurrentState(supportAccessExample, hour(12));
+ assertSerialized(slime,
"{\n" +
" \"state\": {\n" +
" \"supportAccess\": \"ALLOWED\",\n" +
@@ -122,6 +124,12 @@ public class SupportAccessSerializerTest {
" \"at\": \"1970-01-01T02:00:00Z\",\n" +
" \"until\": \"1970-01-02T00:00:00Z\",\n" +
" \"by\": \"andreer\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"state\": \"grant\",\n" +
+ " \"at\": \"1970-01-01T03:00:00Z\",\n" +
+ " \"until\": \"1970-01-01T04:00:00Z\",\n" +
+ " \"by\": \"mortent\"\n" +
" }\n" +
" ],\n" +
" \"grants\": [\n" +
@@ -129,28 +137,26 @@ public class SupportAccessSerializerTest {
" \"requestor\": \"mortent\",\n" +
" \"notBefore\": \"1970-01-01T07:00:00Z\",\n" +
" \"notAfter\": \"1970-01-01T19:00:00Z\"\n" +
- " },\n" +
- " {\n" +
- " \"requestor\": \"mortent\",\n" +
- " \"notBefore\": \"1970-01-01T03:00:00Z\",\n" +
- " \"notAfter\": \"1970-01-01T04:00:00Z\"\n" +
- " }\n" +
+ " }" +
+ "\n" +
" ]\n" +
"}\n");
}
@Test
public void deserialize() {
- assertEquals(supportAccessExample, SupportAccessSerializer.fromSlime(SlimeUtils.jsonToSlime(expectedWithCertificates)));
+ var slime = SupportAccessSerializer.toSlime(supportAccessExample);
+ assertSerialized(slime, expectedWithCertificates);
+
+ var deserialized = SupportAccessSerializer.fromSlime(slime);
+ assertEquals(supportAccessExample, deserialized);
}
private Instant hour(long h) {
return Instant.EPOCH.plus(h, ChronoUnit.HOURS);
}
- private void assertSerialized(SupportAccess supportAccess, boolean includeCertificates, Instant now, String expected) {
- var slime = SupportAccessSerializer.toSlime(supportAccess, includeCertificates, Optional.ofNullable(now));
-
+ private void assertSerialized(Slime slime, String expected) {
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
new JsonFormat(false).encode(out, slime);
assertEquals(expected, out.toString());