Skip to content

Commit ae13f32

Browse files
authored
feat: encode and decode base64 in qute template (#181)
add sample secret copy to config map Signed-off-by: Attila Mészáros <[email protected]>
1 parent 8f9ac7d commit ae13f32

File tree

4 files changed

+114
-7
lines changed

4 files changed

+114
-7
lines changed

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.javaoperatorsdk.operator.glue.templating;
22

3+
import java.nio.charset.StandardCharsets;
4+
import java.util.Base64;
35
import java.util.HashMap;
46
import java.util.Map;
57

@@ -10,6 +12,7 @@
1012
import io.javaoperatorsdk.operator.glue.customresource.glue.Glue;
1113
import io.quarkus.qute.Engine;
1214
import io.quarkus.qute.Template;
15+
import io.quarkus.qute.ValueResolver;
1316

1417
import com.fasterxml.jackson.databind.ObjectMapper;
1518

@@ -21,7 +24,10 @@ public class GenericTemplateHandler {
2124
public static final String WORKFLOW_METADATA_KEY = "glueMetadata";
2225

2326
private static final ObjectMapper objectMapper = new ObjectMapper();
24-
private static final Engine engine = Engine.builder().addDefaults().build();
27+
private static final Engine engine = Engine.builder().addDefaults()
28+
.addValueResolver(base64EncodeResolver())
29+
.addValueResolver(base64DecodeResolver())
30+
.build();
2531

2632
public String processTemplate(Map<String, Map<?, ?>> data, String template,
2733
boolean objectTemplate) {
@@ -77,4 +83,32 @@ public String processTemplate(String template, Glue primary, boolean objectTempl
7783
return Serialization.unmarshal(template, Map.class);
7884
}
7985

86+
static ValueResolver base64DecodeResolver() {
87+
return ValueResolver.builder()
88+
.appliesTo(c -> c.getName().equals("decodeBase64")
89+
&& (c.getBase() instanceof String || c.getBase() instanceof byte[]))
90+
.resolveSync(context -> {
91+
if (context.getBase() instanceof byte[] bytes) {
92+
return new String(Base64.getDecoder().decode(bytes), StandardCharsets.UTF_8);
93+
} else {
94+
return new String(Base64.getDecoder().decode(context.getBase().toString()),
95+
StandardCharsets.UTF_8);
96+
}
97+
}).build();
98+
}
99+
100+
static ValueResolver base64EncodeResolver() {
101+
return ValueResolver.builder()
102+
.appliesTo(c -> c.getName().equals("encodeBase64")
103+
&& (c.getBase() instanceof String || c.getBase() instanceof byte[]))
104+
.applyToBaseClass(String.class)
105+
.resolveSync(context -> {
106+
if (context.getBase() instanceof byte[] bytes) {
107+
return Base64.getEncoder().encodeToString(bytes);
108+
} else {
109+
return Base64.getEncoder().encodeToString(context.getBase().toString().getBytes());
110+
}
111+
}).build();
112+
}
113+
80114
}

src/test/java/io/javaoperatorsdk/operator/glue/GlueTest.java

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.javaoperatorsdk.operator.glue;
22

3+
import java.nio.charset.StandardCharsets;
34
import java.time.Duration;
45
import java.util.ArrayList;
6+
import java.util.Base64;
57
import java.util.List;
68
import java.util.Map;
79
import java.util.stream.IntStream;
@@ -23,6 +25,7 @@
2325
import io.quarkus.test.junit.QuarkusTest;
2426

2527
import static io.javaoperatorsdk.operator.glue.TestUtils.INITIAL_RECONCILE_WAIT_TIMEOUT;
28+
import static io.javaoperatorsdk.operator.glue.TestUtils.loadGlue;
2629
import static org.assertj.core.api.Assertions.assertThat;
2730
import static org.awaitility.Awaitility.await;
2831

@@ -412,6 +415,45 @@ void invalidGlueMessageHandling() {
412415
});
413416
}
414417

