diff options
author | Andreas Eriksen <andreer@verizonmedia.com> | 2021-05-04 16:31:15 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-04 16:31:15 +0200 |
commit | 1a206c055d516c5775e68a80cdd9338c5633a44a (patch) | |
tree | 90dc62a3600090e2086e32425a46e0095a4e9801 /config-model | |
parent | 986202394f3e068ad7871b1b333e006fd1b97408 (diff) |
validate max capacity (#17685)
* validate max capacity
* test actual spend above quota
Diffstat (limited to 'config-model')
2 files changed, 42 insertions, 9 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java index d2b465e9d02..d22affaf5a3 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java @@ -3,15 +3,17 @@ package com.yahoo.vespa.model.application.validation; import com.yahoo.config.model.deploy.DeployState; import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; -import com.yahoo.config.provision.HostSpec; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.SystemName; import com.yahoo.vespa.model.VespaModel; +import org.jetbrains.annotations.NotNull; import java.math.BigDecimal; import java.util.Locale; import java.util.Map; -import java.util.Objects; +import java.util.Set; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -23,6 +25,7 @@ import java.util.stream.Collectors; public class QuotaValidator extends Validator { private static final Logger log = Logger.getLogger(QuotaValidator.class.getName()); + private static final Capacity zeroCapacity = Capacity.from(new ClusterResources(0, 0, NodeResources.zero())); @Override public void validate(VespaModel model, DeployState deployState) { @@ -32,18 +35,35 @@ public class QuotaValidator extends Validator { } private void validateBudget(BigDecimal budget, VespaModel model, SystemName systemName) { - var spend = model.allocatedHosts().getHosts().stream() + + var maxSpend = model.allClusters().stream() + .filter(id -> !adminClusterIds(model).contains(id)) + .map(id -> model.provisioned().all().getOrDefault(id, zeroCapacity)) + .mapToDouble(c -> c.maxResources().cost()) + .sum(); + + var actualSpend = model.allocatedHosts().getHosts().stream() .filter(hostSpec -> hostSpec.membership().get().cluster().type() != ClusterSpec.Type.admin) .mapToDouble(hostSpec -> hostSpec.advertisedResources().cost()) .sum(); - if (Math.abs(spend) < 0.01) { + if (Math.abs(actualSpend) < 0.01) { log.warning("Deploying application " + model.applicationPackage().getApplicationId() + " with zero budget use. This is suspicious, but not blocked"); return; } - throwIfBudgetNegative(spend, budget, systemName); - throwIfBudgetExceeded(spend, budget, systemName); + throwIfBudgetNegative(actualSpend, budget, systemName); + throwIfBudgetExceeded(actualSpend, budget, systemName); + throwIfBudgetExceeded(maxSpend, budget, systemName); + } + + @NotNull + private Set<ClusterSpec.Id> adminClusterIds(VespaModel model) { + return model.allocatedHosts().getHosts().stream() + .map(hostSpec -> hostSpec.membership().orElseThrow().cluster()) + .filter(cluster -> cluster.type() == ClusterSpec.Type.admin) + .map(ClusterSpec::id) + .collect(Collectors.toUnmodifiableSet()); } /** Check that all clusters in the application do not exceed the quota max cluster size. */ diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java index d92ace2939a..e99a92b530a 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java @@ -26,7 +26,7 @@ public class QuotaValidatorTest { @Test public void test_deploy_under_quota() { var tester = new ValidationTester(8, false, new TestProperties().setHostedVespa(true).setQuota(quota).setZone(publicZone)); - tester.deploy(null, getServices("testCluster", 5), Environment.prod, null); + tester.deploy(null, getServices("testCluster", 4), Environment.prod, null); } @Test @@ -54,7 +54,7 @@ public class QuotaValidatorTest { @Test public void test_deploy_above_quota_budget_in_publiccd() { - var tester = new ValidationTester(13, false, new TestProperties().setHostedVespa(true).setQuota(quota).setZone(publicCdZone)); + var tester = new ValidationTester(13, false, new TestProperties().setHostedVespa(true).setQuota(quota.withBudget(BigDecimal.ONE)).setZone(publicCdZone)); try { tester.deploy(null, getServices("testCluster", 10), Environment.prod, null); fail(); @@ -65,6 +65,19 @@ public class QuotaValidatorTest { } @Test + public void test_deploy_max_resources_above_quota() { + var tester = new ValidationTester(13, false, new TestProperties().setHostedVespa(true).setQuota(quota).setZone(publicCdZone)); + try { + tester.deploy(null, getServices("testCluster", 10), Environment.prod, null); + fail(); + } catch (RuntimeException e) { + assertEquals("publiccd: Please free up some capacity! This deployment's quota use ($-.--) exceeds reserved quota ($-.--)!", + ValidationTester.censorNumbers(e.getMessage())); + + } + } + + @Test public void test_deploy_with_negative_budget() { var quota = Quota.unlimited().withBudget(BigDecimal.valueOf(-1)); var tester = new ValidationTester(13, false, new TestProperties().setHostedVespa(true).setQuota(quota).setZone(publicZone)); @@ -88,7 +101,7 @@ public class QuotaValidatorTest { " <document type='music' mode='index'/>" + " </documents>" + " <nodes count='" + nodeCount + "'>" + - " <resources vcpu=\"[0.5, 1]\" memory=\"[1Gb, 3Gb]\" disk=\"[1Gb, 9Gb]\"/>\n" + + " <resources vcpu=\"[0.5, 2]\" memory=\"[1Gb, 6Gb]\" disk=\"[1Gb, 18Gb]\"/>\n" + " </nodes>" + " </content>" + "</services>"; |