Skip to content

Commit b379f06

Browse files
authored
Merge pull request #119 from arvindsv/improve_feedback_loop
Show plugin messages in job console log
2 parents d3b92f1 + 9456ff3 commit b379f06

17 files changed

+203
-106
lines changed

src/main/java/cd/go/contrib/elasticagents/docker/AgentInstances.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ public interface AgentInstances<T> {
2929
* <p>
3030
* So that instances created are auto-registered with the server, the agent instance MUST have an
3131
* <code>autoregister.properties</code> file.
32-
*
3332
* @param request the request object
3433
* @param pluginRequest  the plugin request object
34+
* @param consoleLogAppender appender for console log
3535
*/
36-
T create(CreateAgentRequest request, PluginRequest pluginRequest) throws Exception;
36+
T create(CreateAgentRequest request, PluginRequest pluginRequest, ConsoleLogAppender consoleLogAppender) throws Exception;
3737

3838
/**
3939
* This message is sent when the plugin needs to terminate the agent instance.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package cd.go.contrib.elasticagents.docker;
2+
3+
import java.util.function.Consumer;
4+
5+
public interface ConsoleLogAppender extends Consumer<String> {
6+
}

src/main/java/cd/go/contrib/elasticagents/docker/Constants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public interface Constants {
3030
// The extension point API version that this plugin understands
3131
String PROCESSOR_API_VERSION = "1.0";
3232
String EXTENSION_API_VERSION = "5.0";
33+
String CONSOLE_LOG_API_VERSION = "1.0";
3334

3435
// the identifier of this plugin
3536
GoPluginIdentifier PLUGIN_IDENTIFIER = new GoPluginIdentifier(EXTENSION_TYPE, Collections.singletonList(EXTENSION_API_VERSION));
@@ -40,6 +41,7 @@ public interface Constants {
4041
String REQUEST_SERVER_DELETE_AGENT = REQUEST_SERVER_PREFIX + ".elastic-agents.delete-agents";
4142
String REQUEST_SERVER_LIST_AGENTS = REQUEST_SERVER_PREFIX + ".elastic-agents.list-agents";
4243
String REQUEST_SERVER_SERVER_HEALTH_ADD_MESSAGES = REQUEST_SERVER_PREFIX + ".server-health.add-messages";
44+
String REQUEST_SERVER_APPEND_TO_CONSOLE_LOG = REQUEST_SERVER_PREFIX + ".console-log.append";
4345

4446
// internal use only
4547
String CREATED_BY_LABEL_KEY = "Elastic-Agent-Created-By";

src/main/java/cd/go/contrib/elasticagents/docker/DockerContainer.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ public static DockerContainer fromContainerInfo(ContainerInfo container) {
9595
return new DockerContainer(container.id(), container.name().substring(1), jobIdentifier(container), container.created(), GSON.fromJson(labels.get(Constants.CONFIGURATION_LABEL_KEY), HashMap.class), labels.get(Constants.ENVIRONMENT_LABEL_KEY));
9696
}
9797

98-
public static DockerContainer create(CreateAgentRequest request, PluginSettings settings, DockerClient docker) throws InterruptedException, DockerException {
98+
public static DockerContainer create(CreateAgentRequest request, PluginSettings settings, DockerClient docker,
99+
ConsoleLogAppender consoleLogAppender) throws InterruptedException, DockerException {
99100
String containerName = UUID.randomUUID().toString();
100101

101102
HashMap<String, String> labels = labelsFrom(request);
@@ -105,10 +106,12 @@ public static DockerContainer create(CreateAgentRequest request, PluginSettings
105106
try {
106107
docker.inspectImage(imageName);
107108
if (settings.pullOnContainerCreate()) {
109+
consoleLogAppender.accept("Pulling a fresh version of " + imageName + ".");
108110
LOG.info("Pulling a fresh version of " + imageName + ".");
109111
docker.pull(imageName);
110112
}
111113
} catch (ImageNotFoundException ex) {
114+
consoleLogAppender.accept("Image " + imageName + " not found, attempting to download.");
112115
LOG.info("Image " + imageName + " not found, attempting to download.");
113116
docker.pull(imageName);
114117
}
@@ -147,13 +150,16 @@ public static DockerContainer create(CreateAgentRequest request, PluginSettings
147150
.hostConfig(hostBuilder.build())
148151
.build();
149152

153+
consoleLogAppender.accept(String.format("Creating container: %s", containerName));
150154
ContainerCreation container = docker.createContainer(containerConfig, containerName);
151155
String id = container.id();
152156

153157
ContainerInfo containerInfo = docker.inspectContainer(id);
154158

155159
LOG.debug("Created container " + containerName);
160+
consoleLogAppender.accept(String.format("Starting container: %s", containerName));
156161
docker.startContainer(containerName);
162+
consoleLogAppender.accept(String.format("Started container: %s", containerName));
157163
LOG.debug("container " + containerName + " started");
158164
return new DockerContainer(id, containerName, request.jobIdentifier(), containerInfo.created(), request.properties(), request.environment());
159165
}
@@ -288,5 +294,4 @@ private String readLogs(DockerClient dockerClient) {
288294
return "";
289295
}
290296
}
291-
292297
}

src/main/java/cd/go/contrib/elasticagents/docker/DockerContainers.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public class DockerContainers implements AgentInstances<DockerContainer> {
4646
final Semaphore semaphore = new Semaphore(0, true);
4747

4848
@Override
49-
public DockerContainer create(CreateAgentRequest request, PluginRequest pluginRequest) throws Exception {
49+
public DockerContainer create(CreateAgentRequest request, PluginRequest pluginRequest, ConsoleLogAppender consoleLogAppender) throws Exception {
5050
LOG.info(String.format("[Create Agent] Processing create agent request for %s", request.jobIdentifier()));
5151
ClusterProfileProperties settings = request.getClusterProfileProperties();
5252
final Integer maxAllowedContainers = settings.getMaxDockerContainers();
@@ -58,7 +58,7 @@ public DockerContainer create(CreateAgentRequest request, PluginRequest pluginRe
5858
List<Map<String, String>> messages = new ArrayList<>();
5959
if (semaphore.tryAcquire()) {
6060
pluginRequest.addServerHealthMessage(messages);
61-
DockerContainer container = DockerContainer.create(request, settings, docker(settings));
61+
DockerContainer container = DockerContainer.create(request, settings, docker(settings), consoleLogAppender);
6262
register(container);
6363
jobsWaitingForAgentCreation.remove(request.jobIdentifier());
6464
return container;
@@ -70,6 +70,7 @@ public DockerContainer create(CreateAgentRequest request, PluginRequest pluginRe
7070
messageToBeAdded.put("message", maxLimitExceededMessage);
7171
messages.add(messageToBeAdded);
7272
pluginRequest.addServerHealthMessage(messages);
73+
consoleLogAppender.accept(maxLimitExceededMessage);
7374
LOG.info(maxLimitExceededMessage);
7475
return null;
7576
}

src/main/java/cd/go/contrib/elasticagents/docker/PluginRequest.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@
1717
package cd.go.contrib.elasticagents.docker;
1818

1919
import com.google.gson.Gson;
20+
import cd.go.contrib.elasticagents.docker.models.JobIdentifier;
21+
import com.google.gson.GsonBuilder;
2022
import com.thoughtworks.go.plugin.api.GoApplicationAccessor;
2123
import com.thoughtworks.go.plugin.api.request.DefaultGoApiRequest;
2224
import com.thoughtworks.go.plugin.api.response.GoApiResponse;
2325

2426
import java.util.*;
2527

28+
import static cd.go.contrib.elasticagents.docker.Constants.*;
2629
import static cd.go.contrib.elasticagents.docker.DockerPlugin.LOG;
27-
import static cd.go.contrib.elasticagents.docker.Constants.PROCESSOR_API_VERSION;
28-
import static cd.go.contrib.elasticagents.docker.Constants.PLUGIN_IDENTIFIER;
29-
3030

3131
/**
3232
* Instances of this class know how to send messages to the GoCD Server.
@@ -85,12 +85,29 @@ public void addServerHealthMessage(List<Map<String, String>> messages) {
8585

8686
request.setRequestBody(gson.toJson(messages));
8787

88-
// submit the request
8988
GoApiResponse response = accessor.submit(request);
9089

91-
// check status
9290
if (response.responseCode() != 200) {
9391
LOG.error("The server sent an unexpected status code " + response.responseCode() + " with the response body " + response.responseBody());
9492
}
9593
}
94+
95+
public void appendToConsoleLog(JobIdentifier jobIdentifier, String text) throws ServerRequestFailedException {
96+
Map<String, String> requestMap = new HashMap<>();
97+
requestMap.put("pipelineName", jobIdentifier.getPipelineName());
98+
requestMap.put("pipelineCounter", String.valueOf(jobIdentifier.getPipelineCounter()));
99+
requestMap.put("stageName", jobIdentifier.getStageName());
100+
requestMap.put("stageCounter", jobIdentifier.getStageCounter());
101+
requestMap.put("jobName", jobIdentifier.getJobName());
102+
requestMap.put("text", text);
103+
104+
DefaultGoApiRequest request = new DefaultGoApiRequest(Constants.REQUEST_SERVER_APPEND_TO_CONSOLE_LOG, CONSOLE_LOG_API_VERSION, PLUGIN_IDENTIFIER);
105+
request.setRequestBody(new GsonBuilder().create().toJson(requestMap));
106+
107+
GoApiResponse response = accessor.submit(request);
108+
109+
if (response.responseCode() != 200) {
110+
LOG.error("Failed to append to console log for " + jobIdentifier.represent() + " with text: " + text);
111+
}
112+
}
96113
}

src/main/java/cd/go/contrib/elasticagents/docker/ServerRequestFailedException.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import static java.lang.String.format;
2222

23-
public class ServerRequestFailedException extends Exception {
23+
public class ServerRequestFailedException extends RuntimeException {
2424

2525
private ServerRequestFailedException(GoApiResponse response, String request) {
2626
super(format(
@@ -44,4 +44,8 @@ public static ServerRequestFailedException listAgents(GoApiResponse response) {
4444
public static ServerRequestFailedException getPluginSettings(GoApiResponse response) {
4545
return new ServerRequestFailedException(response, "get plugin settings");
4646
}
47+
48+
public static ServerRequestFailedException appendToConsoleLog(GoApiResponse response) {
49+
return new ServerRequestFailedException(response, "append to console log");
50+
}
4751
}

src/main/java/cd/go/contrib/elasticagents/docker/executors/CreateAgentRequestExecutor.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,21 @@
1616

1717
package cd.go.contrib.elasticagents.docker.executors;
1818

19-
import cd.go.contrib.elasticagents.docker.AgentInstances;
20-
import cd.go.contrib.elasticagents.docker.DockerContainer;
21-
import cd.go.contrib.elasticagents.docker.PluginRequest;
22-
import cd.go.contrib.elasticagents.docker.RequestExecutor;
19+
import cd.go.contrib.elasticagents.docker.*;
2320
import cd.go.contrib.elasticagents.docker.requests.CreateAgentRequest;
2421
import com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse;
2522
import com.thoughtworks.go.plugin.api.response.GoPluginApiResponse;
23+
import org.joda.time.DateTime;
24+
import org.joda.time.LocalDate;
25+
import org.joda.time.LocalTime;
26+
import org.joda.time.format.DateTimeFormat;
27+
import org.joda.time.format.DateTimeFormatter;
28+
import org.joda.time.format.DateTimeFormatterBuilder;
29+
30+
import java.util.Date;
2631

2732
public class CreateAgentRequestExecutor implements RequestExecutor {
33+
private static final DateTimeFormatter MESSAGE_PREFIX_FORMATTER = DateTimeFormat.forPattern("'##|'HH:mm:ss.SSS '[go]'");
2834
private final AgentInstances<DockerContainer> agentInstances;
2935
private final PluginRequest pluginRequest;
3036
private final CreateAgentRequest request;
@@ -37,7 +43,20 @@ public CreateAgentRequestExecutor(CreateAgentRequest request, AgentInstances<Doc
3743

3844
@Override
3945
public GoPluginApiResponse execute() throws Exception {
40-
agentInstances.create(request, pluginRequest);
46+
ConsoleLogAppender consoleLogAppender = text -> {
47+
final String message = String.format("%s %s\n", LocalTime.now().toString(MESSAGE_PREFIX_FORMATTER), text);
48+
pluginRequest.appendToConsoleLog(request.jobIdentifier(), message);
49+
};
50+
51+
consoleLogAppender.accept(String.format("Received request to create a container of %s at %s", request.dockerImage(), new DateTime().toString("yyyy-MM-dd HH:mm:ss ZZ")));
52+
53+
try {
54+
agentInstances.create(request, pluginRequest, consoleLogAppender);
55+
} catch (Exception e) {
56+
consoleLogAppender.accept(String.format("Failed while creating container: %s", e.getMessage()));
57+
throw e;
58+
}
59+
4160
return new DefaultGoPluginApiResponse(200);
4261
}
4362

src/main/java/cd/go/contrib/elasticagents/docker/requests/CreateAgentRequest.java

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,9 @@
2323
import com.google.gson.Gson;
2424
import com.google.gson.GsonBuilder;
2525

26-
import java.io.IOException;
27-
import java.io.StringWriter;
2826
import java.util.ArrayList;
2927
import java.util.Collection;
3028
import java.util.Map;
31-
import java.util.Properties;
3229

3330
import static org.apache.commons.lang.StringUtils.isNotBlank;
3431

@@ -59,7 +56,7 @@ public CreateAgentRequest(String autoRegisterKey, Map<String, String> elasticAge
5956
this.clusterProfileProperties = clusterProfileProperties;
6057
}
6158

62-
public String autoRegisterKey() {
59+
String autoRegisterKey() {
6360
return autoRegisterKey;
6461
}
6562

@@ -87,37 +84,6 @@ public RequestExecutor executor(AgentInstances<DockerContainer> agentInstances,
8784
return new CreateAgentRequestExecutor(this, agentInstances, pluginRequest);
8885
}
8986

90-
public Properties autoregisterProperties(String elasticAgentId) {
91-
Properties properties = new Properties();
92-
93-
if (isNotBlank(autoRegisterKey)) {
94-
properties.put("agent.auto.register.key", autoRegisterKey);
95-
}
96-
97-
if (isNotBlank(environment)) {
98-
properties.put("agent.auto.register.environments", environment);
99-
}
100-
101-
properties.put("agent.auto.register.elasticAgent.agentId", elasticAgentId);
102-
properties.put("agent.auto.register.elasticAgent.pluginId", Constants.PLUGIN_ID);
103-
104-
return properties;
105-
}
106-
107-
public String autoregisterPropertiesAsString(String elasticAgentId) {
108-
Properties properties = autoregisterProperties(elasticAgentId);
109-
110-
StringWriter writer = new StringWriter();
111-
112-
try {
113-
properties.store(writer, "");
114-
} catch (IOException e) {
115-
throw new RuntimeException(e);
116-
}
117-
118-
return writer.toString();
119-
}
120-
12187
public Collection<String> autoregisterPropertiesAsEnvironmentVars(String elasticAgentId) {
12288
ArrayList<String> vars = new ArrayList<>();
12389
if (isNotBlank(autoRegisterKey)) {
@@ -130,4 +96,8 @@ public Collection<String> autoregisterPropertiesAsEnvironmentVars(String elastic
13096
vars.add("GO_EA_AUTO_REGISTER_ELASTIC_PLUGIN_ID=" + Constants.PLUGIN_ID);
13197
return vars;
13298
}
99+
100+
public String dockerImage() {
101+
return properties().get("Image");
102+
}
133103
}

src/main/resources/plugin-settings.template.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
<div class="form_item_block">
3434
<label>Go Server URL (this is passed to the agents, so don't use <code>localhost</code>):<span
35-
class='asterix'>*</span></label>
35+
class='asterix'> *</span></label>
3636
<input type="text" ng-model="go_server_url" ng-required="true" placeholder="https://ipaddress:8154/go"/>
3737
<span class="form_error" ng-show="GOINPUTNAME[go_server_url].$error.server">{{GOINPUTNAME[go_server_url].$error.server}}</span>
3838
</div>
@@ -49,28 +49,28 @@
4949
</div>
5050

5151
<div class="form_item_block">
52-
<label>Agent auto-register Timeout (in minutes):<span class='asterix'>*</span></label>
52+
<label>Agent auto-register Timeout (in minutes):<span class='asterix'> *</span></label>
5353
<input type="text" ng-model="auto_register_timeout" ng-required="true"/>
5454
<span class="form_error" ng-show="GOINPUTNAME[auto_register_timeout].$error.server">{{GOINPUTNAME[auto_register_timeout].$error.server}}</span>
5555
</div>
5656

5757
<div class="form_item_block">
58-
<label>Maximum docker containers to run at any given point in time:<span class='asterix'>*</span></label>
58+
<label>Maximum docker containers to run at any given point in time:<span class='asterix'> *</span></label>
5959
<input type="text" ng-model="max_docker_containers" ng-required="true"/>
6060
<span class="form_error" ng-show="GOINPUTNAME[max_docker_containers].$error.server">{{GOINPUTNAME[max_docker_containers].$error.server}}</span>
6161
</div>
6262

6363
<div class="form_item_block">
64-
<input type="checkbox" ng-model="pull_on_container_create" id="pull_on_container_create" ng-true-value="true" ng-false-value="false"/>
64+
<input type="checkbox" ng-model="pull_on_container_create" id="pull_on_container_create" ng-init="pull_on_container_create = pull_on_container_create || false" ng-true-value="true" ng-false-value="false"/>
6565
<label for="pull_on_container_create">Always pull image before creating the container</label>
6666
<span class="form_error" ng-show="GOINPUTNAME[pull_on_container_create].$error.server">{{GOINPUTNAME[pull_on_container_create].$error.server}}</span>
6767
</div>
6868
</fieldset>
6969
<fieldset>
7070
<legend>Docker client configuration</legend>
7171
<div class="form_item_block">
72-
<label>Docker URI:<span class='asterix'>*</span></label>
73-
<input type="text" ng-model="docker_uri" ng-required="true"/>
72+
<label>Docker URI:<span class='asterix'> *</span></label>
73+
<input type="text" ng-model="docker_uri" ng-required="true" placeholder="For Docker running locally, try unix:///var/run/docker.sock"/>
7474
<span class="form_error" ng-show="GOINPUTNAME[docker_uri].$error.server">{{GOINPUTNAME[docker_uri].$error.server}}</span>
7575
</div>
7676

@@ -96,7 +96,7 @@
9696
<fieldset>
9797
<legend>Docker registry settings</legend>
9898
<div class="form_item_block">
99-
<input type="checkbox" ng-model="enable_private_registry_authentication" id="enable_private_registry_authentication" ng-true-value="true" ng-false-value="false"/>
99+
<input type="checkbox" ng-model="enable_private_registry_authentication" id="enable_private_registry_authentication" ng-init="enable_private_registry_authentication = enable_private_registry_authentication || false" ng-true-value="true" ng-false-value="false"/>
100100
<label for="enable_private_registry_authentication">Use Private Registry</label>
101101
<span class="form_error" ng-show="GOINPUTNAME[enable_private_registry_authentication].$error.server">{{GOINPUTNAME[enable_private_registry_authentication].$error.server}}</span>
102102
</div>

0 commit comments

Comments
 (0)