Skip to content

Commit 042842d

Browse files
committed
test(operator): stream operator pod logs in remote test mode
1 parent 7211b67 commit 042842d

File tree

2 files changed

+103
-3
lines changed

2 files changed

+103
-3
lines changed

operator/controller/src/test/java/io/apicurio/registry/operator/it/ITBase.java

+24-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.apicurio.registry.operator.api.v1.ApicurioRegistry3;
55
import io.fabric8.kubernetes.api.model.HasMetadata;
66
import io.fabric8.kubernetes.api.model.NamespaceBuilder;
7+
import io.fabric8.kubernetes.api.model.Pod;
78
import io.fabric8.kubernetes.api.model.apps.Deployment;
89
import io.fabric8.kubernetes.client.Config;
910
import io.fabric8.kubernetes.client.ConfigBuilder;
@@ -12,6 +13,7 @@
1213
import io.fabric8.kubernetes.client.utils.Serialization;
1314
import io.javaoperatorsdk.operator.Operator;
1415
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
16+
import io.javaoperatorsdk.operator.processing.event.ResourceID;
1517
import io.quarkiverse.operatorsdk.runtime.QuarkusConfigurationService;
1618
import jakarta.enterprise.inject.Instance;
1719
import jakarta.enterprise.inject.spi.CDI;
@@ -22,12 +24,13 @@
2224
import org.slf4j.Logger;
2325
import org.slf4j.LoggerFactory;
2426

25-
import java.io.FileInputStream;
26-
import java.io.IOException;
27+
import java.io.*;
2728
import java.nio.file.Files;
2829
import java.nio.file.Path;
2930
import java.time.Duration;
31+
import java.util.ArrayList;
3032
import java.util.List;
33+
import java.util.Map;
3134
import java.util.UUID;
3235

3336
import static org.assertj.core.api.Assertions.assertThat;
@@ -54,6 +57,7 @@ public enum OperatorDeployment {
5457
protected static Instance<Reconciler<? extends HasMetadata>> reconcilers;
5558
protected static QuarkusConfigurationService configuration;
5659
protected static KubernetesClient client;
60+
protected static PodLogManager podLogManager;
5761
protected static PortForwardManager portForwardManager;
5862
protected static IngressManager ingressManager;
5963
protected static String deploymentTarget;
@@ -79,9 +83,11 @@ public static void before() throws Exception {
7983

8084
portForwardManager = new PortForwardManager(client, namespace);
8185
ingressManager = new IngressManager(client, namespace);
86+
podLogManager = new PodLogManager(client);
8287

8388
if (operatorDeployment == OperatorDeployment.remote) {
8489
createTestResources();
90+
startOperatorLogs();
8591
} else {
8692
createOperator();
8793
registerReconcilers();
@@ -176,6 +182,21 @@ private static void createTestResources() throws Exception {
176182
});
177183
}
178184

185+
private static void startOperatorLogs() {
186+
List<Pod> operatorPods = new ArrayList<>();
187+
await().ignoreExceptions().untilAsserted(() -> {
188+
operatorPods.clear();
189+
operatorPods.addAll(client.pods()
190+
.withLabels(Map.of(
191+
"app.kubernetes.io/name", "apicurio-registry-operator",
192+
"app.kubernetes.io/component", "operator",
193+
"app.kubernetes.io/part-of", "apicurio-registry"))
194+
.list().getItems());
195+
assertThat(operatorPods).hasSize(1);
196+
});
197+
podLogManager.startPodLog(ResourceID.fromResource(operatorPods.get(0)));
198+
}
199+
179200
private static void cleanTestResources() throws Exception {
180201
if (cleanup) {
181202
log.info("Deleting generated resources from Namespace {}", namespace);
@@ -258,7 +279,7 @@ public static void after() throws Exception {
258279
} else {
259280
cleanTestResources();
260281
}
261-
282+
podLogManager.stopAndWait();
262283
if (cleanup) {
263284
log.info("Deleting namespace : {}", namespace);
264285
assertThat(client.namespaces().withName(namespace).delete()).isNotNull();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package io.apicurio.registry.operator.it;
2+
3+
import io.fabric8.kubernetes.client.KubernetesClient;
4+
import io.fabric8.kubernetes.client.dsl.LogWatch;
5+
import io.javaoperatorsdk.operator.processing.event.ResourceID;
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
9+
import java.io.BufferedReader;
10+
import java.io.InputStreamReader;
11+
import java.time.Duration;
12+
import java.time.Instant;
13+
import java.util.Map;
14+
import java.util.concurrent.ConcurrentHashMap;
15+
import java.util.concurrent.atomic.AtomicBoolean;
16+
17+
import static java.util.concurrent.TimeUnit.SECONDS;
18+
import static org.awaitility.Awaitility.await;
19+
20+
public class PodLogManager {
21+
22+
private static final Logger log = LoggerFactory.getLogger(PodLogManager.class);
23+
24+
private final KubernetesClient k8sClient;
25+
26+
private final Map<ResourceID, AtomicBoolean> activePodLogMap = new ConcurrentHashMap<>();
27+
28+
public PodLogManager(KubernetesClient k8sClient) {
29+
this.k8sClient = k8sClient;
30+
}
31+
32+
private String getNamespace(ResourceID id) {
33+
return id.getNamespace().orElse("default");
34+
}
35+
36+
public void startPodLog(ResourceID podID) {
37+
k8sClient.pods().inNamespace(getNamespace(podID)).withName(podID.getName()).waitUntilReady(60,
38+
SECONDS);
39+
new Thread(() -> {
40+
StringBuilder chunk = new StringBuilder();
41+
try (
42+
LogWatch logWatch = k8sClient.pods().inNamespace(getNamespace(podID)).withName(podID.getName()).watchLog();
43+
BufferedReader reader = new BufferedReader(new InputStreamReader(logWatch.getOutput()))
44+
) {
45+
AtomicBoolean stop = new AtomicBoolean(false);
46+
log.debug("START LOG of pod {}/{}", getNamespace(podID), podID.getName());
47+
activePodLogMap.put(podID, stop);
48+
var lastWriteAt = Instant.now();
49+
while (!stop.get()) {
50+
var line = reader.readLine();
51+
if (line != null) {
52+
chunk.append(getNamespace(podID)).append("/").append(podID.getName()).append(" >>> ")
53+
.append(line).append("\n");
54+
if (lastWriteAt.plus(Duration.ofSeconds(5)).isBefore(Instant.now())) {
55+
log.debug("LOG of pod {}/{}:\n{}", getNamespace(podID), podID.getName(), chunk);
56+
chunk.setLength(0);
57+
lastWriteAt = Instant.now();
58+
}
59+
} else {
60+
stop.set(true);
61+
}
62+
}
63+
} catch (Exception ex) {
64+
log.error("Error while reading logs of pod {}/{}", getNamespace(podID), podID.getName(), ex);
65+
} finally {
66+
if (chunk.length() > 0) {
67+
log.debug("LOG of pod {}/{}:\n{}", getNamespace(podID), podID.getName(), chunk);
68+
}
69+
log.debug("END LOG of pod {}/{}", getNamespace(podID), podID.getName());
70+
activePodLogMap.remove(podID);
71+
}
72+
}).start();
73+
}
74+
75+
public void stopAndWait() {
76+
activePodLogMap.values().forEach(stop -> stop.set(true));
77+
await().until(activePodLogMap::isEmpty);
78+
}
79+
}

0 commit comments

Comments
 (0)