Skip to content

Commit cc6c37f

Browse files
laxmikantbpandharemetacosm
authored andcommitted
feature: Micrometer Integration for generating metrics - #64
feature: micrometer integration for genering metrics feature: modified variables and made it private feature: added changes for customize increment method feature: removed ConterDetails class and modified IncrementCounter accordingly feature: modified pom depedency feature: modified pom depedency feature: added changes and wrapped counter and timer with createAndUpdate method feature: modified and added inceremenmt() method feature: modified for enabling google formatter removed spaces removed spaces feature: added counter and timer for delete resource feature: added counter and timer for delete resource feature: added changes for delete resource feature: added changes for delete resource feature: update control modified feature: delete control modified feature: delete control modified test delete resource feature: modiofied to retest feature: modified according to PR review comments feature: added count for each and event (create, update, and delete) feature: added count for each and event (create, update, and delete) feature: added Histogram changes for Time feature: added histogram changes for delete as well feature: modified name field in tags feature: retry counter added
1 parent f5b30da commit cc6c37f

File tree

8 files changed

+233
-6
lines changed

8 files changed

+233
-6
lines changed

operator-framework-core/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
<description>Core framework for implementing Kubernetes operators</description>
1616
<packaging>jar</packaging>
1717

18+
<properties>
19+
<java.version>11</java.version>
20+
<maven.compiler.source>11</maven.compiler.source>
21+
<maven.compiler.target>11</maven.compiler.target>
22+
<micrometer-core.version>1.7.3</micrometer-core.version>
23+
</properties>
24+
1825
<build>
1926
<plugins>
2027
<plugin>
@@ -92,5 +99,10 @@
9299
<artifactId>log4j-core</artifactId>
93100
<scope>test</scope>
94101
</dependency>
102+
<dependency>
103+
<groupId>io.micrometer</groupId>
104+
<artifactId>micrometer-core</artifactId>
105+
<version>${micrometer-core.version}</version>
106+
</dependency>
95107
</dependencies>
96108
</project>
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package io.javaoperatorsdk.operator;
2+
3+
import io.fabric8.kubernetes.client.CustomResource;
4+
import io.javaoperatorsdk.operator.api.Context;
5+
import io.javaoperatorsdk.operator.api.DeleteControl;
6+
import io.javaoperatorsdk.operator.api.ResourceController;
7+
import io.javaoperatorsdk.operator.api.UpdateControl;
8+
import io.javaoperatorsdk.operator.api.config.ControllerConfiguration;
9+
import io.micrometer.core.instrument.*;
10+
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
11+
import io.micrometer.core.instrument.distribution.pause.PauseDetector;
12+
import io.micrometer.core.instrument.noop.*;
13+
import java.util.concurrent.TimeUnit;
14+
import java.util.function.ToDoubleFunction;
15+
import java.util.function.ToLongFunction;
16+
17+
public class Metrics {
18+
public static final Metrics NOOP = new Metrics(new NoopMeterRegistry(Clock.SYSTEM));
19+
private final MeterRegistry registry;
20+
21+
public Metrics(MeterRegistry registry) {
22+
this.registry = registry;
23+
}
24+
25+
public <R extends CustomResource> UpdateControl<R> timeControllerCreateOrUpdate(
26+
ResourceController<R> controller,
27+
ControllerConfiguration<R> configuration,
28+
R resource,
29+
Context<R> context) {
30+
final var name = configuration.getName();
31+
final var timer =
32+
Timer.builder("operator.sdk.controllers.execution.createorupdate")
33+
.tags("controller", name)
34+
.publishPercentiles(0.3, 0.5, 0.95)
35+
.publishPercentileHistogram()
36+
.register(registry);
37+
try {
38+
final var result = timer.record(() -> controller.createOrUpdateResource(resource, context));
39+
String successType = "cr";
40+
if (result.isUpdateStatusSubResource()) {
41+
successType = "status";
42+
}
43+
if (result.isUpdateCustomResourceAndStatusSubResource()) {
44+
successType = "both";
45+
}
46+
registry
47+
.counter(
48+
"operator.sdk.controllers.execution.success", "controller", name, "type", successType)
49+
.increment();
50+
registry
51+
.counter("operator.sdk.events.received", "events", name, "type", "totalEvents")
52+
.increment();
53+
return result;
54+
} catch (Exception e) {
55+
registry
56+
.counter(
57+
"operator.sdk.controllers.execution.failure",
58+
"controller",
59+
name,
60+
"exception",
61+
e.getClass().getSimpleName())
62+
.increment();
63+
registry
64+
.counter("operator.sdk.events.received", "events", name, "exception", "totalEvents")
65+
.increment();
66+
throw e;
67+
}
68+
}
69+
70+
public DeleteControl timeControllerDelete(
71+
ResourceController controller,
72+
ControllerConfiguration configuration,
73+
CustomResource resource,
74+
Context context) {
75+
final var name = configuration.getName();
76+
final var timer =
77+
Timer.builder("operator.sdk.controllers.execution.delete")
78+
.tags("controller", name)
79+
.publishPercentiles(0.3, 0.5, 0.95)
80+
.publishPercentileHistogram()
81+
.register(registry);
82+
try {
83+
final var result = timer.record(() -> controller.deleteResource(resource, context));
84+
String successType = "notDelete";
85+
if (result == DeleteControl.DEFAULT_DELETE) {
86+
successType = "delete";
87+
}
88+
registry
89+
.counter(
90+
"operator.sdk.controllers.execution.success", "controller", name, "type", successType)
91+
.increment();
92+
registry
93+
.counter("operator.sdk.events.received", "events", name, "type", "totalEvents")
94+
.increment();
95+
return result;
96+
} catch (Exception e) {
97+
registry
98+
.counter(
99+
"operator.sdk.controllers.execution.failure",
100+
"controller",
101+
name,
102+
"exception",
103+
e.getClass().getSimpleName())
104+
.increment();
105+
registry
106+
.counter("operator.sdk.events.received", "events", name, "exception", "totalEvents")
107+
.increment();
108+
throw e;
109+
}
110+
}
111+
112+
public void timeControllerRetry() {
113+
114+
registry
115+
.counter(
116+
"operator.sdk.retry.on.exception", "retry", "retryCounter", "type",
117+
"retryException")
118+
.increment();
119+
120+
}
121+
122+
public static class NoopMeterRegistry extends MeterRegistry {
123+
public NoopMeterRegistry(Clock clock) {
124+
super(clock);
125+
}
126+
127+
@Override
128+
protected <T> Gauge newGauge(Meter.Id id, T t, ToDoubleFunction<T> toDoubleFunction) {
129+
return new NoopGauge(id);
130+
}
131+
132+
@Override
133+
protected Counter newCounter(Meter.Id id) {
134+
return new NoopCounter(id);
135+
}
136+
137+
@Override
138+
protected Timer newTimer(
139+
Meter.Id id,
140+
DistributionStatisticConfig distributionStatisticConfig,
141+
PauseDetector pauseDetector) {
142+
return new NoopTimer(id);
143+
}
144+
145+
@Override
146+
protected DistributionSummary newDistributionSummary(
147+
Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, double v) {
148+
return new NoopDistributionSummary(id);
149+
}
150+
151+
@Override
152+
protected Meter newMeter(Meter.Id id, Meter.Type type, Iterable<Measurement> iterable) {
153+
return new NoopMeter(id);
154+
}
155+
156+
@Override
157+
protected <T> FunctionTimer newFunctionTimer(
158+
Meter.Id id,
159+
T t,
160+
ToLongFunction<T> toLongFunction,
161+
ToDoubleFunction<T> toDoubleFunction,
162+
TimeUnit timeUnit) {
163+
return new NoopFunctionTimer(id);
164+
}
165+
166+
@Override
167+
protected <T> FunctionCounter newFunctionCounter(
168+
Meter.Id id, T t, ToDoubleFunction<T> toDoubleFunction) {
169+
return new NoopFunctionCounter(id);
170+
}
171+
172+
@Override
173+
protected TimeUnit getBaseTimeUnit() {
174+
return TimeUnit.SECONDS;
175+
}
176+
177+
@Override
178+
protected DistributionStatisticConfig defaultHistogramConfig() {
179+
return DistributionStatisticConfig.NONE;
180+
}
181+
}
182+
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,28 @@ public class Operator implements AutoCloseable {
2525
private final Object lock;
2626
private final List<ControllerRef> controllers;
2727
private volatile boolean started;
28+
private final Metrics metrics;
2829

29-
public Operator(KubernetesClient k8sClient, ConfigurationService configurationService) {
30+
public Operator(
31+
KubernetesClient k8sClient, ConfigurationService configurationService, Metrics metrics) {
3032
this.k8sClient = k8sClient;
3133
this.configurationService = configurationService;
3234
this.closeables = new ArrayList<>();
3335
this.lock = new Object();
3436
this.controllers = new ArrayList<>();
3537
this.started = false;
38+
this.metrics = metrics;
3639
}
3740

3841
/** Adds a shutdown hook that automatically calls {@link #close()} when the app shuts down. */
3942
public void installShutdownHook() {
4043
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
4144
}
4245

46+
public Operator(KubernetesClient k8sClient, ConfigurationService configurationService) {
47+
this(k8sClient, configurationService, Metrics.NOOP);
48+
}
49+
4350
public KubernetesClient getKubernetesClient() {
4451
return k8sClient;
4552
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import io.fabric8.kubernetes.client.Config;
55
import io.fabric8.kubernetes.client.CustomResource;
6+
import io.javaoperatorsdk.operator.Metrics;
67
import io.javaoperatorsdk.operator.api.ResourceController;
78
import java.util.Set;
89

@@ -93,4 +94,8 @@ default ObjectMapper getObjectMapper() {
9394
default int getTerminationTimeoutSeconds() {
9495
return DEFAULT_TERMINATION_TIMEOUT_SECONDS;
9596
}
97+
98+
default Metrics getMetrics() {
99+
return Metrics.NOOP;
100+
}
96101
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import io.fabric8.kubernetes.client.CustomResource;
88
import io.fabric8.kubernetes.client.dsl.MixedOperation;
9+
import io.javaoperatorsdk.operator.Metrics;
910
import io.javaoperatorsdk.operator.api.ResourceController;
1011
import io.javaoperatorsdk.operator.api.RetryInfo;
1112
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
@@ -16,6 +17,7 @@
1617
import io.javaoperatorsdk.operator.processing.retry.GenericRetry;
1718
import io.javaoperatorsdk.operator.processing.retry.Retry;
1819
import io.javaoperatorsdk.operator.processing.retry.RetryExecution;
20+
import io.micrometer.core.instrument.Clock;
1921
import java.util.HashMap;
2022
import java.util.HashSet;
2123
import java.util.Map;
@@ -46,6 +48,7 @@ public class DefaultEventHandler implements EventHandler {
4648
private final int terminationTimeout;
4749
private final ReentrantLock lock = new ReentrantLock();
4850
private DefaultEventSourceManager eventSourceManager;
51+
private final Metrics metrics = new Metrics(new Metrics.NoopMeterRegistry(Clock.SYSTEM));
4952

5053
public DefaultEventHandler(
5154
ResourceController controller, ControllerConfiguration configuration, MixedOperation client) {
@@ -162,6 +165,7 @@ void eventProcessingFinished(
162165

163166
if (retry != null && postExecutionControl.exceptionDuringExecution()) {
164167
handleRetryOnException(executionScope);
168+
metrics.timeControllerRetry();
165169
return;
166170
}
167171

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,12 @@ private PostExecutionControl handleCreateOrUpdate(
123123
getName(resource),
124124
getVersion(resource),
125125
executionScope);
126-
UpdateControl<R> updateControl = controller.createOrUpdateResource(resource, context);
126+
127+
UpdateControl<R> updateControl =
128+
configuration
129+
.getConfigurationService()
130+
.getMetrics()
131+
.timeControllerCreateOrUpdate(controller, configuration, resource, context);
127132
R updatedCustomResource = null;
128133
if (updateControl.isUpdateCustomResourceAndStatusSubResource()) {
129134
updatedCustomResource = updateCustomResource(updateControl.getCustomResource());
@@ -153,8 +158,12 @@ private PostExecutionControl handleDelete(R resource, Context<R> context) {
153158
"Executing delete for resource: {} with version: {}",
154159
getName(resource),
155160
getVersion(resource));
156-
// todo: this is be executed in a try-catch statement, in case this fails
157-
DeleteControl deleteControl = controller.deleteResource(resource, context);
161+
162+
DeleteControl deleteControl =
163+
configuration
164+
.getConfigurationService()
165+
.getMetrics()
166+
.timeControllerDelete(controller, configuration, resource, context);
158167
final var useFinalizer = configuration.useFinalizer();
159168
if (useFinalizer) {
160169
if (deleteControl == DeleteControl.DEFAULT_DELETE

operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/EventDispatcherTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515

1616
import io.fabric8.kubernetes.client.CustomResource;
1717
import io.fabric8.kubernetes.client.Watcher;
18+
import io.javaoperatorsdk.operator.Metrics;
1819
import io.javaoperatorsdk.operator.TestUtils;
1920
import io.javaoperatorsdk.operator.api.Context;
2021
import io.javaoperatorsdk.operator.api.DeleteControl;
2122
import io.javaoperatorsdk.operator.api.ResourceController;
2223
import io.javaoperatorsdk.operator.api.RetryInfo;
2324
import io.javaoperatorsdk.operator.api.UpdateControl;
25+
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
2426
import io.javaoperatorsdk.operator.api.config.ControllerConfiguration;
2527
import io.javaoperatorsdk.operator.processing.event.Event;
2628
import io.javaoperatorsdk.operator.processing.event.internal.CustomResourceEvent;
@@ -40,6 +42,7 @@ class EventDispatcherTest {
4042
private final ResourceController<CustomResource> controller = mock(ResourceController.class);
4143
private ControllerConfiguration<CustomResource> configuration =
4244
mock(ControllerConfiguration.class);
45+
private final ConfigurationService configService = mock(ConfigurationService.class);
4346
private final EventDispatcher.CustomResourceFacade customResourceFacade =
4447
mock(EventDispatcher.CustomResourceFacade.class);
4548

@@ -51,6 +54,9 @@ void setup() {
5154

5255
when(configuration.getFinalizer()).thenReturn(DEFAULT_FINALIZER);
5356
when(configuration.useFinalizer()).thenCallRealMethod();
57+
when(configuration.getName()).thenReturn("EventDispatcherTestController");
58+
when(configService.getMetrics()).thenReturn(Metrics.NOOP);
59+
when(configuration.getConfigurationService()).thenReturn(configService);
5460
when(controller.createOrUpdateResource(eq(testCustomResource), any()))
5561
.thenReturn(UpdateControl.updateCustomResource(testCustomResource));
5662
when(controller.deleteResource(eq(testCustomResource), any()))
@@ -135,7 +141,6 @@ void callsDeleteIfObjectHasFinalizerAndMarkedForDelete() {
135141
@Test
136142
void callDeleteOnControllerIfMarkedForDeletionWhenNoFinalizerIsConfigured() {
137143
configureToNotUseFinalizer();
138-
139144
markForDeletion(testCustomResource);
140145

141146
eventDispatcher.handleExecution(
@@ -156,6 +161,9 @@ void doNotCallDeleteIfMarkedForDeletionWhenFinalizerHasAlreadyBeenRemoved() {
156161

157162
private void configureToNotUseFinalizer() {
158163
ControllerConfiguration<CustomResource> configuration = mock(ControllerConfiguration.class);
164+
when(configuration.getName()).thenReturn("EventDispatcherTestController");
165+
when(configService.getMetrics()).thenReturn(Metrics.NOOP);
166+
when(configuration.getConfigurationService()).thenReturn(configService);
159167
when(configuration.useFinalizer()).thenReturn(false);
160168
eventDispatcher = new EventDispatcher(controller, configuration, customResourceFacade);
161169
}

operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public void initialize(KubernetesClient k8sClient, ResourceController controller
5959
namespaces.create(
6060
new NamespaceBuilder().withNewMetadata().withName(TEST_NAMESPACE).endMetadata().build());
6161
}
62-
operator = new Operator(k8sClient, configurationService);
62+
operator = new Operator(k8sClient, configurationService, Metrics.NOOP);
6363
final var overriddenConfig =
6464
ControllerConfigurationOverrider.override(config).settingNamespace(TEST_NAMESPACE);
6565
if (retry != null) {

0 commit comments

Comments
 (0)