Skip to content

Commit 1635c9e

Browse files
authored
feat: e2e test to demonstrate namespace deletion problem fix (#2528)
Signed-off-by: Attila Mészáros <[email protected]>
1 parent 8039ef0 commit 1635c9e

File tree

16 files changed

+480
-20
lines changed

16 files changed

+480
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
This sample demonstrates the workaround for problem when a namespace
2+
is being deleted with a running controller, that watches resources
3+
in its own namespace. If the pod or other underlying resources (role,
4+
role binding, service account) are deleted before the cleanup of
5+
the custom resource the namespace deletion is stuck.
6+
7+
see also: https://github.com/operator-framework/java-operator-sdk/pull/2528
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
apiVersion: v1
2+
kind: ServiceAccount
3+
metadata:
4+
name: operator
5+
finalizers:
6+
- controller.deletion/finalizer
7+
8+
---
9+
apiVersion: v1
10+
kind: Pod
11+
metadata:
12+
name: operator
13+
spec:
14+
serviceAccountName: operator
15+
containers:
16+
- name: operator
17+
image: controller-namespace-deletion-operator
18+
imagePullPolicy: Never
19+
env:
20+
- name: POD_NAMESPACE
21+
valueFrom:
22+
fieldRef:
23+
fieldPath: metadata.namespace
24+
terminationGracePeriodSeconds: 30
25+
26+
---
27+
apiVersion: rbac.authorization.k8s.io/v1
28+
kind: RoleBinding
29+
metadata:
30+
name: operator
31+
finalizers:
32+
- controller.deletion/finalizer
33+
subjects:
34+
- kind: ServiceAccount
35+
name: operator
36+
roleRef:
37+
kind: Role
38+
name: operator
39+
apiGroup: rbac.authorization.k8s.io
40+
41+
---
42+
apiVersion: rbac.authorization.k8s.io/v1
43+
kind: Role
44+
metadata:
45+
name: operator
46+
finalizers:
47+
- controller.deletion/finalizer
48+
rules:
49+
- apiGroups:
50+
- "apiextensions.k8s.io"
51+
resources:
52+
- customresourcedefinitions
53+
verbs:
54+
- '*'
55+
- apiGroups:
56+
- "namespacedeletion.io"
57+
resources:
58+
- controllernamespacedeletioncustomresources
59+
- controllernamespacedeletioncustomresources/status
60+
verbs:
61+
- '*'
62+
+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<groupId>io.javaoperatorsdk</groupId>
7+
<artifactId>sample-operators</artifactId>
8+
<version>5.0.0-SNAPSHOT</version>
9+
</parent>
10+
11+
<artifactId>sample-controller-namespace-deletion</artifactId>
12+
<packaging>jar</packaging>
13+
<name>Operator SDK - Samples - Controller Namespace Deletion</name>
14+
<description>Deleting namespace with controller and custom resources</description>
15+
16+
<dependencyManagement>
17+
<dependencies>
18+
<dependency>
19+
<groupId>io.javaoperatorsdk</groupId>
20+
<artifactId>operator-framework-bom</artifactId>
21+
<version>${project.version}</version>
22+
<type>pom</type>
23+
<scope>import</scope>
24+
</dependency>
25+
</dependencies>
26+
</dependencyManagement>
27+
28+
<dependencies>
29+
<dependency>
30+
<groupId>io.javaoperatorsdk</groupId>
31+
<artifactId>operator-framework</artifactId>
32+
</dependency>
33+
<dependency>
34+
<groupId>io.fabric8</groupId>
35+
<artifactId>crd-generator-apt</artifactId>
36+
<scope>provided</scope>
37+
</dependency>
38+
<dependency>
39+
<groupId>org.apache.logging.log4j</groupId>
40+
<artifactId>log4j-slf4j2-impl</artifactId>
41+
<scope>compile</scope>
42+
</dependency>
43+
<dependency>
44+
<groupId>org.apache.logging.log4j</groupId>
45+
<artifactId>log4j-core</artifactId>
46+
<scope>compile</scope>
47+
</dependency>
48+
<dependency>
49+
<groupId>org.takes</groupId>
50+
<artifactId>takes</artifactId>
51+
<version>1.24.4</version>
52+
</dependency>
53+
<dependency>
54+
<groupId>org.awaitility</groupId>
55+
<artifactId>awaitility</artifactId>
56+
<scope>compile</scope>
57+
</dependency>
58+
<dependency>
59+
<groupId>io.javaoperatorsdk</groupId>
60+
<artifactId>operator-framework-junit-5</artifactId>
61+
<scope>test</scope>
62+
</dependency>
63+
<dependency>
64+
<groupId>org.junit.jupiter</groupId>
65+
<artifactId>junit-jupiter-params</artifactId>
66+
<scope>test</scope>
67+
</dependency>
68+
</dependencies>
69+
<build>
70+
<plugins>
71+
<plugin>
72+
<groupId>com.google.cloud.tools</groupId>
73+
<artifactId>jib-maven-plugin</artifactId>
74+
<version>${jib-maven-plugin.version}</version>
75+
<configuration>
76+
<from>
77+
<image>gcr.io/distroless/java17-debian11</image>
78+
</from>
79+
<to>
80+
<image>controller-namespace-deletion-operator</image>
81+
</to>
82+
</configuration>
83+
</plugin>
84+
</plugins>
85+
</build>
86+
87+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.javaoperatorsdk.operator.sample;
2+
3+
import io.fabric8.kubernetes.api.model.Namespaced;
4+
import io.fabric8.kubernetes.client.CustomResource;
5+
import io.fabric8.kubernetes.model.annotation.Group;
6+
import io.fabric8.kubernetes.model.annotation.Version;
7+
8+
@Group("namespacedeletion.io")
9+
@Version("v1")
10+
public class ControllerNamespaceDeletionCustomResource
11+
extends CustomResource<ControllerNamespaceDeletionSpec, ControllerNamespaceDeletionStatus>
12+
implements Namespaced {
13+
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package io.javaoperatorsdk.operator.sample;
2+
3+
import java.time.LocalTime;
4+
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
8+
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
9+
import io.javaoperatorsdk.operator.Operator;
10+
import io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider;
11+
12+
import static java.time.temporal.ChronoUnit.SECONDS;
13+
14+
public class ControllerNamespaceDeletionOperator {
15+
16+
private static final Logger log =
17+
LoggerFactory.getLogger(ControllerNamespaceDeletionOperator.class);
18+
19+
public static void main(String[] args) {
20+
21+
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
22+
log.info("Shutting down...");
23+
boolean allResourcesDeleted = waitUntilResourcesDeleted();
24+
log.info("All resources within timeout: {}", allResourcesDeleted);
25+
}));
26+
27+
Operator operator = new Operator();
28+
operator.register(new ControllerNamespaceDeletionReconciler(),
29+
ControllerConfigurationOverrider::watchingOnlyCurrentNamespace);
30+
operator.start();
31+
}
32+
33+
private static boolean waitUntilResourcesDeleted() {
34+
try (var client = new KubernetesClientBuilder().build()) {
35+
var startTime = LocalTime.now();
36+
while (startTime.until(LocalTime.now(), SECONDS) < 20) {
37+
var items =
38+
client.resources(ControllerNamespaceDeletionCustomResource.class)
39+
.inNamespace(client.getConfiguration().getNamespace())
40+
.list().getItems();
41+
log.info("Custom resource in namespace: {}", items);
42+
if (items.isEmpty()) {
43+
return true;
44+
}
45+
}
46+
return false;
47+
}
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package io.javaoperatorsdk.operator.sample;
2+
3+
import java.time.Duration;
4+
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
8+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
9+
import io.javaoperatorsdk.operator.api.reconciler.Cleaner;
10+
import io.javaoperatorsdk.operator.api.reconciler.Context;
11+
import io.javaoperatorsdk.operator.api.reconciler.DeleteControl;
12+
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
13+
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
14+
15+
public class ControllerNamespaceDeletionReconciler
16+
implements Reconciler<ControllerNamespaceDeletionCustomResource>,
17+
Cleaner<ControllerNamespaceDeletionCustomResource> {
18+
19+
private static final Logger log =
20+
LoggerFactory.getLogger(ControllerNamespaceDeletionReconciler.class);
21+
22+
public static final Duration CLEANUP_DELAY = Duration.ofSeconds(10);
23+
24+
@Override
25+
public UpdateControl<ControllerNamespaceDeletionCustomResource> reconcile(
26+
ControllerNamespaceDeletionCustomResource resource,
27+
Context<ControllerNamespaceDeletionCustomResource> context) {
28+
log.info("Reconciling: {} in namespace: {}", resource.getMetadata().getName(),
29+
resource.getMetadata().getNamespace());
30+
31+
var response = createResponseResource(resource);
32+
response.getStatus().setValue(resource.getSpec().getValue());
33+
34+
return UpdateControl.patchStatus(response);
35+
}
36+
37+
private ControllerNamespaceDeletionCustomResource createResponseResource(
38+
ControllerNamespaceDeletionCustomResource resource) {
39+
var res = new ControllerNamespaceDeletionCustomResource();
40+
res.setMetadata(new ObjectMetaBuilder()
41+
.withName(resource.getMetadata().getName())
42+
.withNamespace(resource.getMetadata().getNamespace())
43+
.build());
44+
res.setStatus(new ControllerNamespaceDeletionStatus());
45+
return res;
46+
}
47+
48+
@Override
49+
public DeleteControl cleanup(ControllerNamespaceDeletionCustomResource resource,
50+
Context<ControllerNamespaceDeletionCustomResource> context) {
51+
log.info("Cleaning up resource");
52+
try {
53+
Thread.sleep(CLEANUP_DELAY.toMillis());
54+
return DeleteControl.defaultDelete();
55+
} catch (InterruptedException e) {
56+
throw new RuntimeException(e);
57+
}
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.javaoperatorsdk.operator.sample;
2+
3+
4+
public class ControllerNamespaceDeletionSpec {
5+
6+
private String value;
7+
8+
public String getValue() {
9+
return value;
10+
}
11+
12+
public void setValue(String value) {
13+
this.value = value;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.javaoperatorsdk.operator.sample;
2+
3+
4+
public class ControllerNamespaceDeletionStatus {
5+
6+
private String value;
7+
8+
public String getValue() {
9+
return value;
10+
}
11+
12+
public void setValue(String value) {
13+
this.value = value;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Configuration status="WARN">
3+
<Appenders>
4+
<Console name="Console" target="SYSTEM_OUT">
5+
<PatternLayout pattern="%d %-30c{1.} [%-5level] %msg%n%throwable"/>
6+
</Console>
7+
</Appenders>
8+
<Loggers>
9+
<Root level="debug">
10+
<AppenderRef ref="Console"/>
11+
</Root>
12+
</Loggers>
13+
</Configuration>

0 commit comments

Comments
 (0)