diff options
author | Martin Polden <mpolden@mpolden.no> | 2022-10-28 09:57:57 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-28 09:57:57 +0200 |
commit | 58e2c2efac9964c14d3c379c38330b0da86ee555 (patch) | |
tree | e6a5a55ff529ac6cd6821064dd32220bbe545143 | |
parent | d092241882ffbcee42d707ac364a39221bbe3990 (diff) | |
parent | ab9903801f0eaf409a7123df1eb386b2f2b02068 (diff) |
Merge pull request #24619 from vespa-engine/mpolden/reject-account-change
Reject cloud account change [run-systemtest]
4 files changed, 138 insertions, 1 deletions
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 dab1eeccc96..68b5adcc84f 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 @@ -11,6 +11,7 @@ import com.yahoo.config.model.deploy.DeployState; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.vespa.model.VespaModel; import com.yahoo.vespa.model.application.validation.change.ChangeValidator; +import com.yahoo.vespa.model.application.validation.change.CloudAccountChangeValidator; import com.yahoo.vespa.model.application.validation.change.ClusterSizeReductionValidator; import com.yahoo.vespa.model.application.validation.change.ConfigValueChangeValidator; import com.yahoo.vespa.model.application.validation.change.ContainerRestartValidator; @@ -118,7 +119,8 @@ public class Validation { new ResourcesReductionValidator(), new ContainerRestartValidator(), new NodeResourceChangeValidator(), - new RedundancyIncreaseValidator() + new RedundancyIncreaseValidator(), + new CloudAccountChangeValidator() }; List<ConfigChangeAction> actions = Arrays.stream(validators) .flatMap(v -> v.validate(currentModel, nextModel, overrides, now).stream()) diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/CloudAccountChangeValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/CloudAccountChangeValidator.java new file mode 100644 index 00000000000..ba8a8819c5b --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/CloudAccountChangeValidator.java @@ -0,0 +1,40 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.application.validation.change; + +import com.yahoo.config.application.api.ValidationOverrides; +import com.yahoo.config.model.api.ConfigChangeAction; +import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.CloudAccount; +import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.vespa.model.VespaModel; + +import java.time.Instant; +import java.util.List; + +/** + * @author mpolden + */ +public class CloudAccountChangeValidator implements ChangeValidator { + + @Override + public List<ConfigChangeAction> validate(VespaModel current, VespaModel next, ValidationOverrides overrides, Instant now) { + for (var clusterId : current.allClusters()) { + CloudAccount currentAccount = cloudAccountOf(current, clusterId); + CloudAccount nextAccount = cloudAccountOf(next, clusterId); + if (currentAccount == null || nextAccount == null) continue; + + if (!nextAccount.equals(currentAccount)) { + throw new IllegalArgumentException("Cannot change cloud account from " + currentAccount + + " to " + nextAccount + ". The existing deployment must be removed " + + "before changing accounts"); + } + } + return List.of(); + } + + private static CloudAccount cloudAccountOf(VespaModel model, ClusterSpec.Id cluster) { + Capacity capacity = model.provisioned().all().get(cluster); + return capacity == null ? null : capacity.cloudAccount().orElse(CloudAccount.empty); + } + +} diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/CloudAccountChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/CloudAccountChangeValidatorTest.java new file mode 100644 index 00000000000..aec6cb468d1 --- /dev/null +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/CloudAccountChangeValidatorTest.java @@ -0,0 +1,90 @@ +package com.yahoo.vespa.model.application.validation.change; + +import com.yahoo.config.application.api.ValidationOverrides; +import com.yahoo.config.model.api.Provisioned; +import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.config.model.deploy.TestProperties; +import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.CloudAccount; +import com.yahoo.config.provision.ClusterResources; +import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.NodeResources; +import com.yahoo.vespa.model.VespaModel; +import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithMockPkg; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * @author mpolden + */ +class CloudAccountChangeValidatorTest { + + @Test + public void validate() { + VespaModel model0 = model(provisioned(capacity(CloudAccount.empty))); + VespaModel model1 = model(provisioned(capacity(new CloudAccount("000000000000")))); + + CloudAccountChangeValidator validator = new CloudAccountChangeValidator(); + try { + validator.validate(model0, model1, ValidationOverrides.empty, Instant.now()); + fail("Expected exception"); + } catch (IllegalArgumentException e) { + assertEquals(e.getMessage(), "Cannot change cloud account from unspecified account to " + + "account '000000000000'. The existing deployment must be removed before " + + "changing accounts"); + } + assertEquals(List.of(), validator.validate(model0, model0, ValidationOverrides.empty, Instant.now())); + assertEquals(List.of(), validator.validate(model1, model1, ValidationOverrides.empty, Instant.now())); + } + + private static Provisioned provisioned(Capacity... capacity) { + Provisioned provisioned = new Provisioned(); + for (int i = 0; i < capacity.length; i++) { + provisioned.add(ClusterSpec.Id.from("c" + i), capacity[i]); + } + return provisioned; + } + + private static Capacity capacity(CloudAccount cloudAccount) { + NodeResources nodeResources = new NodeResources(4, 8, 100, 10); + return Capacity.from(new ClusterResources(2, 1, nodeResources), + new ClusterResources(2, 1, nodeResources), + false, + false, + Optional.of(cloudAccount).filter(account -> !account.isEmpty())); + } + + private static VespaModel model(Provisioned provisioned) { + var properties = new TestProperties(); + properties.setHostedVespa(true); + var deployState = new DeployState.Builder().properties(properties) + .provisioned(provisioned); + String services = """ + <?xml version='1.0' encoding='utf-8' ?> + <services version='1.0'> + <container id='c0' version='1.0'> + <nodes count='2'> + <resources vcpu='4' memory='8Gb' disk='100Gb'/> + </nodes> + </container> + <content id='c1' version='1.0'> + <nodes count='2'> + <resources vcpu='4' memory='8Gb' disk='100Gb'/> + </nodes> + <documents> + <document type='test' mode='index'/> + </documents> + <redundancy>2</redundancy> + </content> + </services>"""; + return new VespaModelCreatorWithMockPkg(null, services, List.of("schema test { document test {} }")) + .create(deployState); + } + +} diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/CloudAccount.java b/config-provisioning/src/main/java/com/yahoo/config/provision/CloudAccount.java index ac237cb1232..1f988ee696e 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/CloudAccount.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/CloudAccount.java @@ -22,4 +22,9 @@ public class CloudAccount extends PatternedStringWrapper<CloudAccount> { return this.equals(empty); } + @Override + public String toString() { + return isEmpty() ? "unspecified account" : "account '" + value() + "'"; + } + } |