-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
RemoteDockerImage.java
122 lines (102 loc) · 4.88 KB
/
RemoteDockerImage.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package org.testcontainers.images;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.ListImagesCmd;
import com.github.dockerjava.api.exception.DockerClientException;
import com.github.dockerjava.api.model.AuthConfig;
import com.github.dockerjava.api.model.Image;
import com.github.dockerjava.core.command.PullImageResultCallback;
import lombok.NonNull;
import lombok.ToString;
import org.slf4j.Logger;
import org.slf4j.profiler.Profiler;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.ContainerFetchException;
import org.testcontainers.utility.DockerImageName;
import org.testcontainers.utility.DockerLoggerFactory;
import org.testcontainers.utility.LazyFuture;
import org.testcontainers.utility.RegistryAuthLocator;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ToString
public class RemoteDockerImage extends LazyFuture<String> {
public static final Set<DockerImageName> AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>();
private DockerImageName imageName;
public RemoteDockerImage(String dockerImageName) {
imageName = new DockerImageName(dockerImageName);
}
public RemoteDockerImage(@NonNull String repository, @NonNull String tag) {
imageName = new DockerImageName(repository, tag);
}
@Override
protected final String resolve() {
Profiler profiler = new Profiler("Rule creation - prefetch image");
Logger logger = DockerLoggerFactory.getLogger(imageName.toString());
profiler.setLogger(logger);
Profiler nested = profiler.startNested("Obtaining client");
DockerClient dockerClient = DockerClientFactory.instance().client();
try {
nested.stop();
profiler.start("Check local images");
int attempts = 0;
Exception lastException = null;
while (true) {
// Does our cache already know the image?
if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) {
logger.trace("{} is already in image name cache", imageName);
break;
}
// Update the cache
ListImagesCmd listImagesCmd = dockerClient.listImagesCmd();
if (Boolean.parseBoolean(System.getProperty("useFilter"))) {
listImagesCmd = listImagesCmd.withImageNameFilter(imageName.toString());
}
List<Image> updatedImages = listImagesCmd.exec();
updatedImages.stream()
.map(Image::getRepoTags)
.filter(Objects::nonNull)
.flatMap(Stream::of)
.map(DockerImageName::new)
.collect(Collectors.toCollection(() -> AVAILABLE_IMAGE_NAME_CACHE));
// And now?
if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) {
logger.trace("{} is in image name cache following listing of images", imageName);
break;
}
// Log only on first attempt
if (attempts == 0) {
logger.info("Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", imageName);
profiler.start("Pull image");
}
if (attempts++ >= 3) {
logger.error("Retry limit reached while trying to pull image: {}. Please check output of `docker pull {}`", imageName, imageName);
throw new ContainerFetchException("Retry limit reached while trying to pull image: " + imageName, lastException);
}
// The image is not available locally - pull it
try {
final RegistryAuthLocator authLocator = new RegistryAuthLocator(dockerClient.authConfig());
final AuthConfig effectiveAuthConfig = authLocator.lookupAuthConfig(imageName);
final PullImageResultCallback callback = new PullImageResultCallback();
dockerClient
.pullImageCmd(imageName.getUnversionedPart())
.withTag(imageName.getVersionPart())
.withAuthConfig(effectiveAuthConfig)
.exec(callback);
callback.awaitCompletion();
AVAILABLE_IMAGE_NAME_CACHE.add(imageName);
break;
} catch (Exception e) {
lastException = e;
}
}
return imageName.toString();
} catch (DockerClientException e) {
throw new ContainerFetchException("Failed to get Docker client for " + imageName, e);
} finally {
profiler.stop().log();
}
}
}