Skip to content

Commit 6bec465

Browse files
authored
Cluster Scoped Parent (#94)
Signed-off-by: Attila Mészáros <[email protected]>
1 parent a9deb9e commit 6bec465

File tree

13 files changed

+260
-46
lines changed

13 files changed

+260
-46
lines changed

docs/reference.md

+11-6
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,17 @@ The `DependentResource` implementation of JOSDK makes all kinds of optimizations
8484

8585
## [GlueOperator resource](https://github.com/csviri/kubernetes-glue-operator/releases/latest/download/glueoperators.glue-v1.yml)
8686

87-
The specs of `GlueOperator` are almost identical to `Glue`, it just adds one additional attribute **`parent`**,
88-
which has the following sub-attributes:
89-
- **`apiVersion`** and **`kind`** - specifies the resources to reconciler according to the spec.
90-
Targets are usually custom resources but not necessarily, it also works with built-in Kubernetes
91-
resources.
92-
- **`labelSelector`** - an optional label selector for the target resources
87+
The specs of `GlueOperator` are almost identical to `Glue`, it just adds some additional attributes:
88+
89+
- **`parent`** - specifies the resources handled by the operator. Targets are usually custom resources but not necessarily,
90+
it also works with built-in Kubernetes resources. With the following sub-attributes:
91+
- **`apiVersion`** and **`kind`** - of the target custom resources.
92+
- **`labelSelector`** - optional label selector for the target resources.
93+
- **`clusterScoped`** - optional boolean value, if the parent resource is cluster scoped. Default is `false`.
94+
- **`glueMetadata`** - optionally, you can customize the `Glue` resource created for each parent resource.
95+
This is especially important when the parent is a cluster scoped resource - in that case it is mandatory to set.
96+
Using this you can specify the **`name`** and **`namespace`** of the created `Glue`.
97+
See usage on the sample [secret-copy-operator](https://github.com/csviri/kubernetes-glue-operator/blob/main/src/test/resources/sample/secretcopy/secret-copy.operator.yaml#L10-L12).
9398

9499
See minimal `GlueOperator` [here](https://github.com/csviri/kubernetes-glue-operator/blob/main/src/test/resources/glueoperator/SimpleGlueOperator.yaml).
95100

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.csviri.operator.glue.customresource.operator;
2+
3+
import java.util.Objects;
4+
5+
public class GlueMetadata {
6+
7+
private String name;
8+
private String namespace;
9+
10+
public String getName() {
11+
return name;
12+
}
13+
14+
public void setName(String name) {
15+
this.name = name;
16+
}
17+
18+
public String getNamespace() {
19+
return namespace;
20+
}
21+
22+
public void setNamespace(String namespace) {
23+
this.namespace = namespace;
24+
}
25+
26+
@Override
27+
public boolean equals(Object o) {
28+
if (this == o)
29+
return true;
30+
if (o == null || getClass() != o.getClass())
31+
return false;
32+
GlueMetadata that = (GlueMetadata) o;
33+
return Objects.equals(name, that.name) && Objects.equals(namespace, that.namespace);
34+
}
35+
36+
@Override
37+
public int hashCode() {
38+
return Objects.hash(name, namespace);
39+
}
40+
}

src/main/java/io/csviri/operator/glue/customresource/operator/GlueOperatorSpec.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public class GlueOperatorSpec extends GlueSpec {
1010
@Required
1111
private Parent parent;
1212

13+
private GlueMetadata glueMetadata;
14+
1315
public Parent getParent() {
1416
return parent;
1517
}
@@ -19,6 +21,14 @@ public GlueOperatorSpec setParent(Parent parent) {
1921
return this;
2022
}
2123

24+
public GlueMetadata getGlueMetadata() {
25+
return glueMetadata;
26+
}
27+
28+
public void setGlueMetadata(GlueMetadata glueMetadata) {
29+
this.glueMetadata = glueMetadata;
30+
}
31+
2232
@Override
2333
public boolean equals(Object o) {
2434
if (this == o)
@@ -28,12 +38,11 @@ public boolean equals(Object o) {
2838
if (!super.equals(o))
2939
return false;
3040
GlueOperatorSpec that = (GlueOperatorSpec) o;
31-
return Objects.equals(parent, that.parent);
41+
return Objects.equals(parent, that.parent) && Objects.equals(glueMetadata, that.glueMetadata);
3242
}
3343

3444
@Override
3545
public int hashCode() {
36-
return Objects.hash(super.hashCode(), parent);
46+
return Objects.hash(super.hashCode(), parent, glueMetadata);
3747
}
38-
3948
}

src/main/java/io/csviri/operator/glue/customresource/operator/Parent.java

+28
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package io.csviri.operator.glue.customresource.operator;
22

3+
import java.util.Objects;
4+
35
public class Parent {
46

57
private String apiVersion;
68
private String kind;
9+
private boolean clusterScoped = false;
710
private String labelSelector;
811

12+
913
public Parent() {}
1014

1115
public Parent(String apiVersion, String kind) {
@@ -38,4 +42,28 @@ public String getLabelSelector() {
3842
public void setLabelSelector(String labelSelector) {
3943
this.labelSelector = labelSelector;
4044
}
45+
46+
public boolean isClusterScoped() {
47+
return clusterScoped;
48+
}
49+
50+
public void setClusterScoped(boolean clusterScoped) {
51+
this.clusterScoped = clusterScoped;
52+
}
53+
54+
@Override
55+
public boolean equals(Object o) {
56+
if (this == o)
57+
return true;
58+
if (o == null || getClass() != o.getClass())
59+
return false;
60+
Parent parent = (Parent) o;
61+
return clusterScoped == parent.clusterScoped && Objects.equals(apiVersion, parent.apiVersion)
62+
&& Objects.equals(kind, parent.kind) && Objects.equals(labelSelector, parent.labelSelector);
63+
}
64+
65+
@Override
66+
public int hashCode() {
67+
return Objects.hash(apiVersion, kind, clusterScoped, labelSelector);
68+
}
4169
}
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
package io.csviri.operator.glue.dependent;
22

33
import io.csviri.operator.glue.customresource.glue.Glue;
4+
import io.csviri.operator.glue.templating.GenericTemplateHandler;
45
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
56
import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected;
67

78
public class GCGenericDependentResource extends GenericDependentResource
89
implements GarbageCollected<Glue> {
910

10-
public GCGenericDependentResource(GenericKubernetesResource desired, String name,
11+
public GCGenericDependentResource(GenericTemplateHandler genericTemplateHandler,
12+
GenericKubernetesResource desired, String name,
1113
boolean clusterScoped) {
12-
super(desired, name, clusterScoped);
14+
super(genericTemplateHandler, desired, name, clusterScoped);
1315
}
1416

15-
public GCGenericDependentResource(String desiredTemplate, String name, boolean clusterScoped) {
16-
super(desiredTemplate, name, clusterScoped);
17+
public GCGenericDependentResource(GenericTemplateHandler genericTemplateHandler,
18+
String desiredTemplate, String name, boolean clusterScoped) {
19+
super(genericTemplateHandler, desiredTemplate, name, clusterScoped);
1720
}
1821
}

src/main/java/io/csviri/operator/glue/dependent/GenericDependentResource.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,24 @@ public class GenericDependentResource
2525
private final boolean clusterScoped;
2626

2727
// optimize share between instances
28-
private final GenericTemplateHandler genericTemplateHandler = new GenericTemplateHandler();
28+
private final GenericTemplateHandler genericTemplateHandler;
2929

30-
public GenericDependentResource(GenericKubernetesResource desired, String name,
30+
public GenericDependentResource(GenericTemplateHandler genericTemplateHandler,
31+
GenericKubernetesResource desired, String name,
3132
boolean clusterScoped) {
3233
super(new GroupVersionKind(desired.getApiVersion(), desired.getKind()));
3334
this.desired = desired;
3435
this.desiredTemplate = null;
3536
this.name = name;
3637
this.clusterScoped = clusterScoped;
38+
this.genericTemplateHandler = genericTemplateHandler;
3739
}
3840

39-
public GenericDependentResource(String desiredTemplate, String name, boolean clusterScoped) {
41+
public GenericDependentResource(GenericTemplateHandler genericTemplateHandler,
42+
String desiredTemplate, String name, boolean clusterScoped) {
4043
super(new GroupVersionKind(Utils.getApiVersionFromTemplate(desiredTemplate),
4144
Utils.getKindFromTemplate(desiredTemplate)));
45+
this.genericTemplateHandler = genericTemplateHandler;
4246
this.name = name;
4347
this.desiredTemplate = desiredTemplate;
4448
this.desired = null;

src/main/java/io/csviri/operator/glue/reconciler/glue/GlueReconciler.java

+13-11
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,14 @@ public class GlueReconciler implements Reconciler<Glue>, Cleaner<Glue>, ErrorSta
4949
private final KubernetesResourceDeletedCondition deletePostCondition =
5050
new KubernetesResourceDeletedCondition();
5151

52-
private final GenericTemplateHandler genericTemplateHandler = new GenericTemplateHandler();
53-
52+
private final GenericTemplateHandler genericTemplateHandler;
5453

5554
public GlueReconciler(ValidationAndErrorHandler validationAndErrorHandler,
56-
InformerRegister informerRegister) {
55+
InformerRegister informerRegister,
56+
GenericTemplateHandler genericTemplateHandler) {
5757
this.validationAndErrorHandler = validationAndErrorHandler;
5858
this.informerRegister = informerRegister;
59+
this.genericTemplateHandler = genericTemplateHandler;
5960
}
6061

6162
/**
@@ -131,7 +132,6 @@ private void registerRelatedResourceInformers(Context<Glue> context,
131132
// todo test
132133
private void cleanupRemovedResourcesFromWorkflow(Context<Glue> context,
133134
Glue primary) {
134-
135135
context.getSecondaryResources(GenericKubernetesResource.class).forEach(r -> {
136136
String dependentName = r.getMetadata().getAnnotations().get(DEPENDENT_NAME_ANNOTATION_KEY);
137137
// dependent name is null for related resources
@@ -200,21 +200,23 @@ private void createAndAddDependentToWorkflow(Glue primary, Context<Glue> context
200200
.ifPresent(c -> builder.withReconcilePrecondition(toCondition(c)));
201201
}
202202

203-
private static GenericDependentResource createDependentResource(DependentResourceSpec spec,
203+
private GenericDependentResource createDependentResource(DependentResourceSpec spec,
204204
boolean leafDependent, Boolean resourceInSameNamespaceAsPrimary) {
205205

206206
if (leafDependent && resourceInSameNamespaceAsPrimary && !spec.isClusterScoped()) {
207207
return spec.getResourceTemplate() != null
208-
? new GCGenericDependentResource(spec.getResourceTemplate(), spec.getName(),
208+
? new GCGenericDependentResource(genericTemplateHandler, spec.getResourceTemplate(),
209+
spec.getName(),
209210
spec.isClusterScoped())
210-
: new GCGenericDependentResource(spec.getResource(), spec.getName(),
211+
: new GCGenericDependentResource(genericTemplateHandler, spec.getResource(),
212+
spec.getName(),
211213
spec.isClusterScoped());
212214
} else {
213215
return spec.getResourceTemplate() != null
214-
? new GenericDependentResource(spec.getResourceTemplate(), spec.getName(),
215-
spec.isClusterScoped())
216-
: new GenericDependentResource(spec.getResource(), spec.getName(),
217-
spec.isClusterScoped());
216+
? new GenericDependentResource(genericTemplateHandler,
217+
spec.getResourceTemplate(), spec.getName(), spec.isClusterScoped())
218+
: new GenericDependentResource(genericTemplateHandler,
219+
spec.getResource(), spec.getName(), spec.isClusterScoped());
218220
}
219221
}
220222

src/main/java/io/csviri/operator/glue/reconciler/operator/GlueOperatorReconciler.java

+43-13
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
import io.csviri.operator.glue.customresource.operator.GlueOperatorSpec;
1616
import io.csviri.operator.glue.customresource.operator.ResourceFlowOperatorStatus;
1717
import io.csviri.operator.glue.reconciler.ValidationAndErrorHandler;
18+
import io.csviri.operator.glue.templating.GenericTemplateHandler;
1819
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
20+
import io.fabric8.kubernetes.api.model.ObjectMeta;
1921
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
2022
import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil;
2123
import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
@@ -26,7 +28,6 @@
2628
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
2729

2830
import jakarta.annotation.PostConstruct;
29-
import jakarta.inject.Inject;
3031

3132
import static io.csviri.operator.glue.reconciler.glue.GlueReconciler.GLUE_RECONCILER_NAME;
3233

@@ -42,19 +43,25 @@ public class GlueOperatorReconciler
4243
public static final String PARENT_RELATED_RESOURCE_NAME = "parent";
4344
public static final String GLUE_OPERATOR_RECONCILER_NAME = "glue-operator";
4445

45-
@Inject
46-
ValidationAndErrorHandler validationAndErrorHandler;
47-
4846
@ConfigProperty(name = "quarkus.operator-sdk.controllers." + GLUE_RECONCILER_NAME + ".selector")
4947
Optional<String> glueLabelSelector;
5048

51-
@Inject
52-
ControllerConfig controllerConfig;
49+
private final ControllerConfig controllerConfig;
50+
private final ValidationAndErrorHandler validationAndErrorHandler;
51+
private final GenericTemplateHandler genericTemplateHandler;
5352

5453
private Map<String, String> defaultGlueLabels;
5554

5655
private InformerEventSource<Glue, GlueOperator> glueEventSource;
5756

57+
public GlueOperatorReconciler(ControllerConfig controllerConfig,
58+
ValidationAndErrorHandler validationAndErrorHandler,
59+
GenericTemplateHandler genericTemplateHandler) {
60+
this.controllerConfig = controllerConfig;
61+
this.validationAndErrorHandler = validationAndErrorHandler;
62+
this.genericTemplateHandler = genericTemplateHandler;
63+
}
64+
5865
@PostConstruct
5966
void init() {
6067
defaultGlueLabels = initDefaultLabelsToAddToGlue();
@@ -94,12 +101,10 @@ private Glue createGlue(GenericKubernetesResource targetParentResource,
94101
GlueOperator glueOperator) {
95102
var glue = new Glue();
96103

97-
glue.setMetadata(new ObjectMetaBuilder()
98-
.withName(
99-
glueName(targetParentResource.getMetadata().getName(), targetParentResource.getKind()))
100-
.withNamespace(targetParentResource.getMetadata().getNamespace())
101-
.withLabels(Map.of(FOR_GLUE_OPERATOR_LABEL_KEY, FOR_GLUE_OPERATOR_LABEL_VALUE))
102-
.build());
104+
ObjectMeta glueMetadata = glueMetadata(glueOperator, targetParentResource);
105+
106+
107+
glue.setMetadata(glueMetadata);
103108
glue.setSpec(toWorkflowSpec(glueOperator.getSpec()));
104109

105110
if (!defaultGlueLabels.isEmpty()) {
@@ -113,13 +118,38 @@ private Glue createGlue(GenericKubernetesResource targetParentResource,
113118
parentRelatedSpec.setKind(parent.getKind());
114119
parentRelatedSpec.setResourceNames(List.of(targetParentResource.getMetadata().getName()));
115120
parentRelatedSpec.setNamespace(targetParentResource.getMetadata().getNamespace());
121+
parentRelatedSpec.setClusterScoped(glueOperator.getSpec().getParent().isClusterScoped());
116122

117123
glue.getSpec().getRelatedResources().add(parentRelatedSpec);
118-
119124
glue.addOwnerReference(targetParentResource);
120125
return glue;
121126
}
122127

128+
private ObjectMeta glueMetadata(GlueOperator glueOperator,
129+
GenericKubernetesResource parent) {
130+
131+
ObjectMetaBuilder objectMetaBuilder = new ObjectMetaBuilder();
132+
133+
var glueMeta = glueOperator.getSpec().getGlueMetadata();
134+
if (glueMeta != null) {
135+
// optimize
136+
var data = Map.of(PARENT_RELATED_RESOURCE_NAME, parent);
137+
var glueName = genericTemplateHandler.processInputAndTemplate(data, glueMeta.getName());
138+
var glueNamespace =
139+
genericTemplateHandler.processInputAndTemplate(data, glueMeta.getNamespace());
140+
objectMetaBuilder.withName(glueName);
141+
objectMetaBuilder.withNamespace(glueNamespace);
142+
} else {
143+
objectMetaBuilder.withName(
144+
glueName(parent.getMetadata().getName(), parent.getKind()))
145+
.withNamespace(parent.getMetadata().getNamespace());
146+
}
147+
148+
objectMetaBuilder
149+
.withLabels(Map.of(FOR_GLUE_OPERATOR_LABEL_KEY, FOR_GLUE_OPERATOR_LABEL_VALUE));
150+
return objectMetaBuilder.build();
151+
}
152+
123153
private GlueSpec toWorkflowSpec(GlueOperatorSpec spec) {
124154
var res = new GlueSpec();
125155
res.setChildResources(new ArrayList<>(spec.getChildResources()));

0 commit comments

Comments
 (0)