Skip to content

Commit dbab033

Browse files
committed
feat: also support cleanup, provide context to dependent resources
1 parent 854283e commit dbab033

File tree

13 files changed

+110
-54
lines changed

13 files changed

+110
-54
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package io.javaoperatorsdk.operator.api.config.dependent;
22

33
import io.fabric8.kubernetes.api.model.HasMetadata;
4+
import io.javaoperatorsdk.operator.api.reconciler.Context;
45

56
@FunctionalInterface
67
public interface Builder<R, P extends HasMetadata> {
7-
R buildFor(P primary);
8+
R buildFor(P primary, Context context);
89
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package io.javaoperatorsdk.operator.api.config.dependent;
2+
3+
import io.fabric8.kubernetes.api.model.HasMetadata;
4+
import io.javaoperatorsdk.operator.api.reconciler.Context;
5+
6+
public interface Cleaner<R, P extends HasMetadata> {
7+
8+
void delete(R fetched, P primary, Context context);
9+
}

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

+19-6
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,43 @@
11
package io.javaoperatorsdk.operator.api.config.dependent;
22

33
import io.fabric8.kubernetes.api.model.HasMetadata;
4+
import io.javaoperatorsdk.operator.api.reconciler.Context;
45
import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
56
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
67

78
public abstract class DependentResource<R, P extends HasMetadata>
8-
implements Builder<R, P>, Updater<R, P>, Persister<R, P> {
9+
implements Builder<R, P>, Updater<R, P>, Persister<R, P>, Cleaner<R, P> {
910

1011
private final Builder<R, P> builder;
1112
private final Updater<R, P> updater;
13+
private final Cleaner<R, P> cleaner;
1214
private final Class<R> resourceType;
1315

14-
public DependentResource(Class<R> resourceType, Builder<R, P> builder, Updater<R, P> updater) {
16+
public DependentResource(Class<R> resourceType, Builder<R, P> builder, Updater<R, P> updater,
17+
Cleaner<R, P> cleaner) {
1518
this.builder = builder;
1619
this.updater = updater;
1720
this.resourceType = resourceType;
21+
this.cleaner = cleaner;
1822
}
1923

2024
public String descriptionFor(R resource) {
2125
return resource.toString();
2226
}
2327

2428
@Override
25-
public R buildFor(P primary) {
26-
return builder.buildFor(primary);
29+
public R buildFor(P primary, Context context) {
30+
return builder.buildFor(primary, context);
2731
}
2832

2933
@Override
30-
public R update(R fetched, P primary) {
31-
return updater.update(fetched, primary);
34+
public R update(R fetched, P primary, Context context) {
35+
return updater.update(fetched, primary, context);
36+
}
37+
38+
@Override
39+
public void delete(R fetched, P primary, Context context) {
40+
cleaner.delete(fetched, primary, context);
3241
}
3342

3443
public Class<R> getResourceType() {
@@ -44,4 +53,8 @@ public boolean creatable() {
4453
public boolean updatable() {
4554
return updater != null;
4655
}
56+
57+
public boolean deletable() {
58+
return cleaner != null;
59+
}
4760
}

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

+15-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.Set;
44

55
import io.fabric8.kubernetes.api.model.HasMetadata;
6+
import io.javaoperatorsdk.operator.api.reconciler.Context;
67
import io.javaoperatorsdk.operator.processing.event.ResourceID;
78
import io.javaoperatorsdk.operator.processing.event.source.AssociatedSecondaryResourceIdentifier;
89
import io.javaoperatorsdk.operator.processing.event.source.PrimaryResourcesRetriever;
@@ -23,6 +24,8 @@
2324

2425
Class<? extends Updater> updater() default DEFAULT_UPDATER.class;
2526

27+
Class<? extends Cleaner> cleaner() default DEFAULT_CLEANER.class;
28+
2629
Class<? extends PrimaryResourcesRetriever> associatedPrimariesRetriever() default DEFAULT_PRIMARIES_RETRIEVER.class;
2730

2831
Class<? extends AssociatedSecondaryResourceIdentifier> associatedSecondaryIdentifier() default DEFAULT_SECONDARY_IDENTIFIER.class;
@@ -47,22 +50,29 @@
4750
String labelSelector() default EMPTY_STRING;
4851

4952

50-
final class DEFAULT_BUILDER implements Builder<HasMetadata, HasMetadata> {
53+
final class DEFAULT_BUILDER implements Builder<Object, HasMetadata> {
5154

5255
@Override
53-
public HasMetadata buildFor(HasMetadata primary) {
56+
public Object buildFor(HasMetadata primary, Context context) {
5457
return null;
5558
}
5659
}
5760

58-
final class DEFAULT_UPDATER implements Updater<HasMetadata, HasMetadata> {
59-
61+
final class DEFAULT_UPDATER implements Updater<Object, HasMetadata> {
6062
@Override
61-
public HasMetadata update(HasMetadata fetched, HasMetadata primary) {
63+
public Object update(Object fetched, HasMetadata primary, Context context) {
6264
return null;
6365
}
6466
}
6567

68+
final class DEFAULT_CLEANER implements Cleaner<Object, HasMetadata> {
69+
70+
@Override
71+
public void delete(Object fetched, HasMetadata primary, Context context) {
72+
73+
}
74+
}
75+
6676

6777
final class DEFAULT_PRIMARIES_RETRIEVER implements PrimaryResourcesRetriever<Object> {
6878

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

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.fabric8.kubernetes.api.model.HasMetadata;
44
import io.fabric8.kubernetes.client.KubernetesClient;
5+
import io.javaoperatorsdk.operator.api.reconciler.Context;
56
import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
67
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
78
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerConfiguration;
@@ -16,8 +17,8 @@ public class KubernetesDependentResource<R extends HasMetadata, P extends HasMet
1617

1718

1819
public KubernetesDependentResource(Class<R> resourceType, boolean owned, Builder<R, P> builder,
19-
Updater<R, P> updater, InformerConfiguration<R, P> configuration) {
20-
super(resourceType, builder, updater);
20+
Updater<R, P> updater, Cleaner<R, P> cleaner, InformerConfiguration<R, P> configuration) {
21+
super(resourceType, builder, updater, cleaner);
2122
this.configuration = configuration;
2223
this.owned = owned;
2324
}
@@ -38,12 +39,12 @@ public EventSource initEventSource(EventSourceContext<P> context) {
3839
}
3940

4041
@Override
41-
public void createOrReplace(R dependentResource) {
42+
public void createOrReplace(R dependentResource, Context context) {
4243
client.resource(dependentResource).createOrReplace();
4344
}
4445

4546
@Override
46-
public R getFor(P primary) {
47+
public R getFor(P primary, Context context) {
4748
return informer.getAssociated(primary).orElse(null);
4849
}
4950

Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package io.javaoperatorsdk.operator.api.config.dependent;
22

33
import io.fabric8.kubernetes.api.model.HasMetadata;
4+
import io.javaoperatorsdk.operator.api.reconciler.Context;
45

56
public interface Persister<R, P extends HasMetadata> {
67

7-
void createOrReplace(R dependentResource);
8+
void createOrReplace(R dependentResource, Context context);
89

9-
R getFor(P primary);
10+
R getFor(P primary, Context context);
1011
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package io.javaoperatorsdk.operator.api.config.dependent;
22

33
import io.fabric8.kubernetes.api.model.HasMetadata;
4+
import io.javaoperatorsdk.operator.api.reconciler.Context;
45

56
@FunctionalInterface
67
public interface Updater<R, P extends HasMetadata> {
78

8-
R update(R fetched, P primary);
9+
R update(R fetched, P primary, Context context);
910
}

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package io.javaoperatorsdk.operator.api.reconciler;
22

3-
import java.util.Map;
43
import java.util.Optional;
4+
import java.util.concurrent.ConcurrentHashMap;
55

66
import io.fabric8.kubernetes.api.model.HasMetadata;
77
import io.javaoperatorsdk.operator.processing.Controller;
8-
import java.util.concurrent.ConcurrentHashMap;
98

109
public class DefaultContext<P extends HasMetadata> implements Context {
1110

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

+42-28
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public Controller(Reconciler<R> reconciler,
6262
prepareEventSources(context).forEach(eventSourceManager::registerEventSource);
6363
}
6464

65-
private void waitUntilStarted() {
65+
private void waitUntilStartedAndInitContext(R resource, Context context) {
6666
if (!started.get()) {
6767
AtomicInteger count = new AtomicInteger(0);
6868
final var waitTime = 50;
@@ -77,11 +77,32 @@ private void waitUntilStarted() {
7777
log.info("Waited {}ms for controller '{}' to finish initializing", count.get() * waitTime,
7878
configuration.getName());
7979
}
80+
81+
if (reconciler instanceof ContextInitializer) {
82+
final var initializer = (ContextInitializer<R>) reconciler;
83+
initializer.initContext(resource, context);
84+
}
8085
}
8186

8287
@Override
8388
public DeleteControl cleanup(R resource, Context context) {
84-
waitUntilStarted();
89+
waitUntilStartedAndInitContext(resource, context);
90+
91+
configuration.getDependentResources().stream()
92+
.filter(DependentResource::deletable)
93+
.forEach(dependent -> {
94+
var dependentResource = dependent.getFor(resource, context);
95+
if (dependentResource != null) {
96+
dependent.delete(dependentResource, resource, context);
97+
logOperationInfo(resource, dependent, dependentResource, "Deleting");
98+
} else {
99+
log.info("Ignoring already deleted {} for '{}' {}",
100+
dependent.getResourceType().getName(),
101+
resource.getMetadata().getName(),
102+
configuration.getResourceTypeName());
103+
}
104+
});
105+
85106
return metrics().timeControllerExecution(
86107
new ControllerExecution<>() {
87108
@Override
@@ -108,30 +129,23 @@ public DeleteControl execute() {
108129

109130
@Override
110131
public UpdateControl<R> reconcile(R resource, Context context) {
111-
waitUntilStarted();
112-
113-
if (reconciler instanceof ContextInitializer) {
114-
final var initializer = (ContextInitializer<R>) reconciler;
115-
initializer.initContext(resource, context);
116-
}
117-
118-
configuration.getDependentResources().forEach(dependent -> {
119-
if (!dependent.creatable() && !dependent.updatable()) {
120-
return;
121-
}
122-
123-
var dependentResource = dependent.getFor(resource);
124-
if (dependent.creatable() && dependentResource == null) {
125-
// we need to create the dependent
126-
dependentResource = dependent.buildFor(resource);
127-
createOrReplaceDependent(resource, dependent, dependentResource, "Creating");
128-
} else if (dependent.updatable()) {
129-
dependentResource = dependent.update(dependentResource, resource);
130-
createOrReplaceDependent(resource, dependent, dependentResource, "Updating");
131-
} else {
132-
logOperationInfo(resource, dependent, dependentResource, "Ignoring");
133-
}
134-
});
132+
waitUntilStartedAndInitContext(resource, context);
133+
134+
configuration.getDependentResources().stream()
135+
.filter(dependent -> !dependent.creatable() && !dependent.updatable())
136+
.forEach(dependent -> {
137+
var dependentResource = dependent.getFor(resource, context);
138+
if (dependent.creatable() && dependentResource == null) {
139+
// we need to create the dependent
140+
dependentResource = dependent.buildFor(resource, context);
141+
createOrReplaceDependent(resource, context, dependent, dependentResource, "Creating");
142+
} else if (dependent.updatable()) {
143+
dependentResource = dependent.update(dependentResource, resource, context);
144+
createOrReplaceDependent(resource, context, dependent, dependentResource, "Updating");
145+
} else {
146+
logOperationInfo(resource, dependent, dependentResource, "Ignoring");
147+
}
148+
});
135149

136150
return metrics().timeControllerExecution(
137151
new ControllerExecution<>() {
@@ -166,7 +180,7 @@ public UpdateControl<R> execute() {
166180

167181
// todo: rename variables more explicitly
168182
private void createOrReplaceDependent(R resource,
169-
DependentResource dependent,
183+
Context context, DependentResource dependent,
170184
Object dependentResource, String operationDescription) {
171185
// add owner reference if needed
172186
if (dependentResource instanceof HasMetadata
@@ -178,7 +192,7 @@ private void createOrReplaceDependent(R resource,
178192

179193
// commit the changes
180194
// todo: add metrics timing for dependent resource
181-
dependent.createOrReplace(dependentResource);
195+
dependent.createOrReplace(dependentResource, context);
182196
}
183197

184198
private void logOperationInfo(R resource, DependentResource dependent,

operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/AnnotationConfiguration.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ public List<? extends DependentResource> getDependentResources() {
130130
final var updater =
131131
valueIfPresentOrNull(dependentConfig, DependentResourceConfiguration::updater,
132132
DependentResourceConfiguration.DEFAULT_UPDATER.class);
133+
final var cleaner =
134+
valueIfPresentOrNull(dependentConfig, DependentResourceConfiguration::cleaner,
135+
DependentResourceConfiguration.DEFAULT_CLEANER.class);
133136
final var resourceType = dependentConfig.resourceType();
134137

135138
if (HasMetadata.class.isAssignableFrom(resourceType)) {
@@ -150,7 +153,7 @@ public List<? extends DependentResource> getDependentResources() {
150153
resourceType, primariesMapper, secondaryMapper,
151154
dependentConfig.skipUpdateIfUnchanged(), namespaces);
152155
dependent = new KubernetesDependentResource(resourceType, dependentConfig.owned(),
153-
builder, updater, configuration);
156+
builder, updater, cleaner, configuration);
154157
} else {
155158
// todo:
156159
dependent = null;

sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/SchemaService.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public Optional<Schema> getSchema(String name) {
2929
}
3030
}
3131

32-
public static void createSchemaAndRelatedUser(Connection connection, String schemaName,
32+
public static Schema createSchemaAndRelatedUser(Connection connection, String schemaName,
3333
String encoding,
3434
String userName,
3535
String password) {
@@ -49,6 +49,8 @@ public static void createSchemaAndRelatedUser(Connection connection, String sche
4949
statement.execute(
5050
format("GRANT ALL ON `%1$s`.* TO '%2$s'", schemaName, userName));
5151
}
52+
53+
return new Schema(schemaName, encoding);
5254
} catch (SQLException e) {
5355
throw new IllegalStateException(e);
5456
}

sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/DeploymentDependentResource.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
66
import io.javaoperatorsdk.operator.api.config.dependent.Builder;
77
import io.javaoperatorsdk.operator.api.config.dependent.Updater;
8+
import io.javaoperatorsdk.operator.api.reconciler.Context;
89

910
public class DeploymentDependentResource
1011
implements Builder<Deployment, Tomcat>, Updater<Deployment, Tomcat> {
1112

1213
@Override
13-
public Deployment buildFor(Tomcat tomcat) {
14+
public Deployment buildFor(Tomcat tomcat, Context context) {
1415
Deployment deployment = TomcatReconciler.loadYaml(Deployment.class, "deployment.yaml");
1516
final ObjectMeta tomcatMetadata = tomcat.getMetadata();
1617
final String tomcatName = tomcatMetadata.getName();
@@ -39,7 +40,7 @@ public Deployment buildFor(Tomcat tomcat) {
3940
}
4041

4142
@Override
42-
public Deployment update(Deployment fetched, Tomcat tomcat) {
43+
public Deployment update(Deployment fetched, Tomcat tomcat, Context context) {
4344
return new DeploymentBuilder(fetched).editSpec().editTemplate().editSpec().editFirstContainer()
4445
.withImage("tomcat:" + tomcat.getSpec().getVersion())
4546
.endContainer().endSpec().endTemplate().endSpec().build();

sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/ServiceDependentResource.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
import io.fabric8.kubernetes.api.model.Service;
55
import io.fabric8.kubernetes.api.model.ServiceBuilder;
66
import io.javaoperatorsdk.operator.api.config.dependent.Builder;
7+
import io.javaoperatorsdk.operator.api.reconciler.Context;
78

89
public class ServiceDependentResource implements Builder<Service, Tomcat> {
910

1011
@Override
11-
public Service buildFor(Tomcat tomcat) {
12+
public Service buildFor(Tomcat tomcat, Context context) {
1213
final ObjectMeta tomcatMetadata = tomcat.getMetadata();
1314
final Service service =
1415
new ServiceBuilder(TomcatReconciler.loadYaml(Service.class, "service.yaml"))

0 commit comments

Comments
 (0)