File tree

18 files changed

+261
-89
lines changed

18 files changed

+261
-89
lines changed
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
import io.fabric8.kubernetes.api.model.HasMetadata;
44
import io.javaoperatorsdk.operator.api.reconciler.Context;
55

6-
public abstract class AbstractDependentResource<R, P extends HasMetadata, C>
7-
implements DependentResource<R, P> {
6+
public interface GenericDependentResource<R, P extends HasMetadata>
7+
extends DependentResource<R, P> {
88

9-
@Override
10-
public void reconcile(P primary, Context context) {
9+
10+
default void reconcile(P primary, Context context) {
1111
var actual = getResource(primary);
1212
var desired = desired(primary, context);
1313
if (actual.isEmpty()) {
@@ -19,13 +19,13 @@ public void reconcile(P primary, Context context) {
1919
}
2020
}
2121

22-
protected abstract R desired(P primary, Context context);
22+
R desired(P primary, Context context);
2323

24-
protected abstract boolean match(R actual, R target, Context context);
24+
boolean match(R actual, R target, Context context);
2525

26-
protected abstract R create(R target, P primary, Context context);
26+
void create(R target, P primary, Context context);
2727

2828
// the actual needed to copy/preserve new labels or annotations
29-
protected abstract R update(R actual, R target, P primary, Context context);
29+
void update(R actual, R target, P primary, Context context);
3030

3131
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.javaoperatorsdk.operator.api.reconciler.dependent;
2+
3+
import io.fabric8.kubernetes.api.model.HasMetadata;
4+
import io.javaoperatorsdk.operator.api.reconciler.Context;
5+
6+
public interface ReadOnlyDependentResource<R, P extends HasMetadata>
7+
extends DependentResource<R, P> {
8+
9+
@Override
10+
default void reconcile(HasMetadata primary, Context context) {}
11+
12+
@Override
13+
default void delete(HasMetadata primary, Context context) {}
14+
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package io.javaoperatorsdk.operator.processing.dependent.external;
2+
3+
public class PollingDependentResourceConfig {
4+
5+
public static final long DEFAULT_POLLING_PERIOD = 700;
6+
7+
private long pollingPeriod = DEFAULT_POLLING_PERIOD;
8+
9+
public PollingDependentResourceConfig() {}
10+
11+
public PollingDependentResourceConfig(long pollingPeriod) {
12+
this.pollingPeriod = pollingPeriod;
13+
}
14+
15+
public PollingDependentResourceConfig setPollingPeriod(long pollingPeriod) {
16+
this.pollingPeriod = pollingPeriod;
17+
return this;
18+
}
19+
20+
public long getPollingPeriod() {
21+
return pollingPeriod;
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.javaoperatorsdk.operator.processing.dependent.external;
2+
3+
import java.util.Map;
4+
import java.util.Optional;
5+
import java.util.function.Supplier;
6+
7+
import io.fabric8.kubernetes.api.model.HasMetadata;
8+
import io.javaoperatorsdk.operator.api.config.Utils;
9+
import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
10+
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResourceConfigurator;
11+
import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider;
12+
import io.javaoperatorsdk.operator.api.reconciler.dependent.ReadOnlyDependentResource;
13+
import io.javaoperatorsdk.operator.processing.event.ResourceID;
14+
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
15+
import io.javaoperatorsdk.operator.processing.event.source.polling.PollingEventSource;
16+
17+
import static io.javaoperatorsdk.operator.processing.dependent.external.PollingDependentResourceConfig.DEFAULT_POLLING_PERIOD;
18+
19+
public abstract class PollingReadOnlyDependentResource<R, P extends HasMetadata, C extends PollingDependentResourceConfig>
20+
implements ReadOnlyDependentResource<R, P>, EventSourceProvider<P>,
21+
Supplier<Map<ResourceID, R>>, DependentResourceConfigurator<C> {
22+
23+
private PollingEventSource<R, P> pollingEventSource;
24+
private long pollingPeriod = DEFAULT_POLLING_PERIOD;
25+
26+
@Override
27+
public EventSource eventSource(EventSourceContext<P> context) {
28+
pollingEventSource = new PollingEventSource<>(this, pollingPeriod, resourceType());
29+
return null;
30+
}
31+
32+
@Override
33+
public Optional<R> getResource(P primaryResource) {
34+
return pollingEventSource.getAssociated(primaryResource);
35+
}
36+
37+
protected Class<R> resourceType() {
38+
return (Class<R>) Utils.getFirstTypeArgumentFromExtendedClass(getClass());
39+
}
40+
41+
@Override
42+
public void configureWith(C config) {
43+
this.pollingPeriod = config.getPollingPeriod();
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.javaoperatorsdk.operator.processing.dependent.kubernetes;
2+
3+
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
4+
5+
import static io.javaoperatorsdk.operator.api.reconciler.Constants.EMPTY_STRING;
6+
7+
public class InformerConfig {
8+
9+
private String[] namespaces = new String[0];
10+
private String labelSelector = EMPTY_STRING;
11+
private ConfigurationService configurationService;
12+
13+
public InformerConfig() {}
14+
15+
public InformerConfig(String[] namespaces, String labelSelector,
16+
ConfigurationService configurationService) {
17+
this.namespaces = namespaces;
18+
this.labelSelector = labelSelector;
19+
this.configurationService = configurationService;
20+
}
21+
22+
public void setNamespaces(String[] namespaces) {
23+
this.namespaces = namespaces;
24+
}
25+
26+
public void setLabelSelector(String labelSelector) {
27+
this.labelSelector = labelSelector;
28+
}
29+
30+
public void setConfigurationService(
31+
ConfigurationService configurationService) {
32+
this.configurationService = configurationService;
33+
}
34+
35+
public String[] namespaces() {
36+
return namespaces;
37+
}
38+
39+
public String labelSelector() {
40+
return labelSelector;
41+
}
42+
43+
public ConfigurationService getConfigurationService() {
44+
return configurationService;
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
public @interface KubernetesDependent {
1313

1414
boolean ADD_OWNER_REFERENCE_DEFAULT = true;
15+
boolean EDIT_ONLY_DEFAULT = false;
1516

1617
boolean addOwnerReference() default ADD_OWNER_REFERENCE_DEFAULT;
1718

19+
boolean editOnly() default EDIT_ONLY_DEFAULT;
20+
1821
/**
1922
* Specified which namespaces this Controller monitors for custom resources events. If no
2023
* namespace is specified then the controller will monitor the namespaces configured for the
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@
99
import io.fabric8.kubernetes.api.model.HasMetadata;
1010
import io.fabric8.kubernetes.client.KubernetesClient;
1111
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
12-
import io.javaoperatorsdk.operator.api.config.Utils;
1312
import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
1413
import io.javaoperatorsdk.operator.api.reconciler.Context;
1514
import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
16-
import io.javaoperatorsdk.operator.api.reconciler.dependent.AbstractDependentResource;
17-
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResourceConfigurator;
18-
import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider;
15+
import io.javaoperatorsdk.operator.api.reconciler.dependent.GenericDependentResource;
1916
import io.javaoperatorsdk.operator.api.reconciler.dependent.KubernetesClientAware;
2017
import io.javaoperatorsdk.operator.processing.event.ResourceID;
2118
import io.javaoperatorsdk.operator.processing.event.source.AssociatedSecondaryResourceIdentifier;
@@ -25,26 +22,27 @@
2522
import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers;
2623

2724
public abstract class KubernetesDependentResource<R extends HasMetadata, P extends HasMetadata>
28-
extends AbstractDependentResource<R, P, KubernetesDependentResourceConfig>
29-
implements KubernetesClientAware, EventSourceProvider<P>,
30-
DependentResourceConfigurator<KubernetesDependentResourceConfig> {
25+
extends KubernetesDependentResourceBase<R, P, KubernetesDependentResourceConfig>
26+
implements GenericDependentResource<R, P>, KubernetesClientAware {
3127

3228
private static final Logger log = LoggerFactory.getLogger(KubernetesDependentResource.class);
3329

30+
protected ResourceMatcher resourceMatcher;
3431
protected KubernetesClient client;
35-
private InformerEventSource<R, P> informerEventSource;
3632
private boolean addOwnerReference;
37-
protected ResourceMatcher resourceMatcher;
33+
private boolean editOnly = false;
34+
3835

3936
@Override
4037
public void configureWith(KubernetesDependentResourceConfig config) {
38+
super.configureWith(config);
4139
configureWith(config.getConfigurationService(), config.labelSelector(),
42-
Set.of(config.namespaces()), config.addOwnerReference());
40+
Set.of(config.namespaces()), config.addOwnerReference(), config.isEditOnly());
4341
}
4442

4543
@SuppressWarnings("unchecked")
4644
private void configureWith(ConfigurationService configService, String labelSelector,
47-
Set<String> namespaces, boolean addOwnerReference) {
45+
Set<String> namespaces, boolean addOwnerReference, boolean editOnly) {
4846
final var primaryResourcesRetriever =
4947
(this instanceof PrimaryResourcesRetriever) ? (PrimaryResourcesRetriever<R>) this
5048
: Mappers.fromOwnerReference();
@@ -59,7 +57,8 @@ private void configureWith(ConfigurationService configService, String labelSelec
5957
.withPrimaryResourcesRetriever(primaryResourcesRetriever)
6058
.withAssociatedSecondaryResourceIdentifier(secondaryResourceIdentifier)
6159
.build();
62-
configureWith(configService, new InformerEventSource<>(ic, client), addOwnerReference);
60+
configureWith(configService, new InformerEventSource<>(ic, client), addOwnerReference,
61+
editOnly);
6362
}
6463

6564
/**
@@ -71,9 +70,10 @@ private void configureWith(ConfigurationService configService, String labelSelec
7170
*/
7271
public void configureWith(ConfigurationService configurationService,
7372
InformerEventSource<R, P> informerEventSource,
74-
boolean addOwnerReference) {
73+
boolean addOwnerReference, boolean editOnly) {
7574
this.informerEventSource = informerEventSource;
7675
this.addOwnerReference = addOwnerReference;
76+
this.editOnly = editOnly;
7777
initResourceMatcherIfNotSet(configurationService);
7878
}
7979

@@ -84,42 +84,39 @@ protected void beforeCreateOrUpdate(R desired, P primary) {
8484
}
8585

8686
@Override
87-
protected boolean match(R actualResource, R desiredResource, Context context) {
87+
public boolean match(R actualResource, R desiredResource, Context context) {
8888
return resourceMatcher.match(actualResource, desiredResource, context);
8989
}
9090

9191
@SuppressWarnings("unchecked")
9292
@Override
93-
protected R create(R target, P primary, Context context) {
93+
public void create(R target, P primary, Context context) {
94+
if (editOnly) {
95+
return;
96+
}
9497
log.debug("Creating target resource with type: " +
9598
"{}, with id: {}", target.getClass(), ResourceID.fromResource(target));
9699
beforeCreateOrUpdate(target, primary);
97100
Class<R> targetClass = (Class<R>) target.getClass();
98-
return client.resources(targetClass).inNamespace(target.getMetadata().getNamespace())
101+
client.resources(targetClass).inNamespace(target.getMetadata().getNamespace())
99102
.create(target);
100103
}
101104

102105
@SuppressWarnings("unchecked")
103106
@Override
104-
protected R update(R actual, R target, P primary, Context context) {
107+
public void update(R actual, R target, P primary, Context context) {
105108
log.debug("Updating target resource with type: {}, with id: {}", target.getClass(),
106109
ResourceID.fromResource(target));
107110
beforeCreateOrUpdate(target, primary);
108111
Class<R> targetClass = (Class<R>) target.getClass();
109-
return client.resources(targetClass).inNamespace(target.getMetadata().getNamespace())
112+
client.resources(targetClass).inNamespace(target.getMetadata().getNamespace())
110113
.replace(target);
111114
}
112115

113116
@Override
114117
public EventSource eventSource(EventSourceContext<P> context) {
115118
initResourceMatcherIfNotSet(context.getConfigurationService());
116-
if (informerEventSource == null) {
117-
configureWith(context.getConfigurationService(), null, null,
118-
KubernetesDependent.ADD_OWNER_REFERENCE_DEFAULT);
119-
log.warn("Using default configuration for " + resourceType().getSimpleName()
120-
+ " KubernetesDependentResource, call configureWith to provide configuration");
121-
}
122-
return informerEventSource;
119+
return super.eventSource(context);
123120
}
124121

125122
public KubernetesDependentResource<R, P> setInformerEventSource(
@@ -137,10 +134,6 @@ public void delete(P primary, Context context) {
137134
}
138135

139136
@SuppressWarnings("unchecked")
140-
protected Class<R> resourceType() {
141-
return (Class<R>) Utils.getFirstTypeArgumentFromExtendedClass(getClass());
142-
}
143-
144137
@Override
145138
public Optional<R> getResource(P primaryResource) {
146139
return informerEventSource.getAssociated(primaryResource);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package io.javaoperatorsdk.operator.processing.dependent.kubernetes;
2+
3+
import io.fabric8.kubernetes.api.model.HasMetadata;
4+
import io.javaoperatorsdk.operator.api.config.Utils;
5+
import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
6+
import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
7+
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResourceConfigurator;
8+
import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider;
9+
import io.javaoperatorsdk.operator.processing.event.ResourceID;
10+
import io.javaoperatorsdk.operator.processing.event.source.AssociatedSecondaryResourceIdentifier;
11+
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
12+
import io.javaoperatorsdk.operator.processing.event.source.PrimaryResourcesRetriever;
13+
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
14+
import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers;
15+
16+
public abstract class KubernetesDependentResourceBase<R extends HasMetadata, P extends HasMetadata, C extends InformerConfig>
17+
implements DependentResourceConfigurator<C>, EventSourceProvider<P> {
18+
19+
protected InformerEventSource<R, P> informerEventSource;
20+
21+
@Override
22+
public void configureWith(C config) {
23+
final var primaryResourcesRetriever =
24+
(this instanceof PrimaryResourcesRetriever) ? (PrimaryResourcesRetriever<R>) this
25+
: Mappers.fromOwnerReference();
26+
final AssociatedSecondaryResourceIdentifier<P> secondaryResourceIdentifier =
27+
(this instanceof AssociatedSecondaryResourceIdentifier)
28+
? (AssociatedSecondaryResourceIdentifier<P>) this
29+
: ResourceID::fromResource;
30+
this.informerEventSource =
31+
(InformerEventSource<R, P>) InformerConfiguration
32+
.from(config.getConfigurationService(), resourceType())
33+
.withLabelSelector(config.labelSelector())
34+
.withNamespaces(config.namespaces())
35+
.withPrimaryResourcesRetriever(primaryResourcesRetriever)
36+
.withAssociatedSecondaryResourceIdentifier(secondaryResourceIdentifier)
37+
.build();
38+
39+
}
40+
41+
protected Class<R> resourceType() {
42+
return (Class<R>) Utils.getFirstTypeArgumentFromExtendedClass(getClass());
43+
}
44+
45+
@Override
46+
public EventSource eventSource(EventSourceContext<P> context) {
47+
configureWith((C) new InformerConfig(null, null, context.getConfigurationService()));
48+
return informerEventSource;
49+
}
50+
51+
}

0 commit comments

Comments
 (0)