Skip to content

Commit

Permalink
remove dependency with pipeline plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
slxiao committed Nov 26, 2019
1 parent 0465d5d commit 2ba9b2c
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
import io.jenkins.plugins.pipelinemonitor.model.TestResults;
import jenkins.model.Jenkins;

import java.io.PrintStream;

import java.util.logging.Level;
import java.util.logging.Logger;

Expand All @@ -34,7 +32,6 @@ public class BuildStatusListener extends RunListener<Run<?, ?>> {
@Override
public void onCompleted(final Run<?, ?> run, TaskListener listener) {

PrintStream errorStream = listener.getLogger();
PipelineMonitorConfiguration configuration = PipelineMonitorConfiguration.getInstance();
if (!configuration.isEnabled()) {
log(Level.WARNING, "pipeline monitor plugin disabled!");
Expand All @@ -43,8 +40,7 @@ public void onCompleted(final Run<?, ?> run, TaskListener listener) {

log(Level.INFO, "start pipeline monitor writer");

PipelineMonitorWriter pmWrite =
new PipelineMonitorWriter(run, errorStream, listener, run.getCharset());
PipelineMonitorWriter pmWrite = new PipelineMonitorWriter(run, run.getCharset());

pmWrite.write(null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,14 @@ public class PipelineMonitorWriter {

private final OutputStream errorStream;
private final Run<?, ?> build;
private final TaskListener listener;
private final BuildData buildData;
private final RemoteServerDao dao;
private boolean connectionBroken;
private Charset charset;

public PipelineMonitorWriter(Run<?, ?> run, OutputStream error, TaskListener listener,
Charset charset) {
this.errorStream = error != null ? error : System.err;
public PipelineMonitorWriter(Run<?, ?> run, Charset charset) {
this.errorStream = System.err;
this.build = run;
this.listener = listener;
this.charset = charset;
this.dao = this.getDaoOrNull();
if (this.dao == null) {
Expand All @@ -75,9 +72,9 @@ public boolean isConnectionBroken() {

BuildData getBuildData() {
if (build instanceof AbstractBuild) {
return new BuildData((AbstractBuild<?, ?>) build, new Date(), listener);
return new BuildData((AbstractBuild<?, ?>) build, new Date());
} else {
return new BuildData(build, new Date(), listener);
return new BuildData(build, new Date());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package io.jenkins.plugins.pipelinemonitor;

import hudson.Extension;
import hudson.model.Queue;
import hudson.model.Run;

import io.jenkins.plugins.pipelinemonitor.model.PipelineStageStatus;

import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;

import jenkins.model.Jenkins;

import org.jenkinsci.plugins.workflow.actions.ErrorAction;
import org.jenkinsci.plugins.workflow.actions.LabelAction;
import org.jenkinsci.plugins.workflow.actions.StageAction;
import org.jenkinsci.plugins.workflow.actions.TagsAction;
import org.jenkinsci.plugins.workflow.actions.ThreadNameAction;
import org.jenkinsci.plugins.workflow.actions.TimingAction;
import org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode;
import org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode;
import org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.flow.GraphListener;
import org.jenkinsci.plugins.workflow.graph.FlowNode;



/**
* GraphListener implementation which provides status (pending, error or success) and timing
* information for each stage in a build.
*
* @author Jeff Pearce (GitHub jeffpeare)
*/
@Extension
public class StageStatusListener implements GraphListener {

/**
* Evaluates if we can provide stats on a node.
*
* @param fn a node in workflow
*/
@Override
public void onNewHead(FlowNode fn) {
try {
if (isStage(fn)) {
log(Level.WARNING, "checkEnableBuildStatus");
} else if (fn instanceof StepAtomNode) {

ErrorAction errorAction = fn.getError();

if (errorAction == null) {
return;
}

List<? extends FlowNode> enclosingBlocks = fn.getEnclosingBlocks();
boolean isInStage = false;

for (FlowNode encosingNode : enclosingBlocks) {
if (isStage(encosingNode)) {
isInStage = true;
}
}

if (isInStage) {
return;
}

// We have a non-declarative atom that isn't in a stage, which has failed.
// Since normal processing is via stages, we'd normally miss this failure;
// send an out of band error notification to make sure it's recordded by any
// interested notifiers
log(Level.WARNING, "Non stage error: " + fn.getDisplayName());

} else if (fn instanceof StepEndNode) {
log(Level.WARNING, "checkEnableBuildStatus");

String startId = ((StepEndNode) fn).getStartNode().getId();
FlowNode startNode = fn.getExecution().getNode(startId);
if (null == startNode) {
return;
}

ErrorAction errorAction = fn.getError();
String nodeName = null;

long time = getTime(startNode, fn);
LabelAction label = startNode.getAction(LabelAction.class);

if (label != null) {
nodeName = label.getDisplayName();
} else if (null != errorAction && startNode instanceof StepStartNode) {
nodeName = ((StepStartNode) startNode).getStepName();
}

if (nodeName != null) {
String buildState = buildStateForStage(startNode, errorAction);
PipelineStageStatus stage = new PipelineStageStatus();
stage.setName(nodeName);
stage.setDuration(time);
stage.setResult(buildState);

Run<?, ?> run = runFor(fn.getExecution());
if (run != null) {
stage.setJenkinsUrl(Jenkins.getInstance().getRootUrl());
stage.setJobName(run.getParent().getName());
stage.setNumber(run.getNumber());
PipelineMonitorWriter pmWrite = new PipelineMonitorWriter(run, run.getCharset());
pmWrite.write(stage);
}
}
}
} catch (IOException ex) {
log(Level.WARNING, "IOException");
}
}

/**
* Determines the appropriate state for a stage.
*
* @param flowNode The stage start node
* @param errorAction The error action from the stage end node
* @return Stage state
*/
static String buildStateForStage(FlowNode flowNode, ErrorAction errorAction) {
String buildState = errorAction == null ? "CompletedSuccess" : "CompletedError";
TagsAction tags = flowNode.getAction(TagsAction.class);
if (tags != null) {
return tags.getTagValue("STAGE_STATUS");
}
return buildState;
}

/**
* Gets the execution time of a block defined by startNode and endNode.
*
* @param startNode startNode of a block
* @param endNode endNode of a block
* @return Execution time of the block
*/
static long getTime(FlowNode startNode, FlowNode endNode) {
TimingAction startTime = startNode.getAction(TimingAction.class);
TimingAction endTime = endNode.getAction(TimingAction.class);

if (startTime != null && endTime != null) {
return endTime.getStartTime() - startTime.getStartTime();
}
return 0;
}

/**
* Determines if a {@link FlowNode} describes a stage. Note: this check is copied from
* PipelineNodeUtil.java in blueocean-plugin
*
* @param node node of a workflow
* @return true if it's a stage node; false otherwise
*/
private static boolean isStage(FlowNode node) {
return node != null
&& (node.getAction(StageAction.class) != null || node.getAction(LabelAction.class) != null
&& node.getAction(ThreadNameAction.class) == null);
}

/**
* Gets the jenkins run object of the specified executing workflow.
*
* @param exec execution of a workflow
* @return jenkins run object of a job
*/
private static @CheckForNull Run<?, ?> runFor(FlowExecution exec) {
Queue.Executable executable;
try {
executable = exec.getOwner().getExecutable();
} catch (IOException x) {
getLogger().log(Level.WARNING, null, x);
return null;
}
if (executable instanceof Run) {
return (Run<?, ?>) executable;
} else {
return null;
}
}

/**
* Prints to stdout or stderr.
*
* @param level INFO/WARNING/ERROR
* @param format String that formats the log
* @param args arguments for the formated log string
*/
private static void log(Level level, String format, Object... args) {
getLogger().log(level, String.format(format, args));
}

/**
* Gets the logger for the listener.
*
* @return logger object
*/
private static Logger getLogger() {
return Logger.getLogger(StageStatusListener.class.getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public JSONObject buildPayload(BuildData buildData, Object object) {
JSONObject payload = new JSONObject();
payload.put("build", buildData.toJson());
if (object != null) {
payload.put("result", JsonUtil.convertToJson(object));
payload.put(object.getClass().getName().split(".")[-1], JsonUtil.convertToJson(object));
}
payload.put("source", "jenkins");
payload.put("@timestamp", FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ public class BuildData {
private String url;

// Freestyle project build
public BuildData(AbstractBuild<?, ?> build, Date currentTime, TaskListener listener) {
public BuildData(AbstractBuild<?, ?> build, Date currentTime) {
url = build.getUrl();
}

// Pipeline project build
public BuildData(Run<?, ?> build, Date currentTime, TaskListener listener) {
public BuildData(Run<?, ?> build, Date currentTime) {
url = build.getUrl();
}

Expand Down

0 comments on commit 2ba9b2c

Please sign in to comment.