Skip to content

Multiple version in CRD Support #879

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 7 commits into from
Jan 31, 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 @@ -180,7 +180,8 @@ public synchronized void stop() {

public synchronized void add(Controller controller) {
final var configuration = controller.getConfiguration();
final var resourceTypeName = configuration.getResourceTypeName();
final var resourceTypeName = ReconcilerUtils
.getResourceTypeNameWithVersion(configuration.getResourceClass());
final var existing = controllers.get(resourceTypeName);
if (existing != null) {
throw new OperatorException("Cannot register controller '" + configuration.getName()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,13 @@ public void setApiVersion(String s) {
return Constants.NO_FINALIZER.equals(finalizer) || validator.isFinalizerValid(finalizer);
}

public static String getResourceTypeName(Class<? extends HasMetadata> resourceClass) {
// todo: use fabric8 method when 5.12 is released
// return HasMetadata.getFullResourceName(resourceClass);
public static String getResourceTypeNameWithVersion(Class<? extends HasMetadata> resourceClass) {
final var version = HasMetadata.getVersion(resourceClass);
return getResourceTypeName(resourceClass) + "/" + version;
}

public static String getResourceTypeName(
Class<? extends HasMetadata> resourceClass) {
final var group = HasMetadata.getGroup(resourceClass);
final var plural = HasMetadata.getPlural(resourceClass);
return (group == null || group.isEmpty()) ? plural : plural + "." + group;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public void start() {
try {
eventSource.start();
} catch (Exception e) {
log.warn("Error starting {} -> {}", eventSource, e);
log.warn("Error starting {}", eventSource, e);
}
}
eventProcessor.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
import io.javaoperatorsdk.operator.processing.Controller;
import io.javaoperatorsdk.operator.sample.simple.DuplicateCRController;
import io.javaoperatorsdk.operator.sample.simple.TestCustomReconciler;
import io.javaoperatorsdk.operator.sample.simple.TestCustomReconcilerV2;
import io.javaoperatorsdk.operator.sample.simple.TestCustomReconcilerOtherV1;
import io.javaoperatorsdk.operator.sample.simple.TestCustomResource;
import io.javaoperatorsdk.operator.sample.simple.TestCustomResourceV2;
import io.javaoperatorsdk.operator.sample.simple.TestCustomResourceOtherV1;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand All @@ -30,11 +30,11 @@ public void shouldNotAddMultipleControllersForSameCustomResource() {
}

@Test
public void addingMultipleControllersForCustomResourcesWithDifferentVersionsShouldNotWork() {
public void addingMultipleControllersForCustomResourcesWithSameVersionsShouldNotWork() {
final var registered = new TestControllerConfiguration<>(new TestCustomReconciler(null),
TestCustomResource.class);
final var duplicated = new TestControllerConfiguration<>(new TestCustomReconcilerV2(),
TestCustomResourceV2.class);
final var duplicated = new TestControllerConfiguration<>(new TestCustomReconcilerOtherV1(),
TestCustomResourceOtherV1.class);

checkException(registered, duplicated);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;

@ControllerConfiguration
public class TestCustomReconcilerV2 implements Reconciler<TestCustomResourceV2> {
public class TestCustomReconcilerOtherV1 implements Reconciler<TestCustomResourceOtherV1> {

@Override
public UpdateControl<TestCustomResourceV2> reconcile(TestCustomResourceV2 resource,
public UpdateControl<TestCustomResourceOtherV1> reconcile(TestCustomResourceOtherV1 resource,
Context context) {
return UpdateControl.noUpdate();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import io.fabric8.kubernetes.model.annotation.Version;

@Group("sample.javaoperatorsdk.io")
@Version("v2")
@Version("v1")
@Kind("TestCustomResource") // this is needed to override the automatically generated kind
public class TestCustomResourceV2
public class TestCustomResourceOtherV1
extends CustomResource<TestCustomResourceSpec, TestCustomResourceStatus> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package io.javaoperatorsdk.operator;

import java.time.Duration;
import java.util.HashMap;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.javaoperatorsdk.operator.config.runtime.DefaultConfigurationService;
import io.javaoperatorsdk.operator.junit.OperatorExtension;
import io.javaoperatorsdk.operator.sample.multiversioncrd.*;

import static org.awaitility.Awaitility.await;

class MultiVersionCRDIT {

public static final String CR_V1_NAME = "crv1";
public static final String CR_V2_NAME = "crv2";
@RegisterExtension
OperatorExtension operator =
OperatorExtension.builder()
.withConfigurationService(DefaultConfigurationService.instance())
.withReconciler(MultiVersionCRDTestReconciler1.class)
.withReconciler(MultiVersionCRDTestReconciler2.class)
.build();

@Test
void multipleCRDVersions() {
operator.create(MultiVersionCRDTestCustomResource1.class, createTestResourceV1WithoutLabel());
operator.create(MultiVersionCRDTestCustomResource2.class, createTestResourceV2WithLabel());

await()
.atMost(Duration.ofSeconds(2))
.pollInterval(Duration.ofMillis(50))
.until(
() -> {
var crV1Now = operator.get(MultiVersionCRDTestCustomResource1.class, CR_V1_NAME);
var crV2Now = operator.get(MultiVersionCRDTestCustomResource2.class, CR_V2_NAME);
return crV1Now.getStatus().getReconciledBy().size() == 1
&& crV1Now.getStatus().getReconciledBy()
.contains(MultiVersionCRDTestReconciler1.class.getSimpleName())
&& crV2Now.getStatus().getReconciledBy().size() == 1
&& crV2Now.getStatus().getReconciledBy()
.contains(MultiVersionCRDTestReconciler2.class.getSimpleName());
});
}

MultiVersionCRDTestCustomResource1 createTestResourceV1WithoutLabel() {
MultiVersionCRDTestCustomResource1 cr = new MultiVersionCRDTestCustomResource1();
cr.setMetadata(new ObjectMeta());
cr.getMetadata().setName(CR_V1_NAME);
cr.setSpec(new MultiVersionCRDTestCustomResourceSpec1());
cr.getSpec().setValue1(1);
cr.getSpec().setValue2(1);
return cr;
}

MultiVersionCRDTestCustomResource2 createTestResourceV2WithLabel() {
MultiVersionCRDTestCustomResource2 cr = new MultiVersionCRDTestCustomResource2();
cr.setMetadata(new ObjectMeta());
cr.getMetadata().setName(CR_V2_NAME);
cr.getMetadata().setLabels(new HashMap<>());
cr.getMetadata().getLabels().put("version", "v2");
cr.setSpec(new MultiVersionCRDTestCustomResourceSpec2());
cr.getSpec().setValue1(1);
return cr;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.javaoperatorsdk.operator.sample.multiversioncrd;

import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Kind;
import io.fabric8.kubernetes.model.annotation.ShortNames;
import io.fabric8.kubernetes.model.annotation.Version;

@Group("sample.javaoperatorsdk")
@Version("v1")
@Kind("MultiVersionCRDTestCustomResource")
@ShortNames("mv1")
public class MultiVersionCRDTestCustomResource1
extends
CustomResource<MultiVersionCRDTestCustomResourceSpec1, MultiVersionCRDTestCustomResourceStatus1>
implements Namespaced {

@Override
protected MultiVersionCRDTestCustomResourceStatus1 initStatus() {
return new MultiVersionCRDTestCustomResourceStatus1();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.javaoperatorsdk.operator.sample.multiversioncrd;

import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Kind;
import io.fabric8.kubernetes.model.annotation.ShortNames;
import io.fabric8.kubernetes.model.annotation.Version;

@Group("sample.javaoperatorsdk")
@Version(value = "v2", storage = false)
@Kind("MultiVersionCRDTestCustomResource")
@ShortNames("mv2")
public class MultiVersionCRDTestCustomResource2
extends
CustomResource<MultiVersionCRDTestCustomResourceSpec2, MultiVersionCRDTestCustomResourceStatus2>
implements Namespaced {

@Override
protected MultiVersionCRDTestCustomResourceStatus2 initStatus() {
return new MultiVersionCRDTestCustomResourceStatus2();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.javaoperatorsdk.operator.sample.multiversioncrd;

public class MultiVersionCRDTestCustomResourceSpec1 {

private int value1;

private int value2;

public int getValue1() {
return value1;
}

public MultiVersionCRDTestCustomResourceSpec1 setValue1(int value1) {
this.value1 = value1;
return this;
}

public int getValue2() {
return value2;
}

public MultiVersionCRDTestCustomResourceSpec1 setValue2(int value2) {
this.value2 = value2;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.javaoperatorsdk.operator.sample.multiversioncrd;

public class MultiVersionCRDTestCustomResourceSpec2 {

private int value1;

public int getValue1() {
return value1;
}

public MultiVersionCRDTestCustomResourceSpec2 setValue1(int value1) {
this.value1 = value1;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.javaoperatorsdk.operator.sample.multiversioncrd;

import java.util.ArrayList;
import java.util.List;

public class MultiVersionCRDTestCustomResourceStatus1 {

private int value1;

private int value2;

private List<String> reconciledBy = new ArrayList<>();

public int getValue1() {
return value1;
}

public MultiVersionCRDTestCustomResourceStatus1 setValue1(int value1) {
this.value1 = value1;
return this;
}

public int getValue2() {
return value2;
}

public MultiVersionCRDTestCustomResourceStatus1 setValue2(int value2) {
this.value2 = value2;
return this;
}

public List<String> getReconciledBy() {
return reconciledBy;
}

public MultiVersionCRDTestCustomResourceStatus1 setReconciledBy(List<String> reconciledBy) {
this.reconciledBy = reconciledBy;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.javaoperatorsdk.operator.sample.multiversioncrd;

import java.util.ArrayList;
import java.util.List;

public class MultiVersionCRDTestCustomResourceStatus2 {

private int value1;

private List<String> reconciledBy = new ArrayList<>();

public int getValue1() {
return value1;
}

public MultiVersionCRDTestCustomResourceStatus2 setValue1(int value1) {
this.value1 = value1;
return this;
}

public List<String> getReconciledBy() {
return reconciledBy;
}

public MultiVersionCRDTestCustomResourceStatus2 setReconciledBy(List<String> reconciledBy) {
this.reconciledBy = reconciledBy;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.javaoperatorsdk.operator.sample.multiversioncrd;

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

import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;

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

@ControllerConfiguration(finalizerName = NO_FINALIZER, labelSelector = "!version")
public class MultiVersionCRDTestReconciler1
implements Reconciler<MultiVersionCRDTestCustomResource1> {

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

@Override
public UpdateControl<MultiVersionCRDTestCustomResource1> reconcile(
MultiVersionCRDTestCustomResource1 resource, Context context) {
log.info("Reconcile MultiVersionCRDTestCustomResource1: {}",
resource.getMetadata().getName());
resource.getStatus().setValue1(resource.getStatus().getValue1() + 1);
resource.getStatus().setValue2(resource.getStatus().getValue2() + 1);
if (!resource.getStatus().getReconciledBy().contains(getClass().getSimpleName())) {
resource.getStatus().getReconciledBy().add(getClass().getSimpleName());
}
return UpdateControl.updateStatus(resource);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.javaoperatorsdk.operator.sample.multiversioncrd;

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

import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;

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

@ControllerConfiguration(
finalizerName = NO_FINALIZER,
labelSelector = "version in (v2)")
public class MultiVersionCRDTestReconciler2
implements Reconciler<MultiVersionCRDTestCustomResource2> {

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

@Override
public UpdateControl<MultiVersionCRDTestCustomResource2> reconcile(
MultiVersionCRDTestCustomResource2 resource, Context context) {
log.info("Reconcile MultiVersionCRDTestCustomResource2: {}",
resource.getMetadata().getName());
resource.getStatus().setValue1(resource.getStatus().getValue1() + 1);
if (!resource.getStatus().getReconciledBy().contains(getClass().getSimpleName())) {
resource.getStatus().getReconciledBy().add(getClass().getSimpleName());
}
return UpdateControl.updateStatus(resource);
}
}