Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add agent point #154

Merged
merged 6 commits into from
Sep 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions doc/available_metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,11 @@ Tags specific for this measurement:
| sqale_index | float | Technical Debt | 2.4 |
| sqale_debt_ratio | float | Technical Debt Ratio | 2.4 |



#### `agent_data` (since 3.4)
| Metric | Type | Description | Introduced in |
| --- | --- | --- | --- |
| agent_name | string | Name of an agent called by the build | |
| agent_label | string | Label of an agent called by the build | |

### Cobertura plugin

Expand Down
1 change: 0 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ THE SOFTWARE.
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-api</artifactId>
<version>${workflow-api.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ public void perform(Run<?, ?> build, TaskListener listener, EnvVars env) {
JenkinsBasePointGenerator jGen = new JenkinsBasePointGenerator(build, listener, measurementRenderer, timestamp, jenkinsEnvParameterTag, jenkinsEnvParameterField, customPrefix, measurementName, env);
addPoints(pointsToWrite, jGen, listener);

AgentPointGenerator agentGen = new AgentPointGenerator(build, listener, measurementRenderer, timestamp, jenkinsEnvParameterTag, customPrefix);
if (agentGen.hasReport()) {
addPoints(pointsToWrite, agentGen, listener);
} else {
logger.log(Level.FINE, "Data source empty: Agent Point");
}

CustomDataPointGenerator cdGen = new CustomDataPointGenerator(build, listener, measurementRenderer, timestamp, jenkinsEnvParameterTag, customPrefix, customData, customDataTags, measurementName);
if (cdGen.hasReport()) {
listener.getLogger().println("[InfluxDB Plugin] Custom data found. Writing to InfluxDB...");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package jenkinsci.plugins.influxdb.generators;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;

import org.apache.commons.collections.CollectionUtils;
import org.jenkinsci.plugins.workflow.actions.WorkspaceAction;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
import org.jenkinsci.plugins.workflow.graph.FlowGraphWalker;
import org.jenkinsci.plugins.workflow.graph.FlowNode;

import com.influxdb.client.write.Point;

import hudson.model.AbstractBuild;
import hudson.model.Node;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.labels.LabelAtom;
import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;

/**
* @author Mathieu Delrocq
*/
public class AgentPointGenerator extends AbstractPointGenerator {

protected static final String AGENT_NAME = "agent_name";
protected static final String AGENT_LABEL = "agent_label";
protected static final String UNIQUE_ID = "unique_id";

private List<Map.Entry<String, String>> agentPoints;
private String customPrefix;

public AgentPointGenerator(Run<?, ?> build, TaskListener listener, ProjectNameRenderer projectNameRenderer,
long timestamp, String jenkinsEnvParameterTag, String customPrefix) {
super(build, listener, projectNameRenderer, timestamp, jenkinsEnvParameterTag);
this.agentPoints = getAgentPoints(build);
this.customPrefix = customPrefix;
}

@Override
public boolean hasReport() {
return CollectionUtils.isNotEmpty(agentPoints);
}

@Override
public Point[] generate() {
List<Point> points = new ArrayList<>();
Map.Entry<String, String> agentPoint = null;
for (int i = 0; i < agentPoints.size(); i++) {
agentPoint = agentPoints.get(i);
Point point = buildPoint("agent_data", customPrefix, build)//
.addTag(UNIQUE_ID, String.valueOf(i+1))//
.addField(AGENT_NAME, agentPoint.getKey())//
.addField(AGENT_LABEL, agentPoint.getValue());
points.add(point);
}
return points.toArray(new Point[0]);
}

public String getFirstAgent() {
return !CollectionUtils.isEmpty(agentPoints) ? agentPoints.get(0).getKey() : "";
}

/**
* Retrieve agent(s) used by the build and return {@link AgentPoint}
*
* @param build
* @return list of {@link AgentPoint}
*/
private List<Map.Entry<String, String>> getAgentPoints(Run<?, ?> build) {
if (build instanceof AbstractBuild) {
return getAgentFromAbstractBuild((AbstractBuild<?, ?>) build);
} else if (build instanceof FlowExecutionOwner.Executable) {
return getAgentsFromPipeline((FlowExecutionOwner.Executable) build);
}
return new ArrayList<>();
}

/**
* Retrieve agent(s) for traditional jobs
*
* @param build
* @return list of {@link AgentPoint}
*/
private List<Map.Entry<String, String>> getAgentFromAbstractBuild(AbstractBuild<?, ?> build) {
List<Map.Entry<String, String>> agentPointsList = new ArrayList<>();
Node node = build.getBuiltOn();
if (node != null) {
agentPointsList
.add(new AbstractMap.SimpleEntry<String, String>(node.getDisplayName(), node.getLabelString()));
}
return agentPointsList;
}

/**
* Retrieve agent(s) for pipeline jobs
*
* @param build
* @return list of {@link AgentPoint}
*/
private List<Map.Entry<String, String>> getAgentsFromPipeline(FlowExecutionOwner.Executable build) {
List<Map.Entry<String, String>> agentPointsList = new ArrayList<>();
FlowExecutionOwner flowExecutionOwner = build.asFlowExecutionOwner();
if (flowExecutionOwner != null) {
FlowExecution flowExecution = flowExecutionOwner.getOrNull();
if (flowExecution != null) {
FlowGraphWalker graphWalker = new FlowGraphWalker(flowExecution);
for (FlowNode flowNode : graphWalker) {
WorkspaceAction workspaceAction = flowNode.getAction(WorkspaceAction.class);
if (null != workspaceAction) {
Set<LabelAtom> labels = workspaceAction.getLabels();
StringJoiner labelString = new StringJoiner(", ");
labelString.setEmptyValue("");
for (LabelAtom label : labels) {
labelString.add(label.getName());
}
String nodeName = workspaceAction.getNode();
agentPointsList
.add(new AbstractMap.SimpleEntry<String, String>(nodeName, labelString.toString()));
}
}
}
}
return agentPointsList;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package jenkinsci.plugins.influxdb.generators;

import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.jenkinsci.plugins.workflow.actions.WorkspaceAction;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import com.influxdb.client.write.Point;

import hudson.model.AbstractBuild;
import hudson.model.HealthReport;
import hudson.model.Job;
import hudson.model.Node;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.labels.LabelAtom;
import jenkins.model.Jenkins;
import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;

/**
* @author Mathieu Delrocq
*/
public class AgentPointGeneratorTest {

private static final String CUSTOM_PREFIX = "test_prefix";
private static final String JOB_NAME = "job_name";

private static final String NODE_NAME = "node_name";
private static final String NODE_LABEL = "node_label";

private Run<?, ?> abstractBuild;
private WorkflowRun pipelineBuild;
private Node node;
private FlowNode flowNode1;
private FlowNode flowNode2;
private List<FlowNode> flowNodeList;
private FlowExecutionOwner flowExecutionOwner;
private FlowExecution flowExecution;
private WorkspaceAction workspaceAction1;
private WorkspaceAction workspaceAction2;
private TaskListener listener;
private long currTime;
private ProjectNameRenderer measurementRenderer;

@Before
public void before() throws Exception {
// Global Mocks
listener = Mockito.mock(TaskListener.class);
currTime = System.currentTimeMillis();
measurementRenderer = new ProjectNameRenderer(CUSTOM_PREFIX, null);
Job<?, ?> job = Mockito.mock(Job.class);
Mockito.when(job.getName()).thenReturn(JOB_NAME);
Mockito.when(job.getRelativeNameFrom(Mockito.nullable(Jenkins.class))).thenReturn("folder/" + JOB_NAME);
Mockito.when(job.getBuildHealth()).thenReturn(new HealthReport());

// Mocks for AbstractBuild
abstractBuild = Mockito.mock(AbstractBuild.class);
Mockito.doReturn(job).when(abstractBuild).getParent();
node = Mockito.mock(Node.class);
Mockito.when(((AbstractBuild<?, ?>) abstractBuild).getBuiltOn()).thenReturn(node);
Mockito.when(node.getDisplayName()).thenReturn(NODE_NAME);
Mockito.when(node.getLabelString()).thenReturn(NODE_LABEL);

// Mock for Pipeline
pipelineBuild = Mockito.mock(WorkflowRun.class);
Mockito.doReturn(job).when(pipelineBuild).getParent();
flowNode1 = Mockito.mock(FlowNode.class);
flowNode2 = Mockito.mock(FlowNode.class);
flowExecutionOwner = Mockito.mock(FlowExecutionOwner.class);
flowExecution = Mockito.mock(FlowExecution.class);
flowNodeList = new ArrayList<FlowNode>();
flowNodeList.add(flowNode1);
flowNodeList.add(flowNode2);
workspaceAction1 = Mockito.mock(WorkspaceAction.class);
workspaceAction2 = Mockito.mock(WorkspaceAction.class);
Set<LabelAtom> labels = new HashSet<LabelAtom>();
LabelAtom label = new LabelAtom(NODE_LABEL);
labels.add(label);
Mockito.when(pipelineBuild.asFlowExecutionOwner()).thenReturn(flowExecutionOwner);
Mockito.when(flowExecutionOwner.getOrNull()).thenReturn(flowExecution);
Mockito.when(flowExecution.getCurrentHeads()).thenReturn(flowNodeList);
Mockito.when(flowNode1.getAction(WorkspaceAction.class)).thenReturn(workspaceAction1);
Mockito.when(flowNode2.getAction(WorkspaceAction.class)).thenReturn(workspaceAction2);
Mockito.when(workspaceAction1.getNode()).thenReturn(NODE_NAME);
Mockito.when(workspaceAction1.getLabels()).thenReturn(labels);
Mockito.when(workspaceAction2.getNode()).thenReturn(NODE_NAME + "2");
Mockito.when(workspaceAction2.getLabels()).thenReturn(labels);
}

@Test
public void pipeline_agent_present() {
AgentPointGenerator gen = new AgentPointGenerator(pipelineBuild, listener, measurementRenderer, currTime,
StringUtils.EMPTY, CUSTOM_PREFIX);
assertTrue(gen.hasReport());
Point[] points = gen.generate();
assertTrue(points != null && points.length != 0);
assertTrue(points[0].hasFields());
String lineProtocol1 = points[0].toLineProtocol();
String lineProtocol2 = points[1].toLineProtocol();
assertTrue(lineProtocol1.contains("agent_name=\"node_name2\""));
assertTrue(lineProtocol1.contains("agent_label=\"node_label\""));
assertTrue(lineProtocol2.contains("agent_name=\"node_name\""));
assertTrue(lineProtocol2.contains("agent_label=\"node_label\""));
}

@Test
public void abstractbuild_agent_present() {
AgentPointGenerator gen = new AgentPointGenerator(abstractBuild, listener, measurementRenderer, currTime,
StringUtils.EMPTY, CUSTOM_PREFIX);
assertTrue(gen.hasReport());
Point[] points = gen.generate();
assertTrue(points != null && points.length != 0);
assertTrue(points[0].hasFields());
String lineProtocol = points[0].toLineProtocol();
assertTrue(lineProtocol.contains("agent_name=\"node_name\""));
assertTrue(lineProtocol.contains("agent_label=\"node_label\""));
}
}