aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Musum <musum@verizonmedia.com>2021-06-28 14:11:41 +0200
committerHarald Musum <musum@verizonmedia.com>2021-06-28 14:11:41 +0200
commit6f01029ff835739c3aa1df78da4cc396633efa1c (patch)
treed0650693f0a4517a7664ec3977efab2847e1264b
parent51b4f15b25068cab7eff193046cb9f18512f4a84 (diff)
Validate validation-overrides.xml when validating model
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/ValidationOverrides.java40
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/ValidationOverridesValidator.java29
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/ValidationOverridesValidatorTest.java89
4 files changed, 141 insertions, 18 deletions
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationOverrides.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationOverrides.java
index 3221df38d4f..82601e6685b 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationOverrides.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationOverrides.java
@@ -49,6 +49,7 @@ public class ValidationOverrides {
private ValidationOverrides(List<Allow> overrides, String xmlForm) {
this.overrides = ImmutableList.copyOf(overrides);
this.xmlForm = xmlForm;
+ validate(Instant.now());
}
/** Throws a ValidationException unless all given validation is overridden at this time */
@@ -74,14 +75,22 @@ public class ValidationOverrides {
/** Returns whether the given (assumed invalid) change is allowed by this at the moment */
public boolean allows(ValidationId validationId, Instant now) {
for (Allow override : overrides) {
- if (now.plus(Duration.ofDays(30)).isBefore(override.until))
- throw new IllegalArgumentException(override + " is too far in the future: Max 30 days is allowed");
if (override.allows(validationId, now))
return true;
}
return false;
}
+ /** Validates overrides (checks 'until' date') */
+ public boolean validate(Instant now) {
+ for (Allow override : overrides) {
+ if (now.plus(Duration.ofDays(30)).isBefore(override.until))
+ throw new IllegalArgumentException("validation-overrides is invalid: " + override +
+ " is too far in the future: Max 30 days is allowed");
+ }
+ return false;
+ }
+
/** Returns the XML form of this, or null if it was not created by fromXml, nor is empty */
public String xmlForm() { return xmlForm; }
@@ -116,23 +125,18 @@ public class ValidationOverrides {
public static ValidationOverrides fromXml(String xmlForm) {
if ( xmlForm.isEmpty()) return ValidationOverrides.empty;
- try {
- // Assume valid structure is ensured by schema validation
- Element root = XML.getDocument(xmlForm).getDocumentElement();
- List<ValidationOverrides.Allow> overrides = new ArrayList<>();
- for (Element allow : XML.getChildren(root, "allow")) {
- Instant until = LocalDate.parse(allow.getAttribute("until"), DateTimeFormatter.ISO_DATE)
- .atStartOfDay().atZone(ZoneOffset.UTC).toInstant()
- .plus(Duration.ofDays(1)); // Make the override valid *on* the "until" date
- Optional<ValidationId> validationId = ValidationId.from(XML.getValue(allow));
- // skip unknown ids as they may be valid for other model versions
- validationId.ifPresent(id -> overrides.add(new Allow(id, until)));
- }
- return new ValidationOverrides(overrides, xmlForm);
- }
- catch (IllegalArgumentException e) {
- throw new IllegalArgumentException("validation-overrides is invalid", e);
+ // Assume valid structure is ensured by schema validation
+ Element root = XML.getDocument(xmlForm).getDocumentElement();
+ List<ValidationOverrides.Allow> overrides = new ArrayList<>();
+ for (Element allow : XML.getChildren(root, "allow")) {
+ Instant until = LocalDate.parse(allow.getAttribute("until"), DateTimeFormatter.ISO_DATE)
+ .atStartOfDay().atZone(ZoneOffset.UTC).toInstant()
+ .plus(Duration.ofDays(1)); // Make the override valid *on* the "until" date
+ Optional<ValidationId> validationId = ValidationId.from(XML.getValue(allow));
+ // skip unknown ids as they may be valid for other model versions
+ validationId.ifPresent(id -> overrides.add(new Allow(id, until)));
}
+ return new ValidationOverrides(overrides, xmlForm);
}
/** A validation override which allows a particular change. Immutable. */
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java
index 84c7a48a998..d59ad5f6252 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java
@@ -70,6 +70,7 @@ public class Validation {
new RankSetupValidator(validationParameters.ignoreValidationErrors()).validate(model, deployState);
new NoPrefixForIndexes().validate(model, deployState);
new DeploymentSpecValidator().validate(model, deployState);
+ new ValidationOverridesValidator().validate(model, deployState);
new RankingConstantsValidator().validate(model, deployState);
new SecretStoreValidator().validate(model, deployState);
new EndpointCertificateSecretsValidator().validate(model, deployState);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ValidationOverridesValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ValidationOverridesValidator.java
new file mode 100644
index 00000000000..0095c2c335d
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ValidationOverridesValidator.java
@@ -0,0 +1,29 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.model.application.validation;
+
+import com.yahoo.config.application.api.ValidationOverrides;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.vespa.model.VespaModel;
+
+import java.io.Reader;
+import java.time.Duration;
+import java.util.Optional;
+
+/**
+ * Validate validation overrides (validation-overrides.xml). Done as a validator to make sure this is
+ * done when validating the mode and not when building the model
+ *
+ * @author hmusum
+ */
+public class ValidationOverridesValidator extends Validator {
+
+ @Override
+ public void validate(VespaModel model, DeployState deployState) {
+ Optional<Reader> overrides = deployState.getApplicationPackage().getValidationOverrides();
+ if (overrides.isEmpty()) return;
+
+ // Dates are validated in constructor
+ ValidationOverrides.fromXml(overrides.get());
+ }
+
+}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ValidationOverridesValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ValidationOverridesValidatorTest.java
new file mode 100644
index 00000000000..453bdf98ce7
--- /dev/null
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ValidationOverridesValidatorTest.java
@@ -0,0 +1,89 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.model.application.validation;
+
+import com.yahoo.config.model.NullConfigModelRegistry;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.config.model.test.MockApplicationPackage;
+import com.yahoo.vespa.model.VespaModel;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author hmusum
+ */
+public class ValidationOverridesValidatorTest {
+
+ private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
+ .withZone(ZoneId.systemDefault());
+
+ @Test
+ public void testValidationOverride() throws IOException, SAXException {
+ String tenDays = dateTimeFormatter.format(Instant.now().plus(Duration.ofDays(10)));
+
+ var validationOverridesXml = "<?xml version='1.0' encoding='UTF-8'?>\n" +
+ " <validation-overrides>\n" +
+ " <allow until='" + tenDays + "'>deployment-removal</allow>\n" +
+ " </validation-overrides>";
+
+ var deployState = createDeployState(validationOverridesXml);
+ new VespaModel(new NullConfigModelRegistry(), deployState);
+ }
+
+ @Test
+ public void testFailsWhenValidationOverrideIsTooFarInFuture() {
+ Instant now = Instant.now();
+ String sixtyDays = dateTimeFormatter.format(now.plus(Duration.ofDays(60)));
+ String sixtyOneDays = dateTimeFormatter.format(now.plus(Duration.ofDays(61)));
+
+ var validationOverrides = "<?xml version='1.0' encoding='UTF-8'?>\n" +
+ "<validation-overrides>\n" +
+ " <allow until='" + sixtyDays + "'>deployment-removal</allow>\n" +
+ "</validation-overrides>";
+ assertValidationError("validation-overrides is invalid: allow 'deployment-removal' until " +
+ sixtyOneDays + "T00:00:00Z is too far in the future: Max 30 days is allowed", validationOverrides);
+ }
+
+ private static void assertValidationError(String message, String validationOverridesXml) {
+ try {
+ var deployState = createDeployState(validationOverridesXml);
+ new VespaModel(new NullConfigModelRegistry(), deployState);
+ fail("Did not get expected exception");
+ } catch (IllegalArgumentException e) {
+ assertEquals(message, e.getMessage());
+ } catch (SAXException|IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static DeployState createDeployState(String validationOverridesXml) {
+ var services = "<services version='1.0'>" +
+ " <admin version='2.0'>" +
+ " <adminserver hostalias='node0' />" +
+ " </admin>" +
+ " <container id='default' version='1.0'>" +
+ " <search/>" +
+ " <nodes>" +
+ " <node hostalias='node0'/>" +
+ " </nodes>" +
+ " </container>" +
+ "</services>";
+
+ var app = new MockApplicationPackage.Builder()
+ .withValidationOverrides(validationOverridesXml)
+ .withServices(services)
+ .build();
+ var builder = new DeployState.Builder().applicationPackage(app);
+ return builder.build();
+ }
+
+}
+