Skip to content

Dependent resources implementation #785

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

Closed
wants to merge 1 commit into from
Closed
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
2 changes: 1 addition & 1 deletion micrometer-support/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>java-operator-sdk</artifactId>
<groupId>io.javaoperatorsdk</groupId>
<version>2.0.3-SNAPSHOT</version>
<version>2.1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
2 changes: 1 addition & 1 deletion operator-framework-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>java-operator-sdk</artifactId>
<version>2.0.3-SNAPSHOT</version>
<version>2.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,103 +1,36 @@
package io.javaoperatorsdk.operator.api.config;

import java.lang.reflect.ParameterizedType;
import java.util.Collections;
import java.util.Set;
import java.util.List;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.javaoperatorsdk.operator.ReconcilerUtils;
import io.javaoperatorsdk.operator.api.reconciler.Constants;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResourceControllerFactory;
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter;
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilters;

public interface ControllerConfiguration<R extends HasMetadata> {
@SuppressWarnings("rawtypes")
public interface ControllerConfiguration<R extends HasMetadata> extends ResourceConfiguration<R> {

default String getName() {
return ReconcilerUtils.getDefaultReconcilerName(getAssociatedReconcilerClassName());
}

default String getResourceTypeName() {
return ReconcilerUtils.getResourceTypeName(getResourceClass());
}

default String getFinalizer() {
return ReconcilerUtils.getDefaultFinalizerName(getResourceClass());
}

/**
* 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;
}

default boolean isGenerationAware() {
return true;
}

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

String getAssociatedReconcilerClassName();

default Set<String> getNamespaces() {
return Collections.emptySet();
}

default boolean watchAllNamespaces() {
return allNamespacesWatched(getNamespaces());
}

static boolean allNamespacesWatched(Set<String> namespaces) {
return namespaces == null || namespaces.isEmpty();
}

default boolean watchCurrentNamespace() {
return currentNamespaceWatched(getNamespaces());
}

static boolean currentNamespaceWatched(Set<String> namespaces) {
return namespaces != null
&& namespaces.size() == 1
&& namespaces.contains(
Constants.WATCH_CURRENT_NAMESPACE);
}

/**
* Computes the effective namespaces based on the set specified by the user, in particular
* retrieves the current namespace from the client when the user specified that they wanted to
* watch the current namespace only.
*
* @return a Set of namespace names the associated controller will watch
*/
default Set<String> getEffectiveNamespaces() {
var targetNamespaces = getNamespaces();
if (watchCurrentNamespace()) {
final var parent = getConfigurationService();
if (parent == null) {
throw new IllegalStateException(
"Parent ConfigurationService must be set before calling this method");
}
targetNamespaces = Collections.singleton(parent.getClientConfiguration().getNamespace());
}
return targetNamespaces;
}

default RetryConfiguration getRetryConfiguration() {
return RetryConfiguration.DEFAULT;
}

ConfigurationService getConfigurationService();

default void setConfigurationService(ConfigurationService service) {}

default boolean useFinalizer() {
return !Constants.NO_FINALIZER
.equals(getFinalizer());
Expand All @@ -114,4 +47,12 @@ default boolean useFinalizer() {
default ResourceEventFilter<R> getEventFilter() {
return ResourceEventFilters.passthrough();
}

default List<DependentResource> getDependentResources() {
return Collections.emptyList();
}

default DependentResourceControllerFactory<R> dependentFactory() {
return new DependentResourceControllerFactory<>() {};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ public ControllerConfiguration<R> build() {
labelSelector,
customResourcePredicate,
original.getResourceClass(),
original.getConfigurationService());
original.getConfigurationService(),
original.getDependentResources());
}

public static <R extends HasMetadata> ControllerConfigurationOverrider<R> override(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.javaoperatorsdk.operator.api.config;

import java.util.Collections;
import java.util.List;
import java.util.Set;

import io.fabric8.kubernetes.api.model.HasMetadata;
Expand All @@ -20,8 +21,10 @@ public class DefaultControllerConfiguration<R extends HasMetadata>
private final String labelSelector;
private final ResourceEventFilter<R> resourceEventFilter;
private final Class<R> resourceClass;
private final List<DependentResource> dependents;
private ConfigurationService service;

// NOSONAR constructor is meant to provide all information
public DefaultControllerConfiguration(
String associatedControllerClassName,
String name,
Expand All @@ -33,7 +36,8 @@ public DefaultControllerConfiguration(
String labelSelector,
ResourceEventFilter<R> resourceEventFilter,
Class<R> resourceClass,
ConfigurationService service) {
ConfigurationService service,
List<DependentResource> dependents) {
this.associatedControllerClassName = associatedControllerClassName;
this.name = name;
this.crdName = crdName;
Expand All @@ -52,6 +56,7 @@ public DefaultControllerConfiguration(
resourceClass == null ? ControllerConfiguration.super.getResourceClass()
: resourceClass;
setConfigurationService(service);
this.dependents = dependents != null ? dependents : Collections.emptyList();
}

@Override
Expand Down Expand Up @@ -102,7 +107,7 @@ public ConfigurationService getConfigurationService() {
@Override
public void setConfigurationService(ConfigurationService service) {
if (this.service != null) {
throw new RuntimeException("A ConfigurationService is already associated with '" + name
throw new IllegalStateException("A ConfigurationService is already associated with '" + name
+ "' ControllerConfiguration. Cannot change it once set!");
}
this.service = service;
Expand All @@ -122,4 +127,9 @@ public Class<R> getResourceClass() {
public ResourceEventFilter<R> getEventFilter() {
return resourceEventFilter;
}

@Override
public List<DependentResource> getDependentResources() {
return dependents;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.javaoperatorsdk.operator.api.config;

import java.util.Collections;
import java.util.Set;

import io.fabric8.kubernetes.api.model.HasMetadata;

public class DefaultResourceConfiguration<R extends HasMetadata>
implements ResourceConfiguration<R> {

private final String labelSelector;
private final Set<String> namespaces;
private final boolean watchAllNamespaces;
private final Class<R> resourceClass;
private ConfigurationService service;

public DefaultResourceConfiguration(String labelSelector, Class<R> resourceClass,
String... namespaces) {
this(labelSelector, resourceClass,
namespaces != null ? Set.of(namespaces) : Collections.emptySet());
}

public DefaultResourceConfiguration(String labelSelector, Class<R> resourceClass,
Set<String> namespaces) {
this.labelSelector = labelSelector;
this.resourceClass = resourceClass;
this.namespaces = namespaces != null ? namespaces : Collections.emptySet();
this.watchAllNamespaces = this.namespaces.isEmpty();
}

@Override
public String getResourceTypeName() {
return ResourceConfiguration.super.getResourceTypeName();
}

@Override
public String getLabelSelector() {
return labelSelector;
}

@Override
public Set<String> getNamespaces() {
return namespaces;
}

@Override
public boolean watchAllNamespaces() {
return watchAllNamespaces;
}

@Override
public ConfigurationService getConfigurationService() {
return service;
}

@Override
public Class<R> getResourceClass() {
return resourceClass;
}

@Override
public void setConfigurationService(ConfigurationService service) {
this.service = service;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.javaoperatorsdk.operator.api.config;

public @interface Dependent {

Class<?> resourceType();

Class<? extends DependentResource> type();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.javaoperatorsdk.operator.api.config;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
import io.javaoperatorsdk.operator.processing.event.source.EventSource;

public interface DependentResource<R, P extends HasMetadata> {
default EventSource initEventSource(EventSourceContext<P> context) {
throw new IllegalStateException("Must be implemented if not automatically provided by the SDK");
};

default Class<R> resourceType() {
return (Class<R>) Utils.getFirstTypeArgumentFromInterface(getClass());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public static void init(ConfigurationService configuration) {
log.debug("Initialized ExecutorServiceManager executor: {}, timeout: {}",
configuration.getExecutorService().getClass(),
configuration.getTerminationTimeoutSeconds());
log.debug("Initialized ExecutorServiceManager executor: {}, timeout: {}",
configuration.getExecutorService().getClass(),
configuration.getTerminationTimeoutSeconds());
} else {
log.debug("Already started, reusing already setup instance!");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.javaoperatorsdk.operator.api.config;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

import static io.javaoperatorsdk.operator.api.reconciler.Constants.EMPTY_STRING;

@Target({ElementType.TYPE})
public @interface KubernetesDependent {

boolean OWNED_DEFAULT = true;
boolean SKIP_UPDATE_DEFAULT = true;

boolean owned() default OWNED_DEFAULT;

boolean skipUpdateIfUnchanged() default SKIP_UPDATE_DEFAULT;

/**
* Specified which namespaces this Controller monitors for custom resources events. If no
* namespace is specified then the controller will monitor all namespaces by default.
*
* @return the list of namespaces this controller monitors
*/
String[] namespaces() default {};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, the namespaces should not be here probably, should come from a central configuration


/**
* 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 label selector
*/
String labelSelector() default EMPTY_STRING;
}
Loading