diff options
Diffstat (limited to 'node-repository')
6 files changed, 72 insertions, 34 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java index d8ea19b7677..845d2763501 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java @@ -127,7 +127,8 @@ public class NodeRepository extends AbstractComponent { Clock.systemUTC(), zone, new DnsNameResolver(), - DockerImage.fromString(config.containerImage()), + DockerImage.fromString(config.containerImage()) + .withReplacedBy(DockerImage.fromString(config.containerImageReplacement())), flagSource, config.useCuratorClientCache(), zone.environment().isProduction() && !zone.getCloud().dynamicProvisioning() ? 1 : 0, @@ -144,7 +145,7 @@ public class NodeRepository extends AbstractComponent { Clock clock, Zone zone, NameResolver nameResolver, - DockerImage dockerImage, + DockerImage containerImage, FlagSource flagSource, boolean useCuratorClientCache, int spareCount, @@ -164,7 +165,7 @@ public class NodeRepository extends AbstractComponent { this.osVersions = new OsVersions(this); this.infrastructureVersions = new InfrastructureVersions(db); this.firmwareChecks = new FirmwareChecks(db, clock); - this.containerImages = new ContainerImages(db, dockerImage); + this.containerImages = new ContainerImages(db, containerImage, flagSource); this.jobControl = new JobControl(new JobControlFlags(db, flagSource)); this.applications = new Applications(db); this.spareCount = spareCount; diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/Expirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/Expirer.java index 43f5210b233..8503d9adab6 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/Expirer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/Expirer.java @@ -8,9 +8,9 @@ import com.yahoo.vespa.hosted.provision.node.History; import java.time.Clock; import java.time.Duration; -import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; +import java.util.stream.Collectors; /** * Superclass of expiry tasks which moves nodes from some state to the dirty state. @@ -28,8 +28,8 @@ public abstract class Expirer extends NodeRepositoryMaintainer { /** The event record type which contains the timestamp to use for expiry */ private final History.Event.Type eventType; + private final Metric metric; private final Clock clock; - private final Duration expiryTime; Expirer(Node.State fromState, History.Event.Type eventType, NodeRepository nodeRepository, @@ -37,20 +37,23 @@ public abstract class Expirer extends NodeRepositoryMaintainer { super(nodeRepository, min(Duration.ofMinutes(10), expiryTime), metric); this.fromState = fromState; this.eventType = eventType; + this.metric = metric; this.clock = clock; this.expiryTime = expiryTime; } @Override protected boolean maintain() { - List<Node> expired = new ArrayList<>(); - for (Node node : nodeRepository().getNodes(fromState)) { - if (isExpired(node)) - expired.add(node); - } - if ( ! expired.isEmpty()) + List<Node> expired = nodeRepository().getNodes(fromState).stream() + .filter(this::isExpired) + .collect(Collectors.toList()); + + if ( ! expired.isEmpty()) { log.info(fromState + " expirer found " + expired.size() + " expired nodes: " + expired); - expire(expired); + expire(expired); + } + + metric.add("expired." + fromState, expired.size(), null); return true; } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImages.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImages.java index 92518239258..45156c57481 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImages.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImages.java @@ -6,6 +6,9 @@ import com.google.common.base.Suppliers; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.NodeType; import com.yahoo.vespa.curator.Lock; +import com.yahoo.vespa.flags.BooleanFlag; +import com.yahoo.vespa.flags.FlagSource; +import com.yahoo.vespa.flags.Flags; import com.yahoo.vespa.hosted.provision.persistence.CuratorDatabaseClient; import java.time.Duration; @@ -23,12 +26,12 @@ import java.util.logging.Logger; */ public class ContainerImages { - private static final Duration defaultCacheTtl = Duration.ofMinutes(1); + private static final Duration cacheTtl = Duration.ofMinutes(1); private static final Logger log = Logger.getLogger(ContainerImages.class.getName()); private final CuratorDatabaseClient db; private final DockerImage defaultImage; - private final Duration cacheTtl; + private final BooleanFlag replaceImage; /** * The container image is read on every request to /nodes/v2/node/[fqdn]. Cache current images to avoid @@ -37,14 +40,10 @@ public class ContainerImages { */ private volatile Supplier<Map<NodeType, DockerImage>> images; - public ContainerImages(CuratorDatabaseClient db, DockerImage defaultImage) { - this(db, defaultImage, defaultCacheTtl); - } - - ContainerImages(CuratorDatabaseClient db, DockerImage defaultImage, Duration cacheTtl) { + public ContainerImages(CuratorDatabaseClient db, DockerImage defaultImage, FlagSource flagSource) { this.db = db; this.defaultImage = defaultImage; - this.cacheTtl = cacheTtl; + this.replaceImage = Flags.REGIONAL_CONTAINER_REGISTRY.bindTo(flagSource); createCache(); } @@ -58,15 +57,14 @@ public class ContainerImages { return images.get(); } - /** Returns the current docker image for given node type, or the type for corresponding child nodes - * if it is a Docker host, or default */ + /** Returns the container image to use for given node type */ public DockerImage imageFor(NodeType type) { NodeType typeToUseForLookup = type.isHost() ? type.childNodeType() : type; DockerImage image = getImages().get(typeToUseForLookup); if (image == null) { - return defaultImage; + image = defaultImage; } - return image.withRegistry(defaultImage.registry()); // Always use the registry configured for this zone. + return rewriteRegistry(image); } /** Set the docker image for nodes of given type */ @@ -84,4 +82,13 @@ public class ContainerImages { } } + /** Rewrite the registry part of given image, using this zone's default image */ + private DockerImage rewriteRegistry(DockerImage image) { + DockerImage zoneImage = defaultImage; + if (zoneImage.replacedBy().isPresent() && replaceImage.value()) { + zoneImage = zoneImage.replacedBy().get(); + } + return image.withRegistry(zoneImage.registry()); + } + } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java index 12528a554d9..1f401983219 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java @@ -32,6 +32,7 @@ public class ReservationExpirerTest { ProvisioningTester tester = new ProvisioningTester.Builder().flavors(flavors.getFlavors()).build(); ManualClock clock = tester.clock(); NodeRepository nodeRepository = tester.nodeRepository(); + TestMetric metric = new TestMetric(); NodeResources nodeResources = new NodeResources(2, 8, 50, 1); NodeResources hostResources = nodeResources.add(nodeResources).add(nodeResources); @@ -47,7 +48,7 @@ public class ReservationExpirerTest { // Reservation times out clock.advance(Duration.ofMinutes(14)); // Reserved but not used time out - new ReservationExpirer(nodeRepository, clock, Duration.ofMinutes(10), new TestMetric()).run(); + new ReservationExpirer(nodeRepository, clock, Duration.ofMinutes(10), metric).run(); // Assert nothing is reserved assertEquals(0, nodeRepository.getNodes(NodeType.tenant, Node.State.reserved).size()); @@ -55,6 +56,7 @@ public class ReservationExpirerTest { assertEquals(2, dirty.size()); assertFalse(dirty.get(0).allocation().isPresent()); assertFalse(dirty.get(1).allocation().isPresent()); + assertEquals(2, metric.values.get("expired.reserved")); } } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImagesTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImagesTest.java index 320feed5d66..94b670f8e5e 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImagesTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ContainerImagesTest.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; +import com.yahoo.vespa.flags.Flags; import com.yahoo.vespa.flags.InMemoryFlagSource; import org.junit.Test; @@ -48,4 +49,26 @@ public class ContainerImagesTest { } } + @Test + public void image_replacement() { + var flagSource = new InMemoryFlagSource(); + var defaultImage = DockerImage.fromString("foo.example.com/vespa/vespa") + .withReplacedBy(DockerImage.fromString("bar.example.com/vespa/vespa")); + var tester = new ProvisioningTester.Builder().defaultImage(defaultImage).flagSource(flagSource).build(); + var hosts = tester.makeReadyNodes(2, "default", NodeType.host); + tester.activateTenantHosts(); + + // Default image is used initially + for (var host : hosts) { + assertEquals(defaultImage, tester.nodeRepository().containerImages().imageFor(host.type())); + } + + // Enabling flag switches to replacement + flagSource.withBooleanFlag(Flags.REGIONAL_CONTAINER_REGISTRY.id(), true); + for (var host : hosts) { + assertEquals(defaultImage.replacedBy().get().asString(), + tester.nodeRepository().containerImages().imageFor(host.type()).asString()); + } + } + } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java index 089e53fba5c..0a24eb82c50 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java @@ -89,11 +89,12 @@ public class ProvisioningTester { private int nextHost = 0; private int nextIP = 0; - public ProvisioningTester(Curator curator, + private ProvisioningTester(Curator curator, NodeFlavors nodeFlavors, HostResourcesCalculator resourcesCalculator, Zone zone, NameResolver nameResolver, + DockerImage containerImage, Orchestrator orchestrator, HostProvisioner hostProvisioner, LoadBalancerServiceMock loadBalancerService, @@ -109,7 +110,7 @@ public class ProvisioningTester { clock, zone, nameResolver, - DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"), + containerImage, flagSource, true, spareCount, @@ -596,9 +597,9 @@ public class ProvisioningTester { private NameResolver nameResolver; private Orchestrator orchestrator; private HostProvisioner hostProvisioner; - private LoadBalancerServiceMock loadBalancerService; private FlagSource flagSource; private int spareCount = 0; + private DockerImage defaultImage = DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"); public Builder curator(Curator curator) { this.curator = curator; @@ -636,6 +637,11 @@ public class ProvisioningTester { return this; } + public Builder defaultImage(DockerImage defaultImage) { + this.defaultImage = defaultImage; + return this; + } + public Builder orchestrator(Orchestrator orchestrator) { this.orchestrator = orchestrator; return this; @@ -646,11 +652,6 @@ public class ProvisioningTester { return this; } - public Builder loadBalancerService(LoadBalancerServiceMock loadBalancerService) { - this.loadBalancerService = loadBalancerService; - return this; - } - public Builder flagSource(FlagSource flagSource) { this.flagSource = flagSource; return this; @@ -678,9 +679,10 @@ public class ProvisioningTester { resourcesCalculator, Optional.ofNullable(zone).orElseGet(Zone::defaultZone), Optional.ofNullable(nameResolver).orElseGet(() -> new MockNameResolver().mockAnyLookup()), + defaultImage, orchestrator, hostProvisioner, - Optional.ofNullable(loadBalancerService).orElseGet(LoadBalancerServiceMock::new), + new LoadBalancerServiceMock(), Optional.ofNullable(flagSource).orElseGet(InMemoryFlagSource::new), spareCount); } |