feat: native open telemetry metrics by csviri · Pull Request #2882 · operator-framework/java-operator-sdk · GitHub
Skip to content
Closed
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
75 changes: 75 additions & 0 deletions open-telemetry-support/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>java-operator-sdk</artifactId>
<version>5.1.2-SNAPSHOT</version>
</parent>

<artifactId>open-telemetry-support</artifactId>
<name>Operator SDK - Open Telemetry Metrics Support</name>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-bom</artifactId>
<version>${opentelemetry.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
<dependency>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>operator-framework-core</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>operator-framework-junit-5</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-httpclient-vertx</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package io.javaoperatorsdk.operator.monitoring.opentelemetry;

import java.util.HashMap;
import java.util.Map;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.javaoperatorsdk.operator.api.monitoring.Metrics;
import io.javaoperatorsdk.operator.api.reconciler.Constants;
import io.javaoperatorsdk.operator.api.reconciler.RetryInfo;
import io.javaoperatorsdk.operator.processing.Controller;
import io.javaoperatorsdk.operator.processing.GroupVersionKind;
import io.javaoperatorsdk.operator.processing.event.Event;
import io.javaoperatorsdk.operator.processing.event.ResourceID;
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEvent;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;

public class OpenTelemetryMetrics implements Metrics {

private Meter meter;
private final boolean collectPerResourceMetrics;

public OpenTelemetryMetrics(Meter meter, boolean collectPerResourceMetrics) {
this.meter = meter;
this.collectPerResourceMetrics = collectPerResourceMetrics;
}

@Override
public void controllerRegistered(Controller<? extends HasMetadata> controller) {}

@Override
public void receivedEvent(Event event, Map<String, Object> metadata) {
if (event instanceof ResourceEvent) {

} else {

}
}

@Override
public void reconcileCustomResource(
HasMetadata resource, RetryInfo retryInfo, Map<String, Object> metadata) {}

@Override
public void failedReconciliation(
HasMetadata resource, Exception exception, Map<String, Object> metadata) {}

@Override
public void reconciliationExecutionStarted(HasMetadata resource, Map<String, Object> metadata) {}

@Override
public void reconciliationExecutionFinished(HasMetadata resource, Map<String, Object> metadata) {}

@Override
public void cleanupDoneFor(ResourceID resourceID, Map<String, Object> metadata) {}

@Override
public void finishedReconciliation(HasMetadata resource, Map<String, Object> metadata) {}

@Override
public <T> T timeControllerExecution(ControllerExecution<T> execution) throws Exception {
return null;
}

@Override
public <T extends Map<?, ?>> T monitorSizeOf(T map, String name) {
return null;
}

private void incrementCounter(
ResourceID id,
String counterName,
Map<String, Object> metadata,
Map<String, String> additionalTags) {

meter.counterBuilder("").buildWithCallback(m -> {}).close();

Map<String, String> tags = new HashMap<>(additionalTags);
addMetadataTags(id, metadata, tags, false);

var counter = meter.counterBuilder(PREFIX + counterName).build();
var attributes = Attributes.builder();
additionalTags.forEach(attributes::put);
counter.add(1, attributes.build());
}

private void addMetadataTags(
ResourceID resourceID,
Map<String, Object> metadata,
Map<String, String> tags,
boolean prefixed) {
if (collectPerResourceMetrics) {
addTag(NAME, resourceID.getName(), tags, prefixed);
addTagOmittingOnEmptyValue(NAMESPACE, resourceID.getNamespace().orElse(null), tags, prefixed);
}
addTag(SCOPE, getScope(resourceID), tags, prefixed);
final var gvk = (GroupVersionKind) metadata.get(Constants.RESOURCE_GVK_KEY);
if (gvk != null) {
addGVKTags(gvk, tags, prefixed);
}
}

private static void addTag(
String name, String value, Map<String, String> tags, boolean prefixed) {
tags.put(getPrefixedMetadataTag(name, prefixed), value);
}

private static void addGVKTags(GroupVersionKind gvk, Map<String, String> tags, boolean prefixed) {
addTagOmittingOnEmptyValue(GROUP, gvk.getGroup(), tags, prefixed);
addTag(VERSION, gvk.getVersion(), tags, prefixed);
addTag(KIND, gvk.getKind(), tags, prefixed);
}

private static String getPrefixedMetadataTag(String tagName, boolean prefixed) {
return prefixed ? METADATA_PREFIX + tagName : tagName;
}

private static void addTagOmittingOnEmptyValue(
String name, String value, Map<String, String> tags, boolean prefixed) {
if (value != null && !value.isBlank()) {
addTag(name, value, tags, prefixed);
}
}

private static String getScope(ResourceID resourceID) {
return resourceID.getNamespace().isPresent() ? NAMESPACE : CLUSTER;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.regex.Pattern;

import io.fabric8.kubernetes.api.builder.Builder;
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.utils.Serialization;
Expand Down Expand Up @@ -73,36 +70,6 @@ public static String getNameFor(Class<? extends Reconciler> reconcilerClass) {
return getDefaultNameFor(reconcilerClass);
}

public static void checkIfCanAddOwnerReference(HasMetadata owner, HasMetadata resource) {
if (owner instanceof GenericKubernetesResource
|| resource instanceof GenericKubernetesResource) {
return;
}
if (owner instanceof Namespaced) {
if (!(resource instanceof Namespaced)) {
throw new OperatorException(
"Cannot add owner reference from a cluster scoped to a namespace scoped resource."
+ resourcesIdentifierDescription(owner, resource));
} else if (!Objects.equals(
owner.getMetadata().getNamespace(), resource.getMetadata().getNamespace())) {
throw new OperatorException(
"Cannot add owner reference between two resource in different namespaces."
+ resourcesIdentifierDescription(owner, resource));
}
}
}

private static String resourcesIdentifierDescription(HasMetadata owner, HasMetadata resource) {
return " Owner name: "
+ owner.getMetadata().getName()
+ " Kind: "
+ owner.getKind()
+ ", Resource name: "
+ resource.getMetadata().getName()
+ " Kind: "
+ resource.getKind();
}

public static String getNameFor(Reconciler reconciler) {
return getNameFor(reconciler.getClass());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.javaoperatorsdk.operator.api.config.informer;

public @interface Field {

String path();

String value();

boolean negated() default false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.javaoperatorsdk.operator.api.config.informer;

import java.util.Arrays;
import java.util.List;

public class FieldSelector {
private final List<Field> fields;

public FieldSelector(List<Field> fields) {
this.fields = fields;
}

public FieldSelector(Field... fields) {
this.fields = Arrays.asList(fields);
}

public List<Field> getFields() {
return fields;
}

public record Field(String path, String value, boolean negated) {
public Field(String path, String value) {
this(path, value, false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.javaoperatorsdk.operator.api.config.informer;

import java.util.ArrayList;
import java.util.List;

public class FieldSelectorBuilder {

private final List<FieldSelector.Field> fields = new ArrayList<>();

public FieldSelectorBuilder withField(String path, String value) {
fields.add(new FieldSelector.Field(path, value));
return this;
}

public FieldSelectorBuilder withoutField(String path, String value) {
fields.add(new FieldSelector.Field(path, value, true));
return this;
}

public FieldSelector build() {
return new FieldSelector(fields);
}
}
Loading