Skip to content

Commit

Permalink
Merge pull request #16 from szpak/JENKINS-28697-aborted-job
Browse files Browse the repository at this point in the history
[JENKINS-28697] Aborted build is marked as FAILED
  • Loading branch information
gschueler committed Mar 22, 2016
2 parents 63984a8 + ccd1f3f commit 5acdd8b
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 74 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -3,3 +3,6 @@
.settings
target
work

*.iml
.idea/
171 changes: 97 additions & 74 deletions src/main/java/org/jenkinsci/plugins/rundeck/RundeckNotifier.java
@@ -1,5 +1,7 @@
package org.jenkinsci.plugins.rundeck;

import static java.lang.String.format;

import hudson.CopyOnWrite;
import hudson.EnvVars;
import hudson.Extension;
Expand Down Expand Up @@ -49,6 +51,7 @@
import org.rundeck.api.RundeckApiException.RundeckApiLoginException;
import org.rundeck.api.RundeckClient;
import org.rundeck.api.RundeckClientBuilder;
import org.rundeck.api.domain.RundeckAbort;
import org.rundeck.api.domain.RundeckExecution;
import org.rundeck.api.domain.RundeckExecution.ExecutionStatus;
import org.rundeck.api.domain.RundeckJob;
Expand All @@ -68,14 +71,16 @@ public class RundeckNotifier extends Notifier {
/** Pattern used for extracting the job reference (project:group/name) */
private static final transient Pattern JOB_REFERENCE_PATTERN = Pattern.compile("^([^:]+?):(.*?)\\/?([^/]+)$");

private static final int DELAY_BETWEEN_POLLS_IN_MILLIS = 5000;

private String rundeckInstance;

private final String jobId;

private final String options;

private final String nodeFilters;

private transient final String tag;

private String[] tags;
Expand All @@ -85,14 +90,14 @@ public class RundeckNotifier extends Notifier {
private final Boolean shouldFailTheBuild;

private final Boolean includeRundeckLogs;

private final Boolean tailLog;

RundeckNotifier(String rundeckInstance, String jobId, String options, String nodeFilters, String tag,
Boolean shouldWaitForRundeckJob, Boolean shouldFailTheBuild) {
this(rundeckInstance, jobId, options, nodeFilters, tag, shouldWaitForRundeckJob, shouldFailTheBuild, false, false);
}

@DataBoundConstructor
public RundeckNotifier(String rundeckInstance, String jobId, String options, String nodeFilters, String tags,
Boolean shouldWaitForRundeckJob, Boolean shouldFailTheBuild, Boolean includeRundeckLogs, Boolean tailLog) {
Expand All @@ -107,7 +112,7 @@ public RundeckNotifier(String rundeckInstance, String jobId, String options, Str
this.includeRundeckLogs = includeRundeckLogs;
this.tailLog = tailLog;
}

public Object readResolve() {
if (StringUtils.isEmpty(rundeckInstance)) {
this.rundeckInstance = "Default";
Expand Down Expand Up @@ -229,65 +234,34 @@ private boolean notifyRundeck(RundeckClient rundeck, AbstractBuild<?, ?> build,
.setNodeFilters(parseProperties(nodeFilters, build, listener))
.build());

listener.getLogger().println("Notification succeeded ! Execution #" + execution.getId() + ", at "
+ execution.getUrl() + " (status : " + execution.getStatus() + ")");
listener.getLogger().printf("Notification succeeded ! Execution #%d, at %s (status : %s)%n",
execution.getId(), execution.getUrl(), execution.getStatus());
build.addAction(new RundeckExecutionBuildBadgeAction(execution.getUrl()));

if (Boolean.TRUE.equals(shouldWaitForRundeckJob)) {
listener.getLogger().println("Waiting for Rundeck execution to finish...");
if (Boolean.TRUE.equals(includeRundeckLogs) && Boolean.TRUE.equals(tailLog)){
listener.getLogger().println("BEGIN RUNDECK TAILED LOG OUTPUT");
RunDeckLogTail runDeckLogTail = new RunDeckLogTail(rundeck, execution.getId());
RunDeckLogTailIterator runDeckLogTailIterator = runDeckLogTail.iterator();
while(runDeckLogTailIterator.hasNext()){
for (RundeckOutputEntry rundeckOutputEntry : runDeckLogTailIterator.next()) {
listener.getLogger().println(String.format("[%s] [%s] %s", new Object[] {rundeckOutputEntry.getTime(), rundeckOutputEntry.getLevel(), rundeckOutputEntry.getMessage()}));
}
}
listener.getLogger().println("END RUNDECK TAILED LOG OUTPUT");

execution = rundeck.getExecution(execution.getId());
logExecutionStatus(listener, execution);
if (Boolean.TRUE.equals(includeRundeckLogs) && Boolean.TRUE.equals(tailLog)) {
execution = waitTailingRundeckLogsAndReturnExecution(rundeck, listener, execution);
} else {
while (ExecutionStatus.RUNNING.equals(execution.getStatus())) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
listener.getLogger().println("Oops, interrupted ! " + e.getMessage());
break;
}
execution = rundeck.getExecution(execution.getId());
}
logExecutionStatus(listener, execution);
execution = waitForRundeckExecutionToFinishAndReturnIt(rundeck, listener, execution);

if (Boolean.TRUE.equals(includeRundeckLogs)) {
listener.getLogger().println("BEGIN RUNDECK LOG OUTPUT");
RundeckOutput rundeckOutput = rundeck.getJobExecutionOutput(execution.getId(), 0, 0, 0);
if (null != rundeckOutput) {
List<RundeckOutputEntry> logEntries = rundeckOutput.getLogEntries();
if (null != logEntries) {
for (int i=0; i<logEntries.size(); i++) {
RundeckOutputEntry rundeckOutputEntry = (RundeckOutputEntry)logEntries.get(i);
listener.getLogger().println(rundeckOutputEntry.getMessage());
}
}
}
listener.getLogger().println("END RUNDECK LOG OUTPUT");
getAndPrintRundeckLogsForExecution(rundeck, listener, execution.getId());
}

}



switch (execution.getStatus()) {
case SUCCEEDED:
return true;
case ABORTED:
case FAILED:
case RUNNING: //possible if it was unable to abort execution after an interruption
if (getShouldFailTheBuild())
build.setResult(Result.FAILURE);
build.setResult(Result.FAILURE);
return false;
default:
return true;
throw new IllegalStateException(format("Unexpected executions status: %s", execution.getStatus()));
}
} else {
return true;
Expand All @@ -308,14 +282,30 @@ private boolean notifyRundeck(RundeckClient rundeck, AbstractBuild<?, ?> build,
}
}

private void logExecutionStatus(BuildListener listener, RundeckExecution execution) {
listener.getLogger().println("Rundeck execution #" + execution.getId() + " finished in "
+ execution.getDuration() + ", with status : " + execution.getStatus());
private RundeckExecution waitTailingRundeckLogsAndReturnExecution(RundeckClient rundeck, BuildListener listener, RundeckExecution execution) {
listener.getLogger().println("BEGIN RUNDECK TAILED LOG OUTPUT");
RunDeckLogTail runDeckLogTail = new RunDeckLogTail(rundeck, execution.getId());
for (List<RundeckOutputEntry> aRunDeckLogTail : runDeckLogTail) {
for (RundeckOutputEntry rundeckOutputEntry : aRunDeckLogTail) {
listener.getLogger().println(String.format("[%s] [%s] %s",
new Object[]{ rundeckOutputEntry.getTime(), rundeckOutputEntry.getLevel(), rundeckOutputEntry.getMessage() }));
}
}
listener.getLogger().println("END RUNDECK TAILED LOG OUTPUT");

execution = rundeck.getExecution(execution.getId());
logExecutionStatus(listener, execution, "finished");
return execution;
}

private void logExecutionStatus(BuildListener listener, RundeckExecution execution, String operationName) {
listener.getLogger().printf("Rundeck execution #%d %s in %s, with status : %s%n", execution.getId(), operationName,
execution.getDuration(), execution.getStatus());
}

/**
* Parse the given input (should be in the Java-Properties syntax) and expand Jenkins environment variables.
*
*
* @param input specified in the Java-Properties syntax (multi-line, key and value separated by = or :)
* @param build for retrieving Jenkins environment variables
* @param listener for retrieving Jenkins environment variables and logging the errors
Expand Down Expand Up @@ -361,6 +351,39 @@ private Properties parseProperties(String input, AbstractBuild<?, ?> build, Buil
}
}

private RundeckExecution waitForRundeckExecutionToFinishAndReturnIt(RundeckClient rundeck, BuildListener listener,
RundeckExecution execution) {
try {
while (ExecutionStatus.RUNNING.equals(execution.getStatus())) {
Thread.sleep(DELAY_BETWEEN_POLLS_IN_MILLIS);
execution = rundeck.getExecution(execution.getId());
}
logExecutionStatus(listener, execution, "finished");
} catch (InterruptedException e) {
listener.getLogger().println("Waiting was interrupted. Probably build was cancelled. Reason: " + e);
listener.getLogger().println("Trying to abort Rundeck execution...");
RundeckAbort rundeckAbort = rundeck.abortExecution(execution.getId());
listener.getLogger().printf("Abort status: %s%n", rundeckAbort.getStatus());
execution = rundeck.getExecution(execution.getId());
logExecutionStatus(listener, execution, "aborted");
}
return execution;
}

private void getAndPrintRundeckLogsForExecution(RundeckClient rundeck, BuildListener listener, Long executionId) {
listener.getLogger().println("BEGIN RUNDECK LOG OUTPUT");
RundeckOutput rundeckOutput = rundeck.getExecutionOutput(executionId, 0, 0, 0);
if (null != rundeckOutput) {
List<RundeckOutputEntry> logEntries = rundeckOutput.getLogEntries();
if (null != logEntries) {
for (RundeckOutputEntry rundeckOutputEntry : logEntries) {
listener.getLogger().println(rundeckOutputEntry.getMessage());
}
}
}
listener.getLogger().println("END RUNDECK LOG OUTPUT");
}

@Override
public Action getProjectAction(AbstractProject<?, ?> project) {
try {
Expand All @@ -384,7 +407,7 @@ public boolean needsToRunAfterFinalized() {
public BuildStepMonitor getRequiredMonitorService() {
return BuildStepMonitor.NONE;
}

public String getRundeckInstance() {
return this.rundeckInstance;
}
Expand All @@ -404,18 +427,18 @@ public String getOptions() {
public String getNodeFilters() {
return nodeFilters;
}

public String getTag() {
StringBuilder builder = new StringBuilder();

for (int i=0; i<tags.length; i++) {
builder.append(tags[i]);

if (i+1<tags.length) {
builder.append(",");
}
}

return builder.toString();
}

Expand All @@ -434,7 +457,7 @@ public Boolean getShouldFailTheBuild() {
public Boolean getIncludeRundeckLogs() {
return includeRundeckLogs;
}

public Boolean getTailLog() {
return tailLog;
}
Expand All @@ -449,21 +472,21 @@ public static final class RundeckDescriptor extends BuildStepDescriptor<Publishe

@Deprecated
private transient RundeckClient rundeckInstance;

@CopyOnWrite
private volatile Map<String, RundeckClient> rundeckInstances = new LinkedHashMap<String, RundeckClient>();

public RundeckDescriptor() {
super();
load();
}

public synchronized void load() {
super.load();
}

// support backward compatibility
protected Object readResolve() {
protected Object readResolve() {
if (rundeckInstance != null) {
Map<String, RundeckClient> instance = new LinkedHashMap<String, RundeckClient>();
instance.put("Default", rundeckInstance);
Expand All @@ -474,23 +497,23 @@ protected Object readResolve() {

@Override
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {

JSONArray instances = json.optJSONArray("inst");

if (instances == null) {
instances = new JSONArray();

if (json.optJSONObject("inst") != null) {
instances.add(json.getJSONObject("inst"));
}
}

Map<String, RundeckClient> newInstances = new LinkedHashMap<String, RundeckClient>(instances.size());

try {
for (int i=0; i< instances.size(); i++) {
JSONObject instance = instances.getJSONObject(i);

if (!StringUtils.isEmpty(instance.getString("name"))) {
RundeckClientBuilder builder = RundeckClient.builder();
builder.url(instance.getString("url"));
Expand All @@ -499,7 +522,7 @@ public boolean configure(StaplerRequest req, JSONObject json) throws FormExcepti
} else {
builder.login(instance.getString("login"), instance.getString("password"));
}

if (instance.optInt("apiversion") > 0) {
builder.version(instance.getInt("apiversion"));
}
Expand All @@ -509,7 +532,7 @@ public boolean configure(StaplerRequest req, JSONObject json) throws FormExcepti
} catch (IllegalArgumentException e) {
// NOP
}

this.setRundeckInstances(newInstances);

save();
Expand Down Expand Up @@ -538,7 +561,7 @@ public Publisher newInstance(StaplerRequest req, JSONObject formData) throws For
formData.getString("tag"),
formData.getBoolean("shouldWaitForRundeckJob"),
formData.getBoolean("shouldFailTheBuild"),
formData.getBoolean("includeRundeckLogs"),
formData.getBoolean("includeRundeckLogs"),
formData.getBoolean("tailLog"));
}

Expand Down Expand Up @@ -579,7 +602,7 @@ public FormValidation doTestConnection(@QueryParameter("rundeck.url") final Stri
return FormValidation.ok("Your Rundeck instance is alive, and your credentials are valid !");
}

public FormValidation doCheckJobIdentifier(@QueryParameter("jobIdentifier") final String jobIdentifier,
public FormValidation doCheckJobIdentifier(@QueryParameter("jobIdentifier") final String jobIdentifier,
@QueryParameter("rundeckInstance") final String rundeckInstance) {
if (this.getRundeckInstance(rundeckInstance) == null) {
return FormValidation.error("Rundeck global configuration is not valid !");
Expand Down Expand Up @@ -645,7 +668,7 @@ public static RundeckJob findJob(String jobIdentifier, RundeckClient rundeckInst
return rundeckInstance.getJob(jobIdentifier);
}
}

@Override
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
return true;
Expand Down Expand Up @@ -688,7 +711,7 @@ public void addRundeckInstance(String key, RundeckClient instance) {
instances.put(key, instance);
this.setRundeckInstances(instances);
}

public Map<String, RundeckClient> getRundeckInstances() {
return rundeckInstances;
}
Expand Down

0 comments on commit 5acdd8b

Please sign in to comment.