Skip to content

feat: option to limit CRs the operator watches #499

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion operator-framework-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,14 @@
</plugins>
</build>


<dependencies>
<!-- We use the OpenShift client, because functionally it is a superset of the Kubernetes client -->
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>openshift-client</artifactId>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
Expand Down Expand Up @@ -97,5 +98,15 @@
<artifactId>log4j-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-server-mock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,13 @@
* @return the list of namespaces this controller monitors
*/
String[] namespaces() default {};

/**
* Optional label selector used to identify the set of custom resources the controller will acc
* upon. The label selector can be made of multiple comma separated requirements that acts as a
* logical AND operator.
*
* @return the finalizer name
*/
String labelSelector() default NULL;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public abstract class AbstractControllerConfiguration<R extends CustomResource>
private final Set<String> namespaces;
private final boolean watchAllNamespaces;
private final RetryConfiguration retryConfiguration;
private final String labelSelector;
private ConfigurationService service;

public AbstractControllerConfiguration(
Expand All @@ -24,7 +25,8 @@ public AbstractControllerConfiguration(
String finalizer,
boolean generationAware,
Set<String> namespaces,
RetryConfiguration retryConfiguration) {
RetryConfiguration retryConfiguration,
String labelSelector) {
this.associatedControllerClassName = associatedControllerClassName;
this.name = name;
this.crdName = crdName;
Expand All @@ -37,6 +39,25 @@ public AbstractControllerConfiguration(
retryConfiguration == null
? ControllerConfiguration.super.getRetryConfiguration()
: retryConfiguration;
this.labelSelector = labelSelector;
}

/**
* @deprecated use
* {@link #AbstractControllerConfiguration(String, String, String, String, boolean, Set, RetryConfiguration, String)}
* instead
*/
@Deprecated
public AbstractControllerConfiguration(
String associatedControllerClassName,
String name,
String crdName,
String finalizer,
boolean generationAware,
Set<String> namespaces,
RetryConfiguration retryConfiguration) {
this(associatedControllerClassName, name, crdName, finalizer, generationAware, namespaces,
retryConfiguration, null);
}

@Override
Expand Down Expand Up @@ -88,4 +109,9 @@ public ConfigurationService getConfigurationService() {
public void setConfigurationService(ConfigurationService service) {
this.service = service;
}

@Override
public String getLabelSelector() {
return labelSelector;
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,46 @@
package io.javaoperatorsdk.operator.api.config;

import io.fabric8.kubernetes.client.CustomResource;
import io.javaoperatorsdk.operator.ControllerUtils;
import io.javaoperatorsdk.operator.api.Controller;
import java.lang.reflect.ParameterizedType;
import java.util.Collections;
import java.util.Set;

public interface ControllerConfiguration<R extends CustomResource> {

String getName();
default String getName() {
return ControllerUtils.getDefaultResourceControllerName(getAssociatedControllerClassName());
}

String getCRDName();
default String getCRDName() {
return CustomResource.getCRDName(getCustomResourceClass());
}

String getFinalizer();
default String getFinalizer() {
return ControllerUtils.getDefaultFinalizerName(getCRDName());
}

/**
* Retrieves the label selector that is used to filter which custom resources are actually watched
* by the associated controller. See
* https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more details on
* syntax.
*
* @return the label selector filtering watched custom resources
*/
default String getLabelSelector() {
return null;
}

boolean isGenerationAware();
default boolean isGenerationAware() {
return true;
}

Class<R> getCustomResourceClass();
default Class<R> getCustomResourceClass() {
ParameterizedType type = (ParameterizedType) getClass().getGenericInterfaces()[0];
return (Class<R>) type.getActualTypeArguments()[0];
}

String getAssociatedControllerClassName();

Expand Down Expand Up @@ -67,7 +92,7 @@ default RetryConfiguration getRetryConfiguration() {

ConfigurationService getConfigurationService();

void setConfigurationService(ConfigurationService service);
default void setConfigurationService(ConfigurationService service) {}

default boolean useFinalizer() {
return !Controller.NO_FINALIZER.equals(getFinalizer());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ public class ControllerConfigurationOverrider<R extends CustomResource> {
private boolean generationAware;
private Set<String> namespaces;
private RetryConfiguration retry;
private String labelSelector;
private final ControllerConfiguration<R> original;

private ControllerConfigurationOverrider(ControllerConfiguration<R> original) {
finalizer = original.getFinalizer();
generationAware = original.isGenerationAware();
namespaces = new HashSet<>(original.getNamespaces());
retry = original.getRetryConfiguration();
labelSelector = original.getLabelSelector();
this.original = original;
}

Expand Down Expand Up @@ -57,6 +59,11 @@ public ControllerConfigurationOverrider<R> withRetry(RetryConfiguration retry) {
return this;
}

public ControllerConfigurationOverrider<R> withLabelSelector(String labelSelector) {
this.labelSelector = labelSelector;
return this;
}

public ControllerConfiguration<R> build() {
return new AbstractControllerConfiguration<R>(
original.getAssociatedControllerClassName(),
Expand All @@ -65,7 +72,8 @@ public ControllerConfiguration<R> build() {
finalizer,
generationAware,
namespaces,
retry) {
retry,
labelSelector) {
@Override
public Class<R> getCustomResourceClass() {
return original.getCustomResourceClass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getVersion;

import io.fabric8.kubernetes.api.model.KubernetesResourceList;
import io.fabric8.kubernetes.api.model.ListOptions;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.client.Watch;
import io.fabric8.kubernetes.client.Watcher;
import io.fabric8.kubernetes.client.WatcherException;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.fabric8.kubernetes.client.utils.Utils;
import io.javaoperatorsdk.operator.api.config.ControllerConfiguration;
import io.javaoperatorsdk.operator.processing.CustomResourceCache;
import io.javaoperatorsdk.operator.processing.KubernetesResourceUtils;
Expand All @@ -33,6 +35,7 @@ public class CustomResourceEventSource<T extends CustomResource<?, ?>> extends A
private final Set<String> targetNamespaces;
private final boolean generationAware;
private final String resourceFinalizer;
private final String labelSelector;
private final Map<String, Long> lastGenerationProcessedSuccessfully = new ConcurrentHashMap<>();
private final List<Watch> watches;
private final String resClass;
Expand All @@ -46,6 +49,7 @@ public CustomResourceEventSource(
configuration.getEffectiveNamespaces(),
configuration.isGenerationAware(),
configuration.getFinalizer(),
configuration.getLabelSelector(),
configuration.getCustomResourceClass(),
new CustomResourceCache(configuration.getConfigurationService().getObjectMapper()));
}
Expand All @@ -55,12 +59,14 @@ public CustomResourceEventSource(
Set<String> targetNamespaces,
boolean generationAware,
String resourceFinalizer,
String labelSelector,
Class<T> resClass) {
this(
client,
targetNamespaces,
generationAware,
resourceFinalizer,
labelSelector,
resClass,
new CustomResourceCache());
}
Expand All @@ -70,27 +76,34 @@ public CustomResourceEventSource(
Set<String> targetNamespaces,
boolean generationAware,
String resourceFinalizer,
String labelSelector,
Class<T> resClass,
CustomResourceCache customResourceCache) {
this.client = client;
this.targetNamespaces = targetNamespaces;
this.generationAware = generationAware;
this.resourceFinalizer = resourceFinalizer;
this.labelSelector = labelSelector;
this.watches = new ArrayList<>();
this.resClass = resClass.getName();
this.customResourceCache = customResourceCache;
}

@Override
public void start() {
var options = new ListOptions();
if (Utils.isNotNullOrEmpty(labelSelector)) {
options.setLabelSelector(labelSelector);
}

if (ControllerConfiguration.allNamespacesWatched(targetNamespaces)) {
var w = client.inAnyNamespace().watch(this);
var w = client.inAnyNamespace().watch(options, this);
watches.add(w);
log.debug("Registered controller {} -> {} for any namespace", resClass, w);
} else {
targetNamespaces.forEach(
ns -> {
var w = client.inNamespace(ns).watch(this);
var w = client.inNamespace(ns).watch(options, this);
watches.add(w);
log.debug("Registered controller {} -> {} for namespace: {}", resClass, w, ns);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.javaoperatorsdk.operator.api.config;

import static org.junit.jupiter.api.Assertions.*;

import io.javaoperatorsdk.operator.sample.simple.TestCustomResource;
import org.junit.jupiter.api.Test;

class ControllerConfigurationTest {

@Test
void getCustomResourceClass() {
final ControllerConfiguration<TestCustomResource> conf = new ControllerConfiguration<>() {
@Override
public String getAssociatedControllerClassName() {
return null;
}

@Override
public ConfigurationService getConfigurationService() {
return null;
}
};
assertEquals(TestCustomResource.class, conf.getCustomResourceClass());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ class CustomResourceEventSourceTest {
EventHandler eventHandler = mock(EventHandler.class);

private CustomResourceEventSource<TestCustomResource> customResourceEventSource =
new CustomResourceEventSource<>(client, null, true, FINALIZER, TestCustomResource.class);
new CustomResourceEventSource<>(
client, null, true, FINALIZER, null, TestCustomResource.class);

@BeforeEach
public void setup() {
Expand Down Expand Up @@ -72,7 +73,8 @@ public void normalExecutionIfGenerationChanges() {
@Test
public void handlesAllEventIfNotGenerationAware() {
customResourceEventSource =
new CustomResourceEventSource<>(client, null, false, FINALIZER, TestCustomResource.class);
new CustomResourceEventSource<>(
client, null, false, FINALIZER, null, TestCustomResource.class);
setup();

TestCustomResource customResource1 = TestUtils.testCustomResource();
Expand Down
Loading