Skip to content

Commit 6d01e2c

Browse files
authored
Minor fixes (#44)
* Remove volumes after test * Added profile level validation
1 parent a0c3558 commit 6d01e2c

22 files changed

Lines changed: 703 additions & 197 deletions

src/main/java/cd/go/contrib/elasticagents/dockerswarm/elasticagent/DockerClientFactory.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,23 @@
3535
import static org.apache.commons.lang.StringUtils.isBlank;
3636

3737
public class DockerClientFactory {
38+
private DefaultDockerClient client;
39+
private PluginSettings pluginSettings;
3840

39-
private static DefaultDockerClient client;
40-
private static PluginSettings pluginSettings;
41+
private static final DockerClientFactory DOCKER_CLIENT_FACTORY = new DockerClientFactory();
4142

42-
public static synchronized DockerClient docker(PluginSettings pluginSettings) throws Exception {
43-
if (pluginSettings.equals(DockerClientFactory.pluginSettings) && DockerClientFactory.client != null) {
44-
return DockerClientFactory.client;
43+
public synchronized DockerClient docker(PluginSettings pluginSettings) throws Exception {
44+
if (pluginSettings.equals(this.pluginSettings) && this.client != null) {
45+
return this.client;
4546
}
4647

47-
DockerClientFactory.pluginSettings = pluginSettings;
48-
DockerClientFactory.client = createClient(pluginSettings);
49-
return DockerClientFactory.client;
48+
this.pluginSettings = pluginSettings;
49+
this.client = createClient(pluginSettings);
50+
return this.client;
51+
}
52+
53+
public static DockerClientFactory instance() {
54+
return DOCKER_CLIENT_FACTORY;
5055
}
5156

5257
private static DefaultDockerClient createClient(PluginSettings pluginSettings) throws Exception {

src/main/java/cd/go/contrib/elasticagents/dockerswarm/elasticagent/DockerMounts.java

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,19 @@
1616

1717
package cd.go.contrib.elasticagents.dockerswarm.elasticagent;
1818

19+
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.utils.Util;
20+
import com.google.gson.Gson;
21+
import com.spotify.docker.client.messages.Volume;
22+
import com.spotify.docker.client.messages.mount.Mount;
23+
24+
import java.util.*;
25+
import java.util.stream.Collectors;
26+
1927
import static cd.go.contrib.elasticagents.dockerswarm.elasticagent.DockerPlugin.LOG;
2028
import static cd.go.contrib.elasticagents.dockerswarm.elasticagent.utils.Util.splitIntoLinesAndTrimSpaces;
2129
import static java.text.MessageFormat.format;
2230
import static java.util.stream.Collectors.toList;
23-
import static org.apache.commons.lang.StringUtils.isBlank;
24-
import static org.apache.commons.lang.StringUtils.isNotBlank;
25-
import static org.apache.commons.lang.StringUtils.stripToEmpty;
26-
27-
import java.util.ArrayList;
28-
import java.util.Arrays;
29-
import java.util.HashMap;
30-
import java.util.List;
31-
import java.util.Map;
32-
import java.util.stream.Collectors;
33-
34-
import com.google.gson.Gson;
35-
import com.spotify.docker.client.messages.Volume;
36-
import com.spotify.docker.client.messages.mount.Mount;
31+
import static org.apache.commons.lang.StringUtils.*;
3732

3833
public class DockerMounts extends ArrayList<DockerMounts.DockerMount> {
3934
private static final Gson GSON = new Gson();
@@ -60,10 +55,13 @@ private static Map<String, String> lineToMap(String line) {
6055
case "type":
6156
map.put("type", stripToEmpty(parts[1]));
6257
break;
63-
case "source": case "src":
58+
case "source":
59+
case "src":
6460
map.put("source", stripToEmpty(parts[1]));
6561
break;
66-
case "target": case "destination": case "dst":
62+
case "target":
63+
case "destination":
64+
case "dst":
6765
map.put("target", stripToEmpty(parts[1]));
6866
break;
6967
default:
@@ -91,6 +89,12 @@ private static Map<String, String> lineToMap(String line) {
9189
}
9290

9391
public List<Mount> toMount(List<Volume> volumes) {
92+
93+
if (Util.isEmpty(volumes)) {
94+
LOG.debug("No volumes to mount.");
95+
return Collections.emptyList();
96+
}
97+
9498
final Map<String, Volume> volumeMap = volumes.stream().collect(Collectors.toMap(o -> o.name(), o -> o));
9599
final List<Mount> mounts = new ArrayList<>();
96100

src/main/java/cd/go/contrib/elasticagents/dockerswarm/elasticagent/DockerPlugin.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@
3131
import com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse;
3232
import com.thoughtworks.go.plugin.api.response.GoPluginApiResponse;
3333

34-
import static cd.go.contrib.elasticagents.dockerswarm.elasticagent.DockerClientFactory.docker;
35-
3634
@Extension
3735
public class DockerPlugin implements GoPlugin {
3836
public static final Logger LOG = Logger.getLoggerFor(DockerPlugin.class);
@@ -65,7 +63,7 @@ public GoPluginApiResponse handle(GoPluginApiRequest request) throws UnhandledRe
6563
case REQUEST_GET_PROFILE_VIEW:
6664
return new GetProfileViewExecutor().execute();
6765
case REQUEST_VALIDATE_PROFILE:
68-
return ProfileValidateRequest.fromJSON(request.requestBody()).executor(docker(pluginRequest.getPluginSettings())).execute();
66+
return ProfileValidateRequest.fromJSON(request.requestBody()).executor(pluginRequest).execute();
6967
case PLUGIN_SETTINGS_GET_ICON:
7068
return new GetPluginSettingsIconExecutor().execute();
7169
case PLUGIN_SETTINGS_GET_CONFIGURATION:
@@ -75,7 +73,7 @@ public GoPluginApiResponse handle(GoPluginApiRequest request) throws UnhandledRe
7573
case REQUEST_GET_CAPABILITIES:
7674
return new GetCapabilitiesExecutor().execute();
7775
case REQUEST_STATUS_REPORT:
78-
return new StatusReportExecutor(docker(pluginRequest.getPluginSettings())).execute();
76+
return new StatusReportExecutor(pluginRequest).execute();
7977
default:
8078
throw new UnhandledRequestTypeException(request.requestName());
8179
}

src/main/java/cd/go/contrib/elasticagents/dockerswarm/elasticagent/DockerServices.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ private void register(DockerService service) {
130130
}
131131

132132
private DockerClient docker(PluginSettings settings) throws Exception {
133-
return DockerClientFactory.docker(settings);
133+
return DockerClientFactory.instance().docker(settings);
134134
}
135135

136136
private DockerServices unregisteredAfterTimeout(PluginSettings settings, Agents knownAgents) throws Exception {

src/main/java/cd/go/contrib/elasticagents/dockerswarm/elasticagent/executors/Metadata.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,11 @@
1616

1717
package cd.go.contrib.elasticagents.dockerswarm.elasticagent.executors;
1818

19+
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.model.ValidationError;
1920
import com.google.gson.annotations.Expose;
2021
import com.google.gson.annotations.SerializedName;
2122
import org.apache.commons.lang.StringUtils;
2223

23-
import java.util.HashMap;
24-
import java.util.Map;
25-
2624
public class Metadata {
2725

2826
@Expose
@@ -46,14 +44,12 @@ public Metadata(String key, ProfileMetadata metadata) {
4644
this.metadata = metadata;
4745
}
4846

49-
public Map<String, String> validate(String input) {
50-
HashMap<String, String> result = new HashMap<>();
51-
String validationError = doValidate(input);
52-
if (StringUtils.isNotBlank(validationError)) {
53-
result.put("key", key);
54-
result.put("message", validationError);
47+
public ValidationError validate(String input) {
48+
String errorMessage = doValidate(input);
49+
if (StringUtils.isNotBlank(errorMessage)) {
50+
return new ValidationError(key, errorMessage);
5551
}
56-
return result;
52+
return null;
5753
}
5854

5955
protected String doValidate(String input) {

src/main/java/cd/go/contrib/elasticagents/dockerswarm/elasticagent/executors/ProfileValidateRequestExecutor.java

Lines changed: 20 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,74 +16,54 @@
1616

1717
package cd.go.contrib.elasticagents.dockerswarm.elasticagent.executors;
1818

19-
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.DockerSecrets;
19+
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.PluginRequest;
2020
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.RequestExecutor;
21+
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.model.ValidationResult;
2122
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.requests.ProfileValidateRequest;
22-
import com.google.gson.Gson;
23-
import com.spotify.docker.client.DockerClient;
23+
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.validator.DockerMountsValidator;
24+
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.validator.DockerSecretValidator;
25+
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.validator.Validatable;
2426
import com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse;
2527
import com.thoughtworks.go.plugin.api.response.GoPluginApiResponse;
2628

27-
import java.util.*;
28-
29-
import static cd.go.contrib.elasticagents.dockerswarm.elasticagent.utils.Util.dockerApiVersionAtLeast;
29+
import java.util.ArrayList;
30+
import java.util.HashSet;
31+
import java.util.List;
32+
import java.util.Set;
3033

3134
public class ProfileValidateRequestExecutor implements RequestExecutor {
3235
private final ProfileValidateRequest request;
33-
private final DockerClient dockerClient;
34-
private static final Gson GSON = new Gson();
36+
private List<Validatable> validators = new ArrayList<>();
3537

36-
public ProfileValidateRequestExecutor(ProfileValidateRequest request, DockerClient dockerClient) {
38+
public ProfileValidateRequestExecutor(ProfileValidateRequest request, PluginRequest pluginRequest) {
3739
this.request = request;
38-
this.dockerClient = dockerClient;
40+
validators.add(new DockerSecretValidator(pluginRequest));
41+
validators.add(new DockerMountsValidator(pluginRequest));
3942
}
4043

4144
@Override
4245
public GoPluginApiResponse execute() throws Exception {
43-
final List<Map<String, String>> result = new ArrayList<>();
4446
final List<String> knownFields = new ArrayList<>();
47+
final ValidationResult validationResult = new ValidationResult();
4548

4649
for (Metadata field : GetProfileMetadataExecutor.FIELDS) {
4750
knownFields.add(field.getKey());
48-
Map<String, String> validationError = field.validate(request.getProperties().get(field.getKey()));
49-
50-
if (!validationError.isEmpty()) {
51-
result.add(validationError);
52-
}
51+
validationResult.addError(field.validate(request.getProperties().get(field.getKey())));
5352
}
5453

55-
5654
final Set<String> set = new HashSet<>(request.getProperties().keySet());
5755
set.removeAll(knownFields);
5856

5957
if (!set.isEmpty()) {
6058
for (String key : set) {
61-
LinkedHashMap<String, String> validationError = new LinkedHashMap<>();
62-
validationError.put("key", key);
63-
validationError.put("message", "Is an unknown property");
64-
result.add(validationError);
59+
validationResult.addError(key, "Is an unknown property.");
6560
}
6661
}
6762

68-
validateDockerSecrets(result);
69-
70-
return DefaultGoPluginApiResponse.success(GSON.toJson(result));
71-
}
72-
73-
private void validateDockerSecrets(List<Map<String, String>> result) {
74-
try {
75-
final DockerSecrets dockerSecrets = DockerSecrets.fromString(request.getProperties().get("Secrets"));
76-
if (!dockerSecrets.isEmpty()) {
77-
if (!dockerApiVersionAtLeast(dockerClient, "1.26")) {
78-
throw new RuntimeException("Docker secret requires api version 1.26 or higher.");
79-
}
80-
dockerSecrets.toSecretBind(dockerClient.listSecrets());
81-
}
82-
} catch (Exception e) {
83-
LinkedHashMap<String, String> validationError = new LinkedHashMap<>();
84-
validationError.put("key", "Secrets");
85-
validationError.put("message", e.getMessage());
86-
result.add(validationError);
63+
for (Validatable validatable : validators) {
64+
validationResult.merge(validatable.validate(request.getProperties()));
8765
}
66+
67+
return DefaultGoPluginApiResponse.success(validationResult.toJSON());
8868
}
8969
}

src/main/java/cd/go/contrib/elasticagents/dockerswarm/elasticagent/executors/StatusReportExecutor.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,39 @@
1616

1717
package cd.go.contrib.elasticagents.dockerswarm.elasticagent.executors;
1818

19+
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.DockerClientFactory;
20+
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.PluginRequest;
1921
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.builders.PluginStatusReportViewBuilder;
2022
import cd.go.contrib.elasticagents.dockerswarm.elasticagent.model.SwarmCluster;
2123
import com.google.gson.JsonObject;
2224
import com.spotify.docker.client.DockerClient;
23-
import com.spotify.docker.client.exceptions.DockerException;
2425
import com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse;
2526
import com.thoughtworks.go.plugin.api.response.GoPluginApiResponse;
2627
import freemarker.template.Template;
27-
import freemarker.template.TemplateException;
2828

2929
import java.io.IOException;
3030

3131
import static cd.go.contrib.elasticagents.dockerswarm.elasticagent.DockerPlugin.LOG;
3232

3333
public class StatusReportExecutor {
34-
private final DockerClient dockerClient;
34+
private final PluginRequest pluginRequest;
35+
private final DockerClientFactory dockerClientFactory;
3536
private final PluginStatusReportViewBuilder statusReportViewBuilder;
3637

37-
public StatusReportExecutor(DockerClient dockerClient) throws IOException {
38-
this(dockerClient, PluginStatusReportViewBuilder.instance());
38+
public StatusReportExecutor(PluginRequest pluginRequest) throws IOException {
39+
this(pluginRequest, DockerClientFactory.instance(), PluginStatusReportViewBuilder.instance());
3940
}
4041

41-
StatusReportExecutor(DockerClient dockerClient, PluginStatusReportViewBuilder statusReportViewBuilder) {
42-
this.dockerClient = dockerClient;
42+
StatusReportExecutor(PluginRequest pluginRequest, DockerClientFactory dockerClientFactory, PluginStatusReportViewBuilder statusReportViewBuilder) {
43+
this.pluginRequest = pluginRequest;
44+
this.dockerClientFactory = dockerClientFactory;
4345
this.statusReportViewBuilder = statusReportViewBuilder;
4446
}
4547

46-
public GoPluginApiResponse execute() throws DockerException, InterruptedException, IOException, TemplateException {
48+
public GoPluginApiResponse execute() throws Exception {
4749
LOG.info("[status-report] Generating status report");
4850

51+
final DockerClient dockerClient = dockerClientFactory.docker(pluginRequest.getPluginSettings());
4952
final SwarmCluster swarmCluster = new SwarmCluster(dockerClient);
5053
final Template template = statusReportViewBuilder.getTemplate("status-report.template.ftlh");
5154
final String statusReportView = statusReportViewBuilder.build(template, swarmCluster);
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2017 ThoughtWorks, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package cd.go.contrib.elasticagents.dockerswarm.elasticagent.model;
18+
19+
import com.google.gson.annotations.Expose;
20+
import com.google.gson.annotations.SerializedName;
21+
22+
import java.text.MessageFormat;
23+
24+
public class ValidationError {
25+
@Expose
26+
@SerializedName("key")
27+
private final String key;
28+
@Expose
29+
@SerializedName("message")
30+
private final String message;
31+
32+
public ValidationError(String key, String message) {
33+
this.key = key;
34+
this.message = message;
35+
}
36+
37+
public String key() {
38+
return key;
39+
}
40+
41+
public String message() {
42+
return message;
43+
}
44+
45+
@Override
46+
public boolean equals(Object o) {
47+
if (this == o) return true;
48+
if (o == null || getClass() != o.getClass()) return false;
49+
50+
ValidationError that = (ValidationError) o;
51+
52+
if (key != null ? !key.equals(that.key) : that.key != null) return false;
53+
return message != null ? message.equals(that.message) : that.message == null;
54+
}
55+
56+
@Override
57+
public int hashCode() {
58+
int result = key != null ? key.hashCode() : 0;
59+
result = 31 * result + (message != null ? message.hashCode() : 0);
60+
return result;
61+
}
62+
63+
@Override
64+
public String toString() {
65+
return MessageFormat.format("ValidationError'{'key=''{0}'', message=''{1}'''}'", key, message);
66+
}
67+
}

0 commit comments

Comments
 (0)