Skip to content

Commit cb4f1a4

Browse files
committed
init docker ssh manger
1 parent ab29f8c commit cb4f1a4

File tree

8 files changed

+287
-8
lines changed

8 files changed

+287
-8
lines changed

docker/src/main/java/com/flowci/docker/ContainerManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
public interface ContainerManager {
1212

13-
List<Container> list(List<String> statusFilter, List<String> nameFilter) throws Exception;
13+
List<Container> list(String statusFilter, String nameFilter) throws Exception;
1414

1515
InspectContainerResponse inspect(String containerId) throws Exception;
1616

docker/src/main/java/com/flowci/docker/DockerSDKManager.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import com.flowci.docker.domain.DockerCallback;
44
import com.flowci.docker.domain.DockerStartOption;
5-
import com.flowci.util.ObjectsHelper;
5+
import com.flowci.util.StringHelper;
66
import com.github.dockerjava.api.DockerClient;
77
import com.github.dockerjava.api.command.*;
88
import com.github.dockerjava.api.exception.DockerException;
@@ -12,6 +12,7 @@
1212
import com.github.dockerjava.api.model.PullResponseItem;
1313
import com.github.dockerjava.core.DefaultDockerClientConfig;
1414
import com.github.dockerjava.core.DockerClientBuilder;
15+
import com.google.common.collect.Lists;
1516
import lombok.extern.log4j.Log4j2;
1617

1718
import java.time.Instant;
@@ -78,16 +79,16 @@ private List<Image> findImage(DockerClient client, String image) {
7879
private class ContainerManagerImpl implements ContainerManager {
7980

8081
@Override
81-
public List<Container> list(List<String> statusFilter, List<String> nameFilter) throws Exception {
82+
public List<Container> list(String statusFilter, String nameFilter) throws Exception {
8283
try (DockerClient client = newClient()) {
8384
ListContainersCmd cmd = client.listContainersCmd().withShowAll(true);
8485

85-
if (ObjectsHelper.hasCollection(nameFilter)) {
86-
cmd.withNameFilter(nameFilter);
86+
if (StringHelper.hasValue(nameFilter)) {
87+
cmd.withNameFilter(Lists.newArrayList(nameFilter));
8788
}
8889

89-
if (ObjectsHelper.hasCollection(statusFilter)) {
90-
cmd.withStatusFilter(statusFilter);
90+
if (StringHelper.hasValue(statusFilter)) {
91+
cmd.withStatusFilter(Lists.newArrayList(statusFilter));
9192
}
9293

9394
return cmd.exec();
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package com.flowci.docker;
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import com.fasterxml.jackson.core.JsonProcessingException;
5+
import com.fasterxml.jackson.databind.DeserializationFeature;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.flowci.docker.domain.DockerStartOption;
8+
import com.flowci.docker.domain.SSHOption;
9+
import com.flowci.util.StringHelper;
10+
import com.github.dockerjava.api.command.InspectContainerResponse;
11+
import com.github.dockerjava.api.model.Container;
12+
import com.github.dockerjava.api.model.Frame;
13+
import com.jcraft.jsch.*;
14+
import lombok.AllArgsConstructor;
15+
import lombok.Getter;
16+
17+
import java.io.*;
18+
import java.util.LinkedList;
19+
import java.util.List;
20+
import java.util.Objects;
21+
import java.util.function.Consumer;
22+
23+
public class DockerSSHManager implements DockerManager {
24+
25+
private static final int ChannelTimeout = 10 * 1000;
26+
27+
private static final ObjectMapper mapper = new ObjectMapper();
28+
29+
private Session session;
30+
31+
private final ContainerManager containerManager = new ContainerManagerImpl();
32+
33+
static {
34+
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
35+
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
36+
}
37+
38+
public DockerSSHManager(SSHOption option) throws Exception {
39+
try {
40+
JSch jsch = new JSch();
41+
jsch.addIdentity("name", option.getPrivateKey().getBytes(), null, null);
42+
43+
session = jsch.getSession(option.getRemoteUser(), option.getRemoteHost(), option.getPort());
44+
session.setConfig("StrictHostKeyChecking", "no");
45+
session.connect(option.getTimeoutInSeconds() * 1000);
46+
} catch (JSchException e) {
47+
this.close();
48+
throw new Exception(String.format("Ssh connection error: %s", e.getMessage()));
49+
}
50+
}
51+
52+
@Override
53+
public ContainerManager getContainerManager() {
54+
return containerManager;
55+
}
56+
57+
@Override
58+
public ImageManager getImageManager() {
59+
return null;
60+
}
61+
62+
private class ContainerManagerImpl implements ContainerManager {
63+
@Override
64+
public List<Container> list(String statusFilter, String nameFilter) throws Exception {
65+
StringBuilder cmd = new StringBuilder();
66+
cmd.append("docker ps -a ");
67+
68+
if (StringHelper.hasValue(statusFilter)) {
69+
cmd.append(String.format("--filter \"status=%s\" ", statusFilter));
70+
}
71+
72+
if (StringHelper.hasValue(nameFilter)) {
73+
cmd.append(String.format("--filter \"name=%s\" ", nameFilter));
74+
}
75+
76+
cmd.append("--format \"{{json .}}\"");
77+
78+
List<Container> list = new LinkedList<>();
79+
80+
Output output = runCmd(cmd.toString(), (line) -> {
81+
try {
82+
list.add(mapper.readValue(line, Container.class));
83+
} catch (JsonProcessingException ignore) {
84+
ignore.printStackTrace();
85+
}
86+
});
87+
88+
if (output.getExit() != 0) {
89+
throw new Exception(output.getErr());
90+
}
91+
92+
return list;
93+
}
94+
95+
@Override
96+
public InspectContainerResponse inspect(String containerId) throws Exception {
97+
return null;
98+
}
99+
100+
@Override
101+
public String start(DockerStartOption option) throws Exception {
102+
return null;
103+
}
104+
105+
@Override
106+
public void wait(String containerId, int timeoutInSeconds, Consumer<Frame> onLog) throws Exception {
107+
108+
}
109+
110+
@Override
111+
public void stop(String containerId) throws Exception {
112+
113+
}
114+
115+
@Override
116+
public void resume(String containerId) throws Exception {
117+
118+
}
119+
120+
@Override
121+
public void delete(String containerId) throws Exception {
122+
123+
}
124+
}
125+
126+
private Output runCmd(String bash, Consumer<String> handler) throws JSchException, IOException {
127+
if (Objects.isNull(session)) {
128+
throw new IllegalStateException("Please init ssh session first");
129+
}
130+
131+
Channel channel = null;
132+
133+
try {
134+
channel = session.openChannel("exec");
135+
try (PipedInputStream out = new PipedInputStream(); PipedInputStream err = new PipedInputStream()) {
136+
ChannelExec exec = (ChannelExec) channel;
137+
exec.setCommand(bash);
138+
139+
exec.setOutputStream(new PipedOutputStream(out));
140+
exec.setErrStream(new PipedOutputStream(err));
141+
142+
channel.connect(ChannelTimeout);
143+
144+
return Output.of(
145+
collectOutput(out, handler).toString(),
146+
collectOutput(err, null).toString(),
147+
channel.getExitStatus()
148+
);
149+
}
150+
} finally {
151+
if (channel != null) {
152+
channel.disconnect();
153+
}
154+
}
155+
}
156+
157+
public void close() {
158+
if (Objects.isNull(session)) {
159+
return;
160+
}
161+
session.disconnect();
162+
}
163+
164+
private static StringBuilder collectOutput(InputStream in, Consumer<String> handler) throws IOException {
165+
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(in))) {
166+
String line;
167+
StringBuilder builder = new StringBuilder();
168+
169+
while ((line = buffer.readLine()) != null) {
170+
builder.append(line);
171+
if (handler != null) {
172+
handler.accept(line);
173+
}
174+
}
175+
176+
return builder;
177+
}
178+
}
179+
180+
@AllArgsConstructor(staticName = "of")
181+
@Getter
182+
private static class Output {
183+
184+
final String out;
185+
186+
final String err;
187+
188+
final int exit;
189+
}
190+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.flowci.docker.domain;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
import lombok.Setter;
6+
7+
@Getter
8+
@AllArgsConstructor(staticName = "of")
9+
public final class SSHOption {
10+
11+
/**
12+
* Private rsa key for remote host access
13+
*/
14+
private final String privateKey;
15+
16+
private final String remoteHost;
17+
18+
private final String remoteUser;
19+
20+
private final int port = 22;
21+
22+
@Setter
23+
private int timeoutInSeconds = 10;
24+
}

docker/src/test/java/com/flowci/docker/test/DockerSDKManagerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public void should_pull_image() throws Exception {
3535
}
3636

3737
@Test
38-
public void should_list_container() throws Exception {
38+
public void should_list_containers() throws Exception {
3939
List<Container> containers = manager.getContainerManager().list(null, null);
4040
Assert.assertNotNull(containers);
4141
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.flowci.docker.test;
2+
3+
import com.flowci.docker.DockerManager;
4+
import com.flowci.docker.DockerSSHManager;
5+
import com.flowci.docker.domain.SSHOption;
6+
import com.flowci.util.StringHelper;
7+
import com.github.dockerjava.api.model.Container;
8+
import org.junit.Assert;
9+
import org.junit.Before;
10+
import org.junit.Test;
11+
12+
import java.io.InputStream;
13+
import java.util.List;
14+
15+
public class DockerSSHManagerTest {
16+
17+
private DockerManager manager;
18+
19+
@Before
20+
public void init() throws Exception {
21+
InputStream pk = load("private.pk");
22+
SSHOption option = SSHOption.of(StringHelper.toString(pk), "192.168.0.106", "yang", 22);
23+
manager = new DockerSSHManager(option);
24+
}
25+
26+
@Test
27+
public void should_list_containers() throws Exception {
28+
List<Container> list = manager.getContainerManager().list(null, null);
29+
Assert.assertNotNull(list);
30+
}
31+
32+
protected InputStream load(String resource) {
33+
return getClass().getClassLoader().getResourceAsStream(resource);
34+
}
35+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIEpAIBAAKCAQEAmeakHHwG5lt7JFgbD7KtMQBPLX8D2Rhx6HaxWARWTV3ndJX2
3+
UFfQQoDVITXO/v09gg5aSUc3t2TDMNkr7NZqrAuqw87LiHCFdji+isJrn7Nuz/7/
4+
R+XO7AqLopTLY2fa+QdjCzTLNlJXmjhkKef6MCjgdYHLsV21Ch6eLKePhX4S5XGl
5+
V89HxZJCRPDN4i1Lqs3YDPoBZ1ZFOcN9mx/b/nSl5Zk7wYXhLlwokkbRnjHCjLpz
6+
C0s1ZEwnkNvnEYD3pvRpdFrOb1+4bDk6RhcKuOhb0J4kv4tBaAevr6rYeff8tUZd
7+
4rdy0z+j971AfXOOnoXetHK5Bwm2R9IhVFJSoQIDAQABAoIBAFwgW7cNa5fzxShB
8+
cZmZDOHbXIpQkT4gyvcsLgcROZRAlTAOIQ+BMZLNykeCDXJIp8w1yUg6UJkAwXN5
9+
XTj+NYbdyRBDt0Tqjonxd+ZacdB8ueXasHgakh5sRaBCT+omnkcaa+imZssSyYvx
10+
G8yWKAcyQQM2enenL5tqLqduepSersQFmjI0WUtZN9mNaTTpEaFsSZV3Z5+xfjth
11+
1uLN2NJlrxVtPOJhZexAckhaKbYASq1Dt4fLmrrB/7PzKTq8mVgpliWXDNJJ6bHw
12+
x8325DHJjeYjWBCt2+UFU8mwzVJG/+g222twxdDmvKlbxFqzHbhqqvES0WwrWTtZ
13+
vS7Sp0ECgYEA5Azy2EqLwNP4ovRICnfpO534WrU98DMp5Sh+kZpJkUJgTb80qQh1
14+
8TyyZAIT+zwUhySAN5ki629NrCBJeWCBvSHGVKXZW3+O1s/P/Novj3HDW0zzDXa+
15+
CqecO/r8dQbXzoVUkDUpmXw2WK4QtfyiOn3DuY7fy8+SnfZDcx+mEnkCgYEArMNC
16+
VATcqcWl19wPjcfCwDVAzPYbgFg7cxVcN0vXpSgB1Bf7iZVOtcxIb+3yZEPct46H
17+
gF1JwSey17kXGKVWTb7q61fBE3KYISnO2XJcISaE1+bki9XX3b7Jc821gEqsHhZs
18+
z4J4yHyjjVZ5YHg/J+AaBw2WgyvvrRt81h/y92kCgYEAqxO7qd5icMmToEs5MAE8
19+
imi7QvDsAiGfsDVsnbXssnfk3u4thxC9ybcK7FNSwgoULD2JoivTMmNRFWszM0be
20+
jQ+lRSMVp7pNYYCMOtdDDoz9TKSxQMxvTHbokWZ7tR7pDhifk38OMgIokuYhyyQ2
21+
X0KxbaZs5OxWxQFVICwB3aECgYBedwn+nQX3gs5TAM40bUUtCjFboTqabCRdohFh
22+
XwQUF5MgYNi2e0CloYT+RvKiJoU6jSHEa74XctiUom1hmY5eRoV3l+1H8AIFKDIl
23+
riV4LvK2h1byq0Q0yMyomJOlCa+haAupT6MxpQ1dDCqkODv4Vs6EisaBBPYyBnS1
24+
NJUK+QKBgQDQ7PodubPevRWI5OCYhl9a0nwGgHie/XV9bdxyUIf/qYTvCL6shdsn
25+
4xIEv6x7OoSMDqj3HSIYJtRZtMzfyeTo/17QR4mWMQrzMpGfdkme3Gh29PktTHWV
26+
/JPX5bd1PSZOw9NbSekmd/kfJgJNcVxf4BlxEr1dZ+qS7QDWYoiUSw==
27+
-----END RSA PRIVATE KEY-----
28+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCZ5qQcfAbmW3skWBsPsq0xAE8tfwPZGHHodrFYBFZNXed0lfZQV9BCgNUhNc7+/T2CDlpJRze3ZMMw2Svs1mqsC6rDzsuIcIV2OL6Kwmufs27P/v9H5c7sCouilMtjZ9r5B2MLNMs2UleaOGQp5/owKOB1gcuxXbUKHp4sp4+FfhLlcaVXz0fFkkJE8M3iLUuqzdgM+gFnVkU5w32bH9v+dKXlmTvBheEuXCiSRtGeMcKMunMLSzVkTCeQ2+cRgPem9Gl0Ws5vX7hsOTpGFwq46FvQniS/i0FoB6+vqth59/y1Rl3it3LTP6P3vUB9c46ehd60crkHCbZH0iFUUlKh [email protected]

0 commit comments

Comments
 (0)