Skip to content

Commit 41c0fe0

Browse files
committed
Fix #17096: support secrets in Docker swarm
1 parent 2d8df59 commit 41c0fe0

4 files changed

Lines changed: 153 additions & 5 deletions

File tree

src/main/java/eu/openanalytics/containerproxy/backend/docker/DockerSwarmBackend.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@
2121
package eu.openanalytics.containerproxy.backend.docker;
2222

2323
import com.spotify.docker.client.DockerClient;
24+
import com.spotify.docker.client.exceptions.DockerException;
2425
import com.spotify.docker.client.messages.mount.Mount;
2526
import com.spotify.docker.client.messages.swarm.DnsConfig;
2627
import com.spotify.docker.client.messages.swarm.EndpointSpec;
2728
import com.spotify.docker.client.messages.swarm.NetworkAttachmentConfig;
2829
import com.spotify.docker.client.messages.swarm.PortConfig;
30+
import com.spotify.docker.client.messages.swarm.Secret;
31+
import com.spotify.docker.client.messages.swarm.SecretBind;
32+
import com.spotify.docker.client.messages.swarm.SecretFile;
2933
import com.spotify.docker.client.messages.swarm.Service;
3034
import com.spotify.docker.client.messages.swarm.ServiceSpec;
3135
import com.spotify.docker.client.messages.swarm.Task;
@@ -39,6 +43,7 @@
3943
import eu.openanalytics.containerproxy.model.runtime.runtimevalues.RuntimeValueKey;
4044
import eu.openanalytics.containerproxy.model.runtime.runtimevalues.UserIdKey;
4145
import eu.openanalytics.containerproxy.model.spec.ContainerSpec;
46+
import eu.openanalytics.containerproxy.model.spec.DockerSwarmSecret;
4247
import eu.openanalytics.containerproxy.util.Retrying;
4348

4449
import java.net.URI;
@@ -84,6 +89,11 @@ protected Container startContainer(ContainerSpec spec, Proxy proxy) throws Excep
8489
}
8590
}
8691

92+
List<SecretBind> secretBinds = new ArrayList<>();
93+
for (DockerSwarmSecret secret : spec.getDockerSwarmSecrets()) {
94+
secretBinds.add(convertSecret(secret));
95+
}
96+
8797
com.spotify.docker.client.messages.swarm.ContainerSpec containerSpec =
8898
com.spotify.docker.client.messages.swarm.ContainerSpec.builder()
8999
.image(spec.getImage())
@@ -92,6 +102,7 @@ protected Container startContainer(ContainerSpec spec, Proxy proxy) throws Excep
92102
.env(convertEnv(buildEnv(spec, proxy)))
93103
.dnsConfig(DnsConfig.builder().nameServers(spec.getDns()).build())
94104
.mounts(mounts)
105+
.secrets(secretBinds)
95106
.build();
96107

97108
NetworkAttachmentConfig[] networks = Arrays
@@ -181,7 +192,33 @@ protected URI calculateTarget(Container container, int containerPort, int servic
181192
return new URI(String.format("%s://%s:%s%s", targetProtocol, targetHostName, targetPort, targetPath));
182193
}
183194

