diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageName.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageName.java index f11439d992..889fee1263 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageName.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageName.java @@ -16,6 +16,9 @@ package org.springframework.boot.buildpack.platform.docker.type; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import org.springframework.util.Assert; /** @@ -25,11 +28,12 @@ import org.springframework.util.Assert; * @author Scott Frederick * @since 2.3.0 * @see ImageReference - * @see ImageReferenceParser * @see #of(String) */ public class ImageName { + private static final Pattern PATTERN = Regex.IMAGE_NAME.compile(); + private static final String DEFAULT_DOMAIN = "docker.io"; private static final String OFFICIAL_REPOSITORY_NAME = "library"; @@ -42,10 +46,10 @@ public class ImageName { private final String string; - ImageName(String domain, String name) { - Assert.hasText(name, "Name must not be empty"); + ImageName(String domain, String path) { + Assert.hasText(path, "Path must not be empty"); this.domain = getDomainOrDefault(domain); - this.name = getNameWithDefaultPath(this.domain, name); + this.name = getNameWithDefaultPath(this.domain, path); this.string = this.domain + "/" + this.name; } @@ -128,8 +132,12 @@ public class ImageName { */ public static ImageName of(String value) { Assert.hasText(value, "Value must not be empty"); - ImageReferenceParser parser = ImageReferenceParser.of(value); - return new ImageName(parser.getDomain(), parser.getName()); + Matcher matcher = PATTERN.matcher(value); + Assert.isTrue(matcher.matches(), + () -> "Unable to parse name \"" + value + "\". " + + "Image name must be in the form '[domainHost:port/][path/]name', " + + "with 'path' and 'name' containing only [a-z0-9][.][_][-]"); + return new ImageName(matcher.group("domain"), matcher.group("path")); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageReference.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageReference.java index 093b510b13..4393cc6fe5 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageReference.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageReference.java @@ -30,13 +30,14 @@ import org.springframework.util.ObjectUtils; * @author Scott Frederick * @since 2.3.0 * @see ImageName - * @see ImageReferenceParser */ public final class ImageReference { - private static final String LATEST = "latest"; + private static final Pattern PATTERN = Regex.IMAGE_REFERENCE.compile(); + + private static final Pattern JAR_VERSION_PATTERN = Pattern.compile("^(.*)(\\-\\d+)$"); - private static final Pattern TRAILING_VERSION_PATTERN = Pattern.compile("^(.*)(\\-\\d+)$"); + private static final String LATEST = "latest"; private final ImageName name; @@ -182,7 +183,7 @@ public final class ImageReference { } String name = filename.substring(0, firstDot); String version = filename.substring(firstDot + 1); - Matcher matcher = TRAILING_VERSION_PATTERN.matcher(name); + Matcher matcher = JAR_VERSION_PATTERN.matcher(name); if (matcher.matches()) { name = matcher.group(1); version = matcher.group(2).substring(1) + "." + version; @@ -224,9 +225,13 @@ public final class ImageReference { */ public static ImageReference of(String value) { Assert.hasText(value, "Value must not be null"); - ImageReferenceParser parser = ImageReferenceParser.of(value); - ImageName name = new ImageName(parser.getDomain(), parser.getName()); - return new ImageReference(name, parser.getTag(), parser.getDigest()); + Matcher matcher = PATTERN.matcher(value); + Assert.isTrue(matcher.matches(), + () -> "Unable to parse image reference \"" + value + "\". " + + "Image reference must be in the form '[domainHost:port/][path/]name[:tag][@digest]', " + + "with 'path' and 'name' containing only [a-z0-9][.][_][-]"); + ImageName name = new ImageName(matcher.group("domain"), matcher.group("path")); + return new ImageReference(name, matcher.group("tag"), matcher.group("digest")); } /** diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceParser.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceParser.java deleted file mode 100644 index b20cc1dc2d..0000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceParser.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.buildpack.platform.docker.type; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A parser for Docker image references in the form - * {@code [domainHost:port/][path/]name[:tag][@digest]}. - * - * @author Scott Frederick - * @see Docker - * grammar reference - * @see Docker grammar - * implementation - * @see How - * are Docker image names parsed? - */ -final class ImageReferenceParser { - - private static final String DOMAIN_SEGMENT_REGEX = "(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])"; - - private static final String DOMAIN_PORT_REGEX = "[0-9]+"; - - private static final String DOMAIN_REGEX = oneOf( - groupOf(DOMAIN_SEGMENT_REGEX, repeating("[.]", DOMAIN_SEGMENT_REGEX)), - groupOf(DOMAIN_SEGMENT_REGEX, "[:]", DOMAIN_PORT_REGEX), - groupOf(DOMAIN_SEGMENT_REGEX, repeating("[.]", DOMAIN_SEGMENT_REGEX), "[:]", DOMAIN_PORT_REGEX), - "localhost"); - - private static final String NAME_CHARS_REGEX = "[a-z0-9]+"; - - private static final String NAME_SEPARATOR_REGEX = "(?:[._]|__|[-]*)"; - - private static final String NAME_SEGMENT_REGEX = groupOf(NAME_CHARS_REGEX, - optional(repeating(NAME_SEPARATOR_REGEX, NAME_CHARS_REGEX))); - - private static final String NAME_PATH_REGEX = groupOf(NAME_SEGMENT_REGEX, - optional(repeating("[/]", NAME_SEGMENT_REGEX))); - - private static final String DIGEST_ALGORITHM_SEGMENT_REGEX = "[A-Za-z][A-Za-z0-9]*"; - - private static final String DIGEST_ALGORITHM_SEPARATOR_REGEX = "[-_+.]"; - - private static final String DIGEST_ALGORITHM_REGEX = groupOf(DIGEST_ALGORITHM_SEGMENT_REGEX, - optional(repeating(DIGEST_ALGORITHM_SEPARATOR_REGEX, DIGEST_ALGORITHM_SEGMENT_REGEX))); - - private static final String DIGEST_VALUE_REGEX = "[0-9A-Fa-f]{32,}"; - - private static final String DIGEST_REGEX = groupOf(DIGEST_ALGORITHM_REGEX, "[:]", DIGEST_VALUE_REGEX); - - private static final String TAG_REGEX = "[\\w][\\w.-]{0,127}"; - - private static final String DOMAIN_CAPTURE_GROUP = "domain"; - - private static final String NAME_CAPTURE_GROUP = "name"; - - private static final String TAG_CAPTURE_GROUP = "tag"; - - private static final String DIGEST_CAPTURE_GROUP = "digest"; - - private static final Pattern REFERENCE_REGEX_PATTERN = patternOf(anchored( - optional(captureOf(DOMAIN_CAPTURE_GROUP, DOMAIN_REGEX), "[/]"), - captureOf(NAME_CAPTURE_GROUP, NAME_PATH_REGEX), optional("[:]", captureOf(TAG_CAPTURE_GROUP, TAG_REGEX)), - optional("[@]", captureOf(DIGEST_CAPTURE_GROUP, DIGEST_REGEX)))); - - private final String domain; - - private final String name; - - private final String tag; - - private final String digest; - - private ImageReferenceParser(String domain, String name, String tag, String digest) { - this.domain = domain; - this.name = name; - this.tag = tag; - this.digest = digest; - } - - String getDomain() { - return this.domain; - } - - String getName() { - return this.name; - } - - String getTag() { - return this.tag; - } - - String getDigest() { - return this.digest; - } - - static ImageReferenceParser of(String reference) { - Matcher matcher = REFERENCE_REGEX_PATTERN.matcher(reference); - if (!matcher.matches()) { - throw new IllegalArgumentException("Unable to parse image reference \"" + reference + "\". " - + "Image reference must be in the form \"[domainHost:port/][path/]name[:tag][@digest]\", " - + "with \"path\" and \"name\" containing only [a-z0-9][.][_][-]"); - } - return new ImageReferenceParser(matcher.group(DOMAIN_CAPTURE_GROUP), matcher.group(NAME_CAPTURE_GROUP), - matcher.group(TAG_CAPTURE_GROUP), matcher.group(DIGEST_CAPTURE_GROUP)); - } - - private static Pattern patternOf(String... expressions) { - return Pattern.compile(join(expressions)); - } - - private static String groupOf(String... expressions) { - return "(?:" + join(expressions) + ')'; - } - - private static String captureOf(String groupName, String... expressions) { - return "(?<" + groupName + ">" + join(expressions) + ')'; - } - - private static String oneOf(String... expressions) { - return groupOf(String.join("|", expressions)); - } - - private static String optional(String... expressions) { - return groupOf(join(expressions)) + '?'; - } - - private static String repeating(String... expressions) { - return groupOf(join(expressions)) + '+'; - } - - private static String anchored(String... expressions) { - return '^' + join(expressions) + '$'; - } - - private static String join(String... expressions) { - return String.join("", expressions); - } - -} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Regex.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Regex.java new file mode 100644 index 0000000000..2307ccc4e5 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Regex.java @@ -0,0 +1,143 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.buildpack.platform.docker.type; + +import java.util.regex.Pattern; + +/** + * Regular Expressions for image names and references based on those found in the Docker + * codebase. + * + * @author Scott Frederick + * @author Phillip Webb + * @see Docker + * grammar reference + * @see Docker grammar + * implementation + * @see How + * are Docker image names parsed? + */ +final class Regex implements CharSequence { + + private static final Regex DOMAIN; + static { + Regex component = Regex.oneOf("[a-zA-Z0-9]", "[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]"); + Regex dotComponent = Regex.group("[.]", component); + Regex colonPort = Regex.of("[:][0-9]+"); + Regex dottedDomain = Regex.group(component, dotComponent.oneOrMoreTimes()); + Regex dottedDomainAndPort = Regex.group(component, dotComponent.oneOrMoreTimes(), colonPort); + Regex nameAndPort = Regex.group(component, colonPort); + DOMAIN = Regex.oneOf(dottedDomain, nameAndPort, dottedDomainAndPort, "localhost"); + } + + private static final Regex PATH_COMPONENT; + static { + Regex segment = Regex.of("[a-z0-9]+"); + Regex separator = Regex.group("[._]|__|[-]*"); + Regex separatedSegment = Regex.group(separator, segment).oneOrMoreTimes(); + PATH_COMPONENT = Regex.of(segment, Regex.group(separatedSegment).zeroOrOnce()); + } + + private static final Regex PATH; + static { + Regex component = PATH_COMPONENT; + Regex slashComponent = Regex.group("[/]", component); + Regex slashComponents = Regex.group(slashComponent.oneOrMoreTimes()); + PATH = Regex.of(component, slashComponents.zeroOrOnce()); + } + + static final Regex IMAGE_NAME; + static { + Regex domain = DOMAIN.capturedAs("domain"); + Regex domainSlash = Regex.group(domain, "[/]"); + Regex path = PATH.capturedAs("path"); + Regex optionalDomainSlash = domainSlash.zeroOrOnce(); + IMAGE_NAME = Regex.of(optionalDomainSlash, path); + } + + private static final Regex TAG_REGEX = Regex.of("[\\w][\\w.-]{0,127}"); + + private static final Regex DIGEST_REGEX = Regex + .of("[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[A-Fa-f0-9]]{32,}"); + + static final Regex IMAGE_REFERENCE; + static { + Regex tag = TAG_REGEX.capturedAs("tag"); + Regex digest = DIGEST_REGEX.capturedAs("digest"); + Regex atDigest = Regex.group("[@]", digest); + Regex colonTag = Regex.group("[:]", tag); + IMAGE_REFERENCE = Regex.of(IMAGE_NAME, colonTag.zeroOrOnce(), atDigest.zeroOrOnce()); + } + + private final String value; + + private Regex(CharSequence value) { + this.value = value.toString(); + } + + private Regex oneOrMoreTimes() { + return new Regex(this.value + "+"); + } + + private Regex zeroOrOnce() { + return new Regex(this.value + "?"); + } + + private Regex capturedAs(String name) { + return new Regex("(?<" + name + ">" + this + ")"); + } + + Pattern compile() { + return Pattern.compile("^" + this.value + "$"); + } + + @Override + public int length() { + return this.value.length(); + } + + @Override + public char charAt(int index) { + return this.value.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return this.value.subSequence(start, end); + } + + @Override + public String toString() { + return this.value; + } + + private static Regex of(CharSequence... expressions) { + return new Regex(String.join("", expressions)); + } + + private static Regex oneOf(CharSequence... expressions) { + return new Regex("(?:" + String.join("|", expressions) + ")"); + } + + private static Regex group(CharSequence... expressions) { + return new Regex("(?:" + String.join("", expressions) + ")"); + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageNameTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageNameTests.java index f994e40047..c1e667e093 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageNameTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageNameTests.java @@ -71,12 +71,42 @@ class ImageNameTests { @Test void ofWhenSimpleNameAndPortCreatesImageName() { + ImageName imageName = ImageName.of("repo:8080/ubuntu"); + assertThat(imageName.toString()).isEqualTo("repo:8080/ubuntu"); + assertThat(imageName.getDomain()).isEqualTo("repo:8080"); + assertThat(imageName.getName()).isEqualTo("ubuntu"); + } + + @Test + void ofWhenSimplePathAndPortCreatesImageName() { ImageName imageName = ImageName.of("repo:8080/canonical/ubuntu"); assertThat(imageName.toString()).isEqualTo("repo:8080/canonical/ubuntu"); assertThat(imageName.getDomain()).isEqualTo("repo:8080"); assertThat(imageName.getName()).isEqualTo("canonical/ubuntu"); } + @Test + void ofWhenNameWithLongPathCreatesImageName() { + ImageName imageName = ImageName.of("path1/path2/path3/ubuntu"); + assertThat(imageName.toString()).isEqualTo("docker.io/path1/path2/path3/ubuntu"); + assertThat(imageName.getDomain()).isEqualTo("docker.io"); + assertThat(imageName.getName()).isEqualTo("path1/path2/path3/ubuntu"); + } + + @Test + void ofWhenLocalhostDomainCreatesImageName() { + ImageName imageName = ImageName.of("localhost/ubuntu"); + assertThat(imageName.getDomain()).isEqualTo("localhost"); + assertThat(imageName.getName()).isEqualTo("ubuntu"); + } + + @Test + void ofWhenLocalhostDomainAndPathCreatesImageName() { + ImageName imageName = ImageName.of("localhost/library/ubuntu"); + assertThat(imageName.getDomain()).isEqualTo("localhost"); + assertThat(imageName.getName()).isEqualTo("library/ubuntu"); + } + @Test void ofWhenLegacyDomainUsesNewDomain() { ImageName imageName = ImageName.of("index.docker.io/ubuntu"); @@ -96,6 +126,25 @@ class ImageNameTests { assertThatIllegalArgumentException().isThrownBy(() -> ImageName.of("")).withMessage("Value must not be empty"); } + @Test + void ofWhenContainsUppercaseThrowsException() { + assertThatIllegalArgumentException().isThrownBy(() -> ImageName.of("Test")) + .withMessageContaining("Unable to parse name").withMessageContaining("Test"); + } + + @Test + void ofWhenNameIncludesTagThrowsException() { + assertThatIllegalArgumentException().isThrownBy(() -> ImageName.of("ubuntu:latest")) + .withMessageContaining("Unable to parse name").withMessageContaining(":latest"); + } + + @Test + void ofWhenNameIncludeDigestThrowsException() { + assertThatIllegalArgumentException().isThrownBy( + () -> ImageName.of("ubuntu@sha256:47bfdb88c3ae13e488167607973b7688f69d9e8c142c2045af343ec199649c09")) + .withMessageContaining("Unable to parse name").withMessageContaining("@sha256:47b"); + } + @Test void hashCodeAndEquals() { ImageName n1 = ImageName.of("ubuntu"); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceParserTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceParserTests.java deleted file mode 100644 index f7abe4898d..0000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceParserTests.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.buildpack.platform.docker.type; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - -/** - * Tests for {@link ImageReferenceParser}. - * - * @author Scott Frederick - */ -class ImageReferenceParserTests { - - @Test - void unableToParseWithUppercaseInName() { - assertThatIllegalArgumentException().isThrownBy(() -> ImageReferenceParser.of("Test")) - .withMessageContaining("Test"); - } - - @Test - void parsesName() { - ImageReferenceParser parser = ImageReferenceParser.of("ubuntu"); - assertThat(parser.getDomain()).isNull(); - assertThat(parser.getName()).isEqualTo("ubuntu"); - assertThat(parser.getTag()).isNull(); - assertThat(parser.getDigest()).isNull(); - } - - @Test - void parsesNameWithPath() { - ImageReferenceParser parser = ImageReferenceParser.of("library/ubuntu"); - assertThat(parser.getDomain()).isNull(); - assertThat(parser.getName()).isEqualTo("library/ubuntu"); - assertThat(parser.getTag()).isNull(); - assertThat(parser.getDigest()).isNull(); - } - - @Test - void parsesNameWithLongPath() { - ImageReferenceParser parser = ImageReferenceParser.of("path1/path2/path3/ubuntu"); - assertThat(parser.getDomain()).isNull(); - assertThat(parser.getName()).isEqualTo("path1/path2/path3/ubuntu"); - assertThat(parser.getTag()).isNull(); - assertThat(parser.getDigest()).isNull(); - } - - @Test - void parsesDomainAndNameWithPath() { - ImageReferenceParser parser = ImageReferenceParser.of("repo.example.com/library/ubuntu"); - assertThat(parser.getDomain()).isEqualTo("repo.example.com"); - assertThat(parser.getName()).isEqualTo("library/ubuntu"); - assertThat(parser.getTag()).isNull(); - assertThat(parser.getDigest()).isNull(); - } - - @Test - void parsesDomainWithPortAndNameWithPath() { - ImageReferenceParser parser = ImageReferenceParser.of("repo.example.com:8080/library/ubuntu"); - assertThat(parser.getDomain()).isEqualTo("repo.example.com:8080"); - assertThat(parser.getName()).isEqualTo("library/ubuntu"); - assertThat(parser.getTag()).isNull(); - assertThat(parser.getDigest()).isNull(); - } - - @Test - void parsesSimpleDomainWithPortAndName() { - ImageReferenceParser parser = ImageReferenceParser.of("repo:8080/ubuntu"); - assertThat(parser.getDomain()).isEqualTo("repo:8080"); - assertThat(parser.getName()).isEqualTo("ubuntu"); - assertThat(parser.getTag()).isNull(); - assertThat(parser.getDigest()).isNull(); - } - - @Test - void parsesSimpleDomainWithPortAndNameWithPath() { - ImageReferenceParser parser = ImageReferenceParser.of("repo:8080/library/ubuntu"); - assertThat(parser.getDomain()).isEqualTo("repo:8080"); - assertThat(parser.getName()).isEqualTo("library/ubuntu"); - assertThat(parser.getTag()).isNull(); - assertThat(parser.getDigest()).isNull(); - } - - @Test - void parsesLocalhostDomainAndName() { - ImageReferenceParser parser = ImageReferenceParser.of("localhost/ubuntu"); - assertThat(parser.getDomain()).isEqualTo("localhost"); - assertThat(parser.getName()).isEqualTo("ubuntu"); - assertThat(parser.getTag()).isNull(); - assertThat(parser.getDigest()).isNull(); - } - - @Test - void parsesLocalhostDomainAndNameWithPath() { - ImageReferenceParser parser = ImageReferenceParser.of("localhost/library/ubuntu"); - assertThat(parser.getDomain()).isEqualTo("localhost"); - assertThat(parser.getName()).isEqualTo("library/ubuntu"); - assertThat(parser.getTag()).isNull(); - assertThat(parser.getDigest()).isNull(); - } - - @Test - void parsesNameAndTag() { - ImageReferenceParser parser = ImageReferenceParser.of("ubuntu:v1"); - assertThat(parser.getDomain()).isNull(); - assertThat(parser.getName()).isEqualTo("ubuntu"); - assertThat(parser.getTag()).isEqualTo("v1"); - assertThat(parser.getDigest()).isNull(); - } - - @Test - void parsesNameAndDigest() { - ImageReferenceParser parser = ImageReferenceParser - .of("ubuntu@sha256:47bfdb88c3ae13e488167607973b7688f69d9e8c142c2045af343ec199649c09"); - assertThat(parser.getDomain()).isNull(); - assertThat(parser.getName()).isEqualTo("ubuntu"); - assertThat(parser.getTag()).isNull(); - assertThat(parser.getDigest()) - .isEqualTo("sha256:47bfdb88c3ae13e488167607973b7688f69d9e8c142c2045af343ec199649c09"); - } - - @Test - void parsesReferenceWithTag() { - ImageReferenceParser parser = ImageReferenceParser.of("repo.example.com:8080/library/ubuntu:v1"); - assertThat(parser.getDomain()).isEqualTo("repo.example.com:8080"); - assertThat(parser.getName()).isEqualTo("library/ubuntu"); - assertThat(parser.getTag()).isEqualTo("v1"); - assertThat(parser.getDigest()).isNull(); - } - - @Test - void parsesReferenceWithDigest() { - ImageReferenceParser parser = ImageReferenceParser.of( - "repo.example.com:8080/library/ubuntu@sha256:47bfdb88c3ae13e488167607973b7688f69d9e8c142c2045af343ec199649c09"); - assertThat(parser.getDomain()).isEqualTo("repo.example.com:8080"); - assertThat(parser.getName()).isEqualTo("library/ubuntu"); - assertThat(parser.getTag()).isNull(); - assertThat(parser.getDigest()) - .isEqualTo("sha256:47bfdb88c3ae13e488167607973b7688f69d9e8c142c2045af343ec199649c09"); - } - -} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceTests.java index 5b22402767..134db76f11 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceTests.java @@ -102,6 +102,16 @@ class ImageReferenceTests { assertThat(reference.toString()).isEqualTo("docker.io/library/ubuntu:bionic"); } + @Test + void ofDomainPortAndTag() { + ImageReference reference = ImageReference.of("repo.example.com:8080/library/ubuntu:v1"); + assertThat(reference.getDomain()).isEqualTo("repo.example.com:8080"); + assertThat(reference.getName()).isEqualTo("library/ubuntu"); + assertThat(reference.getTag()).isEqualTo("v1"); + assertThat(reference.getDigest()).isNull(); + assertThat(reference.toString()).isEqualTo("repo.example.com:8080/library/ubuntu:v1"); + } + @Test void ofNameAndDigest() { ImageReference reference = ImageReference