Skip to content
Draft
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
@@ -1,14 +1,18 @@
package io.javaoperatorsdk.operator.api.reconciler;

import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.javaoperatorsdk.operator.api.config.ControllerConfiguration;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedWorkflowAndDependentResourceContext;
import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedWorkflowAndDependentResourceContext;
import io.javaoperatorsdk.operator.processing.Controller;
Expand All @@ -24,6 +28,7 @@ public class DefaultContext<P extends HasMetadata> implements Context<P> {
private final ControllerConfiguration<P> controllerConfiguration;
private final DefaultManagedWorkflowAndDependentResourceContext<P>
defaultManagedDependentResourceContext;
private final Map<DependentResource<?, P>, Object> desiredStates = new ConcurrentHashMap<>();

public DefaultContext(RetryInfo retryInfo, Controller<P> controller, P primaryResource) {
this.retryInfo = retryInfo;
Expand Down Expand Up @@ -123,4 +128,12 @@ public DefaultContext<P> setRetryInfo(RetryInfo retryInfo) {
this.retryInfo = retryInfo;
return this;
}

@SuppressWarnings("unchecked")
public <R> R desiredStateFor(
DependentResource<R, P> dependentResource, Function<P, R> desiredStateComputer) {
return (R)
desiredStates.computeIfAbsent(
dependentResource, ignored -> desiredStateComputer.apply(getPrimaryResource()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.DefaultContext;
import io.javaoperatorsdk.operator.api.reconciler.Ignore;
import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
Expand Down Expand Up @@ -70,7 +71,7 @@ protected ReconcileResult<R> reconcile(P primary, R actualResource, Context<P> c
if (creatable() || updatable()) {
if (actualResource == null) {
if (creatable) {
var desired = desired(primary, context);
var desired = doDesired(context);
throwIfNull(desired, primary, "Desired");
logForOperation("Creating", primary, desired);
var createdResource = handleCreate(desired, primary, context);
Expand All @@ -80,7 +81,7 @@ protected ReconcileResult<R> reconcile(P primary, R actualResource, Context<P> c
if (updatable()) {
final Matcher.Result<R> match = match(actualResource, primary, context);
if (!match.matched()) {
final var desired = match.computedDesired().orElseGet(() -> desired(primary, context));
final var desired = match.computedDesired().orElseGet(() -> doDesired(context));
throwIfNull(desired, primary, "Desired");
logForOperation("Updating", primary, desired);
var updatedResource = handleUpdate(actualResource, desired, primary, context);
Expand Down Expand Up @@ -112,7 +113,6 @@ protected ReconcileResult<R> reconcile(P primary, R actualResource, Context<P> c

@Override
public Optional<R> getSecondaryResource(P primary, Context<P> context) {

var secondaryResources = context.getSecondaryResources(resourceType());
if (secondaryResources.isEmpty()) {
return Optional.empty();
Expand All @@ -136,7 +136,7 @@ public Optional<R> getSecondaryResource(P primary, Context<P> context) {
*/
protected Optional<R> selectTargetSecondaryResource(
Set<R> secondaryResources, P primary, Context<P> context) {
R desired = desired(primary, context);
R desired = doDesired(context);
var targetResources = secondaryResources.stream().filter(r -> r.equals(desired)).toList();
if (targetResources.size() > 1) {
throw new IllegalStateException(
Expand Down Expand Up @@ -205,6 +205,12 @@ protected R desired(P primary, Context<P> context) {
+ " updated");
}

protected R doDesired(Context<P> context) {
assert context instanceof DefaultContext<P>;
DefaultContext<P> defaultContext = (DefaultContext<P>) context;
return defaultContext.desiredStateFor(this, p -> desired(p, defaultContext));
}

public void delete(P primary, Context<P> context) {
dependentResourceReconciler.delete(primary, context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ protected void handleExplicitStateCreation(P primary, R created, Context<P> cont

@Override
public Matcher.Result<R> match(R resource, P primary, Context<P> context) {
var desired = desired(primary, context);
var desired = doDesired(context);
return Matcher.Result.computed(resource.equals(desired), desired);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import io.javaoperatorsdk.operator.api.reconciler.Ignore;
import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter;
import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result;

class BulkDependentResourceReconciler<R, P extends HasMetadata>
implements DependentResourceReconciler<R, P> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public static <R extends HasMetadata, P extends HasMetadata> Matcher.Result<R> m
Context<P> context,
boolean labelsAndAnnotationsEquality,
String... ignorePaths) {
final var desired = dependentResource.desired(primary, context);
final var desired = dependentResource.doDesired(context);
return match(desired, actualResource, labelsAndAnnotationsEquality, context, ignorePaths);
}

Expand All @@ -135,7 +135,7 @@ public static <R extends HasMetadata, P extends HasMetadata> Matcher.Result<R> m
boolean specEquality,
boolean labelsAndAnnotationsEquality,
String... ignorePaths) {
final var desired = dependentResource.desired(primary, context);
final var desired = dependentResource.doDesired(context);
return match(
desired, actualResource, labelsAndAnnotationsEquality, specEquality, context, ignorePaths);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public R update(R actual, R desired, P primary, Context<P> context) {

@Override
public Result<R> match(R actualResource, P primary, Context<P> context) {
final var desired = desired(primary, context);
final var desired = doDesired(context);
return match(actualResource, desired, primary, context);
}

Expand Down Expand Up @@ -288,16 +288,16 @@ protected Optional<R> selectTargetSecondaryResource(
* @return id of the target managed resource
*/
protected ResourceID targetSecondaryResourceID(P primary, Context<P> context) {
return ResourceID.fromResource(desired(primary, context));
return ResourceID.fromResource(doDesired(context));
}

protected boolean addOwnerReference() {
return garbageCollected;
}

@Override
protected R desired(P primary, Context<P> context) {
return super.desired(primary, context);
protected R doDesired(Context<P> context) {
return super.doDesired(context);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.DefaultContext;
import io.javaoperatorsdk.operator.sample.simple.TestCustomResource;

import static org.junit.jupiter.api.Assertions.*;
Expand All @@ -15,15 +16,18 @@

class AbstractDependentResourceTest {

private static final TestCustomResource PRIMARY = new TestCustomResource();
private static final DefaultContext<TestCustomResource> CONTEXT =
new DefaultContext<>(mock(), mock(), PRIMARY);

@Test
void throwsExceptionIfDesiredIsNullOnCreate() {
TestDependentResource testDependentResource = new TestDependentResource();
testDependentResource.setSecondary(null);
testDependentResource.setDesired(null);

assertThrows(
DependentResourceException.class,
() -> testDependentResource.reconcile(new TestCustomResource(), null));
DependentResourceException.class, () -> testDependentResource.reconcile(PRIMARY, CONTEXT));
}

@Test
Expand All @@ -33,8 +37,7 @@ void throwsExceptionIfDesiredIsNullOnUpdate() {
testDependentResource.setDesired(null);

assertThrows(
DependentResourceException.class,
() -> testDependentResource.reconcile(new TestCustomResource(), null));
DependentResourceException.class, () -> testDependentResource.reconcile(PRIMARY, CONTEXT));
}

@Test
Expand All @@ -44,8 +47,7 @@ void throwsExceptionIfCreateReturnsNull() {
testDependentResource.setDesired(configMap());

assertThrows(
DependentResourceException.class,
() -> testDependentResource.reconcile(new TestCustomResource(), null));
DependentResourceException.class, () -> testDependentResource.reconcile(PRIMARY, CONTEXT));
}

@Test
Expand All @@ -55,8 +57,7 @@ void throwsExceptionIfUpdateReturnsNull() {
testDependentResource.setDesired(configMap());

assertThrows(
DependentResourceException.class,
() -> testDependentResource.reconcile(new TestCustomResource(), null));
DependentResourceException.class, () -> testDependentResource.reconcile(PRIMARY, CONTEXT));
}

private ConfigMap configMap() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,48 @@
import java.util.Map;
import java.util.Optional;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
import io.fabric8.kubernetes.api.model.apps.DeploymentStatusBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.javaoperatorsdk.operator.MockKubernetesClient;
import io.javaoperatorsdk.operator.ReconcilerUtils;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.DefaultContext;

import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher.match;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@SuppressWarnings({"unchecked"})
class GenericKubernetesResourceMatcherTest {

private static final Context context = mock(Context.class);
private static final Context context = new TestContext();

private static class TestContext extends DefaultContext<HasMetadata> {
private final KubernetesClient client = MockKubernetesClient.client(HasMetadata.class);

public TestContext() {
this(null);
}

public TestContext(HasMetadata primary) {
super(mock(), mock(), primary);
}

@Override
public KubernetesClient getClient() {
return client;
}
}

Deployment actual = createDeployment();
Deployment desired = createDeployment();
TestDependentResource dependentResource = new TestDependentResource(desired);

@BeforeAll
static void setUp() {
final var client = MockKubernetesClient.client(HasMetadata.class);
when(context.getClient()).thenReturn(client);
}

@Test
void matchesTrivialCases() {
assertThat(GenericKubernetesResourceMatcher.match(desired, actual, context).matched()).isTrue();
Expand Down Expand Up @@ -62,9 +73,10 @@ void matchesWithStrongSpecEquality() {
@Test
void doesNotMatchRemovedValues() {
actual = createDeployment();
final var localContext = new TestContext(createPrimary("removed"));
assertThat(
GenericKubernetesResourceMatcher.match(
dependentResource.desired(createPrimary("removed"), null), actual, context)
dependentResource.doDesired(localContext), actual, localContext)
.matched())
.withFailMessage("Removing values in metadata should lead to a mismatch")
.isFalse();
Expand Down