1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.micronaut.build.services;
17
18 import com.github.dockerjava.api.DockerClient;
19 import com.github.dockerjava.api.command.BuildImageCmd;
20 import com.github.dockerjava.api.command.BuildImageResultCallback;
21 import com.github.dockerjava.api.command.CreateContainerCmd;
22 import com.github.dockerjava.api.command.CreateContainerResponse;
23 import com.github.dockerjava.api.command.PushImageCmd;
24 import com.github.dockerjava.api.command.StartContainerCmd;
25 import com.github.dockerjava.api.command.WaitContainerCmd;
26 import com.github.dockerjava.api.command.WaitContainerResultCallback;
27 import com.github.dockerjava.api.exception.DockerClientException;
28 import com.github.dockerjava.api.model.Bind;
29 import com.github.dockerjava.api.model.BuildResponseItem;
30 import com.github.dockerjava.api.model.HostConfig;
31 import com.github.dockerjava.core.DefaultDockerClientConfig;
32 import com.github.dockerjava.core.DockerClientConfig;
33 import com.github.dockerjava.core.DockerClientImpl;
34 import com.github.dockerjava.transport.DockerHttpClient;
35 import com.github.dockerjava.zerodep.ZerodepDockerHttpClient;
36 import io.micronaut.build.DockerfileMojo;
37 import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
38 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
39 import org.apache.commons.compress.utils.IOUtils;
40 import org.apache.commons.io.FileUtils;
41 import org.apache.commons.lang3.StringUtils;
42 import org.apache.maven.project.MavenProject;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 import javax.inject.Inject;
47 import javax.inject.Singleton;
48 import java.io.File;
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.nio.file.Files;
52 import java.util.concurrent.TimeUnit;
53
54
55
56
57
58
59
60 @Singleton
61 public class DockerService {
62
63 private static final Logger LOG = LoggerFactory.getLogger(DockerService.class);
64
65 private final DockerClient dockerClient;
66 private final MavenProject mavenProject;
67
68 @SuppressWarnings("CdiInjectionPointsInspection")
69 @Inject
70 public DockerService(MavenProject mavenProject) {
71 this.mavenProject = mavenProject;
72 DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build();
73 DockerHttpClient httpClient = new ZerodepDockerHttpClient.Builder()
74 .dockerHost(config.getDockerHost())
75 .sslConfig(config.getSSLConfig())
76 .build();
77 dockerClient = DockerClientImpl.getInstance(config, httpClient);
78 }
79
80
81
82
83 public BuildImageCmd buildImageCmd(String dockerfileName) throws IOException {
84 return dockerClient.buildImageCmd(loadDockerfileAsResource(dockerfileName));
85 }
86
87
88
89
90 public BuildImageCmd buildImageCmd() {
91 return dockerClient.buildImageCmd();
92 }
93
94
95
96
97
98
99 public String buildImage(BuildImageCmd builder) {
100 BuildImageResultCallback resultCallback = new BuildImageResultCallback() {
101 @Override
102 public void onNext(BuildResponseItem item) {
103 super.onNext(item);
104 if (item.isErrorIndicated() && item.getErrorDetail() != null) {
105 LOG.error(item.getErrorDetail().getMessage());
106 } else if (item.getStream() != null) {
107 String msg = StringUtils.removeEnd(item.getStream(), System.lineSeparator());
108 LOG.info(msg);
109 }
110 }
111 };
112
113 return builder
114 .exec(resultCallback)
115 .awaitImageId();
116 }
117
118
119
120
121
122
123
124 public void runPrivilegedImageAndWait(String imageId, Integer timeoutSeconds, String checkpointNetworkName, String... binds) throws IOException {
125 try (CreateContainerCmd create = dockerClient.createContainerCmd(imageId)) {
126 HostConfig hostConfig = create.getHostConfig();
127 if (hostConfig == null) {
128 throw new DockerClientException("When setting binds and privileged, hostConfig was null. Please check your docker installation and try again");
129 }
130 hostConfig.withPrivileged(true);
131 if (checkpointNetworkName != null) {
132 hostConfig.withNetworkMode(checkpointNetworkName);
133 }
134 for (String bind : binds) {
135 hostConfig.withBinds(Bind.parse(bind));
136 }
137 CreateContainerResponse createResponse = create.exec();
138 try (StartContainerCmd start = dockerClient.startContainerCmd(createResponse.getId())) {
139 start.exec();
140 LOG.info("Container started: {} {}", createResponse.getId(), start.getContainerId());
141 try (WaitContainerCmd wait = dockerClient.waitContainerCmd(createResponse.getId())) {
142 WaitContainerResultCallback waitResult = wait.start();
143 LOG.info("Waiting {} seconds for completion", timeoutSeconds);
144 Integer exitcode = waitResult.awaitStatusCode(timeoutSeconds, TimeUnit.SECONDS);
145 if (exitcode != 0) {
146 throw new IOException("Image " + imageId + " exited with code " + exitcode);
147 }
148 }
149 }
150 }
151 }
152
153
154
155
156 public File copyFromContainer(String imageId, String containerPath) {
157 CreateContainerCmd containerCmd = dockerClient.createContainerCmd(imageId);
158 CreateContainerResponse container = containerCmd.exec();
159 dockerClient.startContainerCmd(container.getId());
160 InputStream nativeImage = dockerClient.copyArchiveFromContainerCmd(container.getId(), containerPath).exec();
161
162 try (TarArchiveInputStream fin = new TarArchiveInputStream(nativeImage)) {
163 TarArchiveEntry tarEntry = fin.getNextTarEntry();
164 File file = new File(mavenProject.getBuild().getDirectory(), tarEntry.getName());
165 if (!file.getCanonicalFile().toPath().startsWith(mavenProject.getBuild().getDirectory())) {
166 throw new IOException("Entry is outside of the target directory");
167 }
168
169 IOUtils.copy(fin, Files.newOutputStream(file.toPath()));
170
171 return file;
172 } catch (IOException e) {
173 LOG.error("Failed to copy file from container", e);
174 } finally {
175 containerCmd.close();
176 }
177 return null;
178 }
179
180
181
182
183 public File loadDockerfileAsResource(String name) throws IOException {
184 return loadDockerfileAsResource(name, DockerfileMojo.DOCKERFILE);
185 }
186
187
188
189
190 public File loadDockerfileAsResource(String name, String targetFileName) throws IOException {
191 String path = "/dockerfiles/" + name;
192 InputStream stream = getClass().getResourceAsStream(path);
193 if (stream != null) {
194 File dockerfile = new File(mavenProject.getBuild().getDirectory(), targetFileName);
195 FileUtils.copyInputStreamToFile(stream, dockerfile);
196 return dockerfile;
197 }
198 return null;
199 }
200
201
202
203
204 public PushImageCmd pushImageCmd(String imageName) {
205 return dockerClient.pushImageCmd(imageName);
206 }
207 }