in.ashwanthkumar.gocd.client.GoCD Maven / Gradle / Ivy
package in.ashwanthkumar.gocd.client;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import in.ashwanthkumar.gocd.client.types.*;
import in.ashwanthkumar.gocd.client.http.HttpClient;
import in.ashwanthkumar.utils.collections.Lists;
import in.ashwanthkumar.utils.func.Function;
import in.ashwanthkumar.utils.func.Predicate;
import in.ashwanthkumar.utils.lang.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static in.ashwanthkumar.utils.collections.Lists.filter;
import static in.ashwanthkumar.utils.collections.Lists.map;
/**
* GoCD Client that's used for accessing some official and un-official APIs.
*
* Note - This is by no means a complete implementation of all the endpoints. Feel free to submit a PR of an
* endpoint that's missing.
*/
public class GoCD {
private static Logger LOG = LoggerFactory.getLogger(GoCD.class);
private String server;
private HttpClient client;
public GoCD(String server, String username, String password) {
this.server = server;
this.client = new HttpClient(username, password);
}
/* for tests */ GoCD(String server, String username, String password, String mockResponse) {
this(server, username, password);
this.client.setMockResponse(mockResponse);
}
public List allPipelineNames(final String pipelinePrefix) throws IOException {
String xml = client.getXML(buildUrl("/go/api/pipelines.xml"));
Document doc = Jsoup.parse(xml);
Elements pipelineElements = doc.select("pipeline[href]");
List pipelines = filter(map(pipelineElements, new Function() {
@Override
public String apply(Element element) {
String href = element.attr("href");
String apiPrefix = "/go/api/pipelines/";
return href.substring(href.indexOf(apiPrefix) + apiPrefix.length(), href.indexOf("/stages.xml"));
}
}), new Predicate() {
@Override
public Boolean apply(String s) {
return StringUtils.isEmpty(pipelinePrefix) || s.startsWith(pipelinePrefix);
}
});
return pipelines;
}
public List upstreamDependencies(String pipeline, int version) throws IOException {
JsonObject result = client.getRawJson(buildUrl("/go/pipelines/value_stream_map/" + pipeline + "/" + version + ".json")).getAsJsonObject();
List dependencies = Lists.of(new PipelineDependency(pipeline, version));
// happens typically when we check for next run
// in case of connection errors it should fail before this
if (!result.has("levels")) return dependencies;
JsonArray levels = result.getAsJsonArray("levels");
for (JsonElement level : levels) {
JsonArray nodes = level.getAsJsonObject().getAsJsonArray("nodes");
for (JsonElement node : nodes) {
JsonObject nodeObj = node.getAsJsonObject();
String name = nodeObj.get("name").getAsString();
// The VSM JSON is always ordered (left to right in VSM view) set of dependencies
if (name.equals(pipeline)) return dependencies;
// We pick only the PIPELINE type dependencies
if (nodeObj.get("node_type").getAsString().equalsIgnoreCase("PIPELINE")) {
JsonArray instances = nodeObj.getAsJsonArray("instances");
for (JsonElement instance : instances) {
int counter = instance.getAsJsonObject().get("counter").getAsInt();
dependencies.add(
new PipelineDependency()
.setPipelineName(name)
.setVersion(counter)
);
}
}
}
}
return dependencies;
}
public PipelineStatus pipelineStatus(String pipeline) throws IOException {
return client.getAs(buildUrl("/go/api/pipelines/" + pipeline + "/status"), PipelineStatus.class);
}
public Pipeline pipelineInstance(String pipeline, int pipelineCounter) throws IOException {
return client.getAs(buildUrl("/go/api/pipelines/" + pipeline + "/instace/" + pipelineCounter), Pipeline.class);
}
public History pipelineHistory(String pipeline) throws IOException {
return pipelineHistory(pipeline, 0);
}
public History pipelineHistory(String pipeline, int offset) throws IOException {
return client.getAs(buildUrl("/go/api/pipelines/" + pipeline + "/history/" + offset), History.class);
}
public Map pipelineRunStatus(String pipeline) throws IOException {
return pipelineRunStatus(pipeline, 0);
}
public Map pipelineRunStatus(String pipelineName, int offset) throws IOException {
Map result = new TreeMap<>(Collections.reverseOrder());
History history = pipelineHistory(pipelineName, offset);
for (Pipeline pipeline : history.getPipelines()) {
if (pipeline.isPreparingToSchedule())
continue;
PipelineRunStatus status = pipelineStatusFrom(pipeline.getStages());
LOG.debug(pipeline + "@" + pipeline.getCounter() + " has " + status);
result.put(pipeline.getCounter(), status);
}
return result;
}
private PipelineRunStatus pipelineStatusFrom(List stages) {
for (Stage stage : stages) {
// Since there isn't an universal way to say if the pipeline has failed or not, because
// A stage could fail, but we could deem it unimportant (for the time being) and continue the pipeline.
// We are a little sensitive about what we call failures of a pipeline. Possible Reasons -
// 1. Any 1 stage failure is considered a pipeline failure.
// 2. If the pipeline doesn't run to completion (paused or locked) is considered a failure.
boolean stageFailed = StringUtils.isEmpty(stage.getResult()) || stage.getResult().equalsIgnoreCase("failed");
if (stageFailed) {
return PipelineRunStatus.FAILED;
}
}
return PipelineRunStatus.PASSED;
}
private String buildUrl(String resource) {
try {
return URI.create(String.format("%s/%s", server, resource)).normalize().toURL().toExternalForm();
} catch (MalformedURLException e) {
LOG.error(e.getMessage(), e);
throw new RuntimeException(e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy