Skip to content

Dependent resource hierarchy #958

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 4 commits 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
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.javaoperatorsdk.operator.api.reconciler.Context;

public abstract class AbstractDependentResource<R, P extends HasMetadata, C>
implements DependentResource<R, P> {
public interface GenericDependentResource<R, P extends HasMetadata>
extends DependentResource<R, P> {

@Override
public void reconcile(P primary, Context context) {

default void reconcile(P primary, Context context) {
var actual = getResource(primary);
var desired = desired(primary, context);
if (actual.isEmpty()) {
Expand All @@ -19,13 +19,13 @@ public void reconcile(P primary, Context context) {
}
}

protected abstract R desired(P primary, Context context);
R desired(P primary, Context context);

protected abstract boolean match(R actual, R target, Context context);
boolean match(R actual, R target, Context context);

protected abstract R create(R target, P primary, Context context);
void create(R target, P primary, Context context);

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

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.javaoperatorsdk.operator.api.reconciler.dependent;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.javaoperatorsdk.operator.api.reconciler.Context;

public interface ReadOnlyDependentResource<R, P extends HasMetadata>
extends DependentResource<R, P> {

@Override
default void reconcile(HasMetadata primary, Context context) {}

@Override
default void delete(HasMetadata primary, Context context) {}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.javaoperatorsdk.operator.processing.dependent.external;

public class PollingDependentResourceConfig {

public static final long DEFAULT_POLLING_PERIOD = 700;

private long pollingPeriod = DEFAULT_POLLING_PERIOD;

public PollingDependentResourceConfig() {}

public PollingDependentResourceConfig(long pollingPeriod) {
this.pollingPeriod = pollingPeriod;
}

public PollingDependentResourceConfig setPollingPeriod(long pollingPeriod) {
this.pollingPeriod = pollingPeriod;
return this;
}

public long getPollingPeriod() {
return pollingPeriod;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.javaoperatorsdk.operator.processing.dependent.external;

import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.javaoperatorsdk.operator.api.config.Utils;
import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResourceConfigurator;
import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider;
import io.javaoperatorsdk.operator.api.reconciler.dependent.ReadOnlyDependentResource;
import io.javaoperatorsdk.operator.processing.event.ResourceID;
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
import io.javaoperatorsdk.operator.processing.event.source.polling.PollingEventSource;

import static io.javaoperatorsdk.operator.processing.dependent.external.PollingDependentResourceConfig.DEFAULT_POLLING_PERIOD;

public abstract class PollingReadOnlyDependentResource<R, P extends HasMetadata, C extends PollingDependentResourceConfig>
implements ReadOnlyDependentResource<R, P>, EventSourceProvider<P>,
Supplier<Map<ResourceID, R>>, DependentResourceConfigurator<C> {

private PollingEventSource<R, P> pollingEventSource;
private long pollingPeriod = DEFAULT_POLLING_PERIOD;

@Override
public EventSource eventSource(EventSourceContext<P> context) {
pollingEventSource = new PollingEventSource<>(this, pollingPeriod, resourceType());
return null;
}

@Override
public Optional<R> getResource(P primaryResource) {
return pollingEventSource.getAssociated(primaryResource);
}

protected Class<R> resourceType() {
return (Class<R>) Utils.getFirstTypeArgumentFromExtendedClass(getClass());
}

@Override
public void configureWith(C config) {
this.pollingPeriod = config.getPollingPeriod();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.javaoperatorsdk.operator.processing.dependent.kubernetes;

import io.javaoperatorsdk.operator.api.config.ConfigurationService;

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

public class InformerConfig {

private String[] namespaces = new String[0];
private String labelSelector = EMPTY_STRING;
private ConfigurationService configurationService;

public InformerConfig() {}

public InformerConfig(String[] namespaces, String labelSelector,
ConfigurationService configurationService) {
this.namespaces = namespaces;
this.labelSelector = labelSelector;
this.configurationService = configurationService;
}

public void setNamespaces(String[] namespaces) {
this.namespaces = namespaces;
}

public void setLabelSelector(String labelSelector) {
this.labelSelector = labelSelector;
}

public void setConfigurationService(
ConfigurationService configurationService) {
this.configurationService = configurationService;
}

public String[] namespaces() {
return namespaces;
}

public String labelSelector() {
return labelSelector;
}

public ConfigurationService getConfigurationService() {
return configurationService;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
public @interface KubernetesDependent {

boolean ADD_OWNER_REFERENCE_DEFAULT = true;
boolean EDIT_ONLY_DEFAULT = false;

boolean addOwnerReference() default ADD_OWNER_REFERENCE_DEFAULT;

boolean editOnly() default EDIT_ONLY_DEFAULT;

/**
* Specified which namespaces this Controller monitors for custom resources events. If no
* namespace is specified then the controller will monitor the namespaces configured for the
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
package io.javaoperatorsdk.operator.processing.dependent.kubernetes;

import java.util.Optional;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
import io.javaoperatorsdk.operator.api.config.Utils;
import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
import io.javaoperatorsdk.operator.api.reconciler.dependent.AbstractDependentResource;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResourceConfigurator;
import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider;
import io.javaoperatorsdk.operator.api.reconciler.dependent.KubernetesClientAware;
import io.javaoperatorsdk.operator.api.reconciler.dependent.GenericDependentResource;
import io.javaoperatorsdk.operator.processing.event.ResourceID;
import io.javaoperatorsdk.operator.processing.event.source.AssociatedSecondaryResourceIdentifier;
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
Expand All @@ -25,27 +19,27 @@
import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers;

public abstract class KubernetesDependentResource<R extends HasMetadata, P extends HasMetadata>
extends AbstractDependentResource<R, P, KubernetesDependentResourceConfig>
implements KubernetesClientAware, EventSourceProvider<P>,
DependentResourceConfigurator<KubernetesDependentResourceConfig> {
extends KubernetesDependentResourceBase<R, P, KubernetesDependentResourceConfig>
implements GenericDependentResource<R, P> {

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

protected KubernetesClient client;
private InformerEventSource<R, P> informerEventSource;
private boolean addOwnerReference;
protected ResourceMatcher resourceMatcher;
protected ResourceUpdatePreProcessor<R> resourceUpdatePreProcessor;
private boolean addOwnerReference;
private boolean editOnly = false;


@Override
public void configureWith(KubernetesDependentResourceConfig config) {
super.configureWith(config);
configureWith(config.getConfigurationService(), config.labelSelector(),
Set.of(config.namespaces()), config.addOwnerReference());
Set.of(config.namespaces()), config.addOwnerReference(), config.isEditOnly());
}

@SuppressWarnings("unchecked")
private void configureWith(ConfigurationService configService, String labelSelector,
Set<String> namespaces, boolean addOwnerReference) {
Set<String> namespaces, boolean addOwnerReference, boolean editOnly) {
final var primaryResourcesRetriever =
(this instanceof PrimaryResourcesRetriever) ? (PrimaryResourcesRetriever<R>) this
: Mappers.fromOwnerReference();
Expand All @@ -60,7 +54,8 @@ private void configureWith(ConfigurationService configService, String labelSelec
.withPrimaryResourcesRetriever(primaryResourcesRetriever)
.withAssociatedSecondaryResourceIdentifier(secondaryResourceIdentifier)
.build();
configureWith(configService, new InformerEventSource<>(ic, client), addOwnerReference);
configureWith(configService, new InformerEventSource<>(ic, client), addOwnerReference,
editOnly);
}

/**
Expand All @@ -72,9 +67,10 @@ private void configureWith(ConfigurationService configService, String labelSelec
*/
public void configureWith(ConfigurationService configurationService,
InformerEventSource<R, P> informerEventSource,
boolean addOwnerReference) {
boolean addOwnerReference, boolean editOnly) {
this.informerEventSource = informerEventSource;
this.addOwnerReference = addOwnerReference;
this.editOnly = editOnly;
initResourceMatcherAndUpdatePreProcessorIfNotSet(configurationService);
}

Expand All @@ -85,42 +81,39 @@ protected void beforeCreate(R desired, P primary) {
}

@Override
protected boolean match(R actualResource, R desiredResource, Context context) {
public boolean match(R actualResource, R desiredResource, Context context) {
return resourceMatcher.match(actualResource, desiredResource, context);
}

@SuppressWarnings("unchecked")
@Override
protected R create(R target, P primary, Context context) {
public void create(R target, P primary, Context context) {
if (editOnly) {
return;
}
log.debug("Creating target resource with type: " +
"{}, with id: {}", target.getClass(), ResourceID.fromResource(target));
beforeCreate(target, primary);
Class<R> targetClass = (Class<R>) target.getClass();
return client.resources(targetClass).inNamespace(target.getMetadata().getNamespace())
client.resources(targetClass).inNamespace(target.getMetadata().getNamespace())
.create(target);
}

@SuppressWarnings("unchecked")
@Override
protected R update(R actual, R target, P primary, Context context) {
public void update(R actual, R target, P primary, Context context) {
log.debug("Updating target resource with type: {}, with id: {}", target.getClass(),
ResourceID.fromResource(target));
Class<R> targetClass = (Class<R>) target.getClass();
var updatedActual = resourceUpdatePreProcessor.replaceSpecOnActual(actual, target);
return client.resources(targetClass).inNamespace(target.getMetadata().getNamespace())
client.resources(targetClass).inNamespace(target.getMetadata().getNamespace())
.replace(updatedActual);
}

@Override
public EventSource eventSource(EventSourceContext<P> context) {
initResourceMatcherAndUpdatePreProcessorIfNotSet(context.getConfigurationService());
if (informerEventSource == null) {
configureWith(context.getConfigurationService(), null, null,
KubernetesDependent.ADD_OWNER_REFERENCE_DEFAULT);
log.warn("Using default configuration for " + resourceType().getSimpleName()
+ " KubernetesDependentResource, call configureWith to provide configuration");
}
return informerEventSource;
return super.eventSource(context);
}

public KubernetesDependentResource<R, P> setInformerEventSource(
Expand All @@ -138,20 +131,6 @@ public void delete(P primary, Context context) {
}

@SuppressWarnings("unchecked")
protected Class<R> resourceType() {
return (Class<R>) Utils.getFirstTypeArgumentFromExtendedClass(getClass());
}

@Override
public Optional<R> getResource(P primaryResource) {
return informerEventSource.getAssociated(primaryResource);
}

@Override
public void setKubernetesClient(KubernetesClient kubernetesClient) {
this.client = kubernetesClient;
}

/**
* Override this method to configure resource matcher
*
Expand Down
Loading