mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Allow for predicate based checking of image names
Update `DockerComposeConnectionDetailsFactory` to accept a `Predicate` based check to determine if the source should be accepted. The existing name based checks have also been improved to allow names outside of official docker images. The `ImageReference` and `ImageName` classes have been mainly copied from `org.springframework.boot.buildpack.platform.docker.type`. Closes gh-35154
This commit is contained in:
parent
19221f00f3
commit
0f032c290a
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2021 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.docker.compose.core;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Docker image name of the form {@literal "docker.io/library/ubuntu"}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @author Scott Frederick
|
||||||
|
*/
|
||||||
|
class ImageName {
|
||||||
|
|
||||||
|
private static final String DEFAULT_DOMAIN = "docker.io";
|
||||||
|
|
||||||
|
private static final String OFFICIAL_REPOSITORY_NAME = "library";
|
||||||
|
|
||||||
|
private static final String LEGACY_DOMAIN = "index.docker.io";
|
||||||
|
|
||||||
|
private final String domain;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final String string;
|
||||||
|
|
||||||
|
ImageName(String domain, String path) {
|
||||||
|
Assert.hasText(path, "Path must not be empty");
|
||||||
|
this.domain = getDomainOrDefault(domain);
|
||||||
|
this.name = getNameWithDefaultPath(this.domain, path);
|
||||||
|
this.string = this.domain + "/" + this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the domain for this image name.
|
||||||
|
* @return the domain
|
||||||
|
*/
|
||||||
|
String getDomain() {
|
||||||
|
return this.domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of this image.
|
||||||
|
* @return the image name
|
||||||
|
*/
|
||||||
|
String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ImageName other = (ImageName) obj;
|
||||||
|
boolean result = true;
|
||||||
|
result = result && this.domain.equals(other.domain);
|
||||||
|
result = result && this.name.equals(other.name);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + this.domain.hashCode();
|
||||||
|
result = prime * result + this.name.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String parseDomain(String value) {
|
||||||
|
int firstSlash = value.indexOf('/');
|
||||||
|
String candidate = (firstSlash != -1) ? value.substring(0, firstSlash) : null;
|
||||||
|
if (candidate != null && Regex.DOMAIN.matcher(candidate).matches()) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ImageName of(String value) {
|
||||||
|
Assert.hasText(value, "Value must not be empty");
|
||||||
|
String domain = parseDomain(value);
|
||||||
|
String path = (domain != null) ? value.substring(domain.length() + 1) : value;
|
||||||
|
Assert.isTrue(Regex.PATH.matcher(path).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(domain, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2023 the original author or authors.
|
* Copyright 2012-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,31 +16,68 @@
|
|||||||
|
|
||||||
package org.springframework.boot.docker.compose.core;
|
package org.springframework.boot.docker.compose.core;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A docker image reference of form
|
* A reference to a Docker image of the form {@code "imagename[:tag|@digest]"}.
|
||||||
* {@code [<registry>/][<project>/]<image>[:<tag>|@<digest>]}.
|
|
||||||
*
|
*
|
||||||
* @author Moritz Halbritter
|
|
||||||
* @author Andy Wilkinson
|
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @since 3.1.0
|
* @author Scott Frederick
|
||||||
* @see <a href="https://docs.docker.com/compose/compose-file/#image">docker
|
* @since 2.3.0
|
||||||
* documentation</a>
|
|
||||||
*/
|
*/
|
||||||
public final class ImageReference {
|
public final class ImageReference {
|
||||||
|
|
||||||
private final String reference;
|
private final ImageName name;
|
||||||
|
|
||||||
private final String imageName;
|
private final String tag;
|
||||||
|
|
||||||
ImageReference(String reference) {
|
private final String digest;
|
||||||
this.reference = reference;
|
|
||||||
int lastSlashIndex = reference.lastIndexOf('/');
|
private final String string;
|
||||||
String imageTagDigest = (lastSlashIndex != -1) ? reference.substring(lastSlashIndex + 1) : reference;
|
|
||||||
int digestIndex = imageTagDigest.indexOf('@');
|
private ImageReference(ImageName name, String tag, String digest) {
|
||||||
String imageTag = (digestIndex != -1) ? imageTagDigest.substring(0, digestIndex) : imageTagDigest;
|
Assert.notNull(name, "Name must not be null");
|
||||||
int colon = imageTag.indexOf(':');
|
this.name = name;
|
||||||
this.imageName = (colon != -1) ? imageTag.substring(0, colon) : imageTag;
|
this.tag = tag;
|
||||||
|
this.digest = digest;
|
||||||
|
this.string = buildString(name.toString(), tag, digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the domain for this image name.
|
||||||
|
* @return the domain
|
||||||
|
* @see ImageName#getDomain()
|
||||||
|
*/
|
||||||
|
public String getDomain() {
|
||||||
|
return this.name.getDomain();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of this image.
|
||||||
|
* @return the image name
|
||||||
|
* @see ImageName#getName()
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return this.name.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the tag from the reference or {@code null}.
|
||||||
|
* @return the referenced tag
|
||||||
|
*/
|
||||||
|
public String getTag() {
|
||||||
|
return this.tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the digest from the reference or {@code null}.
|
||||||
|
* @return the referenced digest
|
||||||
|
*/
|
||||||
|
public String getDigest() {
|
||||||
|
return this.digest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -52,35 +89,84 @@ public final class ImageReference {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ImageReference other = (ImageReference) obj;
|
ImageReference other = (ImageReference) obj;
|
||||||
return this.reference.equals(other.reference);
|
boolean result = true;
|
||||||
|
result = result && this.name.equals(other.name);
|
||||||
|
result = result && ObjectUtils.nullSafeEquals(this.tag, other.tag);
|
||||||
|
result = result && ObjectUtils.nullSafeEquals(this.digest, other.digest);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return this.reference.hashCode();
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + this.name.hashCode();
|
||||||
|
result = prime * result + ObjectUtils.nullSafeHashCode(this.tag);
|
||||||
|
result = prime * result + ObjectUtils.nullSafeHashCode(this.digest);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.reference;
|
return this.string;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildString(String name, String tag, String digest) {
|
||||||
|
StringBuilder string = new StringBuilder(name);
|
||||||
|
if (tag != null) {
|
||||||
|
string.append(":").append(tag);
|
||||||
|
}
|
||||||
|
if (digest != null) {
|
||||||
|
string.append("@").append(digest);
|
||||||
|
}
|
||||||
|
return string.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the referenced image, excluding the registry or project. For example, a
|
* Create a new {@link ImageReference} from the given value. The following value forms
|
||||||
* reference of {@code my_private.registry:5000/redis:5} would return {@code redis}.
|
* can be used:
|
||||||
* @return the referenced image
|
* <ul>
|
||||||
*/
|
* <li>{@code name} (maps to {@code docker.io/library/name})</li>
|
||||||
public String getImageName() {
|
* <li>{@code domain/name}</li>
|
||||||
return this.imageName;
|
* <li>{@code domain:port/name}</li>
|
||||||
}
|
* <li>{@code domain:port/name:tag}</li>
|
||||||
|
* <li>{@code domain:port/name@digest}</li>
|
||||||
/**
|
* </ul>
|
||||||
* Create an image reference from the given String value.
|
* @param value the value to parse
|
||||||
* @param value the string used to create the reference
|
|
||||||
* @return an {@link ImageReference} instance
|
* @return an {@link ImageReference} instance
|
||||||
*/
|
*/
|
||||||
public static ImageReference of(String value) {
|
public static ImageReference of(String value) {
|
||||||
return (value != null) ? new ImageReference(value) : null;
|
Assert.hasText(value, "Value must not be null");
|
||||||
|
String domain = ImageName.parseDomain(value);
|
||||||
|
String path = (domain != null) ? value.substring(domain.length() + 1) : value;
|
||||||
|
String digest = null;
|
||||||
|
int digestSplit = path.indexOf("@");
|
||||||
|
if (digestSplit != -1) {
|
||||||
|
String remainder = path.substring(digestSplit + 1);
|
||||||
|
Matcher matcher = Regex.DIGEST.matcher(remainder);
|
||||||
|
if (matcher.find()) {
|
||||||
|
digest = remainder.substring(0, matcher.end());
|
||||||
|
remainder = remainder.substring(matcher.end());
|
||||||
|
path = path.substring(0, digestSplit) + remainder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String tag = null;
|
||||||
|
int tagSplit = path.lastIndexOf(":");
|
||||||
|
if (tagSplit != -1) {
|
||||||
|
String remainder = path.substring(tagSplit + 1);
|
||||||
|
Matcher matcher = Regex.TAG.matcher(remainder);
|
||||||
|
if (matcher.find()) {
|
||||||
|
tag = remainder.substring(0, matcher.end());
|
||||||
|
remainder = remainder.substring(matcher.end());
|
||||||
|
path = path.substring(0, tagSplit) + remainder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert.isTrue(Regex.PATH.matcher(path).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(domain, path);
|
||||||
|
return new ImageReference(name, tag, digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 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.docker.compose.core;
|
||||||
|
|
||||||
|
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 <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 Regex implements CharSequence {
|
||||||
|
|
||||||
|
static final Pattern 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").compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
static final Pattern PATH;
|
||||||
|
static {
|
||||||
|
Regex component = PATH_COMPONENT;
|
||||||
|
Regex slashComponent = Regex.group("[/]", component);
|
||||||
|
Regex slashComponents = Regex.group(slashComponent.oneOrMoreTimes());
|
||||||
|
PATH = Regex.of(component, slashComponents.zeroOrOnce()).compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
static final Pattern TAG = Regex.of("^[\\w][\\w.-]{0,127}").compile();
|
||||||
|
|
||||||
|
static final Pattern DIGEST = Regex.of("^[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[A-Fa-f0-9]]{32,}")
|
||||||
|
.compile();
|
||||||
|
|
||||||
|
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 + "?");
|
||||||
|
}
|
||||||
|
|
||||||
|
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) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 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.docker.compose.service.connection;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import org.springframework.boot.docker.compose.core.ImageReference;
|
||||||
|
import org.springframework.boot.docker.compose.core.RunningService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Predicate} that matches against connection names.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
class ConnectionNamePredicate implements Predicate<DockerComposeConnectionSource> {
|
||||||
|
|
||||||
|
private String required;
|
||||||
|
|
||||||
|
ConnectionNamePredicate(String required) {
|
||||||
|
this.required = asCanonicalName(required);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(DockerComposeConnectionSource source) {
|
||||||
|
String actual = getActual(source.getRunningService());
|
||||||
|
return this.required.equals(actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getActual(RunningService service) {
|
||||||
|
String label = service.labels().get("org.springframework.boot.service-connection");
|
||||||
|
return (label != null) ? asCanonicalName(label) : service.image().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String asCanonicalName(String name) {
|
||||||
|
return ImageReference.of(name).getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -17,6 +17,7 @@
|
|||||||
package org.springframework.boot.docker.compose.service.connection;
|
package org.springframework.boot.docker.compose.service.connection;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory;
|
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory;
|
||||||
@ -40,7 +41,7 @@ import org.springframework.util.ObjectUtils;
|
|||||||
public abstract class DockerComposeConnectionDetailsFactory<D extends ConnectionDetails>
|
public abstract class DockerComposeConnectionDetailsFactory<D extends ConnectionDetails>
|
||||||
implements ConnectionDetailsFactory<DockerComposeConnectionSource, D> {
|
implements ConnectionDetailsFactory<DockerComposeConnectionSource, D> {
|
||||||
|
|
||||||
private final String connectionName;
|
private final Predicate<DockerComposeConnectionSource> predicate;
|
||||||
|
|
||||||
private final String[] requiredClassNames;
|
private final String[] requiredClassNames;
|
||||||
|
|
||||||
@ -50,7 +51,17 @@ public abstract class DockerComposeConnectionDetailsFactory<D extends Connection
|
|||||||
* @param requiredClassNames the names of classes that must be present
|
* @param requiredClassNames the names of classes that must be present
|
||||||
*/
|
*/
|
||||||
protected DockerComposeConnectionDetailsFactory(String connectionName, String... requiredClassNames) {
|
protected DockerComposeConnectionDetailsFactory(String connectionName, String... requiredClassNames) {
|
||||||
this.connectionName = connectionName;
|
this(new ConnectionNamePredicate(connectionName), requiredClassNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link DockerComposeConnectionDetailsFactory} instance.
|
||||||
|
* @param predicate a predicate used to check when a service is accepted
|
||||||
|
* @param requiredClassNames the names of classes that must be present
|
||||||
|
*/
|
||||||
|
protected DockerComposeConnectionDetailsFactory(Predicate<DockerComposeConnectionSource> predicate,
|
||||||
|
String... requiredClassNames) {
|
||||||
|
this.predicate = predicate;
|
||||||
this.requiredClassNames = requiredClassNames;
|
this.requiredClassNames = requiredClassNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,12 +71,7 @@ public abstract class DockerComposeConnectionDetailsFactory<D extends Connection
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean accept(DockerComposeConnectionSource source) {
|
private boolean accept(DockerComposeConnectionSource source) {
|
||||||
return hasRequiredClasses() && this.connectionName.equals(getConnectionName(source.getRunningService()));
|
return hasRequiredClasses() && this.predicate.test(source);
|
||||||
}
|
|
||||||
|
|
||||||
private String getConnectionName(RunningService service) {
|
|
||||||
String connectionName = service.labels().get("org.springframework.boot.service-connection");
|
|
||||||
return (connectionName != null) ? connectionName : service.image().getImageName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasRequiredClasses() {
|
private boolean hasRequiredClasses() {
|
||||||
|
@ -35,7 +35,8 @@ class ZipkinDockerComposeConnectionDetailsFactory
|
|||||||
private static final int ZIPKIN_PORT = 9411;
|
private static final int ZIPKIN_PORT = 9411;
|
||||||
|
|
||||||
ZipkinDockerComposeConnectionDetailsFactory() {
|
ZipkinDockerComposeConnectionDetailsFactory() {
|
||||||
super("zipkin", "org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinAutoConfiguration");
|
super("openzipkin/zipkin",
|
||||||
|
"org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinAutoConfiguration");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -133,7 +133,7 @@ class DefaultDockerComposeTests {
|
|||||||
assertThat(runningServices).hasSize(1);
|
assertThat(runningServices).hasSize(1);
|
||||||
RunningService runningService = runningServices.get(0);
|
RunningService runningService = runningServices.get(0);
|
||||||
assertThat(runningService.name()).isEqualTo("name");
|
assertThat(runningService.name()).isEqualTo("name");
|
||||||
assertThat(runningService.image()).hasToString("redis");
|
assertThat(runningService.image()).hasToString("docker.io/library/redis");
|
||||||
assertThat(runningService.host()).isEqualTo(HOST);
|
assertThat(runningService.host()).isEqualTo(HOST);
|
||||||
assertThat(runningService.ports().getAll()).isEmpty();
|
assertThat(runningService.ports().getAll()).isEmpty();
|
||||||
assertThat(runningService.env()).containsExactly(entry("a", "b"));
|
assertThat(runningService.env()).containsExactly(entry("a", "b"));
|
||||||
|
@ -77,13 +77,13 @@ class DefaultRunningServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void imageReturnsImageFromPsResponse() {
|
void imageReturnsImageFromPsResponse() {
|
||||||
assertThat(this.runningService.image()).hasToString("redis");
|
assertThat(this.runningService.image()).hasToString("docker.io/library/redis");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // gh-34992
|
@Test // gh-34992
|
||||||
void imageWhenUsingEarlierDockerVersionReturnsImageFromInspectResult() {
|
void imageWhenUsingEarlierDockerVersionReturnsImageFromInspectResult() {
|
||||||
DefaultRunningService runningService = createRunningService(false);
|
DefaultRunningService runningService = createRunningService(false);
|
||||||
assertThat(runningService.image()).hasToString("redis");
|
assertThat(runningService.image()).hasToString("docker.io/library/redis");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 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.docker.compose.core;
|
||||||
|
|
||||||
|
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 ImageName}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @author Scott Frederick
|
||||||
|
*/
|
||||||
|
class ImageNameTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofWhenNameOnlyCreatesImageName() {
|
||||||
|
ImageName imageName = ImageName.of("ubuntu");
|
||||||
|
assertThat(imageName).hasToString("docker.io/library/ubuntu");
|
||||||
|
assertThat(imageName.getDomain()).isEqualTo("docker.io");
|
||||||
|
assertThat(imageName.getName()).isEqualTo("library/ubuntu");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofWhenSlashedNameCreatesImageName() {
|
||||||
|
ImageName imageName = ImageName.of("canonical/ubuntu");
|
||||||
|
assertThat(imageName).hasToString("docker.io/canonical/ubuntu");
|
||||||
|
assertThat(imageName.getDomain()).isEqualTo("docker.io");
|
||||||
|
assertThat(imageName.getName()).isEqualTo("canonical/ubuntu");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofWhenLocalhostNameCreatesImageName() {
|
||||||
|
ImageName imageName = ImageName.of("localhost/canonical/ubuntu");
|
||||||
|
assertThat(imageName).hasToString("localhost/canonical/ubuntu");
|
||||||
|
assertThat(imageName.getDomain()).isEqualTo("localhost");
|
||||||
|
assertThat(imageName.getName()).isEqualTo("canonical/ubuntu");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofWhenDomainAndNameCreatesImageName() {
|
||||||
|
ImageName imageName = ImageName.of("repo.spring.io/canonical/ubuntu");
|
||||||
|
assertThat(imageName).hasToString("repo.spring.io/canonical/ubuntu");
|
||||||
|
assertThat(imageName.getDomain()).isEqualTo("repo.spring.io");
|
||||||
|
assertThat(imageName.getName()).isEqualTo("canonical/ubuntu");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofWhenDomainNameAndPortCreatesImageName() {
|
||||||
|
ImageName imageName = ImageName.of("repo.spring.io:8080/canonical/ubuntu");
|
||||||
|
assertThat(imageName).hasToString("repo.spring.io:8080/canonical/ubuntu");
|
||||||
|
assertThat(imageName.getDomain()).isEqualTo("repo.spring.io:8080");
|
||||||
|
assertThat(imageName.getName()).isEqualTo("canonical/ubuntu");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofWhenSimpleNameAndPortCreatesImageName() {
|
||||||
|
ImageName imageName = ImageName.of("repo:8080/ubuntu");
|
||||||
|
assertThat(imageName).hasToString("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).hasToString("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).hasToString("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");
|
||||||
|
assertThat(imageName).hasToString("docker.io/library/ubuntu");
|
||||||
|
assertThat(imageName.getDomain()).isEqualTo("docker.io");
|
||||||
|
assertThat(imageName.getName()).isEqualTo("library/ubuntu");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofWhenNameIsNullThrowsException() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> ImageName.of(null))
|
||||||
|
.withMessage("Value must not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofWhenNameIsEmptyThrowsException() {
|
||||||
|
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");
|
||||||
|
ImageName n2 = ImageName.of("library/ubuntu");
|
||||||
|
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).hasSameHashCodeAs(n2).hasSameHashCodeAs(n3).hasSameHashCodeAs(n4).hasSameHashCodeAs(n5);
|
||||||
|
assertThat(n1).isEqualTo(n1).isEqualTo(n2).isEqualTo(n3).isEqualTo(n4).isNotEqualTo(n6);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -19,85 +19,150 @@ package org.springframework.boot.docker.compose.core;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link ImageReference}.
|
* Tests for {@link ImageReference}.
|
||||||
*
|
*
|
||||||
* @author Moritz Halbritter
|
|
||||||
* @author Andy Wilkinson
|
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
class ImageReferenceTests {
|
class ImageReferenceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getImageNameWhenImageOnly() {
|
void ofSimpleName() {
|
||||||
ImageReference imageReference = ImageReference.of("redis");
|
ImageReference reference = ImageReference.of("ubuntu");
|
||||||
assertThat(imageReference.getImageName()).isEqualTo("redis");
|
assertThat(reference.getDomain()).isEqualTo("docker.io");
|
||||||
|
assertThat(reference.getName()).isEqualTo("library/ubuntu");
|
||||||
|
assertThat(reference.getTag()).isNull();
|
||||||
|
assertThat(reference.getDigest()).isNull();
|
||||||
|
assertThat(reference).hasToString("docker.io/library/ubuntu");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getImageNameWhenImageAndTag() {
|
void ofLibrarySlashName() {
|
||||||
ImageReference imageReference = ImageReference.of("redis:5");
|
ImageReference reference = ImageReference.of("library/ubuntu");
|
||||||
assertThat(imageReference.getImageName()).isEqualTo("redis");
|
assertThat(reference.getDomain()).isEqualTo("docker.io");
|
||||||
|
assertThat(reference.getName()).isEqualTo("library/ubuntu");
|
||||||
|
assertThat(reference.getTag()).isNull();
|
||||||
|
assertThat(reference.getDigest()).isNull();
|
||||||
|
assertThat(reference).hasToString("docker.io/library/ubuntu");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getImageNameWhenImageAndDigest() {
|
void ofSlashName() {
|
||||||
ImageReference imageReference = ImageReference
|
ImageReference reference = ImageReference.of("adoptopenjdk/openjdk11");
|
||||||
.of("redis@sha256:0ed5d5928d4737458944eb604cc8509e245c3e19d02ad83935398bc4b991aac7");
|
assertThat(reference.getDomain()).isEqualTo("docker.io");
|
||||||
assertThat(imageReference.getImageName()).isEqualTo("redis");
|
assertThat(reference.getName()).isEqualTo("adoptopenjdk/openjdk11");
|
||||||
|
assertThat(reference.getTag()).isNull();
|
||||||
|
assertThat(reference.getDigest()).isNull();
|
||||||
|
assertThat(reference).hasToString("docker.io/adoptopenjdk/openjdk11");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getImageNameWhenProjectAndImage() {
|
void ofCustomDomain() {
|
||||||
ImageReference imageReference = ImageReference.of("library/redis");
|
ImageReference reference = ImageReference.of("repo.example.com/java/jdk");
|
||||||
assertThat(imageReference.getImageName()).isEqualTo("redis");
|
assertThat(reference.getDomain()).isEqualTo("repo.example.com");
|
||||||
|
assertThat(reference.getName()).isEqualTo("java/jdk");
|
||||||
|
assertThat(reference.getTag()).isNull();
|
||||||
|
assertThat(reference.getDigest()).isNull();
|
||||||
|
assertThat(reference).hasToString("repo.example.com/java/jdk");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getImageNameWhenRegistryLibraryAndImage() {
|
void ofCustomDomainAndPort() {
|
||||||
ImageReference imageReference = ImageReference.of("docker.io/library/redis");
|
ImageReference reference = ImageReference.of("repo.example.com:8080/java/jdk");
|
||||||
assertThat(imageReference.getImageName()).isEqualTo("redis");
|
assertThat(reference.getDomain()).isEqualTo("repo.example.com:8080");
|
||||||
|
assertThat(reference.getName()).isEqualTo("java/jdk");
|
||||||
|
assertThat(reference.getTag()).isNull();
|
||||||
|
assertThat(reference.getDigest()).isNull();
|
||||||
|
assertThat(reference).hasToString("repo.example.com:8080/java/jdk");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getImageNameWhenRegistryLibraryImageAndTag() {
|
void ofLegacyDomain() {
|
||||||
ImageReference imageReference = ImageReference.of("docker.io/library/redis:5");
|
ImageReference reference = ImageReference.of("index.docker.io/ubuntu");
|
||||||
assertThat(imageReference.getImageName()).isEqualTo("redis");
|
assertThat(reference.getDomain()).isEqualTo("docker.io");
|
||||||
|
assertThat(reference.getName()).isEqualTo("library/ubuntu");
|
||||||
|
assertThat(reference.getTag()).isNull();
|
||||||
|
assertThat(reference.getDigest()).isNull();
|
||||||
|
assertThat(reference).hasToString("docker.io/library/ubuntu");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getImageNameWhenRegistryLibraryImageAndDigest() {
|
void ofNameAndTag() {
|
||||||
ImageReference imageReference = ImageReference
|
ImageReference reference = ImageReference.of("ubuntu:bionic");
|
||||||
.of("docker.io/library/redis@sha256:0ed5d5928d4737458944eb604cc8509e245c3e19d02ad83935398bc4b991aac7");
|
assertThat(reference.getDomain()).isEqualTo("docker.io");
|
||||||
assertThat(imageReference.getImageName()).isEqualTo("redis");
|
assertThat(reference.getName()).isEqualTo("library/ubuntu");
|
||||||
|
assertThat(reference.getTag()).isEqualTo("bionic");
|
||||||
|
assertThat(reference.getDigest()).isNull();
|
||||||
|
assertThat(reference).hasToString("docker.io/library/ubuntu:bionic");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getImageNameWhenRegistryWithPort() {
|
void ofDomainPortAndTag() {
|
||||||
ImageReference imageReference = ImageReference.of("my_private.registry:5000/redis");
|
ImageReference reference = ImageReference.of("repo.example.com:8080/library/ubuntu:v1");
|
||||||
assertThat(imageReference.getImageName()).isEqualTo("redis");
|
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).hasToString("repo.example.com:8080/library/ubuntu:v1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getImageNameWhenRegistryWithPortAndTag() {
|
void ofNameAndDigest() {
|
||||||
ImageReference imageReference = ImageReference.of("my_private.registry:5000/redis:5");
|
ImageReference reference = ImageReference
|
||||||
assertThat(imageReference.getImageName()).isEqualTo("redis");
|
.of("ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||||
|
assertThat(reference.getDomain()).isEqualTo("docker.io");
|
||||||
|
assertThat(reference.getName()).isEqualTo("library/ubuntu");
|
||||||
|
assertThat(reference.getTag()).isNull();
|
||||||
|
assertThat(reference.getDigest())
|
||||||
|
.isEqualTo("sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||||
|
assertThat(reference).hasToString(
|
||||||
|
"docker.io/library/ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void toStringReturnsReferenceString() {
|
void ofNameAndTagAndDigest() {
|
||||||
ImageReference imageReference = ImageReference.of("docker.io/library/redis");
|
ImageReference reference = ImageReference
|
||||||
assertThat(imageReference).hasToString("docker.io/library/redis");
|
.of("ubuntu:bionic@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||||
|
assertThat(reference.getDomain()).isEqualTo("docker.io");
|
||||||
|
assertThat(reference.getName()).isEqualTo("library/ubuntu");
|
||||||
|
assertThat(reference.getTag()).isEqualTo("bionic");
|
||||||
|
assertThat(reference.getDigest())
|
||||||
|
.isEqualTo("sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||||
|
assertThat(reference).hasToString(
|
||||||
|
"docker.io/library/ubuntu:bionic@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofCustomDomainAndPortWithTag() {
|
||||||
|
ImageReference reference = ImageReference
|
||||||
|
.of("example.com:8080/canonical/ubuntu:bionic@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||||
|
assertThat(reference.getDomain()).isEqualTo("example.com:8080");
|
||||||
|
assertThat(reference.getName()).isEqualTo("canonical/ubuntu");
|
||||||
|
assertThat(reference.getTag()).isEqualTo("bionic");
|
||||||
|
assertThat(reference.getDigest())
|
||||||
|
.isEqualTo("sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||||
|
assertThat(reference).hasToString(
|
||||||
|
"example.com:8080/canonical/ubuntu:bionic@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofWhenHasIllegalCharacter() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> ImageReference
|
||||||
|
.of("registry.example.com/example/example-app:1.6.0-dev.2.uncommitted+wip.foo.c75795d"))
|
||||||
|
.withMessageContaining("Unable to parse image reference");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void equalsAndHashCode() {
|
void equalsAndHashCode() {
|
||||||
ImageReference imageReference1 = ImageReference.of("docker.io/library/redis");
|
ImageReference r1 = ImageReference.of("ubuntu:bionic");
|
||||||
ImageReference imageReference2 = ImageReference.of("docker.io/library/redis");
|
ImageReference r2 = ImageReference.of("docker.io/library/ubuntu:bionic");
|
||||||
ImageReference imageReference3 = ImageReference.of("docker.io/library/other");
|
ImageReference r3 = ImageReference.of("docker.io/library/ubuntu:latest");
|
||||||
assertThat(imageReference1.hashCode()).isEqualTo(imageReference2.hashCode());
|
assertThat(r1).hasSameHashCodeAs(r2);
|
||||||
assertThat(imageReference1).isEqualTo(imageReference1).isEqualTo(imageReference2).isNotEqualTo(imageReference3);
|
assertThat(r1).isEqualTo(r1).isEqualTo(r2).isNotEqualTo(r3);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 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.docker.compose.service.connection;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.docker.compose.core.ImageReference;
|
||||||
|
import org.springframework.boot.docker.compose.core.RunningService;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ConnectionNamePredicate}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
class ConnectionNamePredicateTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void offical() {
|
||||||
|
assertThat(predicateOf("elasticsearch")).accepts(sourceOf("elasticsearch"));
|
||||||
|
assertThat(predicateOf("elasticsearch")).accepts(sourceOf("library/elasticsearch"));
|
||||||
|
assertThat(predicateOf("elasticsearch")).accepts(sourceOf("docker.io/library/elasticsearch"));
|
||||||
|
assertThat(predicateOf("elasticsearch")).accepts(sourceOf("docker.io/elasticsearch"));
|
||||||
|
assertThat(predicateOf("elasticsearch")).accepts(sourceOf("docker.io/elasticsearch:latest"));
|
||||||
|
assertThat(predicateOf("elasticsearch")).rejects(sourceOf("redis"));
|
||||||
|
assertThat(predicateOf("elasticsearch")).rejects(sourceOf("library/redis"));
|
||||||
|
assertThat(predicateOf("elasticsearch")).rejects(sourceOf("docker.io/library/redis"));
|
||||||
|
assertThat(predicateOf("elasticsearch")).rejects(sourceOf("docker.io/redis"));
|
||||||
|
assertThat(predicateOf("elasticsearch")).rejects(sourceOf("docker.io/redis"));
|
||||||
|
assertThat(predicateOf("zipkin")).rejects(sourceOf("openzipkin/zipkin"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void organization() {
|
||||||
|
assertThat(predicateOf("openzipkin/zipkin")).accepts(sourceOf("openzipkin/zipkin"));
|
||||||
|
assertThat(predicateOf("openzipkin/zipkin")).accepts(sourceOf("openzipkin/zipkin:latest"));
|
||||||
|
assertThat(predicateOf("openzipkin/zipkin")).rejects(sourceOf("openzipkin/zapkin"));
|
||||||
|
assertThat(predicateOf("openzipkin/zipkin")).rejects(sourceOf("zipkin"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customDomain() {
|
||||||
|
assertThat(predicateOf("redis")).accepts(sourceOf("internalhost:8080/library/redis"));
|
||||||
|
assertThat(predicateOf("redis")).accepts(sourceOf("myhost.com/library/redis"));
|
||||||
|
assertThat(predicateOf("redis")).accepts(sourceOf("myhost.com:8080/library/redis"));
|
||||||
|
assertThat(predicateOf("redis")).rejects(sourceOf("internalhost:8080/redis"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void labeled() {
|
||||||
|
assertThat(predicateOf("redis")).accepts(sourceOf("internalhost:8080/myredis", "redis"));
|
||||||
|
assertThat(predicateOf("redis")).accepts(sourceOf("internalhost:8080/myredis", "library/redis"));
|
||||||
|
assertThat(predicateOf("openzipkin/zipkin"))
|
||||||
|
.accepts(sourceOf("internalhost:8080/libs/libs/mzipkin", "openzipkin/zipkin"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Predicate<DockerComposeConnectionSource> predicateOf(String required) {
|
||||||
|
return new ConnectionNamePredicate(required);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DockerComposeConnectionSource sourceOf(String connectioName) {
|
||||||
|
return sourceOf(connectioName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DockerComposeConnectionSource sourceOf(String connectioName, String label) {
|
||||||
|
DockerComposeConnectionSource source = mock(DockerComposeConnectionSource.class);
|
||||||
|
RunningService runningService = mock(RunningService.class);
|
||||||
|
given(source.getRunningService()).willReturn(runningService);
|
||||||
|
given(runningService.image()).willReturn(ImageReference.of(connectioName));
|
||||||
|
if (label != null) {
|
||||||
|
given(runningService.labels()).willReturn(Map.of("org.springframework.boot.service-connection", label));
|
||||||
|
}
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -56,7 +56,7 @@ The following service connections are currently supported:
|
|||||||
| Containers named "redis"
|
| Containers named "redis"
|
||||||
|
|
||||||
| `ZipkinConnectionDetails`
|
| `ZipkinConnectionDetails`
|
||||||
| Containers named "zipkin".
|
| Containers named "openzipkin/zipkin".
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user