Skip to content

feat: use SSA matcher flag for child resources #150

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
Jan 1, 2025
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
4 changes: 4 additions & 0 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ It has several attributes:
(Same as `reconcilePrecondition` in Java Operator SDK)
- **`readyPostCondition`** - condition to check if the resource is considered to be ready. If a resource is ready all the resources, which depend on it
can proceed in reconciliation.
- **`matcher`** - Match resources with Java Operator SDK Server Side Apply based matcher (default `SSA`). Matching resources
is makes the reconciliation much more efficient, since controller updates the resource only if truly changed. However,
it is not possible to match resources because of some characteristics of Kubernetes API (default values, value conversions, etc)
so you can always opt out the matching (use value `NONE`), and update the resource on every reconciliation.

#### Built-in conditions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public class DependentResourceSpec {

private String resourceTemplate;

private Matcher matcher = Matcher.SSA;

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

@PreserveUnknownFields
Expand Down Expand Up @@ -92,6 +94,14 @@ public void setClusterScoped(boolean clusterScoped) {
this.clusterScoped = clusterScoped;
}

public Matcher getMatcher() {
return matcher;
}

public void setMatcher(Matcher matcher) {
this.matcher = matcher;
}

@Override
public boolean equals(Object o) {
if (this == o)
Expand All @@ -101,15 +111,15 @@ public boolean equals(Object o) {
DependentResourceSpec that = (DependentResourceSpec) o;
return clusterScoped == that.clusterScoped && Objects.equals(name, that.name)
&& Objects.equals(resource, that.resource)
&& Objects.equals(resourceTemplate, that.resourceTemplate)
&& Objects.equals(resourceTemplate, that.resourceTemplate) && matcher == that.matcher
&& Objects.equals(dependsOn, that.dependsOn)
&& Objects.equals(readyPostCondition, that.readyPostCondition)
&& Objects.equals(condition, that.condition);
}

@Override
public int hashCode() {
return Objects.hash(name, clusterScoped, resource, resourceTemplate, dependsOn,
return Objects.hash(name, clusterScoped, resource, resourceTemplate, matcher, dependsOn,
readyPostCondition, condition);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.javaoperatorsdk.operator.glue.customresource.glue;

public enum Matcher {
NONE, SSA
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected;
import io.javaoperatorsdk.operator.glue.customresource.glue.Glue;
import io.javaoperatorsdk.operator.glue.customresource.glue.Matcher;
import io.javaoperatorsdk.operator.glue.templating.GenericTemplateHandler;

public class GCGenericDependentResource extends GenericDependentResource
implements GarbageCollected<Glue> {

public GCGenericDependentResource(GenericTemplateHandler genericTemplateHandler,
GenericKubernetesResource desired, String name,
boolean clusterScoped) {
super(genericTemplateHandler, desired, name, clusterScoped);
boolean clusterScoped, Matcher matcher) {
super(genericTemplateHandler, desired, name, clusterScoped, matcher);
}

public GCGenericDependentResource(GenericTemplateHandler genericTemplateHandler,
String desiredTemplate, String name, boolean clusterScoped) {
super(genericTemplateHandler, desiredTemplate, name, clusterScoped);
String desiredTemplate, String name, boolean clusterScoped, Matcher matcher) {
super(genericTemplateHandler, desiredTemplate, name, clusterScoped, matcher);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter;
import io.javaoperatorsdk.operator.glue.Utils;
import io.javaoperatorsdk.operator.glue.customresource.glue.Glue;
import io.javaoperatorsdk.operator.glue.customresource.glue.Matcher;
import io.javaoperatorsdk.operator.glue.reconciler.glue.GlueReconciler;
import io.javaoperatorsdk.operator.glue.templating.GenericTemplateHandler;
import io.javaoperatorsdk.operator.processing.GroupVersionKind;
Expand All @@ -23,28 +24,31 @@ public class GenericDependentResource
private final String desiredTemplate;
private final String name;
private final boolean clusterScoped;
private final Matcher matcher;

// optimize share between instances
private final GenericTemplateHandler genericTemplateHandler;

public GenericDependentResource(GenericTemplateHandler genericTemplateHandler,
GenericKubernetesResource desired, String name,
boolean clusterScoped) {
boolean clusterScoped, Matcher matcher) {
super(new GroupVersionKind(desired.getApiVersion(), desired.getKind()));
this.desired = desired;
this.matcher = matcher;
this.desiredTemplate = null;
this.name = name;
this.clusterScoped = clusterScoped;
this.genericTemplateHandler = genericTemplateHandler;
}

public GenericDependentResource(GenericTemplateHandler genericTemplateHandler,
String desiredTemplate, String name, boolean clusterScoped) {
String desiredTemplate, String name, boolean clusterScoped, Matcher matcher) {
super(new GroupVersionKind(Utils.getApiVersionFromTemplate(desiredTemplate),
Utils.getKindFromTemplate(desiredTemplate)));
this.genericTemplateHandler = genericTemplateHandler;
this.name = name;
this.desiredTemplate = desiredTemplate;
this.matcher = matcher;
this.desired = null;
this.clusterScoped = clusterScoped;
}
Expand Down Expand Up @@ -75,6 +79,10 @@ public Result<GenericKubernetesResource> match(GenericKubernetesResource actualR
&& actualResource.getApiVersion().equals("apps/v1")) {
return super.match(actualResource, primary, context);
}
return Result.nonComputed(false);
if (Matcher.SSA.equals(matcher)) {
return super.match(actualResource, primary, context);
} else {
return Result.nonComputed(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -211,16 +211,17 @@ private GenericDependentResource createDependentResource(DependentResourceSpec s
return spec.getResourceTemplate() != null
? new GCGenericDependentResource(genericTemplateHandler, spec.getResourceTemplate(),
spec.getName(),
spec.isClusterScoped())
spec.isClusterScoped(), spec.getMatcher())
: new GCGenericDependentResource(genericTemplateHandler, spec.getResource(),
spec.getName(),
spec.isClusterScoped());
spec.isClusterScoped(), spec.getMatcher());
} else {
return spec.getResourceTemplate() != null
? new GenericDependentResource(genericTemplateHandler,
spec.getResourceTemplate(), spec.getName(), spec.isClusterScoped())
spec.getResourceTemplate(), spec.getName(), spec.isClusterScoped(),
spec.getMatcher())
: new GenericDependentResource(genericTemplateHandler,
spec.getResource(), spec.getName(), spec.isClusterScoped());
spec.getResource(), spec.getName(), spec.isClusterScoped(), spec.getMatcher());
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/test/java/io/javaoperatorsdk/operator/glue/GlueTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.javaoperatorsdk.operator.glue.reconciler.ValidationAndErrorHandler;
import io.quarkus.test.junit.QuarkusTest;

import static io.javaoperatorsdk.operator.glue.TestUtils.INITIAL_RECONCILE_WAIT_TIMEOUT;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

Expand Down Expand Up @@ -354,6 +355,19 @@ void pathRelatedResourceStatus(String glueFileName) {
});
}

@Test
void customizeMatcher() {
var glue = createGlue("/glue/SimpleNotUseSSA.yaml");

await().pollDelay(INITIAL_RECONCILE_WAIT_TIMEOUT).untilAsserted(() -> {
assertThat(get(ConfigMap.class, "simple-glue-no-ssa-configmap")).isNotNull();
});
delete(glue);
await().pollDelay(INITIAL_RECONCILE_WAIT_TIMEOUT).untilAsserted(() -> {
assertThat(get(ConfigMap.class, "simple-glue-no-ssa-configmap")).isNull();
});
}

private List<Glue> testWorkflowList(int num) {
List<Glue> res = new ArrayList<>();
IntStream.range(0, num).forEach(index -> {
Expand Down
16 changes: 16 additions & 0 deletions src/test/resources/glue/SimpleNotUseSSA.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Invalid GLUE, presents resources with non-unique name
apiVersion: io.javaoperatorsdk.operator.glue/v1beta1
kind: Glue
metadata:
name: simple-glue-no-ssa
spec:
childResources:
- name: configMap
matcher: NONE
resource:
apiVersion: v1
kind: ConfigMap
metadata:
name: simple-glue-no-ssa-configmap
data:
key: "value1"
Loading