From 9624701b8f3fa93779aa6fb352aacebb6dd138ac Mon Sep 17 00:00:00 2001 From: Valerij Fredriksen Date: Tue, 3 May 2022 16:06:14 +0200 Subject: Generalize ContainerImageExpirer to ArtifactExpirer --- .../api/integration/ServiceRegistry.java | 4 +- .../api/integration/artifact/Artifact.java | 105 +++++++++++++++++++++ .../api/integration/artifact/ArtifactRegistry.java | 20 ++++ .../api/integration/artifact/package-info.java | 8 ++ .../api/integration/container/ContainerImage.java | 97 ------------------- .../integration/container/ContainerRegistry.java | 20 ---- .../api/integration/container/package-info.java | 8 -- .../controller/maintenance/ArtifactExpirer.java | 71 ++++++++++++++ .../maintenance/ContainerImageExpirer.java | 71 -------------- .../maintenance/ControllerMaintenance.java | 2 +- .../integration/ArtifactRegistryMock.java | 39 ++++++++ .../integration/ContainerRegistryMock.java | 39 -------- .../integration/ServiceRegistryMock.java | 4 +- .../maintenance/ArtifactExpirerTest.java | 62 ++++++++++++ .../maintenance/ContainerImageExpirerTest.java | 63 ------------- .../restapi/controller/responses/maintenance.json | 6 +- 16 files changed, 313 insertions(+), 306 deletions(-) create mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/artifact/Artifact.java create mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/artifact/ArtifactRegistry.java create mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/artifact/package-info.java delete mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/container/ContainerImage.java delete mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/container/ContainerRegistry.java delete mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/container/package-info.java create mode 100644 controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirer.java delete mode 100644 controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ContainerImageExpirer.java create mode 100644 controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ArtifactRegistryMock.java delete mode 100644 controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ContainerRegistryMock.java create mode 100644 controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirerTest.java delete mode 100644 controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ContainerImageExpirerTest.java diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java index be32b74591b..c258f088eeb 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.api.integration; import com.yahoo.config.provision.HostName; import com.yahoo.vespa.hosted.controller.api.identifiers.ControllerVersion; import com.yahoo.vespa.hosted.controller.api.integration.archive.ArchiveService; +import com.yahoo.vespa.hosted.controller.api.integration.artifact.ArtifactRegistry; import com.yahoo.vespa.hosted.controller.api.integration.athenz.AccessControlService; import com.yahoo.vespa.hosted.controller.api.integration.aws.CloudEventFetcher; import com.yahoo.vespa.hosted.controller.api.integration.aws.ResourceTagger; @@ -14,7 +15,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanRegistry; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateProvider; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateValidator; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer; -import com.yahoo.vespa.hosted.controller.api.integration.container.ContainerRegistry; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationStore; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ArtifactRepository; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterCloud; @@ -99,7 +99,7 @@ public interface ServiceRegistry { BillingDatabaseClient billingDatabase(); - ContainerRegistry containerRegistry(); + ArtifactRegistry containerRegistry(); TenantSecretService tenantSecretService(); diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/artifact/Artifact.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/artifact/Artifact.java new file mode 100644 index 00000000000..94997b7dbba --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/artifact/Artifact.java @@ -0,0 +1,105 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.artifact; + +import com.yahoo.component.Version; + +import java.time.Instant; +import java.util.Objects; +import java.util.Optional; + +/** + * A registry artifact (e.g. container image or RPM) + * + * @author mpolden + */ +public class Artifact { + + private final String id; + private final Optional registry; + private final Optional repository; + private final Instant createdAt; + private final Version version; + private final Optional architecture; + + public Artifact(String id, String registry, String repository, Instant createdAt, Version version, Optional architecture) { + this.id = Objects.requireNonNull(id); + this.registry = Optional.of(registry); + this.repository = Optional.of(repository); + this.createdAt = Objects.requireNonNull(createdAt); + this.version = Objects.requireNonNull(version); + } + + public Artifact(String id, Instant createdAt, Version version) { + this.id = Objects.requireNonNull(id); + this.registry = Optional.empty(); + this.repository = Optional.empty(); + this.createdAt = Objects.requireNonNull(createdAt); + this.version = Objects.requireNonNull(version); + this.architecture = Objects.requireNonNull(architecture); + } + + /** Unique identifier of this */ + public String id() { + return id; + } + + /** The registry holding this artifact */ + public Optional registry() { + return registry; + } + + /** Repository of this artifact */ + public Optional repository() { + return repository; + } + + /** The time this was created */ + public Instant createdAt() { + return createdAt; + } + + /** The version of this */ + public Version version() { + return version; + } + + /** The architecture of this, if any */ + public Optional architecture() { + return architecture; + } + + /** The tag of this image */ + public String tag() { + return version().toFullString() + architecture.map(arch -> "-" + arch.name()).orElse(""); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Artifact that = (Artifact) o; + return id.equals(that.id) && + registry.equals(that.registry) && + repository.equals(that.repository) && + createdAt.equals(that.createdAt) && + version.equals(that.version) && + architecture.equals(that.architecture); + } + + @Override + public int hashCode() { + return Objects.hash(id, registry, repository, createdAt, version, architecture); + } + + @Override + public String toString() { + String name = repository.isPresent() ? registry.get() + "/" + repository.get() : id; + return "artifact " + name + " [version=" + version.toFullString() + ",createdAt=" + createdAt + ",architecture=" + architecture.map(Enum::name).orElse("") + "]"; + } + + public enum Architecture { + amd64, + arm64, + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/artifact/ArtifactRegistry.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/artifact/ArtifactRegistry.java new file mode 100644 index 00000000000..8d3c19083c9 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/artifact/ArtifactRegistry.java @@ -0,0 +1,20 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.artifact; + + +import java.util.List; + +/** + * A registry of artifacts (e.g. container image or RPM). + * + * @author mpolden + */ +public interface ArtifactRegistry { + + /** Delete all given artifacts */ + void deleteAll(List images); + + /** Returns a list of all artifacts in this system */ + List list(); + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/artifact/package-info.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/artifact/package-info.java new file mode 100644 index 00000000000..8e12be06583 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/artifact/package-info.java @@ -0,0 +1,8 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/** + * @author mpolden + */ +@ExportPackage +package com.yahoo.vespa.hosted.controller.api.integration.artifact; + +import com.yahoo.osgi.annotation.ExportPackage; diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/container/ContainerImage.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/container/ContainerImage.java deleted file mode 100644 index 9652eeef530..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/container/ContainerImage.java +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.api.integration.container; - -import com.yahoo.component.Version; - -import java.time.Instant; -import java.util.Objects; -import java.util.Optional; - -/** - * A container image. - * - * @author mpolden - */ -public class ContainerImage { - - private final String id; - private final String registry; - private final String repository; - private final Instant createdAt; - private final Version version; - private final Optional architecture; - - public ContainerImage(String id, String registry, String repository, Instant createdAt, Version version, Optional architecture) { - this.id = Objects.requireNonNull(id); - this.registry = Objects.requireNonNull(registry); - this.repository = Objects.requireNonNull(repository); - this.createdAt = Objects.requireNonNull(createdAt); - this.version = Objects.requireNonNull(version); - this.architecture = Objects.requireNonNull(architecture); - } - - /** Unique identifier of this */ - public String id() { - return id; - } - - /** The registry holding this image */ - public String registry() { - return registry; - } - - /** Repository of this image */ - public String repository() { - return repository; - } - - /** The time this was created */ - public Instant createdAt() { - return createdAt; - } - - /** The version of this */ - public Version version() { - return version; - } - - /** The architecture of this, if any */ - public Optional architecture() { - return architecture; - } - - /** The tag of this image */ - public String tag() { - return version().toFullString() + architecture.map(arch -> "-" + arch.name()).orElse(""); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ContainerImage that = (ContainerImage) o; - return id.equals(that.id) && - registry.equals(that.registry) && - repository.equals(that.repository) && - createdAt.equals(that.createdAt) && - version.equals(that.version) && - architecture.equals(that.architecture); - } - - @Override - public int hashCode() { - return Objects.hash(id, registry, repository, createdAt, version, architecture); - } - - @Override - public String toString() { - return "container image " + repository + " [registry=" + registry + ",version=" + version.toFullString() + - ",createdAt=" + createdAt + ",architecture=" + architecture.map(Enum::name).orElse("") + "]"; - } - - public enum Architecture { - amd64, - arm64, - } - -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/container/ContainerRegistry.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/container/ContainerRegistry.java deleted file mode 100644 index 78757ad995a..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/container/ContainerRegistry.java +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.api.integration.container; - - -import java.util.List; - -/** - * A registry of container images. - * - * @author mpolden - */ -public interface ContainerRegistry { - - /** Delete all given images */ - void deleteAll(List images); - - /** Returns a list of all container images in this system */ - List list(); - -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/container/package-info.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/container/package-info.java deleted file mode 100644 index ca1f6afc5db..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/container/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -/** - * @author mpolden - */ -@ExportPackage -package com.yahoo.vespa.hosted.controller.api.integration.container; - -import com.yahoo.osgi.annotation.ExportPackage; 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 new file mode 100644 index 00000000000..a7cff2fdda0 --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirer.java @@ -0,0 +1,71 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.maintenance; + +import com.yahoo.component.Version; +import com.yahoo.config.provision.SystemName; +import com.yahoo.vespa.hosted.controller.Controller; +import com.yahoo.vespa.hosted.controller.api.integration.artifact.Artifact; +import com.yahoo.vespa.hosted.controller.versions.VersionStatus; +import com.yahoo.vespa.hosted.controller.versions.VespaVersion; + +import java.time.Duration; +import java.time.Instant; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +/** + * Periodically expire unused artifacts, e.g. container images and RPMs. + * + * @author mpolden + */ +public class ArtifactExpirer extends ControllerMaintainer { + + private static final Logger log = Logger.getLogger(ArtifactExpirer.class.getName()); + + private static final Duration MIN_AGE = Duration.ofDays(14); + + public ArtifactExpirer(Controller controller, Duration interval) { + super(controller, interval, null, expiringSystems()); + } + + @Override + protected double maintain() { + Instant now = controller().clock().instant(); + VersionStatus versionStatus = controller().readVersionStatus(); + List artifactsToExpire = controller().serviceRegistry().containerRegistry().list().stream() + .filter(artifact -> isExpired(artifact, now, versionStatus)) + .collect(Collectors.toList()); + if (!artifactsToExpire.isEmpty()) { + log.log(Level.INFO, "Expiring " + artifactsToExpire.size() + " artifacts: " + artifactsToExpire); + controller().serviceRegistry().containerRegistry().deleteAll(artifactsToExpire); + } + return 1.0; + } + + /** Returns whether given artifact is expired */ + private boolean isExpired(Artifact artifact, Instant now, VersionStatus versionStatus) { + List versions = versionStatus.versions(); + if (versions.isEmpty()) return false; + + if (versionStatus.isActive(artifact.version())) return false; + if (artifact.createdAt().isAfter(now.minus(MIN_AGE))) return false; + + Version maxVersion = versions.stream().map(VespaVersion::versionNumber).max(Comparator.naturalOrder()).get(); + if (artifact.version().isAfter(maxVersion)) return false; // A future version + + return true; + } + + /** Returns systems where artifacts can be expired */ + private static Set expiringSystems() { + // Run only in public and main. Public systems have distinct container registries, while main and CD have + // shared registries. + return EnumSet.of(SystemName.Public, SystemName.PublicCd, SystemName.main); + } + +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ContainerImageExpirer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ContainerImageExpirer.java deleted file mode 100644 index c87dd262fa3..00000000000 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ContainerImageExpirer.java +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.maintenance; - -import com.yahoo.component.Version; -import com.yahoo.config.provision.SystemName; -import com.yahoo.vespa.hosted.controller.Controller; -import com.yahoo.vespa.hosted.controller.api.integration.container.ContainerImage; -import com.yahoo.vespa.hosted.controller.versions.VersionStatus; -import com.yahoo.vespa.hosted.controller.versions.VespaVersion; - -import java.time.Duration; -import java.time.Instant; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -/** - * Periodically expire unused container images. - * - * @author mpolden - */ -public class ContainerImageExpirer extends ControllerMaintainer { - - private static final Logger log = Logger.getLogger(ContainerImageExpirer.class.getName()); - - private static final Duration MIN_AGE = Duration.ofDays(14); - - public ContainerImageExpirer(Controller controller, Duration interval) { - super(controller, interval, null, expiringSystems()); - } - - @Override - protected double maintain() { - Instant now = controller().clock().instant(); - VersionStatus versionStatus = controller().readVersionStatus(); - List imagesToExpire = controller().serviceRegistry().containerRegistry().list().stream() - .filter(image -> isExpired(image, now, versionStatus)) - .collect(Collectors.toList()); - if (!imagesToExpire.isEmpty()) { - log.log(Level.INFO, "Expiring " + imagesToExpire.size() + " container images: " + imagesToExpire); - controller().serviceRegistry().containerRegistry().deleteAll(imagesToExpire); - } - return 1.0; - } - - /** Returns whether given image is expired */ - private boolean isExpired(ContainerImage image, Instant now, VersionStatus versionStatus) { - List versions = versionStatus.versions(); - if (versions.isEmpty()) return false; - - if (versionStatus.isActive(image.version())) return false; - if (image.createdAt().isAfter(now.minus(MIN_AGE))) return false; - - Version maxVersion = versions.stream().map(VespaVersion::versionNumber).max(Comparator.naturalOrder()).get(); - if (image.version().isAfter(maxVersion)) return false; // A future version - - return true; - } - - /** Returns systems where images can be expired */ - private static Set expiringSystems() { - // Run only in public and main. Public systems have distinct container registries, while main and CD have - // shared registries. - return EnumSet.of(SystemName.Public, SystemName.PublicCd, SystemName.main); - } - -} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java index 041d0694ca9..e929940b68b 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java @@ -63,7 +63,7 @@ public class ControllerMaintenance extends AbstractComponent { maintainers.add(new ResourceTagMaintainer(controller, intervals.resourceTagMaintainer, controller.serviceRegistry().resourceTagger())); maintainers.add(new SystemRoutingPolicyMaintainer(controller, intervals.systemRoutingPolicyMaintainer)); maintainers.add(new ApplicationMetaDataGarbageCollector(controller, intervals.applicationMetaDataGarbageCollector)); - maintainers.add(new ContainerImageExpirer(controller, intervals.containerImageExpirer)); + maintainers.add(new ArtifactExpirer(controller, intervals.containerImageExpirer)); maintainers.add(new HostInfoUpdater(controller, intervals.hostInfoUpdater)); maintainers.add(new ReindexingTriggerer(controller, intervals.reindexingTriggerer)); maintainers.add(new EndpointCertificateMaintainer(controller, intervals.endpointCertificateMaintainer)); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ArtifactRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ArtifactRegistryMock.java new file mode 100644 index 00000000000..1d3974fe7a7 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ArtifactRegistryMock.java @@ -0,0 +1,39 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.integration; + +import com.yahoo.vespa.hosted.controller.api.integration.artifact.Artifact; +import com.yahoo.vespa.hosted.controller.api.integration.artifact.ArtifactRegistry; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author mpolden + */ +public class ArtifactRegistryMock implements ArtifactRegistry { + + private static final Comparator comparator = Comparator.comparing((Artifact artifact) -> artifact.registry().orElse("")) + .thenComparing(artifact -> artifact.repository().orElse("")) + .thenComparing(Artifact::version); + + private final Map images = new HashMap<>(); + + public ArtifactRegistryMock add(Artifact image) { + images.put(image.id(), image); + return this; + } + + @Override + public void deleteAll(List images) { + images.forEach(image -> this.images.remove(image.id())); + } + + @Override + public List list() { + return images.values().stream().sorted(comparator).collect(Collectors.toUnmodifiableList()); + } + +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ContainerRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ContainerRegistryMock.java deleted file mode 100644 index 4791017548d..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ContainerRegistryMock.java +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.integration; - -import com.yahoo.vespa.hosted.controller.api.integration.container.ContainerImage; -import com.yahoo.vespa.hosted.controller.api.integration.container.ContainerRegistry; - -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * @author mpolden - */ -public class ContainerRegistryMock implements ContainerRegistry { - - private static final Comparator comparator = Comparator.comparing(ContainerImage::registry) - .thenComparing(ContainerImage::repository) - .thenComparing(ContainerImage::version); - - private final Map images = new HashMap<>(); - - public ContainerRegistryMock add(ContainerImage image) { - images.put(image.id(), image); - return this; - } - - @Override - public void deleteAll(List images) { - images.forEach(image -> this.images.remove(image.id())); - } - - @Override - public List list() { - return images.values().stream().sorted(comparator).collect(Collectors.toUnmodifiableList()); - } - -} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java index 0afb1e67a85..0b37c4ebb8d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java @@ -82,7 +82,7 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg private final MockResourceTagger mockResourceTagger = new MockResourceTagger(); private final RoleService roleService = new MockRoleService(); private final BillingController billingController = new MockBillingController(clock); - private final ContainerRegistryMock containerRegistry = new ContainerRegistryMock(); + private final ArtifactRegistryMock containerRegistry = new ArtifactRegistryMock(); private final NoopTenantSecretService tenantSecretService = new NoopTenantSecretService(); private final ArchiveService archiveService = new MockArchiveService(); private final MockChangeRequestClient changeRequestClient = new MockChangeRequestClient(); @@ -236,7 +236,7 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg } @Override - public ContainerRegistryMock containerRegistry() { + public ArtifactRegistryMock containerRegistry() { return containerRegistry; } 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 new file mode 100644 index 00000000000..ddc1c0b599f --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirerTest.java @@ -0,0 +1,62 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.maintenance; + +import com.yahoo.component.Version; +import com.yahoo.vespa.hosted.controller.api.integration.artifact.Artifact; +import com.yahoo.vespa.hosted.controller.api.integration.container.ContainerImage.Architecture; +import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; +import com.yahoo.vespa.hosted.controller.integration.ArtifactRegistryMock; +import org.junit.Test; + +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; + +/** + * @author mpolden + */ +public class ArtifactExpirerTest { + + @Test + public void maintain() { + DeploymentTester tester = new DeploymentTester(); + ArtifactExpirer expirer = new ArtifactExpirer(tester.controller(), Duration.ofDays(1)); + ArtifactRegistryMock registry = tester.controllerTester().serviceRegistry().containerRegistry(); + + Instant instant = tester.clock().instant(); + Artifact image0 = new Artifact("image0", "registry.example.com", "vespa/vespa", instant, Version.fromString("7.1"), Optional.empty()); + Artifact image1 = new Artifact("image1", "registry.example.com", "vespa/vespa", instant, Version.fromString("7.2"), Optional.of(Architecture.amd64)); + Artifact image2 = new Artifact("image2", "registry.example.com", "vespa/vespa", instant, Version.fromString("7.4"), Optional.of(Architecture.amd64)); + registry.add(image0) + .add(image1) + .add(image2); + + // Make one image active + tester.controllerTester().upgradeSystem(image1.version()); + + // Nothing is expired initially + expirer.maintain(); + assertEquals(List.of(image0, image1, image2), registry.list()); + + // Nothing happens 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 becomes active. The active and future version are kept + Artifact image3 = new Artifact("image3", "registry.example.com", "vespa/vespa", tester.clock().instant(), Version.fromString("7.3"), Optional.of(Architecture.amd64)); + registry.add(image3); + tester.controllerTester().upgradeSystem(image3.version()); + expirer.maintain(); + assertEquals(List.of(image3, image2), registry.list()); + } + +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ContainerImageExpirerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ContainerImageExpirerTest.java deleted file mode 100644 index baebf7239ee..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ContainerImageExpirerTest.java +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.maintenance; - -import com.yahoo.component.Version; -import com.yahoo.vespa.hosted.controller.api.integration.container.ContainerImage; -import com.yahoo.vespa.hosted.controller.api.integration.container.ContainerImage.Architecture; -import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; -import com.yahoo.vespa.hosted.controller.integration.ContainerRegistryMock; -import org.junit.Test; - -import java.time.Duration; -import java.time.Instant; -import java.util.List; -import java.util.Optional; - -import static org.junit.Assert.assertEquals; - -/** - * @author mpolden - */ -public class ContainerImageExpirerTest { - - @Test - public void maintain() { - DeploymentTester tester = new DeploymentTester(); - ContainerImageExpirer expirer = new ContainerImageExpirer(tester.controller(), Duration.ofDays(1)); - ContainerRegistryMock registry = tester.controllerTester().serviceRegistry().containerRegistry(); - - Instant instant = tester.clock().instant(); - ContainerImage image0 = new ContainerImage("image0", "registry.example.com", "vespa/vespa", instant, Version.fromString("7.1"), Optional.empty()); - ContainerImage image1 = new ContainerImage("image1", "registry.example.com", "vespa/vespa", instant, Version.fromString("7.2"), Optional.of(Architecture.amd64)); - ContainerImage image2 = new ContainerImage("image2", "registry.example.com", "vespa/vespa", instant, Version.fromString("7.4"), Optional.of(Architecture.amd64)); - - registry.add(image0) - .add(image1) - .add(image2); - - // Make one image active - tester.controllerTester().upgradeSystem(image1.version()); - - // Nothing is expired initially - expirer.maintain(); - assertEquals(List.of(image0, image1, image2), registry.list()); - - // Nothing happens 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 becomes active. The active and future version are kept - ContainerImage image3 = new ContainerImage("image3", "registry.example.com", "vespa/vespa", tester.clock().instant(), Version.fromString("7.3"), Optional.of(Architecture.amd64)); - registry.add(image3); - tester.controllerTester().upgradeSystem(image3.version()); - expirer.maintain(); - assertEquals(List.of(image3, image2), registry.list()); - } - -} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json index 79e11fa1140..8b2e5578ae0 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json @@ -12,6 +12,9 @@ { "name": "ArchiveUriUpdater" }, + { + "name": "ArtifactExpirer" + }, { "name": "BillingDatabaseMaintainer" }, @@ -27,9 +30,6 @@ { "name": "ContactInformationMaintainer" }, - { - "name": "ContainerImageExpirer" - }, { "name": "CostReportMaintainer" }, -- cgit v1.2.3