Skip to content

feat: throw exception if desired is null #1180

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 3 commits into from
Apr 26, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public ReconcileResult<R> reconcile(P primary, Context<P> context) {
if (maybeActual.isEmpty()) {
if (creatable) {
var desired = desired(primary, context);
throwIfNull(desired, primary, "Desired");
logForOperation("Creating", primary, desired);
var createdResource = handleCreate(desired, primary, context);
return ReconcileResult.resourceCreated(createdResource);
Expand All @@ -43,6 +44,7 @@ public ReconcileResult<R> reconcile(P primary, Context<P> context) {
final var match = updater.match(actual, primary, context);
if (!match.matched()) {
final var desired = match.computedDesired().orElse(desired(primary, context));
throwIfNull(desired, primary, "Desired");
logForOperation("Updating", primary, desired);
var updatedResource = handleUpdate(actual, desired, primary, context);
return ReconcileResult.resourceUpdated(updatedResource);
Expand All @@ -59,6 +61,13 @@ public ReconcileResult<R> reconcile(P primary, Context<P> context) {
return ReconcileResult.noOperation(maybeActual.orElse(null));
}

private void throwIfNull(R desired, P primary, String descriptor) {
if (desired == null) {
throw new DependentResourceException(
descriptor + " cannot be null. Primary ID: " + ResourceID.fromResource(primary));
}
}

private void logForOperation(String operation, P primary, R desired) {
final var desiredDesc = desired instanceof HasMetadata
? "'" + ((HasMetadata) desired).getMetadata().getName() + "' "
Expand All @@ -71,6 +80,7 @@ private void logForOperation(String operation, P primary, R desired) {
protected R handleCreate(R desired, P primary, Context<P> context) {
ResourceID resourceID = ResourceID.fromResource(primary);
R created = creator.create(desired, primary, context);
throwIfNull(created, primary, "Created resource");
onCreated(resourceID, created);
return created;
}
Expand Down Expand Up @@ -98,6 +108,7 @@ protected R handleCreate(R desired, P primary, Context<P> context) {
protected R handleUpdate(R actual, R desired, P primary, Context<P> context) {
ResourceID resourceID = ResourceID.fromResource(primary);
R updated = updater.update(actual, desired, primary, context);
throwIfNull(updated, primary, "Updated resource");
onUpdated(resourceID, updated, actual);
return updated;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.javaoperatorsdk.operator.processing.dependent;

import io.javaoperatorsdk.operator.OperatorException;

public class DependentResourceException extends OperatorException {

public DependentResourceException() {}

public DependentResourceException(String message) {
super(message);
}

public DependentResourceException(Throwable cause) {
super(cause);
}

public DependentResourceException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package io.javaoperatorsdk.operator.processing.dependent;

import java.util.Optional;

import org.junit.jupiter.api.Test;

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.processing.event.ResourceID;
import io.javaoperatorsdk.operator.sample.simple.TestCustomResource;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class AbstractDependentResourceTest {


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

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

}

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

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

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

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

@Test
void throwsExceptionIfUpdateReturnsNull() {
TestDependentResource testDependentResource = new TestDependentResource();
testDependentResource.setSecondary(configMap());
testDependentResource.setDesired(configMap());

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

private ConfigMap configMap() {
ConfigMap configMap = new ConfigMap();
configMap.setMetadata(new ObjectMetaBuilder()
.withName("test")
.withNamespace("default")
.build());
return configMap;
}

private static class TestDependentResource
extends AbstractDependentResource<ConfigMap, TestCustomResource>
implements Creator<ConfigMap, TestCustomResource>, Updater<ConfigMap, TestCustomResource> {

private ConfigMap secondary;
private ConfigMap desired;

@Override
public Class<ConfigMap> resourceType() {
return ConfigMap.class;
}

@Override
public Optional<ConfigMap> getSecondaryResource(TestCustomResource primary) {
return Optional.ofNullable(secondary);
}

@Override
protected void onCreated(ResourceID primaryResourceId, ConfigMap created) {}

@Override
protected void onUpdated(ResourceID primaryResourceId, ConfigMap updated, ConfigMap actual) {}

@Override
protected ConfigMap desired(TestCustomResource primary, Context<TestCustomResource> context) {
return desired;
}

public ConfigMap getSecondary() {
return secondary;
}

public TestDependentResource setSecondary(ConfigMap secondary) {
this.secondary = secondary;
return this;
}

public ConfigMap getDesired() {
return desired;
}

public TestDependentResource setDesired(ConfigMap desired) {
this.desired = desired;
return this;
}

@Override
public ConfigMap create(ConfigMap desired, TestCustomResource primary,
Context<TestCustomResource> context) {
return null;
}

@Override
public ConfigMap update(ConfigMap actual, ConfigMap desired, TestCustomResource primary,
Context<TestCustomResource> context) {
return null;
}

@Override
public Matcher.Result<ConfigMap> match(ConfigMap actualResource, TestCustomResource primary,
Context<TestCustomResource> context) {
var result = mock(Matcher.Result.class);
when(result.matched()).thenReturn(false);
return result;
}
}
}