Skip to content

Commit b9e0e9a

Browse files
committed
feat!: adapt monitoring code to new implementation
- record: * received events * deleted events * reconciliation attempts with retry information * reconciliation successes * reconciliation failure with exception information - move metrics-related code to monitoring package - cleaned-up obsolete/deprecated code
1 parent 892880b commit b9e0e9a

File tree

17 files changed

+218
-239
lines changed

17 files changed

+218
-239
lines changed

micrometer-support/src/main/java/io/javaoperatorsdk/operator/micrometer/MicrometerMetrics.java

Lines changed: 0 additions & 84 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package io.javaoperatorsdk.operator.monitoring.micrometer;
2+
3+
import java.util.Collections;
4+
import java.util.LinkedList;
5+
import java.util.List;
6+
import java.util.Map;
7+
8+
import io.javaoperatorsdk.operator.api.RetryInfo;
9+
import io.javaoperatorsdk.operator.api.monitoring.Metrics;
10+
import io.javaoperatorsdk.operator.processing.event.CustomResourceID;
11+
import io.javaoperatorsdk.operator.processing.event.Event;
12+
import io.micrometer.core.instrument.MeterRegistry;
13+
import io.micrometer.core.instrument.Timer;
14+
15+
public class MicrometerMetrics implements Metrics {
16+
17+
private static final String PREFIX = "operator.sdk.";
18+
private static final String RECONCILIATIONS = "reconciliations.";
19+
private final MeterRegistry registry;
20+
21+
public MicrometerMetrics(MeterRegistry registry) {
22+
this.registry = registry;
23+
}
24+
25+
public <T> T timeControllerExecution(ControllerExecution<T> execution) {
26+
final var name = execution.controllerName();
27+
final var execName = PREFIX + "controllers.execution." + execution.name();
28+
final var timer =
29+
Timer.builder(execName)
30+
.tags("controller", name)
31+
.publishPercentiles(0.3, 0.5, 0.95)
32+
.publishPercentileHistogram()
33+
.register(registry);
34+
try {
35+
final var result = timer.record(execution::execute);
36+
final var successType = execution.successTypeName(result);
37+
registry
38+
.counter(execName + ".success", "controller", name, "type", successType)
39+
.increment();
40+
return result;
41+
} catch (Exception e) {
42+
final var exception = e.getClass().getSimpleName();
43+
registry
44+
.counter(execName + ".failure", "controller", name, "exception", exception)
45+
.increment();
46+
throw e;
47+
}
48+
}
49+
50+
public void receivedEvent(Event event) {
51+
incrementCounter(event.getRelatedCustomResourceID(), "events.received", "event",
52+
event.getClass().getSimpleName());
53+
}
54+
55+
@Override
56+
public void cleanupDoneFor(CustomResourceID customResourceUid) {
57+
incrementCounter(customResourceUid, "events.delete");
58+
}
59+
60+
public void reconcileCustomResource(CustomResourceID customResourceID,
61+
RetryInfo retryInfo) {
62+
incrementCounter(customResourceID, RECONCILIATIONS + "started",
63+
RECONCILIATIONS + "retries.number", "" + retryInfo.getAttemptCount(),
64+
RECONCILIATIONS + "retries.last", "" + retryInfo.isLastAttempt());
65+
}
66+
67+
@Override
68+
public void finishedReconciliation(CustomResourceID customResourceID) {
69+
incrementCounter(customResourceID, RECONCILIATIONS + "success");
70+
}
71+
72+
public void failedReconciliation(CustomResourceID customResourceID, RuntimeException exception) {
73+
var cause = exception.getCause();
74+
if (cause == null) {
75+
cause = exception;
76+
} else if (cause instanceof RuntimeException) {
77+
cause = cause.getCause() != null ? cause.getCause() : cause;
78+
}
79+
incrementCounter(customResourceID, RECONCILIATIONS + "failed", "exception",
80+
cause.getClass().getSimpleName());
81+
}
82+
83+
public <T extends Map<?, ?>> T monitorSizeOf(T map, String name) {
84+
return registry.gaugeMapSize(PREFIX + name + ".size", Collections.emptyList(), map);
85+
}
86+
87+
private void incrementCounter(CustomResourceID id, String counterName, String... additionalTags) {
88+
var tags = List.of(
89+
"name", id.getName(),
90+
"name", id.getName(), "namespace", id.getNamespace().orElse(""),
91+
"scope", id.getNamespace().isPresent() ? "namespace" : "cluster");
92+
if (additionalTags != null && additionalTags.length > 0) {
93+
tags = new LinkedList<>(tags);
94+
tags.addAll(List.of(additionalTags));
95+
}
96+
registry.counter(PREFIX + counterName, tags.toArray(new String[0])).increment();
97+
}
98+
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/EventListUtils.java

Lines changed: 0 additions & 22 deletions
This file was deleted.

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Metrics.java

Lines changed: 0 additions & 37 deletions
This file was deleted.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.javaoperatorsdk.operator.api;
2+
3+
import io.fabric8.kubernetes.client.CustomResource;
4+
import io.javaoperatorsdk.operator.processing.event.EventSourceManager;
5+
6+
public interface EventSourceInitializer<T extends CustomResource<?, ?>> {
7+
8+
/**
9+
* In this typically you might want to register event sources. But can access
10+
* CustomResourceEventSource, what might be handy for some edge cases.
11+
*
12+
* @param eventSourceManager the {@link EventSourceManager} where event sources can be registered.
13+
*/
14+
void prepareEventSources(EventSourceManager<T> eventSourceManager);
15+
16+
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/ResourceController.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.javaoperatorsdk.operator.api;
22

33
import io.fabric8.kubernetes.client.CustomResource;
4-
import io.javaoperatorsdk.operator.processing.event.EventSourceManager;
54

65
public interface ResourceController<R extends CustomResource> {
76

@@ -49,11 +48,4 @@ default DeleteControl deleteResource(R resource, Context<R> context) {
4948
*/
5049
UpdateControl<R> createOrUpdateResource(R resource, Context<R> context);
5150

52-
/**
53-
* In init typically you might want to register event sources.
54-
*
55-
* @param eventSourceManager the {@link EventSourceManager} which handles this controller and with
56-
* which event sources can be registered
57-
*/
58-
default void init(EventSourceManager eventSourceManager) {}
5951
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
import io.fabric8.kubernetes.client.Config;
88
import io.fabric8.kubernetes.client.CustomResource;
9-
import io.javaoperatorsdk.operator.Metrics;
109
import io.javaoperatorsdk.operator.api.ResourceController;
10+
import io.javaoperatorsdk.operator.api.monitoring.Metrics;
1111

1212
import com.fasterxml.jackson.core.JsonProcessingException;
1313
import com.fasterxml.jackson.databind.ObjectMapper;

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
import io.fabric8.kubernetes.client.Config;
66
import io.fabric8.kubernetes.client.CustomResource;
7-
import io.javaoperatorsdk.operator.Metrics;
87
import io.javaoperatorsdk.operator.api.ResourceController;
8+
import io.javaoperatorsdk.operator.api.monitoring.Metrics;
99

1010
public class ConfigurationServiceOverrider {
1111
private final ConfigurationService original;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.javaoperatorsdk.operator.api.monitoring;
2+
3+
import java.util.Map;
4+
5+
import io.javaoperatorsdk.operator.api.RetryInfo;
6+
import io.javaoperatorsdk.operator.processing.event.CustomResourceID;
7+
import io.javaoperatorsdk.operator.processing.event.Event;
8+
9+
public interface Metrics {
10+
Metrics NOOP = new Metrics() {};
11+
12+
default void receivedEvent(Event event) {}
13+
14+
default void reconcileCustomResource(CustomResourceID customResourceID,
15+
RetryInfo retryInfo) {}
16+
17+
default void failedReconciliation(CustomResourceID customResourceID,
18+
RuntimeException exception) {}
19+
20+
default void cleanupDoneFor(CustomResourceID customResourceUid) {};
21+
22+
default void finishedReconciliation(CustomResourceID resourceID) {};
23+
24+
25+
interface ControllerExecution<T> {
26+
String name();
27+
28+
String controllerName();
29+
30+
String successTypeName(T result);
31+
32+
T execute();
33+
}
34+
35+
default <T> T timeControllerExecution(ControllerExecution<T> execution) {
36+
return execution.execute();
37+
}
38+
39+
default <T extends Map<?, ?>> T monitorSizeOf(T map, String name) {
40+
return map;
41+
}
42+
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ConfiguredController.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,20 @@
1111
import io.fabric8.kubernetes.client.dsl.MixedOperation;
1212
import io.fabric8.kubernetes.client.dsl.Resource;
1313
import io.javaoperatorsdk.operator.CustomResourceUtils;
14-
import io.javaoperatorsdk.operator.Metrics.ControllerExecution;
1514
import io.javaoperatorsdk.operator.MissingCRDException;
1615
import io.javaoperatorsdk.operator.OperatorException;
17-
import io.javaoperatorsdk.operator.api.Context;
18-
import io.javaoperatorsdk.operator.api.DeleteControl;
19-
import io.javaoperatorsdk.operator.api.ResourceController;
20-
import io.javaoperatorsdk.operator.api.UpdateControl;
16+
import io.javaoperatorsdk.operator.api.*;
2117
import io.javaoperatorsdk.operator.api.config.ControllerConfiguration;
18+
import io.javaoperatorsdk.operator.api.monitoring.Metrics.ControllerExecution;
2219
import io.javaoperatorsdk.operator.processing.event.DefaultEventSourceManager;
2320
import io.javaoperatorsdk.operator.processing.event.EventSourceManager;
2421

2522
public class ConfiguredController<R extends CustomResource<?, ?>> implements ResourceController<R>,
26-
Closeable {
23+
Closeable, EventSourceInitializer {
2724
private final ResourceController<R> controller;
2825
private final ControllerConfiguration<R> configuration;
2926
private final KubernetesClient kubernetesClient;
30-
private EventSourceManager eventSourceManager;
27+
private DefaultEventSourceManager eventSourceManager;
3128

3229
public ConfiguredController(ResourceController<R> controller,
3330
ControllerConfiguration<R> configuration,
@@ -97,7 +94,7 @@ public UpdateControl<R> execute() {
9794
}
9895

9996
@Override
100-
public void init(EventSourceManager eventSourceManager) {
97+
public void prepareEventSources(EventSourceManager eventSourceManager) {
10198
throw new UnsupportedOperationException("This method should never be called directly");
10299
}
103100

@@ -169,7 +166,9 @@ public void start() throws OperatorException {
169166

170167
try {
171168
eventSourceManager = new DefaultEventSourceManager<>(this);
172-
controller.init(eventSourceManager);
169+
if (controller instanceof EventSourceInitializer) {
170+
((EventSourceInitializer) controller).prepareEventSources(eventSourceManager);
171+
}
173172
} catch (MissingCRDException e) {
174173
throwMissingCRDException(crdName, specVersion, controllerName);
175174
}

0 commit comments

Comments
 (0)