184-
@Override
195+
private SecretBind convertSecret(DockerSwarmSecret secret) throws DockerException, InterruptedException {
196+
if (secret.getName() == null) {
197+
throw new IllegalArgumentException("No name for a Docker swarm secret provided");
198+
}
199+
return SecretBind.builder()
200+
.secretName(secret.getName())
201+
.secretId(getSecretId(secret.getName()))
202+
.file(
203+
SecretFile.builder()
204+
.name(Optional.ofNullable(secret.getTarget()).orElse(secret.getName()))
205+
.gid(Optional.ofNullable(secret.getGid()).orElse("0"))
206+
.uid(Optional.ofNullable(secret.getUid()).orElse("0"))
207+
.mode(Long.parseLong(Optional.ofNullable(secret.getMode()).orElse("444"), 8))
208+
.build()
209+
)
210+
.build();
211+
212+
}
213+
214+
private String getSecretId(String secretName) throws DockerException, InterruptedException {
215+
return dockerClient.listSecrets().stream()
216+
.filter(it -> it.secretSpec().name().equals(secretName))
217+
.findFirst()
218+
.map(Secret::id).orElseThrow(() -> new IllegalArgumentException("Secret not found!"));
219+
}
220+
221+
@Override
185222
protected void doStopProxy(Proxy proxy) throws Exception {
186223
for (Container container: proxy.getContainers()) {
187224
String serviceId = (String) container.getParameters().get(PARAM_SERVICE_ID);

src/main/java/eu/openanalytics/containerproxy/model/spec/ContainerSpec.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
*/
2121
package eu.openanalytics.containerproxy.model.spec;
2222

23+
import java.util.ArrayList;
2324
import java.util.Arrays;
2425
import java.util.HashMap;
26+
import java.util.List;
2527
import java.util.Map;
2628

2729
public class ContainerSpec {
@@ -43,6 +45,7 @@ public class ContainerSpec {
4345
private String targetPath;
4446
private Map<String, String> labels = new HashMap<>();
4547
private Map<String, String> settings = new HashMap<>();
48+
private List<DockerSwarmSecret> dockerSwarmSecrets = new ArrayList();
4649

4750
public String getImage() {
4851
return image;
@@ -153,6 +156,14 @@ public void setTargetPath(String targetPath) {
153156
this.targetPath = targetPath;
154157
}
155158

159+
public List<DockerSwarmSecret> getDockerSwarmSecrets() {
160+
return dockerSwarmSecrets;
161+
}
162+
163+
public void setDockerSwarmSecrets(List<DockerSwarmSecret> dockerSwarmSecrets) {
164+
this.dockerSwarmSecrets = dockerSwarmSecrets;
165+
}
166+
156167
public void copy(ContainerSpec target) {
157168
target.setImage(image);
158169
if (cmd != null) target.setCmd(Arrays.copyOf(cmd, cmd.length));
@@ -182,6 +193,10 @@ public void copy(ContainerSpec target) {
182193
if (target.getSettings() == null) target.setSettings(new HashMap<>());
183194
target.getSettings().putAll(settings);
184195
}
196+
if (dockerSwarmSecrets != null) {
197+
if (target.getDockerSwarmSecrets() == null) target.setDockerSwarmSecrets(new ArrayList<>());
198+
target.getDockerSwarmSecrets().addAll(dockerSwarmSecrets);
199+
}
185200
target.setTargetPath(targetPath);
186201
}
187202

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* ContainerProxy
3+
*
4+
* Copyright (C) 2016-2021 Open Analytics
5+
*
6+
* ===========================================================================
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the Apache License as published by
10+
* The Apache Software Foundation, either version 2 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* Apache License for more details.
17+
*
18+
* You should have received a copy of the Apache License
19+
* along with this program. If not, see <http://www.apache.org/licenses/>
20+
*/
21+
package eu.openanalytics.containerproxy.model.spec;
22+
23+
public class DockerSwarmSecret {
24+
25+
private String name;
26+
private String target;
27+
private String gid;
28+
private String uid;
29+
private String mode;
30+
31+
public DockerSwarmSecret() {
32+
}
33+
34+
public DockerSwarmSecret(String name, String target, String gid, String uid, String mode) {
35+
this.name = name;
36+
this.target = target;
37+
this.gid = gid;
38+
this.uid = uid;
39+
this.mode = mode;
40+
}
41+
42+
public String getName() {
43+
return name;
44+
}
45+
46+
public void setName(String name) {
47+
this.name = name;
48+
}
49+
50+
public String getTarget() {
51+
return target;
52+
}
53+
54+
public void setTarget(String target) {
55+
this.target = target;
56+
}
57+
58+
public String getGid() {
59+
return gid;
60+
}
61+
62+
public void setGid(String gid) {
63+
this.gid = gid;
64+
}
65+
66+
public String getUid() {
67+
return uid;
68+
}
69+
70+
public void setUid(String uid) {
71+
this.uid = uid;
72+
}
73+
74+
public String getMode() {
75+
return mode;
76+
}
77+
78+
public void setMode(String mode) {
79+
this.mode = mode;
80+
}
81+
}

src/main/java/eu/openanalytics/containerproxy/spec/expression/ExpressionAwareContainerSpec.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@
2020
*/
2121
package eu.openanalytics.containerproxy.spec.expression;
2222

23-
import java.util.HashMap;
24-
import java.util.Map;
25-
2623
import eu.openanalytics.containerproxy.model.runtime.Proxy;
2724
import eu.openanalytics.containerproxy.model.spec.ContainerSpec;
28-
import org.springframework.data.util.Pair;
25+
import eu.openanalytics.containerproxy.model.spec.DockerSwarmSecret;
2926
import org.springframework.security.core.Authentication;
3027

28+
import java.util.HashMap;
29+
import java.util.List;
30+
import java.util.Map;
31+
import java.util.stream.Collectors;
32+
3133
/**
3234
* Adds expression support to ContainerSpecs.
3335
* <p>
@@ -113,6 +115,19 @@ public boolean isPrivileged() {
113115
return source.isPrivileged();
114116
}
115117

118+
public List<DockerSwarmSecret> getDockerSwarmSecrets() {
119+
if (source.getDockerSwarmSecrets() == null) return null;
120+
return source.getDockerSwarmSecrets().stream().map(
121+
it -> new DockerSwarmSecret(
122+
resolve(it.getName()),
123+
resolve(it.getTarget()),
124+
resolve(it.getGid()),
125+
resolve(it.getUid()),
126+
resolve(it.getMode())
127+
)
128+
).collect(Collectors.toList());
129+
}
130+
116131
@Override
117132
public Map<String, String> getLabels() {
118133
if (source.getLabels() == null) return null;

0 commit comments

Comments
 (0)