diff options
author | Martin Polden <mpolden@mpolden.no> | 2020-11-05 19:49:37 +0100 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2020-11-06 09:13:38 +0100 |
commit | 7c5646844e0eea6a02a933e7b2d330ee613808da (patch) | |
tree | 0b9d870ccd079acf801f1d6d7aeb2453c19db600 /config-provisioning | |
parent | aeb457fa416c242601c6036ad3a3282df2b719fe (diff) |
Replace image atomically
Diffstat (limited to 'config-provisioning')
3 files changed, 38 insertions, 14 deletions
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/DockerImage.java b/config-provisioning/src/main/java/com/yahoo/config/provision/DockerImage.java index 5ea2286c316..cc1ae60ba0a 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/DockerImage.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/DockerImage.java @@ -14,16 +14,18 @@ import java.util.Optional; // TODO: Rename to ContainerImage. Compatibility with older config-models must be preserved. public class DockerImage { - public static final DockerImage EMPTY = new DockerImage("", "", Optional.empty()); + public static final DockerImage EMPTY = new DockerImage("", "", Optional.empty(), Optional.empty()); private final String registry; private final String repository; private final Optional<String> tag; + private final Optional<DockerImage> replacedBy; - DockerImage(String registry, String repository, Optional<String> tag) { + DockerImage(String registry, String repository, Optional<String> tag, Optional<DockerImage> replacedBy) { this.registry = Objects.requireNonNull(registry, "registry must be non-null"); this.repository = Objects.requireNonNull(repository, "repository must be non-null"); this.tag = Objects.requireNonNull(tag, "tag must be non-null"); + this.replacedBy = Objects.requireNonNull(replacedBy); } /** Returns the registry-part of this, i.e. the host/port of the registry. */ @@ -38,7 +40,7 @@ public class DockerImage { /** Returns the registry and repository for this image, excluding its tag */ public String untagged() { - return new DockerImage(registry, repository, Optional.empty()).asString(); + return new DockerImage(registry, repository, Optional.empty(), replacedBy).asString(); } /** Returns this image's tag, if any */ @@ -51,14 +53,24 @@ public class DockerImage { return tag.map(Version::new).orElse(Version.emptyVersion); } + /** The image that replaces this, if any */ + public Optional<DockerImage> replacedBy() { + return replacedBy; + } + /** Returns a copy of this tagged with the given version */ public DockerImage withTag(Version version) { - return new DockerImage(registry, repository, Optional.of(version.toFullString())); + return new DockerImage(registry, repository, Optional.of(version.toFullString()), replacedBy); } /** Returns a copy of this with registry set to given value */ public DockerImage withRegistry(String registry) { - return new DockerImage(registry, repository, tag); + return new DockerImage(registry, repository, tag, replacedBy); + } + + /** Returns a copy of this with replacement image set to given value */ + public DockerImage withReplacedBy(DockerImage image) { + return new DockerImage(registry, repository, tag, Optional.of(image).filter(i -> !i.equals(EMPTY))); } public String asString() { @@ -78,16 +90,17 @@ public class DockerImage { DockerImage that = (DockerImage) o; return registry.equals(that.registry) && repository.equals(that.repository) && - tag.equals(that.tag); + tag.equals(that.tag) && + replacedBy.equals(that.replacedBy); } @Override public int hashCode() { - return Objects.hash(registry, repository, tag); + return Objects.hash(registry, repository, tag, replacedBy); } public static DockerImage from(String registry, String repository) { - return new DockerImage(registry, repository, Optional.empty()); + return new DockerImage(registry, repository, Optional.empty(), Optional.empty()); } public static DockerImage fromString(String s) { @@ -101,11 +114,11 @@ public class DockerImage { if (repository.isEmpty()) throw new IllegalArgumentException("Repository must be non-empty in '" + s + "'"); int tagStart = repository.indexOf(':'); - if (tagStart < 0) return new DockerImage(registry, repository, Optional.empty()); + if (tagStart < 0) return new DockerImage(registry, repository, Optional.empty(), Optional.empty()); String tag = repository.substring(tagStart + 1); repository = repository.substring(0, tagStart); - return new DockerImage(registry, repository, Optional.of(tag)); + return new DockerImage(registry, repository, Optional.of(tag), Optional.empty()); } } diff --git a/config-provisioning/src/main/resources/configdefinitions/config.provisioning.node-repository.def b/config-provisioning/src/main/resources/configdefinitions/config.provisioning.node-repository.def index 6409bbd1966..091f518a683 100644 --- a/config-provisioning/src/main/resources/configdefinitions/config.provisioning.node-repository.def +++ b/config-provisioning/src/main/resources/configdefinitions/config.provisioning.node-repository.def @@ -4,6 +4,10 @@ namespace=config.provisioning # Default container image to use for nodes. containerImage string default="registry.example.com:9999/myorg/vespa" +# A replacement container image to use for nodes. This is used to avoid nodes seeing different wanted images in the +# transition to a new default image or registry. +containerImageReplacement string default="" + # Whether to cache data read from ZooKeeper in-memory. useCuratorClientCache bool default=false diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/DockerImageTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/DockerImageTest.java index ca41f4628cc..f05ebb6e517 100644 --- a/config-provisioning/src/test/java/com/yahoo/config/provision/DockerImageTest.java +++ b/config-provisioning/src/test/java/com/yahoo/config/provision/DockerImageTest.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.Optional; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** @@ -19,10 +20,10 @@ public class DockerImageTest { public void parse() { Map<String, DockerImage> tests = Map.of( "", DockerImage.EMPTY, - "registry.example.com:9999/vespa/vespa:7.42", new DockerImage("registry.example.com:9999", "vespa/vespa", Optional.of("7.42")), - "registry.example.com/vespa/vespa:7.42", new DockerImage("registry.example.com", "vespa/vespa", Optional.of("7.42")), - "registry.example.com:9999/vespa/vespa", new DockerImage("registry.example.com:9999", "vespa/vespa", Optional.empty()), - "registry.example.com/vespa/vespa", new DockerImage("registry.example.com", "vespa/vespa", Optional.empty()) + "registry.example.com:9999/vespa/vespa:7.42", new DockerImage("registry.example.com:9999", "vespa/vespa", Optional.of("7.42"), Optional.empty()), + "registry.example.com/vespa/vespa:7.42", new DockerImage("registry.example.com", "vespa/vespa", Optional.of("7.42"), Optional.empty()), + "registry.example.com:9999/vespa/vespa", new DockerImage("registry.example.com:9999", "vespa/vespa", Optional.empty(), Optional.empty()), + "registry.example.com/vespa/vespa", new DockerImage("registry.example.com", "vespa/vespa", Optional.empty(), Optional.empty()) ); tests.forEach((value, expected) -> { DockerImage parsed = DockerImage.fromString(value); @@ -52,4 +53,10 @@ public class DockerImageTest { } } + @Test + public void empty_replacement() { + DockerImage image = new DockerImage("foo.example.com", "vespa/vespa", Optional.of("7.42"), Optional.empty()); + assertTrue(image.withReplacedBy(DockerImage.EMPTY).replacedBy().isEmpty()); + } + } |