418+
@Test
419+
void secretToConfigMapCopy() {
420+
String nsa = "namespace-a";
421+
String nsb = "namespace-b";
422+
423+
createNamespace(nsa);
424+
createNamespace(nsb);
425+
createSecretToCopy(nsb);
426+
427+
var glue = client.resource(loadGlue("/glue/CopySecretToConfigMap.yaml"))
428+
.createOr(NonDeletingOperation::update);
429+
430+
await().untilAsserted(() -> {
431+
var cm = client.configMaps().inNamespace(nsa).withName("my-secret-copy").get();
432+
assertThat(cm).isNotNull();
433+
assertThat(cm.getData())
434+
.containsExactlyInAnyOrderEntriesOf(Map.of("key1", "value1", "key2", "value2"));
435+
});
436+
437+
deleteInOwnNamespace(glue);
438+
await().pollDelay(INITIAL_RECONCILE_WAIT_TIMEOUT).untilAsserted(() -> {
439+
var g = get(Glue.class, "secret-to-configmap", nsa);
440+
assertThat(g).isNull();
441+
});
442+
}
443+
444+
private Secret createSecretToCopy(String nsb) {
445+
var secret = new Secret();
446+
secret.setMetadata(new ObjectMetaBuilder()
447+
.withName("secret-to-copy")
448+
.withNamespace(nsb)
449+
.build());
450+
secret.setData(Map.of("key1",
451+
Base64.getEncoder().encodeToString("value1".getBytes(StandardCharsets.UTF_8)),
452+
"key2",
453+
Base64.getEncoder().encodeToString("value2".getBytes(StandardCharsets.UTF_8))));
454+
455+
return client.resource(secret).createOr(NonDeletingOperation::update);
456+
}
415457

416458
private List<Glue> testWorkflowList(int num) {
417459
List<Glue> res = new ArrayList<>();
@@ -423,7 +465,4 @@ private List<Glue> testWorkflowList(int num) {
423465
});
424466
return res;
425467
}
426-
427-
428-
429468
}

src/test/java/io/javaoperatorsdk/operator/glue/TestBase.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public void prepareNamespace(TestInfo testInfo) {
3737
testInfo.getTestMethod()
3838
.ifPresent(method -> testNamespace = KubernetesResourceUtil.sanitizeName(method.getName()));
3939

40-
client.namespaces().resource(testNamespace(testNamespace)).create();
40+
createNamespace(testNamespace);
4141
}
4242

4343
@AfterEach
@@ -49,7 +49,11 @@ void cleanupNamespace() {
4949
});
5050
}
5151

52-
protected Namespace testNamespace(String name) {
52+
protected Namespace createNamespace(String name) {
53+
return client.namespaces().resource(namespace(name)).createOr(NonDeletingOperation::update);
54+
}
55+
56+
protected Namespace namespace(String name) {
5357
return new NamespaceBuilder().withMetadata(new ObjectMetaBuilder()
5458
.withName(name)
5559
.build()).build();
@@ -72,6 +76,10 @@ protected <T extends HasMetadata> T get(Class<T> clazz, String name) {
7276
return client.resources(clazz).inNamespace(testNamespace).withName(name).get();
7377
}
7478

79+
protected <T extends HasMetadata> T get(Class<T> clazz, String name, String namespace) {
80+
return client.resources(clazz).inNamespace(namespace).withName(name).get();
81+
}
82+
7583
protected <T extends HasMetadata> List<T> list(Class<T> clazz) {
7684
return client.resources(clazz).inNamespace(testNamespace).list().getItems();
7785
}
@@ -93,6 +101,8 @@ protected void delete(HasMetadata resource) {
93101
client.resource(resource).inNamespace(testNamespace).delete();
94102
}
95103

96-
104+
protected void deleteInOwnNamespace(HasMetadata resource) {
105+
client.resource(resource).delete();
106+
}
97107

98108
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
apiVersion: io.javaoperatorsdk.operator.glue/v1beta1
2+
kind: Glue
3+
metadata:
4+
name: "secret-to-configmap"
5+
namespace: namespace-a
6+
spec:
7+
childResources:
8+
- name: configmap
9+
resourceTemplate: |
10+
apiVersion: v1
11+
kind: ConfigMap
12+
metadata:
13+
name: my-secret-copy
14+
namespace: namespace-a
15+
data:
16+
{#for entry in secret-to-copy.data}
17+
{entry.key}: {entry.value.decodeBase64}
18+
{/for}
19+
relatedResources:
20+
- name: secret-to-copy
21+
apiVersion: v1
22+
kind: Secret
23+
resourceNames: ["secret-to-copy"]
24+
namespace: namespace-b

0 commit comments

Comments
 (0)