diff --git a/src/main/java/io/jenkins/plugins/opentelemetry/backend/GrafanaBackend.java b/src/main/java/io/jenkins/plugins/opentelemetry/backend/GrafanaBackend.java index 37f182618..c0f3166dc 100644 --- a/src/main/java/io/jenkins/plugins/opentelemetry/backend/GrafanaBackend.java +++ b/src/main/java/io/jenkins/plugins/opentelemetry/backend/GrafanaBackend.java @@ -57,7 +57,11 @@ public class GrafanaBackend extends ObservabilityBackend { private String grafanaBaseUrl; private String grafanaMetricsDashboard; - private String tempoDataSourceIdentifier = DEFAULT_TEMPO_DATA_SOURCE_IDENTIFIER; + + @Deprecated + private String tempoDataSourceIdentifier; + + private String tempoDataSourceUid = DEFAULT_TEMPO_DATA_SOURCE_IDENTIFIER; private String grafanaOrgId = DEFAULT_GRAFANA_ORG_ID; @@ -68,17 +72,27 @@ public class GrafanaBackend extends ObservabilityBackend { @DataBoundConstructor public GrafanaBackend() {} + protected void readResolve() { + if (tempoDataSourceIdentifier != null) { + if (tempoDataSourceUid == null || tempoDataSourceUid.equals(DEFAULT_TEMPO_DATA_SOURCE_IDENTIFIER)) { + this.tempoDataSourceUid = tempoDataSourceIdentifier; + } + this.tempoDataSourceIdentifier = null; + } + } + @Nullable @Override public String getTraceVisualisationUrlTemplate() { - return "${" + TemplateBindings.GRAFANA_BASE_URL + "}" + "/explore?orgId=" + return "${" + TemplateBindings.GRAFANA_BASE_URL + "}" + "/explore?schemaVersion=1&orgId=" + "${" - + TemplateBindings.GRAFANA_ORG_ID + "}" + "&left=%7B%22datasource%22:%22" + + TemplateBindings.GRAFANA_ORG_ID + "}" + "&panes=%7B%22pane1%22:%7B%22datasource%22:%22" + "${" - + TemplateBindings.GRAFANA_TEMPO_DATASOURCE_IDENTIFIER + "}" + + TemplateBindings.GRAFANA_TEMPO_DATASOURCE_UID + "}" + "%22,%22queries%22:%5B%7B%22refId%22:%22A%22,%22datasource%22:%7B%22type%22:%22tempo%22,%22uid%22:%22" + "${" - + TemplateBindings.GRAFANA_TEMPO_DATASOURCE_IDENTIFIER + "}" + "%22%7D,%22queryType%22:%22${" + + TemplateBindings.GRAFANA_TEMPO_DATASOURCE_UID + "}" + + "%22%7D,%22queryType%22:%22${" + TemplateBindings.GRAFANA_TEMPO_QUERY_TYPE + "}%22,%22query%22:%22" + "${traceId}" + "%22%7D%5D,%22range%22:%7B%22from%22:%22" + "${" @@ -88,7 +102,7 @@ public String getTraceVisualisationUrlTemplate() { + "${" + TemplateBindings.START_TIME + ".plusSeconds(600).atZone(java.util.TimeZone.getDefault().toZoneId()).toInstant().toEpochMilli()}" - + "%22%7D%7D"; + + "%22%7D%7D%7D"; } /** @@ -125,13 +139,13 @@ public boolean equals(Object o) { GrafanaBackend that = (GrafanaBackend) o; return Objects.equals(grafanaOrgId, that.grafanaOrgId) && Objects.equals(grafanaBaseUrl, that.grafanaBaseUrl) - && Objects.equals(tempoDataSourceIdentifier, that.tempoDataSourceIdentifier) + && Objects.equals(tempoDataSourceUid, that.tempoDataSourceUid) && Objects.equals(tempoQueryType, that.tempoQueryType); } @Override public int hashCode() { - return Objects.hash(grafanaBaseUrl, tempoDataSourceIdentifier, grafanaOrgId, tempoQueryType); + return Objects.hash(grafanaBaseUrl, tempoDataSourceUid, grafanaOrgId, tempoQueryType); } @Override @@ -144,12 +158,20 @@ public Map mergeBindings(Map bindings) { @Override public Map getBindings() { Map bindings = Map.of( - TemplateBindings.BACKEND_NAME, getName(), - TemplateBindings.BACKEND_24_24_ICON_URL, "/plugin/opentelemetry/images/24x24/grafana.png", - TemplateBindings.GRAFANA_BASE_URL, this.getGrafanaBaseUrl(), - TemplateBindings.GRAFANA_ORG_ID, String.valueOf(this.getGrafanaOrgId()), - TemplateBindings.GRAFANA_TEMPO_DATASOURCE_IDENTIFIER, this.getTempoDataSourceIdentifier(), - TemplateBindings.GRAFANA_TEMPO_QUERY_TYPE, this.getTempoQueryType()); + TemplateBindings.BACKEND_NAME, + getName(), + TemplateBindings.BACKEND_24_24_ICON_URL, + "/plugin/opentelemetry/images/24x24/grafana.png", + TemplateBindings.GRAFANA_BASE_URL, + this.getGrafanaBaseUrl(), + TemplateBindings.GRAFANA_ORG_ID, + String.valueOf(this.getGrafanaOrgId()), + TemplateBindings.GRAFANA_TEMPO_DATASOURCE_IDENTIFIER, + this.getTempoDataSourceUid(), + TemplateBindings.GRAFANA_TEMPO_DATASOURCE_UID, + this.getTempoDataSourceUid(), + TemplateBindings.GRAFANA_TEMPO_QUERY_TYPE, + this.getTempoQueryType()); if (grafanaLogsBackend instanceof TemplateBindingsProvider) { Map logsBackendBindings = ((TemplateBindingsProvider) grafanaLogsBackend).getBindings(); @@ -178,16 +200,25 @@ public void setGrafanaBaseUrl(String grafanaBaseUrl) { this.grafanaBaseUrl = grafanaBaseUrl; } - @DataBoundSetter + @Deprecated public String getTempoDataSourceIdentifier() { return tempoDataSourceIdentifier; } - @DataBoundSetter + @Deprecated public void setTempoDataSourceIdentifier(String tempoDataSourceIdentifier) { this.tempoDataSourceIdentifier = tempoDataSourceIdentifier; } + public String getTempoDataSourceUid() { + return tempoDataSourceUid; + } + + @DataBoundSetter + public void setTempoDataSourceUid(String tempoDataSourceUid) { + this.tempoDataSourceUid = tempoDataSourceUid; + } + @DataBoundSetter public void setGrafanaMetricsDashboard(String grafanaMetricsDashboard) { this.grafanaMetricsDashboard = grafanaMetricsDashboard; @@ -250,6 +281,10 @@ public String getDefaultTempoDataSourceIdentifier() { return DEFAULT_TEMPO_DATA_SOURCE_IDENTIFIER; } + public String getDefaultTempoDataSourceUid() { + return DEFAULT_TEMPO_DATA_SOURCE_IDENTIFIER; + } + public String getDefaultTempoQueryType() { return DEFAULT_TEMPO_QUERY_TYPE; } @@ -280,6 +315,7 @@ public ListBoxModel doFillTempoQueryTypeItems() { public interface TemplateBindings extends ObservabilityBackend.TemplateBindings { String GRAFANA_BASE_URL = "grafanaBaseUrl"; String GRAFANA_TEMPO_DATASOURCE_IDENTIFIER = "grafanaTempoDatasourceIdentifier"; + String GRAFANA_TEMPO_DATASOURCE_UID = "grafanaTempoDatasourceUid"; String GRAFANA_LOKI_DATASOURCE_IDENTIFIER = "grafanaLokiDatasourceIdentifier"; String GRAFANA_ORG_ID = "grafanaOrgId"; String GRAFANA_TEMPO_QUERY_TYPE = "grafanaTempoQueryType"; diff --git a/src/main/resources/io/jenkins/plugins/opentelemetry/backend/GrafanaBackend/config.jelly b/src/main/resources/io/jenkins/plugins/opentelemetry/backend/GrafanaBackend/config.jelly index 146d53a2e..79f22100a 100644 --- a/src/main/resources/io/jenkins/plugins/opentelemetry/backend/GrafanaBackend/config.jelly +++ b/src/main/resources/io/jenkins/plugins/opentelemetry/backend/GrafanaBackend/config.jelly @@ -22,9 +22,9 @@

Grafana

- - + + diff --git a/src/test/java/io/jenkins/plugins/opentelemetry/backend/GrafanaBackendTest.java b/src/test/java/io/jenkins/plugins/opentelemetry/backend/GrafanaBackendTest.java index 49feea795..697096b35 100644 --- a/src/test/java/io/jenkins/plugins/opentelemetry/backend/GrafanaBackendTest.java +++ b/src/test/java/io/jenkins/plugins/opentelemetry/backend/GrafanaBackendTest.java @@ -5,16 +5,48 @@ package io.jenkins.plugins.opentelemetry.backend; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import org.junit.jupiter.api.Test; public class GrafanaBackendTest { @Test public void testTraceUrl() { + GrafanaBackend grafanaBackend = new GrafanaBackend(); + grafanaBackend.setGrafanaBaseUrl("https://cleclerc.grafana.net"); + grafanaBackend.setGrafanaOrgId("1"); + grafanaBackend.setTempoDataSourceUid("my-awesome-custom-uid"); + grafanaBackend.setTempoQueryType("traceql"); + + LocalDateTime buildTime = + LocalDateTime.parse("2023-02-05 23:31:52.610", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")); + + Map bindings = new HashMap<>(); + bindings.put("serviceName", "jenkins"); + bindings.put("rootSpanName", "BUILD my-app"); + bindings.put("traceId", "f464e1f32444443d3fc00fdb19e5c124"); + bindings.put("spanId", "00799ea60984f33f"); + bindings.put("startTime", buildTime); + + String actualUrl = grafanaBackend.getTraceVisualisationUrl(bindings); + System.out.println(actualUrl); + + assertTrue(Objects.requireNonNull(actualUrl).contains("schemaVersion=1"), "URL must contain schemaVersion=1"); + assertTrue(actualUrl.contains("panes="), "URL must use 'panes' instead of 'left'"); + assertTrue(actualUrl.contains("my-awesome-custom-uid"), "URL must contain the new Tempo UID"); + assertTrue(actualUrl.contains("f464e1f32444443d3fc00fdb19e5c124"), "URL must contain the exact traceId"); + } + + @Test + @SuppressWarnings("deprecation") + public void testTraceUrlFallbackToIdentifier() { GrafanaBackend grafanaBackend = new GrafanaBackend(); grafanaBackend.setGrafanaBaseUrl("https://cleclerc.grafana.net"); grafanaBackend.setGrafanaOrgId("1"); @@ -31,7 +63,70 @@ public void testTraceUrl() { bindings.put("spanId", "00799ea60984f33f"); bindings.put("startTime", buildTime); - String actualTraceVisualisationUrl = grafanaBackend.getTraceVisualisationUrl(bindings); - System.out.println(actualTraceVisualisationUrl); + String actualUrl = grafanaBackend.getTraceVisualisationUrl(bindings); + + assertTrue(Objects.requireNonNull(actualUrl).contains("schemaVersion=1"), "URL must contain schemaVersion=1"); + assertTrue(actualUrl.contains("panes="), "URL must use 'panes' instead of 'left'"); + assertTrue( + actualUrl.contains("grafanacloud-traces"), + "URL must fall back to the tempoDataSourceIdentifier when UID is missing"); + assertTrue(actualUrl.contains("f464e1f32444443d3fc00fdb19e5c124"), "URL must contain the exact traceId"); + } + + @Test + public void testEqualsAndHashCode() { + GrafanaBackend backend1 = new GrafanaBackend(); + backend1.setGrafanaBaseUrl("http://grafana"); + backend1.setGrafanaOrgId("1"); + backend1.setTempoDataSourceUid("tempo-uid"); + backend1.setTempoQueryType("traceql"); + + GrafanaBackend backend2 = new GrafanaBackend(); + backend2.setGrafanaBaseUrl("http://grafana"); + backend2.setGrafanaOrgId("1"); + backend2.setTempoDataSourceUid("tempo-uid"); + backend2.setTempoQueryType("traceql"); + + assertEquals(backend1, backend2); + assertEquals(backend1.hashCode(), backend2.hashCode()); + } + + @Test + @SuppressWarnings("deprecation") + public void testReadResolveMigratesIdentifierToUid() { + GrafanaBackend backend = new GrafanaBackend(); + + backend.setTempoDataSourceIdentifier("old-datasource-id"); + backend.readResolve(); + + assertEquals("old-datasource-id", backend.getTempoDataSourceUid()); + } + + @Test + @SuppressWarnings("deprecation") + public void testReadResolveDoesNotOverrideExistingUid() { + GrafanaBackend backend = new GrafanaBackend(); + + backend.setTempoDataSourceIdentifier("old-id"); + backend.setTempoDataSourceUid("new-uid"); + backend.readResolve(); + + assertEquals("new-uid", backend.getTempoDataSourceUid()); + } + + @Test + public void testDescriptorDefaultTempoDataSourceUid() { + GrafanaBackend.DescriptorImpl descriptor = new GrafanaBackend.DescriptorImpl(); + + assertEquals("grafanacloud-traces", descriptor.getDefaultTempoDataSourceUid()); + } + + @Test + public void testDescriptorDefaults() { + GrafanaBackend.DescriptorImpl descriptor = new GrafanaBackend.DescriptorImpl(); + + assertEquals("grafanacloud-traces", descriptor.getDefaultTempoDataSourceUid()); + assertEquals("1", descriptor.getDefaultGrafanaOrgId()); + assertEquals("traceql", descriptor.getDefaultTempoQueryType()); } }