summaryrefslogtreecommitdiffstats
path: root/controller-server/src
diff options
context:
space:
mode:
authorHarald Musum <musum@yahooinc.com>2023-09-07 08:58:22 +0200
committerHarald Musum <musum@yahooinc.com>2023-09-07 08:58:22 +0200
commit96be61b9539cf99d2ed151b8052951823cad63f3 (patch)
tree335e125f860156855982d6a23d2e0a6d59d66727 /controller-server/src
parent22ccfc422bf32f3f7c9d419340ae463bd869fe5e (diff)
Consider config models in use when expiring artifacts
Keep artifacts with the same version as a config model version we support. In addition, in cd systems we don't want to expire any artifacts with the same version as a config model version in use in the correspondin main/public system This change should only end up expiring _fewer_ artifacts than before.
Diffstat (limited to 'controller-server/src')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirer.java60
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirerTest.java53
-rw-r--r--controller-server/src/test/resources/config-models/cd/config-models-cd.xml25
-rw-r--r--controller-server/src/test/resources/config-models/cd/config-models-main.xml15
-rw-r--r--controller-server/src/test/resources/config-models/empty/config-models-cd.xml0
-rw-r--r--controller-server/src/test/resources/config-models/empty/config-models-main.xml0
6 files changed, 146 insertions, 7 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirer.java
index 32d06286820..d268a0ebc22 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirer.java
@@ -3,21 +3,32 @@ package com.yahoo.vespa.hosted.controller.maintenance;
import com.yahoo.component.Version;
import com.yahoo.config.provision.CloudName;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.api.integration.artifact.Artifact;
import com.yahoo.vespa.hosted.controller.api.integration.artifact.ArtifactRegistry;
import com.yahoo.vespa.hosted.controller.versions.VersionStatus;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.Comparator;
import java.util.List;
+import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import static com.yahoo.yolean.Exceptions.uncheck;
+import static java.util.logging.Level.INFO;
/**
- * Periodically expire unused artifacts, e.g. container images and RPMs.
+ * Periodically expire unused artifacts, e.g. container images and RPMs. Artifacts with a version that is
+ * present in config-models-*.xml are never expired (in cd we .
*
* @author mpolden
*/
@@ -27,8 +38,15 @@ public class ArtifactExpirer extends ControllerMaintainer {
private static final Duration MIN_AGE = Duration.ofDays(14);
+ private final Path configModelPath;
+
public ArtifactExpirer(Controller controller, Duration interval) {
+ this(controller, interval, Paths.get(Defaults.getDefaults().underVespaHome("conf/configserver-app/")));
+ }
+
+ public ArtifactExpirer(Controller controller, Duration interval, Path configModelPath) {
super(controller, interval);
+ this.configModelPath = configModelPath;
}
@Override
@@ -46,10 +64,10 @@ public class ArtifactExpirer extends ControllerMaintainer {
try {
Instant now = controller().clock().instant();
List<Artifact> artifactsToExpire = artifactRegistry.list().stream()
- .filter(artifact -> isExpired(artifact, now, versionStatus))
+ .filter(artifact -> isExpired(artifact, now, versionStatus, modelVersionsInUse()))
.toList();
if (!artifactsToExpire.isEmpty()) {
- log.log(Level.INFO, "Expiring " + artifactsToExpire.size() + " artifacts in " + cloudName + ": " + artifactsToExpire);
+ log.log(INFO, "Expiring " + artifactsToExpire.size() + " artifacts in " + cloudName + ": " + artifactsToExpire);
artifactRegistry.deleteAll(artifactsToExpire);
}
return 0;
@@ -60,10 +78,11 @@ public class ArtifactExpirer extends ControllerMaintainer {
}
/** Returns whether given artifact is expired */
- private boolean isExpired(Artifact artifact, Instant now, VersionStatus versionStatus) {
+ private boolean isExpired(Artifact artifact, Instant now, VersionStatus versionStatus, Set<Version> versionsInUse) {
List<VespaVersion> versions = versionStatus.versions();
- if (versions.isEmpty()) return false;
+ versionsInUse.addAll(versions.stream().map(VespaVersion::versionNumber).collect(Collectors.toSet()));
+ if (versionsInUse.contains(artifact.version())) return false;
if (versionStatus.isActive(artifact.version())) return false;
if (artifact.createdAt().isAfter(now.minus(MIN_AGE))) return false;
@@ -73,4 +92,35 @@ public class ArtifactExpirer extends ControllerMaintainer {
return true;
}
+ /** Model versions in use in this system, and, if this is a CD system, in the main/public system */
+ private Set<Version> modelVersionsInUse() {
+ var system = controller().system();
+ var versions = versionsForSystem(system);
+
+ if (controller().system().isCd()) {
+ if (system == SystemName.PublicCd)
+ versions.addAll(versionsForSystem(SystemName.Public));
+ else if (system == SystemName.cd)
+ versions.addAll(versionsForSystem(SystemName.main));
+ }
+
+ log.log(INFO, "model versions in use : " + versions);
+ return versions;
+ }
+
+ private Set<Version> versionsForSystem(SystemName systemName) {
+ var versions = readConfigModelVersionsForSystem(systemName.name());
+ log.log(INFO, "versions for system " + systemName.name() + ": " + versions);
+ return versions;
+ }
+
+ private Set<Version> readConfigModelVersionsForSystem(String systemName) {
+ List<String> lines = uncheck(() -> Files.readAllLines(configModelPath.resolve("config-models-" + systemName + ".xml")));
+ return lines.stream()
+ .filter(line -> line.contains("VespaModelFactory."))
+ .map(line -> line.substring(line.indexOf("id='VespaModelFactory") + 22, line.indexOf("' class")))
+ .map(Version::fromString)
+ .collect(Collectors.toSet());
+ }
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirerTest.java
index e79793bab61..58ac302d567 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirerTest.java
@@ -3,11 +3,15 @@ package com.yahoo.vespa.hosted.controller.maintenance;
import com.yahoo.component.Version;
import com.yahoo.config.provision.CloudName;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.api.integration.artifact.Artifact;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
import com.yahoo.vespa.hosted.controller.integration.ArtifactRegistryMock;
import org.junit.jupiter.api.Test;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
@@ -19,10 +23,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
*/
public class ArtifactExpirerTest {
+ private static final Path configModelPath = Paths.get("src/test/resources/config-models/");
+
@Test
void maintain() {
DeploymentTester tester = new DeploymentTester();
- ArtifactExpirer expirer = new ArtifactExpirer(tester.controller(), Duration.ofDays(1));
+ // Note: No models in config-models-*.xml
+ ArtifactExpirer expirer = new ArtifactExpirer(tester.controller(), Duration.ofDays(1), configModelPath.resolve("empty"));
ArtifactRegistryMock registry = tester.controllerTester().serviceRegistry().artifactRegistry(CloudName.DEFAULT).orElseThrow();
Instant instant = tester.clock().instant();
@@ -52,7 +59,7 @@ public class ArtifactExpirerTest {
expirer.maintain();
assertEquals(List.of(image1, image2, image3), registry.list());
- // A new version becomes is published and controllers upgrade. This version, the system version + its unofficial
+ // A new version is published and controllers upgrade. This version, the system version + its unofficial
// version and future versions are all kept
Artifact image4 = new Artifact("image4", "registry.example.com", "vespa/vespa", "7.3.0-arm64", tester.clock().instant(), Version.fromString("7.3.0"));
registry.add(image4);
@@ -66,4 +73,46 @@ public class ArtifactExpirerTest {
assertEquals(List.of(image4, image3), registry.list());
}
+ @Test
+ void maintainWithConfigModelsInUse() {
+ DeploymentTester tester = new DeploymentTester(new ControllerTester(SystemName.cd));
+ ArtifactExpirer expirer = new ArtifactExpirer(tester.controller(), Duration.ofDays(1), configModelPath.resolve("cd"));
+ ArtifactRegistryMock registry = tester.controllerTester().serviceRegistry().artifactRegistry(CloudName.DEFAULT).orElseThrow();
+
+ Instant instant = tester.clock().instant();
+ // image0 (with version 8.210.1) is not present in config-models-*.xml
+ Artifact image0 = new Artifact("image0", "registry.example.com", "vespa/vespa", "8.210.1", instant, Version.fromString("8.210.1"));
+ Artifact image1 = new Artifact("image1", "registry.example.com", "vespa/vespa", "8.220.15", instant, Version.fromString("8.220.15"));
+ Artifact image2 = new Artifact("image2", "registry.example.com", "vespa/vespa", "8.223.1", instant, Version.fromString("8.223.1"));
+
+ registry.add(image0)
+ .add(image1)
+ .add(image2);
+
+ // Make one image active
+ tester.controllerTester().upgradeSystem(image1.version());
+
+ // Nothing is expired initially, image2 is not active, but version is one of known config model versions
+ expirer.maintain();
+ assertEquals(List.of(image0, image1, image2), registry.list());
+
+ // Nothing is expired as not enough time has passed since image creation
+ tester.clock().advance(Duration.ofDays(1));
+ expirer.maintain();
+ assertEquals(List.of(image0, image1, image2), registry.list());
+
+ // Enough time passes to expire unused image
+ tester.clock().advance(Duration.ofDays(13).plus(Duration.ofSeconds(1)));
+ expirer.maintain();
+ assertEquals(List.of(image1, image2), registry.list());
+
+ // A new version is published and controllers upgrade. This version, the system version + its unofficial
+ // version and future versions are all kept
+ Artifact image4 = new Artifact("image4", "registry.example.com", "vespa/vespa", "8.223.2-arm64", tester.clock().instant(), Version.fromString("8.223.2"));
+ registry.add(image4);
+ tester.controllerTester().upgradeController(image4.version());
+ expirer.maintain();
+ assertEquals(List.of(image1, image2, image4), registry.list());
+ }
+
}
diff --git a/controller-server/src/test/resources/config-models/cd/config-models-cd.xml b/controller-server/src/test/resources/config-models/cd/config-models-cd.xml
new file mode 100644
index 00000000000..2ed82101f73
--- /dev/null
+++ b/controller-server/src/test/resources/config-models/cd/config-models-cd.xml
@@ -0,0 +1,25 @@
+ <component id='VespaModelFactory.8.218.31' class='com.yahoo.vespa.model.VespaModelFactory' bundle='config-model-fat-amended:8.218.31' />
+ <component id='YahooAdminModelAmender.8.218.31' class='com.yahoo.vespa.model.admin.amender.YahooAdminModelAmender' bundle='config-model-fat-amended:8.218.31' />
+ <component id='YahooModelValidator.8.218.31' class='com.yahoo.vespa.model.application.validation.YahooModelValidator' bundle='config-model-fat-amended:8.218.31' />
+ <component id='YahooContainerModelAmender.8.218.31' class='com.yahoo.vespa.model.container.amender.YahooContainerModelAmender' bundle='config-model-fat-amended:8.218.31' />
+ <component id='YahooContentContainerModelAmender.8.218.31' class='com.yahoo.vespa.model.container.amender.YahooContentContainerModelAmender' bundle='config-model-fat-amended:8.218.31' />
+ <component id='VespaModelFactory.8.222.21' class='com.yahoo.vespa.model.VespaModelFactory' bundle='config-model-fat-amended:8.222.21' />
+ <component id='YahooAdminModelAmender.8.222.21' class='com.yahoo.vespa.model.admin.amender.YahooAdminModelAmender' bundle='config-model-fat-amended:8.222.21' />
+ <component id='YahooModelValidator.8.222.21' class='com.yahoo.vespa.model.application.validation.YahooModelValidator' bundle='config-model-fat-amended:8.222.21' />
+ <component id='YahooContainerModelAmender.8.222.21' class='com.yahoo.vespa.model.container.amender.YahooContainerModelAmender' bundle='config-model-fat-amended:8.222.21' />
+ <component id='YahooContentContainerModelAmender.8.222.21' class='com.yahoo.vespa.model.container.amender.YahooContentContainerModelAmender' bundle='config-model-fat-amended:8.222.21' />
+ <component id='VespaModelFactory.8.222.22' class='com.yahoo.vespa.model.VespaModelFactory' bundle='config-model-fat-amended:8.222.22' />
+ <component id='YahooAdminModelAmender.8.222.22' class='com.yahoo.vespa.model.admin.amender.YahooAdminModelAmender' bundle='config-model-fat-amended:8.222.22' />
+ <component id='YahooModelValidator.8.222.22' class='com.yahoo.vespa.model.application.validation.YahooModelValidator' bundle='config-model-fat-amended:8.222.22' />
+ <component id='YahooContainerModelAmender.8.222.22' class='com.yahoo.vespa.model.container.amender.YahooContainerModelAmender' bundle='config-model-fat-amended:8.222.22' />
+ <component id='YahooContentContainerModelAmender.8.222.22' class='com.yahoo.vespa.model.container.amender.YahooContentContainerModelAmender' bundle='config-model-fat-amended:8.222.22' />
+ <component id='VespaModelFactory.8.223.1' class='com.yahoo.vespa.model.VespaModelFactory' bundle='config-model-fat-amended:8.223.1' />
+ <component id='YahooAdminModelAmender.8.223.1' class='com.yahoo.vespa.model.admin.amender.YahooAdminModelAmender' bundle='config-model-fat-amended:8.223.1' />
+ <component id='YahooModelValidator.8.223.1' class='com.yahoo.vespa.model.application.validation.YahooModelValidator' bundle='config-model-fat-amended:8.223.1' />
+ <component id='YahooContainerModelAmender.8.223.1' class='com.yahoo.vespa.model.container.amender.YahooContainerModelAmender' bundle='config-model-fat-amended:8.223.1' />
+ <component id='YahooContentContainerModelAmender.8.223.1' class='com.yahoo.vespa.model.container.amender.YahooContentContainerModelAmender' bundle='config-model-fat-amended:8.223.1' />
+ <component id='VespaModelFactory.8.223.2' class='com.yahoo.vespa.model.VespaModelFactory' bundle='config-model-fat-amended:8.223.2' />
+ <component id='YahooAdminModelAmender.8.223.2' class='com.yahoo.vespa.model.admin.amender.YahooAdminModelAmender' bundle='config-model-fat-amended:8.223.2' />
+ <component id='YahooModelValidator.8.223.2' class='com.yahoo.vespa.model.application.validation.YahooModelValidator' bundle='config-model-fat-amended:8.223.2' />
+ <component id='YahooContainerModelAmender.8.223.2' class='com.yahoo.vespa.model.container.amender.YahooContainerModelAmender' bundle='config-model-fat-amended:8.223.2' />
+ <component id='YahooContentContainerModelAmender.8.223.2' class='com.yahoo.vespa.model.container.amender.YahooContentContainerModelAmender' bundle='config-model-fat-amended:8.223.2' />
diff --git a/controller-server/src/test/resources/config-models/cd/config-models-main.xml b/controller-server/src/test/resources/config-models/cd/config-models-main.xml
new file mode 100644
index 00000000000..3297840acd6
--- /dev/null
+++ b/controller-server/src/test/resources/config-models/cd/config-models-main.xml
@@ -0,0 +1,15 @@
+ <component id='VespaModelFactory.8.218.31' class='com.yahoo.vespa.model.VespaModelFactory' bundle='config-model-fat-amended:8.218.31' />
+ <component id='YahooAdminModelAmender.8.218.31' class='com.yahoo.vespa.model.admin.amender.YahooAdminModelAmender' bundle='config-model-fat-amended:8.218.31' />
+ <component id='YahooModelValidator.8.218.31' class='com.yahoo.vespa.model.application.validation.YahooModelValidator' bundle='config-model-fat-amended:8.218.31' />
+ <component id='YahooContainerModelAmender.8.218.31' class='com.yahoo.vespa.model.container.amender.YahooContainerModelAmender' bundle='config-model-fat-amended:8.218.31' />
+ <component id='YahooContentContainerModelAmender.8.218.31' class='com.yahoo.vespa.model.container.amender.YahooContentContainerModelAmender' bundle='config-model-fat-amended:8.218.31' />
+ <component id='VespaModelFactory.8.219.19' class='com.yahoo.vespa.model.VespaModelFactory' bundle='config-model-fat-amended:8.219.19' />
+ <component id='YahooAdminModelAmender.8.219.19' class='com.yahoo.vespa.model.admin.amender.YahooAdminModelAmender' bundle='config-model-fat-amended:8.219.19' />
+ <component id='YahooModelValidator.8.219.19' class='com.yahoo.vespa.model.application.validation.YahooModelValidator' bundle='config-model-fat-amended:8.219.19' />
+ <component id='YahooContainerModelAmender.8.219.19' class='com.yahoo.vespa.model.container.amender.YahooContainerModelAmender' bundle='config-model-fat-amended:8.219.19' />
+ <component id='YahooContentContainerModelAmender.8.219.19' class='com.yahoo.vespa.model.container.amender.YahooContentContainerModelAmender' bundle='config-model-fat-amended:8.219.19' />
+ <component id='VespaModelFactory.8.220.15' class='com.yahoo.vespa.model.VespaModelFactory' bundle='config-model-fat-amended:8.220.15' />
+ <component id='YahooAdminModelAmender.8.220.15' class='com.yahoo.vespa.model.admin.amender.YahooAdminModelAmender' bundle='config-model-fat-amended:8.220.15' />
+ <component id='YahooModelValidator.8.220.15' class='com.yahoo.vespa.model.application.validation.YahooModelValidator' bundle='config-model-fat-amended:8.220.15' />
+ <component id='YahooContainerModelAmender.8.220.15' class='com.yahoo.vespa.model.container.amender.YahooContainerModelAmender' bundle='config-model-fat-amended:8.220.15' />
+ <component id='YahooContentContainerModelAmender.8.220.15' class='com.yahoo.vespa.model.container.amender.YahooContentContainerModelAmender' bundle='config-model-fat-amended:8.222.22' />
diff --git a/controller-server/src/test/resources/config-models/empty/config-models-cd.xml b/controller-server/src/test/resources/config-models/empty/config-models-cd.xml
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/controller-server/src/test/resources/config-models/empty/config-models-cd.xml
diff --git a/controller-server/src/test/resources/config-models/empty/config-models-main.xml b/controller-server/src/test/resources/config-models/empty/config-models-main.xml
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/controller-server/src/test/resources/config-models/empty/config-models-main.xml