diff --git a/pom.xml b/pom.xml index 506cb01872..c1450b06b6 100644 --- a/pom.xml +++ b/pom.xml @@ -84,6 +84,11 @@ org.jenkins-ci.plugins authentication-tokens + + org.jenkins-ci.plugins + cloud-stats + 302.v45b_647b_90608 + @@ -140,6 +145,8 @@ org.jenkins-ci.plugins.workflow workflow-durable-task-step + + org.jenkins-ci.plugins.workflow workflow-step-api diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer.java index 2355dbc225..c84e2e81e4 100644 --- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer.java +++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesComputer.java @@ -1,5 +1,6 @@ package org.csanchez.jenkins.plugins.kubernetes; +import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.model.Computer; import hudson.model.Executor; @@ -23,6 +24,8 @@ import jenkins.model.Jenkins; import org.acegisecurity.Authentication; import org.apache.commons.lang.StringUtils; +import org.jenkinsci.plugins.cloudstats.ProvisioningActivity; +import org.jenkinsci.plugins.cloudstats.TrackedItem; import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuthException; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; @@ -34,7 +37,7 @@ /** * @author Carlos Sanchez carlos@apache.org */ -public class KubernetesComputer extends AbstractCloudComputer { +public class KubernetesComputer extends AbstractCloudComputer implements TrackedItem { private static final Logger LOGGER = Logger.getLogger(KubernetesComputer.class.getName()); private boolean launching; @@ -203,4 +206,15 @@ public void setAcceptingTasks(boolean acceptingTasks) { launching = false; } } + + @CheckForNull + @Override + public ProvisioningActivity.Id getId() { + KubernetesSlave slave = getNode(); + if (slave != null) { + return slave.getId(); + } + + return null; + } } diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesSlave.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesSlave.java index cf59602b5d..0cc5b5b627 100644 --- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesSlave.java +++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesSlave.java @@ -49,6 +49,8 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; import org.csanchez.jenkins.plugins.kubernetes.pod.retention.PodRetention; +import org.jenkinsci.plugins.cloudstats.ProvisioningActivity; +import org.jenkinsci.plugins.cloudstats.TrackedItem; import org.jenkinsci.plugins.durabletask.executors.OnceRetentionStrategy; import org.jenkinsci.plugins.kubernetes.auth.KubernetesAuthException; import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner; @@ -58,7 +60,7 @@ /** * @author Carlos Sanchez carlos@apache.org */ -public class KubernetesSlave extends AbstractCloudSlave { +public class KubernetesSlave extends AbstractCloudSlave implements TrackedItem { private static final Logger LOGGER = Logger.getLogger(KubernetesSlave.class.getName()); @@ -85,6 +87,9 @@ public class KubernetesSlave extends AbstractCloudSlave { @CheckForNull private transient Pod pod; + @NonNull + private final ProvisioningActivity.Id id; + @NonNull public PodTemplate getTemplate() throws IllegalStateException { // Look up updated pod template after a restart @@ -210,6 +215,7 @@ protected KubernetesSlave( this.cloudName = cloudName; this.template = template; this.podTemplateId = template.getId(); + this.id = new ProvisioningActivity.Id(cloudName, template.getName(), name); } public String getCloudName() { @@ -450,6 +456,12 @@ private void deleteSlavePod(TaskListener listener, KubernetesClient client) thro listener.getLogger().println(msg); } + @CheckForNull + @Override + public ProvisioningActivity.Id getId() { + return id; + } + @Override public String toString() { return String.format("KubernetesSlave name: %s", name); diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/StandardPlannedNodeBuilder.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/StandardPlannedNodeBuilder.java index f279521532..95cba818ea 100644 --- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/StandardPlannedNodeBuilder.java +++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/StandardPlannedNodeBuilder.java @@ -1,10 +1,12 @@ package org.csanchez.jenkins.plugins.kubernetes; -import hudson.Util; import hudson.model.Descriptor; +import hudson.model.Node; import hudson.slaves.NodeProvisioner; import java.io.IOException; import java.util.concurrent.CompletableFuture; +import org.jenkinsci.plugins.cloudstats.ProvisioningActivity; +import org.jenkinsci.plugins.cloudstats.TrackedPlannedNode; /** * The default {@link PlannedNodeBuilder} implementation, in case there is other registered. @@ -14,20 +16,25 @@ public class StandardPlannedNodeBuilder extends PlannedNodeBuilder { public NodeProvisioner.PlannedNode build() { KubernetesCloud cloud = getCloud(); PodTemplate t = getTemplate(); - CompletableFuture f; - String displayName; + CompletableFuture f; + ProvisioningActivity.Id id = null; try { KubernetesSlave agent = KubernetesSlave.builder() .podTemplate(t.isUnwrapped() ? t : cloud.getUnwrappedTemplate(t)) .cloud(cloud) .build(); - displayName = agent.getDisplayName(); + id = agent.getId(); // always use one sourced from the slave we are provisioning so the identity is + // maintained f = CompletableFuture.completedFuture(agent); } catch (IOException | Descriptor.FormException e) { - displayName = null; - f = new CompletableFuture(); + f = new CompletableFuture<>(); f.completeExceptionally(e); } - return new NodeProvisioner.PlannedNode(Util.fixNull(displayName), f, getNumExecutors()); + + if (id == null) { + id = new ProvisioningActivity.Id(cloud.name, t.getName()); + } + + return new TrackedPlannedNode(id, getNumExecutors(), f); } } diff --git a/src/test/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesSlaveTest.java b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesSlaveTest.java index 3655b7d4c9..87dc773220 100644 --- a/src/test/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesSlaveTest.java +++ b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesSlaveTest.java @@ -38,6 +38,7 @@ import org.csanchez.jenkins.plugins.kubernetes.pod.retention.OnFailure; import org.csanchez.jenkins.plugins.kubernetes.pod.retention.PodRetention; import org.csanchez.jenkins.plugins.kubernetes.volumes.PodVolume; +import org.jenkinsci.plugins.cloudstats.ProvisioningActivity; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.JenkinsRule; @@ -70,6 +71,18 @@ public void testGetSlaveName() { ("jenkins-agent-[0-9a-z]{5}")); } + @Test + public void testProvisioningActivityId() throws Descriptor.FormException, IOException { + PodTemplate pt = new PodTemplate("x"); + pt.setName("Template"); + KubernetesSlave slave = new KubernetesSlave("Node", pt, "bar", "Cloud", "", null, null); + ProvisioningActivity.Id id = slave.getId(); + assertNotNull(id); + assertEquals(id.getCloudName(), "Cloud"); + assertEquals(id.getTemplateName(), "Template"); + assertEquals(id.getNodeName(), "Node"); + } + @Test public void testGetPodRetention() { try { diff --git a/src/test/java/org/csanchez/jenkins/plugins/kubernetes/StandardPlannedNodeBuilderTest.java b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/StandardPlannedNodeBuilderTest.java new file mode 100644 index 0000000000..4e2a8bdb27 --- /dev/null +++ b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/StandardPlannedNodeBuilderTest.java @@ -0,0 +1,36 @@ +package org.csanchez.jenkins.plugins.kubernetes; + +import static org.csanchez.jenkins.plugins.kubernetes.KubernetesTestUtil.assertRegex; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import hudson.slaves.NodeProvisioner; +import org.jenkinsci.plugins.cloudstats.ProvisioningActivity; +import org.jenkinsci.plugins.cloudstats.TrackedPlannedNode; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +public class StandardPlannedNodeBuilderTest { + + @Rule + public JenkinsRule r = new JenkinsRule(); + + @Test + public void testBuild() { + KubernetesCloud cloud = new KubernetesCloud("Cloud"); + PodTemplate template = new PodTemplate("t"); + template.setName("Template"); + StandardPlannedNodeBuilder builder = new StandardPlannedNodeBuilder(); + builder.cloud(cloud); + builder.template(template); + builder.numExecutors(1); + + NodeProvisioner.PlannedNode plannedNode = builder.build(); + assertTrue(plannedNode instanceof TrackedPlannedNode); + ProvisioningActivity.Id id = ((TrackedPlannedNode) plannedNode).getId(); + assertEquals(id.getCloudName(), "Cloud"); + assertEquals(id.getTemplateName(), "Template"); + assertRegex(id.getNodeName(), "template-\\w{5}"); + } +}