diff --git a/src/main/java/org/jenkinsci/plugins/prometheus/collectors/CollectorType.java b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/CollectorType.java index fd19083e3..a01367012 100644 --- a/src/main/java/org/jenkinsci/plugins/prometheus/collectors/CollectorType.java +++ b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/CollectorType.java @@ -6,6 +6,7 @@ public enum CollectorType { JENKINS_UPTIME_GAUGE("uptime"), JENKINS_VERSION_INFO_GAUGE("version"), NODES_ONLINE_GAUGE("nodes_online"), + NODES_OFFLINE_GAUGE("nodes_offline"), BUILD_DURATION_GAUGE("build_duration_milliseconds"), BUILD_DURATION_SUMMARY("duration_milliseconds_summary"), BUILD_FAILED_COUNTER("failed_build_count"), diff --git a/src/main/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/JenkinsCollectorFactory.java b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/JenkinsCollectorFactory.java index f6af9a2f2..e26d32a52 100644 --- a/src/main/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/JenkinsCollectorFactory.java +++ b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/JenkinsCollectorFactory.java @@ -22,10 +22,15 @@ public JenkinsCollectorFactory() { case JENKINS_UP_GAUGE: return saveBuildCollector(new JenkinsUpGauge(labelNames, namespace, subsystem)); case NODES_ONLINE_GAUGE: - if (!isNodeOnlineGaugeEnabled()) { + if (!isNodeStatusGaugeEnabled()) { return new NoOpMetricCollector<>(); } return saveBuildCollector(new NodesOnlineGauge(labelNames, namespace, subsystem)); + case NODES_OFFLINE_GAUGE: + if (!isNodeStatusGaugeEnabled()) { + return new NoOpMetricCollector<>(); + } + return saveBuildCollector(new NodesOfflineGauge(labelNames, namespace, subsystem)); case JENKINS_UPTIME_GAUGE: return saveBuildCollector(new JenkinsUptimeGauge(labelNames, namespace, subsystem)); case JENKINS_VERSION_INFO_GAUGE: @@ -35,7 +40,7 @@ public JenkinsCollectorFactory() { } } - private boolean isNodeOnlineGaugeEnabled() { + private boolean isNodeStatusGaugeEnabled() { return PrometheusConfiguration.get().isCollectNodeStatus(); } diff --git a/src/main/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/NodesOfflineGauge.java b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/NodesOfflineGauge.java new file mode 100644 index 000000000..0f9046332 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/NodesOfflineGauge.java @@ -0,0 +1,51 @@ +package org.jenkinsci.plugins.prometheus.collectors.jenkins; + +import hudson.model.Computer; +import hudson.model.Node; +import io.prometheus.client.Gauge; +import io.prometheus.client.SimpleCollector; +import jenkins.model.Jenkins; +import org.jenkinsci.plugins.prometheus.collectors.BaseMetricCollector; +import org.jenkinsci.plugins.prometheus.collectors.CollectorType; + +public class NodesOfflineGauge extends BaseMetricCollector { + + NodesOfflineGauge(String[] labelNames, String namespace, String subsystem) { + super(labelNames, namespace, subsystem); + } + + @Override + protected CollectorType getCollectorType() { + return CollectorType.NODES_OFFLINE_GAUGE; + } + + @Override + protected String getHelpText() { + return "Jenkins nodes offline status"; + } + + @Override + protected SimpleCollector.Builder getCollectorBuilder() { + return Gauge.build(); + } + + @Override + public void calculateMetric(Jenkins jenkinsObject, String[] labelValues) { + if (jenkinsObject == null) { + return; + } + for (Node node : jenkinsObject.getNodes()) { + //Check whether the node is online or offline + Computer comp = node.toComputer(); + if (comp == null) { + continue; + } + + if (comp.isOffline()) { // https://javadoc.jenkins.io/hudson/model/Computer.html + this.collector.labels(node.getNodeName()).set(1); + } else { + this.collector.labels(node.getNodeName()).set(0); + } + } + } +} diff --git a/src/test/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/JenkinsVersionInfoTest.java b/src/test/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/JenkinsVersionInfoTest.java index 14e964dac..bb0efc42c 100644 --- a/src/test/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/JenkinsVersionInfoTest.java +++ b/src/test/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/JenkinsVersionInfoTest.java @@ -42,9 +42,6 @@ public void testCollectResult() throws Exception { static void setFinalStatic(Field field, Object newValue) throws Exception { field.setAccessible(true); - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, newValue); } diff --git a/src/test/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/NodesOfflineGaugeTest.java b/src/test/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/NodesOfflineGaugeTest.java new file mode 100644 index 000000000..4841b989c --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/NodesOfflineGaugeTest.java @@ -0,0 +1,74 @@ +package org.jenkinsci.plugins.prometheus.collectors.jenkins; + +import hudson.model.Computer; +import hudson.model.Node; +import io.prometheus.client.Collector; +import jenkins.model.Jenkins; + +import org.jenkinsci.plugins.prometheus.collectors.testutils.MockedJenkinsTest; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class NodesOfflineGaugeTest extends MockedJenkinsTest { + + @Test + public void testCollectResult() throws Exception { + + + setFinalStatic(Jenkins.class.getDeclaredField("VERSION"), "123"); + + List nodes = new ArrayList<>(); + nodes.add(mockNode("node1", true)); + nodes.add(mockNode("node2", false)); + nodes.add(mockNode("node3", true)); + nodes.add(mockNode("nullNode", false)); + when(mock.getNodes()).thenReturn(nodes); + + NodesOfflineGauge sut = new NodesOfflineGauge(new String[]{"node"}, getNamespace(), getSubSystem()); + sut.calculateMetric(mock, getLabelValues()); + + List collect = sut.collect(); + + validateMetricFamilySampleListSize(collect, 1); + + Collector.MetricFamilySamples samples = collect.get(0); + // we pass 4 nodes but we only get 3 -> there's a node with null computer + validateMetricFamilySampleSize(samples, 3); + for (Collector.MetricFamilySamples.Sample sample : samples.samples) { + System.out.println(sample); + if (sample.labelValues.contains("node1")) { + validateValue(sample, 1.0); + } + if (sample.labelValues.contains("node2")) { + validateValue(sample, 0.0); + } + if (sample.labelValues.contains("node3")) { + validateValue(sample, 1.0); + } + } + System.out.println(samples); + validateNames(samples, new String[]{"default_jenkins_nodes_offline"}); + } + + private Node mockNode(String nodeName, boolean isOnline) { + Node nodeMock = mock(Node.class); + if (!"nullNode".equalsIgnoreCase(nodeName)) { + Computer computerMock = mock(Computer.class); + when(computerMock.isOffline()).thenReturn(isOnline); + when(nodeMock.toComputer()).thenReturn(computerMock); + } + when(nodeMock.getNodeName()).thenReturn(nodeName); + return nodeMock; + } + + static void setFinalStatic(Field field, Object newValue) throws Exception { + field.setAccessible(true); + field.set(null, newValue); + } +} \ No newline at end of file diff --git a/src/test/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/NodesOnlineGaugeTest.java b/src/test/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/NodesOnlineGaugeTest.java index cc2e5a895..6f07b8ca2 100644 --- a/src/test/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/NodesOnlineGaugeTest.java +++ b/src/test/java/org/jenkinsci/plugins/prometheus/collectors/jenkins/NodesOnlineGaugeTest.java @@ -70,9 +70,6 @@ private Node mockNode(String nodeName, boolean isOnline) { static void setFinalStatic(Field field, Object newValue) throws Exception { field.setAccessible(true); - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, newValue); } } \ No newline at end of file