Skip to content

Commit 5f14b4e

Browse files
authored
improve: double bracket in object template (#166)
Signed-off-by: Attila Mészáros <[email protected]>
1 parent c70006b commit 5f14b4e

File tree

9 files changed

+94
-22
lines changed

9 files changed

+94
-22
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ spec:
6767
parent:
6868
apiVersion: glueoperator.sample/v1 # watches all the custom resource of type WebPage
6969
kind: WebPage
70-
statusTemplate: | # update the status of the custom resource at the end of reconciliation
71-
observedGeneration: {parent.metadata.generation}
70+
status: # update the status of the custom resource at the end of reconciliation
71+
observedGeneration: "{{parent.metadata.generation}}" # since it's a non-string value needs double curly brackets
7272
childResources:
7373
- name: htmlconfigmap
7474
resource:

docs/reference.md

+25-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,31 @@ Although it is limited only to Kubernetes resources it makes it very easy to use
88

99
## Generic Notes
1010

11-
- All templates (both object and string based) uses [Qute templating engine](https://quarkus.io/guides/qute-reference). While objects allow only
12-
placeholders, you can use the full power of qute in string templates.
11+
- All templates (both object and string-based) uses [Qute templating engine](https://quarkus.io/guides/qute-reference). While objects allow only
12+
placeholders, you can use the full power of qute in string templates.
13+
14+
ONLY for object-based templates (thus not string templates) the values can be set using the placeholder notation from Qute:
15+
```yaml
16+
value: "{string.value}"
17+
```
18+
With this standard notation, the result value will be always encoded in double quotes:
19+
```yaml
20+
value: "1"
21+
```
22+
Since there is no simple way to check if the referenced value is a string or other value
23+
(boolean, numeric, etc) for non-string values, user should use double brackets:
24+
```yaml
25+
value: "{{nonstring.value}}"
26+
```
27+
what would result in a value without enclosed double quotes in the produced yaml:
28+
```yaml
29+
value: 1
30+
```
31+
See sample [here](https://github.com/java-operator-sdk/kubernetes-glue-operator/blob/main/src/test/resources/sample/webpage/webpage.operator.yaml#L10).
32+
Implementation wise, this is a preprocessor that strips the enclosed quotes and additional curly bracket
33+
before it is passed to Qute.
34+
In the future, we might remove such obligation by checking the type
35+
of the target value in the related schema.
1336
1437
## [Glue resource](https://github.com/java-operator-sdk/kubernetes-glue-operator/releases/latest/download/glues.glue-v1.yml)
1538

src/main/java/io/javaoperatorsdk/operator/glue/dependent/GenericBulkDependentResource.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public GenericBulkDependentResource(GenericTemplateHandler genericTemplateHandle
2929
public Map<String, GenericKubernetesResource> desiredResources(Glue primary,
3030
Context<Glue> context) {
3131

32-
var res = genericTemplateHandler.processTemplate(desiredTemplate, primary, context);
32+
var res = genericTemplateHandler.processTemplate(desiredTemplate, primary, false, context);
3333
var desiredList = Serialization.unmarshal(res, GenericKubernetesResourceList.class).getItems();
3434
desiredList.forEach(r -> {
3535
r.getMetadata().getAnnotations()

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ public GenericDependentResource(GenericTemplateHandler genericTemplateHandler,
5555
@Override
5656
protected GenericKubernetesResource desired(Glue primary,
5757
Context<Glue> context) {
58+
boolean objectTemplate = desired != null;
59+
var template = objectTemplate ? Serialization.asYaml(desired) : desiredTemplate;
5860

59-
var template = desired == null ? desiredTemplate : Serialization.asYaml(desired);
60-
61-
var res = genericTemplateHandler.processTemplate(template, primary, context);
61+
var res = genericTemplateHandler.processTemplate(template, primary, objectTemplate, context);
6262
var resultDesired = Serialization.unmarshal(res, GenericKubernetesResource.class);
6363

6464
resultDesired.getMetadata().getAnnotations()

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

+6-5
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ private void createAndAddDependentToWorkflow(Glue primary, Context<Glue> context
195195
// todo test processing ns as template
196196
// name can reference related resources todo doc
197197
var targetNamespace = Utils.getNamespace(spec).map(ns -> genericTemplateHandler
198-
.processTemplate(ns, primary, context));
198+
.processTemplate(ns, primary, false, context));
199199
var resourceInSameNamespaceAsPrimary =
200200
targetNamespace.map(n -> n.trim().equals(primary.getMetadata().getNamespace().trim()))
201201
.orElse(true);
@@ -205,7 +205,7 @@ private void createAndAddDependentToWorkflow(Glue primary, Context<Glue> context
205205

206206
if (!(dr instanceof BulkDependentResource<?, ?>)) {
207207
dr.setResourceDiscriminator(new GenericResourceDiscriminator(dr.getGroupVersionKind(),
208-
genericTemplateHandler.processTemplate(Utils.getName(spec), primary, context),
208+
genericTemplateHandler.processTemplate(Utils.getName(spec), primary, false, context),
209209
targetNamespace.orElse(null)));
210210
}
211211

@@ -267,10 +267,11 @@ private void patchRelatedResourcesStatus(Context<Glue> context,
267267
targetRelatedResources.forEach(r -> {
268268
var relatedResources = Utils.getRelatedResources(primary, r, context);
269269

270-
var template = r.getStatusPatchTemplate() != null ? r.getStatusPatchTemplate()
271-
: Serialization.asYaml(r.getStatusPatch());
270+
var objectTemplate = r.getStatusPatch() != null;
271+
var template =
272+
objectTemplate ? Serialization.asYaml(r.getStatusPatch()) : r.getStatusPatchTemplate();
272273
var resultTemplate =
273-
genericTemplateHandler.processTemplate(actualData, template);
274+
genericTemplateHandler.processTemplate(actualData, template, objectTemplate);
274275
var statusObjectMap = GenericTemplateHandler.parseTemplateToMapObject(resultTemplate);
275276
relatedResources.forEach((n, kr) -> {
276277
kr.setAdditionalProperty("status", statusObjectMap);

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,10 @@ private ObjectMeta glueMetadata(GlueOperator glueOperator,
144144
if (glueMeta != null) {
145145
// optimize
146146
var data = Map.of(PARENT_RELATED_RESOURCE_NAME, parent);
147-
var glueName = genericTemplateHandler.processInputAndTemplate(data, glueMeta.getName());
147+
var glueName =
148+
genericTemplateHandler.processInputAndTemplate(data, glueMeta.getName(), false);
148149
var glueNamespace =
149-
genericTemplateHandler.processInputAndTemplate(data, glueMeta.getNamespace());
150+
genericTemplateHandler.processInputAndTemplate(data, glueMeta.getNamespace(), false);
150151
objectMetaBuilder.withName(glueName);
151152
objectMetaBuilder.withNamespace(glueNamespace);
152153
} else {

src/main/java/io/javaoperatorsdk/operator/glue/templating/GenericTemplateHandler.java

+14-5
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,31 @@ public class GenericTemplateHandler {
2323
private static final ObjectMapper objectMapper = new ObjectMapper();
2424
private static final Engine engine = Engine.builder().addDefaults().build();
2525

26-
public String processTemplate(Map<String, Map<?, ?>> data, String template) {
26+
public String processTemplate(Map<String, Map<?, ?>> data, String template,
27+
boolean objectTemplate) {
28+
if (objectTemplate) {
29+
template = handleDoubleCurlyBrackets(template);
30+
}
2731
Template parsedTemplate = engine.parse(template);
2832
return parsedTemplate.data(data).render();
2933
}
3034

35+
private String handleDoubleCurlyBrackets(String template) {
36+
template = template.replace("\"{{", "{");
37+
return template.replace("}}\"", "}");
38+
}
39+
3140
public String processInputAndTemplate(Map<String, GenericKubernetesResource> data,
32-
String template) {
41+
String template, boolean objectTemplate) {
3342
Map<String, Map<?, ?>> res =
3443
genericKubernetesResourceDataToGenericData(data);
35-
return processTemplate(res, template);
44+
return processTemplate(res, template, objectTemplate);
3645
}
3746

38-
public String processTemplate(String template, Glue primary,
47+
public String processTemplate(String template, Glue primary, boolean objectTemplate,
3948
Context<Glue> context) {
4049
var data = createDataWithResources(primary, context);
41-
return processTemplate(data, template);
50+
return processTemplate(data, template, objectTemplate);
4251
}
4352

4453
private static Map<String, Map<?, ?>> genericKubernetesResourceDataToGenericData(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.javaoperatorsdk.operator.glue.templating;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
import io.fabric8.kubernetes.client.utils.Serialization;
9+
10+
import static org.junit.jupiter.api.Assertions.*;
11+
12+
class GenericTemplateHandlerTest {
13+
14+
GenericTemplateHandler templateHandler = new GenericTemplateHandler();
15+
16+
@Test
17+
void testDoubleCurlyBrackets() {
18+
var template = """
19+
intValue: "{{spec.intvalue}}"
20+
stringValue: "{spec.stringvalue}"
21+
""";
22+
23+
Map<String, Map<?, ?>> data = new HashMap<>();
24+
25+
Map values = new HashMap();
26+
values.put("intvalue", 1);
27+
values.put("stringvalue", "value1");
28+
data.put("spec", values);
29+
30+
var result = templateHandler.processTemplate(data, template, true);
31+
32+
Map mapResult = Serialization.unmarshal(result, Map.class);
33+
34+
assertEquals(1, mapResult.get("intValue"));
35+
assertEquals("value1", mapResult.get("stringValue"));
36+
}
37+
38+
}

src/test/resources/sample/webpage/webpage.operator.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ spec:
66
parent:
77
apiVersion: glueoperator.sample/v1
88
kind: WebPage
9-
statusTemplate: |
10-
observedGeneration: {parent.metadata.generation}
9+
status:
10+
observedGeneration: "{{parent.metadata.generation}}"
1111
childResources:
1212
- name: htmlconfigmap
1313
resource:

0 commit comments

Comments
 (0)