mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Validate image references before passing to CNB builder
Prior to this commit, an image name or run image name derived from the project name or provided by the user would be passed to the CNB builder without validation by the Maven plugin build-image goal or Gradle plugin bootBuildImage task. This could lead to error messages from the plugins that are difficult to understand and diagnose. This commit makes parsing of the image names more strict, based on the grammar implemented by the Docker go library. This provides validation of the image names before passing them to the builder, with a more descriptive error message when parsing and validation fails. Fixes gh-21495
This commit is contained in:
parent
63423e7d71
commit
28643e4d2d
@ -22,8 +22,10 @@ import org.springframework.util.Assert;
|
||||
* A Docker image name of the form {@literal "docker.io/library/ubuntu"}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
* @since 2.3.0
|
||||
* @see ImageReference
|
||||
* @see ImageReferenceParser
|
||||
* @see #of(String)
|
||||
*/
|
||||
public class ImageName {
|
||||
@ -41,11 +43,10 @@ public class ImageName {
|
||||
private final String string;
|
||||
|
||||
ImageName(String domain, String name) {
|
||||
Assert.hasText(domain, "Domain must not be empty");
|
||||
Assert.hasText(name, "Name must not be empty");
|
||||
this.domain = domain;
|
||||
this.name = name;
|
||||
this.string = domain + "/" + name;
|
||||
this.domain = getDomainOrDefault(domain);
|
||||
this.name = getNameWithDefaultPath(this.domain, name);
|
||||
this.string = this.domain + "/" + this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,6 +101,20 @@ public class ImageName {
|
||||
return this.string;
|
||||
}
|
||||
|
||||
private String getDomainOrDefault(String domain) {
|
||||
if (domain == null || LEGACY_DOMAIN.equals(domain)) {
|
||||
return DEFAULT_DOMAIN;
|
||||
}
|
||||
return domain;
|
||||
}
|
||||
|
||||
private String getNameWithDefaultPath(String domain, String name) {
|
||||
if (DEFAULT_DOMAIN.equals(domain) && !name.contains("/")) {
|
||||
return OFFICIAL_REPOSITORY_NAME + "/" + name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ImageName} from the given value. The following value forms can
|
||||
* be used:
|
||||
@ -112,26 +127,9 @@ public class ImageName {
|
||||
* @return an {@link ImageName} instance
|
||||
*/
|
||||
public static ImageName of(String value) {
|
||||
String[] split = split(value);
|
||||
return new ImageName(split[0], split[1]);
|
||||
}
|
||||
|
||||
static String[] split(String value) {
|
||||
Assert.hasText(value, "Value must not be empty");
|
||||
String domain = DEFAULT_DOMAIN;
|
||||
int firstSlash = value.indexOf('/');
|
||||
if (firstSlash != -1) {
|
||||
String firstSegment = value.substring(0, firstSlash);
|
||||
if (firstSegment.contains(".") || firstSegment.contains(":") || "localhost".equals(firstSegment)) {
|
||||
domain = LEGACY_DOMAIN.equals(firstSegment) ? DEFAULT_DOMAIN : firstSegment;
|
||||
value = value.substring(firstSlash + 1);
|
||||
}
|
||||
}
|
||||
if (DEFAULT_DOMAIN.equals(domain) && !value.contains("/")) {
|
||||
value = OFFICIAL_REPOSITORY_NAME + "/" + value;
|
||||
}
|
||||
return new String[] { domain, value };
|
||||
|
||||
ImageReferenceParser parser = ImageReferenceParser.of(value);
|
||||
return new ImageName(parser.getDomain(), parser.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,9 +30,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Scott Frederick
|
||||
* @since 2.3.0
|
||||
* @see ImageName
|
||||
* @see <a href=
|
||||
* "https://stackoverflow.com/questions/37861791/how-are-docker-image-names-parsed">How
|
||||
* are Docker image names parsed?</a>
|
||||
* @see ImageReferenceParser
|
||||
*/
|
||||
public final class ImageReference {
|
||||
|
||||
@ -180,7 +178,7 @@ public final class ImageReference {
|
||||
filename = filename.substring(0, filename.length() - 4);
|
||||
int firstDot = filename.indexOf('.');
|
||||
if (firstDot == -1) {
|
||||
return ImageReference.of(filename);
|
||||
return of(filename);
|
||||
}
|
||||
String name = filename.substring(0, firstDot);
|
||||
String version = filename.substring(firstDot + 1);
|
||||
@ -226,8 +224,9 @@ public final class ImageReference {
|
||||
*/
|
||||
public static ImageReference of(String value) {
|
||||
Assert.hasText(value, "Value must not be null");
|
||||
String[] domainAndValue = ImageName.split(value);
|
||||
return of(domainAndValue[0], domainAndValue[1]);
|
||||
ImageReferenceParser parser = ImageReferenceParser.of(value);
|
||||
ImageName name = new ImageName(parser.getDomain(), parser.getName());
|
||||
return new ImageReference(name, parser.getTag(), parser.getDigest());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -261,21 +260,4 @@ public final class ImageReference {
|
||||
return new ImageReference(name, tag, digest);
|
||||
}
|
||||
|
||||
private static ImageReference of(String domain, String value) {
|
||||
String digest = null;
|
||||
int lastAt = value.indexOf('@');
|
||||
if (lastAt != -1) {
|
||||
digest = value.substring(lastAt + 1);
|
||||
value = value.substring(0, lastAt);
|
||||
}
|
||||
String tag = null;
|
||||
int firstColon = value.indexOf(':');
|
||||
if (firstColon != -1) {
|
||||
tag = value.substring(firstColon + 1);
|
||||
value = value.substring(0, firstColon);
|
||||
}
|
||||
ImageName name = new ImageName(domain, value);
|
||||
return new ImageReference(name, tag, digest);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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 <a href=
|
||||
* "https://github.com/docker/distribution/blob/master/reference/reference.go">Docker
|
||||
* grammar reference</a>
|
||||
* @see <a href=
|
||||
* "https://github.com/docker/distribution/blob/master/reference/regexp.go">Docker grammar
|
||||
* implementation</a>
|
||||
* @see <a href=
|
||||
* "https://stackoverflow.com/questions/37861791/how-are-docker-image-names-parsed">How
|
||||
* are Docker image names parsed?</a>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -100,9 +100,9 @@ public class BuildRequestTests {
|
||||
@Test
|
||||
void withBuilderWhenHasDigestUpdatesBuilder() throws IOException {
|
||||
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar")).withBuilder(ImageReference
|
||||
.of("spring/builder:@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d"));
|
||||
.of("spring/builder@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d"));
|
||||
assertThat(request.getBuilder().toString()).isEqualTo(
|
||||
"docker.io/spring/builder:@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||
"docker.io/spring/builder@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -115,9 +115,9 @@ public class BuildRequestTests {
|
||||
@Test
|
||||
void withRunImageWhenHasDigestUpdatesRunImage() throws IOException {
|
||||
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar")).withRunImage(ImageReference
|
||||
.of("example.com/custom/run-image:@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d"));
|
||||
.of("example.com/custom/run-image@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d"));
|
||||
assertThat(request.getRunImage().toString()).isEqualTo(
|
||||
"example.com/custom/run-image:@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||
"example.com/custom/run-image@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -115,7 +115,7 @@ class BuilderTests {
|
||||
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any()))
|
||||
.willAnswer(withPulledImage(builderImage));
|
||||
given(docker.image().pull(eq(ImageReference.of(
|
||||
"docker.io/cloudfoundry/run:@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d")),
|
||||
"docker.io/cloudfoundry/run@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d")),
|
||||
any())).willAnswer(withPulledImage(runImage));
|
||||
Builder builder = new Builder(BuildLog.to(out), docker);
|
||||
BuildRequest request = getTestRequest();
|
||||
|
@ -25,6 +25,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* Tests for {@link ImageName}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class ImageNameTests {
|
||||
|
||||
@ -99,11 +100,13 @@ class ImageNameTests {
|
||||
void hashCodeAndEquals() {
|
||||
ImageName n1 = ImageName.of("ubuntu");
|
||||
ImageName n2 = ImageName.of("library/ubuntu");
|
||||
ImageName n3 = ImageName.of("docker.io/library/ubuntu");
|
||||
ImageName n4 = ImageName.of("index.docker.io/library/ubuntu");
|
||||
ImageName n5 = ImageName.of("alpine");
|
||||
assertThat(n1.hashCode()).isEqualTo(n2.hashCode()).isEqualTo(n3.hashCode()).isEqualTo(n4.hashCode());
|
||||
assertThat(n1).isEqualTo(n1).isEqualTo(n2).isEqualTo(n3).isEqualTo(n4).isNotEqualTo(n5);
|
||||
ImageName n3 = ImageName.of("docker.io/ubuntu");
|
||||
ImageName n4 = ImageName.of("docker.io/library/ubuntu");
|
||||
ImageName n5 = ImageName.of("index.docker.io/library/ubuntu");
|
||||
ImageName n6 = ImageName.of("alpine");
|
||||
assertThat(n1.hashCode()).isEqualTo(n2.hashCode()).isEqualTo(n3.hashCode()).isEqualTo(n4.hashCode())
|
||||
.isEqualTo(n5.hashCode());
|
||||
assertThat(n1).isEqualTo(n1).isEqualTo(n2).isEqualTo(n3).isEqualTo(n4).isNotEqualTo(n6);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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");
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -135,6 +135,16 @@ class BootBuildImageIntegrationTests {
|
||||
assertThat(result.getOutput()).containsPattern("Builder lifecycle '.*' failed with status code");
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void failsWithInvalidImageName() {
|
||||
writeMainClass();
|
||||
writeLongNameResource();
|
||||
BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage", "--imageName=example/Invalid-Image-Name");
|
||||
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.FAILED);
|
||||
assertThat(result.getOutput()).containsPattern("Unable to parse image reference")
|
||||
.containsPattern("example/Invalid-Image-Name");
|
||||
}
|
||||
|
||||
private void writeMainClass() {
|
||||
File examplePackage = new File(this.gradleBuild.getProjectDir(), "src/main/java/example");
|
||||
examplePackage.mkdirs();
|
||||
|
Loading…
Reference in New Issue
Block a user