Skip to content

Update both status and custom resource #258

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 9 commits into from
Dec 17, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -27,6 +27,15 @@ public static <T extends CustomResource> UpdateControl<T> updateStatusSubResourc
return new UpdateControl<>(customResource, true, false);
}

/**
* As a results of this there will be two call to K8S API. First the custom resource will be
* updates then the status sub-resource.
*/
public static <T extends CustomResource>
UpdateControl<T> updateCustomResourceAndStatusSubResource(T customResource) {
return new UpdateControl<>(customResource, true, true);
}

public static <T extends CustomResource> UpdateControl<T> noUpdate() {
return new UpdateControl<>(null, false, false);
}
Expand All @@ -42,4 +51,8 @@ public boolean isUpdateStatusSubResource() {
public boolean isUpdateCustomResource() {
return updateCustomResource;
}

public boolean isUpdateCustomResourceAndStatusSubResource() {
return updateCustomResource && updateStatusSubResource;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,16 @@ private PostExecutionControl handleCreateOrUpdate(
UpdateControl<? extends CustomResource> updateControl =
controller.createOrUpdateResource(resource, context);
CustomResource updatedCustomResource = null;
if (updateControl.isUpdateStatusSubResource()) {
if (updateControl.isUpdateCustomResourceAndStatusSubResource()) {
updatedCustomResource = updateCustomResource(updateControl.getCustomResource());
updatedCustomResource = customResourceFacade.updateStatus(updatedCustomResource);
} else if (updateControl.isUpdateStatusSubResource()) {
updatedCustomResource =
customResourceFacade.updateStatus(updateControl.getCustomResource());
} else if (updateControl.isUpdateCustomResource()) {
updatedCustomResource = updateCustomResource(updateControl.getCustomResource());
}

if (updatedCustomResource != null) {
return PostExecutionControl.customResourceUpdated(updatedCustomResource);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ void updatesOnlyStatusSubResource() {
verify(customResourceFacade, never()).replaceWithLock(any());
}

@Test
void updatesBothResourceAndStatus() {
when(controller.createOrUpdateResource(eq(testCustomResource), any()))
.thenReturn(UpdateControl.updateCustomResourceAndStatusSubResource(testCustomResource));
when(customResourceFacade.replaceWithLock(testCustomResource)).thenReturn(testCustomResource);

eventDispatcher.handleExecution(
executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource));

verify(customResourceFacade, times(1)).replaceWithLock(testCustomResource);
verify(customResourceFacade, times(1)).updateStatus(testCustomResource);
}

@Test
void callCreateOrUpdateOnModifiedResource() {
eventDispatcher.handleExecution(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ public KubernetesClient getK8sClient() {
return crOperations;
}

public CustomResource getCustomResource(String name) {
return getCrOperations().inNamespace(TEST_NAMESPACE).withName(name).get();
}

public Operator getOperator() {
return operator;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,12 @@ public static TestCustomResource testCustomResource(String uid) {
resource.getSpec().setValue("test-value");
return resource;
}

public static void waitXms(int x) {
try {
Thread.sleep(x);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package io.javaoperatorsdk.operator;

import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.javaoperatorsdk.operator.doubleupdate.subresource.DoubleUpdateTestCustomResource;
import io.javaoperatorsdk.operator.doubleupdate.subresource.DoubleUpdateTestCustomResourceController;
import io.javaoperatorsdk.operator.doubleupdate.subresource.DoubleUpdateTestCustomResourceSpec;
import io.javaoperatorsdk.operator.doubleupdate.subresource.DoubleUpdateTestCustomResourceStatus;
import io.javaoperatorsdk.operator.sample.subresource.SubResourceTestCustomResourceStatus;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

import java.util.concurrent.TimeUnit;

import static io.javaoperatorsdk.operator.IntegrationTestSupport.TEST_NAMESPACE;
import static io.javaoperatorsdk.operator.TestUtils.waitXms;
import static io.javaoperatorsdk.operator.doubleupdate.subresource.DoubleUpdateTestCustomResourceController.TEST_ANNOTATION;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class UpdatingResAndSubResIT {

private IntegrationTestSupport integrationTestSupport = new IntegrationTestSupport();

@BeforeEach
public void initAndCleanup() {
KubernetesClient k8sClient = new DefaultKubernetesClient();
integrationTestSupport.initialize(
k8sClient, new DoubleUpdateTestCustomResourceController(), "doubleupdate-test-crd.yaml");
integrationTestSupport.cleanup();
}

@Test
public void updatesSubResourceStatus() {
integrationTestSupport.teardownIfSuccess(
() -> {
DoubleUpdateTestCustomResource resource = createTestCustomResource("1");
integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource);

awaitStatusUpdated(resource.getMetadata().getName());
// wait for sure, there are no more events
waitXms(200);

DoubleUpdateTestCustomResource customResource =
(DoubleUpdateTestCustomResource)
integrationTestSupport.getCustomResource(resource.getMetadata().getName());
assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(1);
assertThat(customResource.getStatus())
.isEqualTo(DoubleUpdateTestCustomResourceStatus.State.SUCCESS);
assertThat(customResource.getMetadata().getAnnotations().get(TEST_ANNOTATION))
.isNotNull();
});
}

void awaitStatusUpdated(String name) {
await("cr status updated")
.atMost(5, TimeUnit.SECONDS)
.untilAsserted(
() -> {
DoubleUpdateTestCustomResource cr =
(DoubleUpdateTestCustomResource)
integrationTestSupport
.getCrOperations()
.inNamespace(TEST_NAMESPACE)
.withName(name)
.get();
assertThat(cr.getMetadata().getFinalizers()).hasSize(1);
assertThat(cr).isNotNull();
assertThat(cr.getStatus()).isNotNull();
assertThat(cr.getStatus().getState())
.isEqualTo(SubResourceTestCustomResourceStatus.State.SUCCESS);
});
}

public DoubleUpdateTestCustomResource createTestCustomResource(String id) {
DoubleUpdateTestCustomResource resource = new DoubleUpdateTestCustomResource();
resource.setMetadata(
new ObjectMetaBuilder()
.withName("doubleupdateresource-" + id)
.withNamespace(TEST_NAMESPACE)
.build());
resource.setKind("DoubleUpdateSample");
resource.setSpec(new DoubleUpdateTestCustomResourceSpec());
resource.getSpec().setValue(id);
return resource;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.javaoperatorsdk.operator.doubleupdate.subresource;

import io.fabric8.kubernetes.client.CustomResource;

public class DoubleUpdateTestCustomResource extends CustomResource {

private DoubleUpdateTestCustomResourceSpec spec;

private DoubleUpdateTestCustomResourceStatus status;

public DoubleUpdateTestCustomResourceSpec getSpec() {
return spec;
}

public void setSpec(DoubleUpdateTestCustomResourceSpec spec) {
this.spec = spec;
}

public DoubleUpdateTestCustomResourceStatus getStatus() {
return status;
}

public void setStatus(DoubleUpdateTestCustomResourceStatus status) {
this.status = status;
}

@Override
public String toString() {
return "TestCustomResource{"
+ "spec="
+ spec
+ ", status="
+ status
+ ", extendedFrom="
+ super.toString()
+ '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.javaoperatorsdk.operator.doubleupdate.subresource;

import io.javaoperatorsdk.operator.TestExecutionInfoProvider;
import io.javaoperatorsdk.operator.api.*;

import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Controller(crdName = DoubleUpdateTestCustomResourceController.CRD_NAME)
public class DoubleUpdateTestCustomResourceController
implements ResourceController<DoubleUpdateTestCustomResource>, TestExecutionInfoProvider {

public static final String CRD_NAME = "doubleupdatesamples.sample.javaoperatorsdk";
private static final Logger log =
LoggerFactory.getLogger(DoubleUpdateTestCustomResourceController.class);
public static final String TEST_ANNOTATION = "TestAnnotation";
public static final String TEST_ANNOTATION_VALUE = "TestAnnotationValue";
private final AtomicInteger numberOfExecutions = new AtomicInteger(0);

@Override
public DeleteControl deleteResource(
DoubleUpdateTestCustomResource resource, Context<DoubleUpdateTestCustomResource> context) {
return DeleteControl.DEFAULT_DELETE;
}

@Override
public UpdateControl<DoubleUpdateTestCustomResource> createOrUpdateResource(
DoubleUpdateTestCustomResource resource, Context<DoubleUpdateTestCustomResource> context) {
numberOfExecutions.addAndGet(1);

log.info("Value: " + resource.getSpec().getValue());

resource.getMetadata().setAnnotations(new HashMap<>());
resource.getMetadata().getAnnotations().put(TEST_ANNOTATION, TEST_ANNOTATION_VALUE);
ensureStatusExists(resource);
resource.getStatus().setState(DoubleUpdateTestCustomResourceStatus.State.SUCCESS);

return UpdateControl.updateCustomResourceAndStatusSubResource(resource);
}

private void ensureStatusExists(DoubleUpdateTestCustomResource resource) {
DoubleUpdateTestCustomResourceStatus status = resource.getStatus();
if (status == null) {
status = new DoubleUpdateTestCustomResourceStatus();
resource.setStatus(status);
}
}

public int getNumberOfExecutions() {
return numberOfExecutions.get();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.javaoperatorsdk.operator.doubleupdate.subresource;

public class DoubleUpdateTestCustomResourceSpec {

private String value;

public String getValue() {
return value;
}

public DoubleUpdateTestCustomResourceSpec setValue(String value) {
this.value = value;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.javaoperatorsdk.operator.doubleupdate.subresource;

public class DoubleUpdateTestCustomResourceStatus {

private State state;

public State getState() {
return state;
}

public DoubleUpdateTestCustomResourceStatus setState(State state) {
this.state = state;
return this;
}

public enum State {
SUCCESS,
ERROR
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: doubleupdatesamples.sample.javaoperatorsdk
spec:
group: sample.javaoperatorsdk
version: v1
subresources:
status: {}
scope: Namespaced
names:
plural: doubleupdatesamples
singular: doubleupdatesample
kind: DoubleUpdateSample
shortNames:
- ss