Skip to content

Commit 95ef3fc

Browse files
committed
HYPERFLEET-864 - test: add e2e test for Go template structural syntax in manifest refs
Adds cl-template adapter config and e2e test that verifies Go template conditionals ({{ if }}, {{ else }}, {{ end }}) work correctly in manifest ref files. Test creates a cluster, verifies the adapter renders the template and creates a ConfigMap with correct content.
1 parent 54d983a commit 95ef3fc

5 files changed

Lines changed: 337 additions & 0 deletions

File tree

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package adapter
2+
3+
import (
4+
"context"
5+
6+
"github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega" //nolint:staticcheck // dot import for test readability
8+
9+
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/api/openapi"
10+
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/client"
11+
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/helper"
12+
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/labels"
13+
)
14+
15+
var _ = ginkgo.Describe("[Suite: adapter] Manifest ref with Go template structural syntax",
16+
ginkgo.Label(labels.Tier1),
17+
func() {
18+
var h *helper.Helper
19+
20+
ginkgo.BeforeEach(func() {
21+
h = helper.New()
22+
})
23+
24+
ginkgo.It("should render Go template conditionals in manifest ref and create ConfigMap",
25+
func(ctx context.Context) {
26+
adapterName := "cl-template"
27+
28+
ginkgo.By("Create test cluster")
29+
cluster, err := h.Client.CreateClusterFromPayload(ctx, h.TestDataPath("payloads/clusters/cluster-request.json"))
30+
Expect(err).NotTo(HaveOccurred(), "failed to create cluster")
31+
Expect(cluster.Id).NotTo(BeNil(), "cluster ID should be generated")
32+
clusterID := *cluster.Id
33+
clusterName := cluster.Name
34+
ginkgo.GinkgoWriter.Printf("Created cluster ID: %s, Name: %s\n", clusterID, clusterName)
35+
36+
ginkgo.DeferCleanup(func(ctx context.Context) {
37+
ginkgo.By("Cleanup test cluster " + clusterID)
38+
if err := h.CleanupTestCluster(ctx, clusterID); err != nil {
39+
ginkgo.GinkgoWriter.Printf("Warning: failed to cleanup cluster %s: %v\n", clusterID, err)
40+
}
41+
})
42+
43+
ginkgo.By("Wait for adapter to process cluster and report status")
44+
Eventually(func(g Gomega) {
45+
statuses, err := h.Client.GetClusterStatuses(ctx, clusterID)
46+
g.Expect(err).NotTo(HaveOccurred(), "failed to get cluster statuses")
47+
g.Expect(statuses.Items).NotTo(BeEmpty(), "adapter should have reported status")
48+
49+
var adapterStatus *openapi.AdapterStatus
50+
for i, adapter := range statuses.Items {
51+
if adapter.Adapter == adapterName {
52+
adapterStatus = &statuses.Items[i]
53+
break
54+
}
55+
}
56+
57+
g.Expect(adapterStatus).NotTo(BeNil(),
58+
"adapter %s should be present in adapter statuses", adapterName)
59+
60+
// Verify Applied condition is True (manifest rendered and applied successfully)
61+
hasApplied := h.HasAdapterCondition(adapterStatus.Conditions,
62+
client.ConditionTypeApplied, openapi.AdapterConditionStatusTrue)
63+
g.Expect(hasApplied).To(BeTrue(),
64+
"adapter Applied condition should be True - Go template manifest was rendered and applied")
65+
66+
// Verify Available condition is True
67+
hasAvailable := h.HasAdapterCondition(adapterStatus.Conditions,
68+
client.ConditionTypeAvailable, openapi.AdapterConditionStatusTrue)
69+
g.Expect(hasAvailable).To(BeTrue(),
70+
"adapter Available condition should be True - resources are ready")
71+
72+
}, h.Cfg.Timeouts.Adapter.Processing, h.Cfg.Polling.Interval).Should(Succeed())
73+
74+
ginkgo.By("Verify ConfigMap was created with correct Go template rendered content")
75+
cm, err := h.GetConfigMap(ctx, clusterID, "template-test-"+clusterID)
76+
Expect(err).NotTo(HaveOccurred(), "failed to get ConfigMap created by Go template manifest")
77+
78+
// Verify template-rendered data fields
79+
Expect(cm.Data).To(HaveKeyWithValue("cluster-id", clusterID),
80+
"ConfigMap should have cluster-id from Go template rendering")
81+
Expect(cm.Data).To(HaveKeyWithValue("cluster-name", clusterName),
82+
"ConfigMap should have cluster-name from Go template rendering")
83+
84+
// Verify conditional rendering: ci defaults to "false", so ci-enabled should be "false"
85+
Expect(cm.Data).To(HaveKeyWithValue("ci-enabled", "false"),
86+
"ConfigMap should have ci-enabled=false from {{ if eq .ci \"true\" }} / {{ else }} template")
87+
88+
// Verify labels were rendered correctly
89+
Expect(cm.Labels).To(HaveKeyWithValue("hyperfleet.io/cluster-id", clusterID),
90+
"ConfigMap should have cluster-id label")
91+
Expect(cm.Labels).To(HaveKeyWithValue("hyperfleet.io/cluster-name", clusterName),
92+
"ConfigMap should have cluster-name label")
93+
Expect(cm.Labels).To(HaveKeyWithValue("e2e.hyperfleet.io/managed-by", "test-framework"),
94+
"ConfigMap should have managed-by label")
95+
96+
// Verify conditional label: testRunId defaults to "TEST_RUN_ID" (truthy), so test-run-id label should exist
97+
Expect(cm.Labels).To(HaveKey("e2e.hyperfleet.io/test-run-id"),
98+
"ConfigMap should have test-run-id label from {{ if .testRunId }} template")
99+
100+
ginkgo.GinkgoWriter.Printf("Successfully validated Go template structural syntax in manifest ref\n")
101+
})
102+
},
103+
)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
adapter:
2+
name: cl-template
3+
#version: "0.1.0"
4+
5+
# Log the full merged configuration after load (default: false)
6+
debug_config: false
7+
log:
8+
level: debug
9+
10+
clients:
11+
hyperfleet_api:
12+
base_url: http://hyperfleet-api:8000
13+
version: v1
14+
timeout: 2s
15+
retry_attempts: 3
16+
retry_backoff: exponential
17+
18+
broker:
19+
subscription_id: CHANGE_ME
20+
topic: CHANGE_ME
21+
22+
kubernetes:
23+
api_version: "v1"
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# Adapter task configuration for testing Go template structural syntax in manifest refs
2+
# This tests {{ if }}, {{ else }}, {{ end }} blocks in externally referenced manifests
3+
4+
# Parameters with all required variables
5+
params:
6+
- name: "clusterId"
7+
source: "event.id"
8+
type: "string"
9+
required: true
10+
- name: "testRunId"
11+
source: "env.TEST_RUN_ID"
12+
type: "string"
13+
required: false
14+
default: "TEST_RUN_ID"
15+
- name: "ci"
16+
source: "env.CI"
17+
type: "string"
18+
required: false
19+
default: "false"
20+
21+
# Preconditions
22+
preconditions:
23+
- name: "clusterStatus"
24+
api_call:
25+
method: "GET"
26+
url: "/clusters/{{ .clusterId }}"
27+
timeout: 10s
28+
retry_attempts: 3
29+
retry_backoff: "exponential"
30+
capture:
31+
- name: "clusterName"
32+
field: "name"
33+
- name: "generationSpec"
34+
field: "generation"
35+
- name: "clusterNotReady"
36+
expression: |
37+
status.conditions.filter(c, c.type == "Ready").size() > 0
38+
? status.conditions.filter(c, c.type == "Ready")[0].status != "True"
39+
: true
40+
- name: "clusterReadyTTL"
41+
expression: |
42+
(timestamp(now()) - timestamp(
43+
status.conditions.filter(c, c.type == "Ready").size() > 0
44+
? status.conditions.filter(c, c.type == "Ready")[0].last_transition_time
45+
: now()
46+
)).getSeconds() > 300
47+
48+
- name: "validationCheck"
49+
expression: |
50+
clusterNotReady || clusterReadyTTL
51+
52+
# Resources - uses manifest ref with Go template structural syntax
53+
resources:
54+
- name: "clusterNamespace"
55+
transport:
56+
client: "kubernetes"
57+
manifest:
58+
apiVersion: v1
59+
kind: Namespace
60+
metadata:
61+
name: "{{ .clusterId }}"
62+
labels:
63+
hyperfleet.io/cluster-id: "{{ .clusterId }}"
64+
hyperfleet.io/cluster-name: "{{ .clusterName }}"
65+
e2e.hyperfleet.io/managed-by: "test-framework"
66+
discovery:
67+
namespace: "*"
68+
by_selectors:
69+
label_selector:
70+
hyperfleet.io/cluster-id: "{{ .clusterId }}"
71+
72+
- name: "templateConfigMap"
73+
transport:
74+
client: "kubernetes"
75+
manifest:
76+
ref: "/etc/adapter/configmap.yaml"
77+
discovery:
78+
namespace: "{{ .clusterId }}"
79+
by_selectors:
80+
label_selector:
81+
hyperfleet.io/cluster-id: "{{ .clusterId }}"
82+
e2e.hyperfleet.io/managed-by: "test-framework"
83+
84+
# Post-processing
85+
post:
86+
payloads:
87+
- name: "clusterStatusPayload"
88+
build:
89+
adapter: "{{ .adapter.name }}"
90+
conditions:
91+
- type: "Applied"
92+
status:
93+
expression: |
94+
resources.?templateConfigMap.?metadata.?name.orValue("") != "" ? "True" : "False"
95+
reason:
96+
expression: |
97+
resources.?templateConfigMap.?metadata.?name.orValue("") != ""
98+
? "ConfigMapCreated"
99+
: "ConfigMapPending"
100+
message:
101+
expression: |
102+
resources.?templateConfigMap.?metadata.?name.orValue("") != ""
103+
? "ConfigMap created successfully with Go template rendering"
104+
: "ConfigMap creation in progress"
105+
- type: "Available"
106+
status:
107+
expression: |
108+
resources.?templateConfigMap.?metadata.?name.orValue("") != ""
109+
&& resources.?clusterNamespace.?status.?phase.orValue("") == "Active"
110+
? "True" : "False"
111+
reason:
112+
expression: |
113+
resources.?templateConfigMap.?metadata.?name.orValue("") != ""
114+
&& resources.?clusterNamespace.?status.?phase.orValue("") == "Active"
115+
? "ResourcesReady"
116+
: "ResourcesNotReady"
117+
message:
118+
expression: |
119+
resources.?templateConfigMap.?metadata.?name.orValue("") != ""
120+
&& resources.?clusterNamespace.?status.?phase.orValue("") == "Active"
121+
? "All resources created and ready"
122+
: "Resources not yet ready"
123+
- type: "Health"
124+
status:
125+
expression: |
126+
adapter.?executionStatus.orValue("") == "success" ? "True" : "False"
127+
reason:
128+
expression: |
129+
adapter.?errorReason.orValue("") != "" ? adapter.?errorReason.orValue("") : "Healthy"
130+
message:
131+
expression: |
132+
adapter.?errorMessage.orValue("") != "" ? adapter.?errorMessage.orValue("") : "All adapter operations completed successfully"
133+
observed_generation:
134+
expression: "generationSpec"
135+
observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}"
136+
137+
data:
138+
configmap:
139+
name:
140+
expression: |
141+
resources.?templateConfigMap.?metadata.?name.orValue("")
142+
namespace:
143+
name:
144+
expression: |
145+
resources.?clusterNamespace.?metadata.?name.orValue("")
146+
status:
147+
expression: |
148+
resources.?clusterNamespace.?status.?phase.orValue("")
149+
150+
post_actions:
151+
- name: "reportClusterStatus"
152+
api_call:
153+
method: "POST"
154+
url: "/clusters/{{ .clusterId }}/statuses"
155+
headers:
156+
- name: "Content-Type"
157+
value: "application/json"
158+
body: "{{ .clusterStatusPayload }}"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: "template-test-{{ .clusterId }}"
5+
namespace: "{{ .clusterId }}"
6+
labels:
7+
hyperfleet.io/cluster-id: "{{ .clusterId }}"
8+
hyperfleet.io/cluster-name: "{{ .clusterName }}"
9+
e2e.hyperfleet.io/managed-by: "test-framework"
10+
{{ if .testRunId }}
11+
e2e.hyperfleet.io/test-run-id: "{{ .testRunId }}"
12+
{{ end }}
13+
data:
14+
cluster-id: "{{ .clusterId }}"
15+
cluster-name: "{{ .clusterName }}"
16+
{{ if eq .ci "true" }}
17+
ci-enabled: "true"
18+
{{ else }}
19+
ci-enabled: "false"
20+
{{ end }}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
adapterConfig:
2+
create: true
3+
files:
4+
adapter-config.yaml: cl-template/adapter-config.yaml
5+
log:
6+
level: debug
7+
8+
adapterTaskConfig:
9+
create: true
10+
files:
11+
task-config.yaml: cl-template/adapter-task-config.yaml
12+
configmap.yaml: cl-template/adapter-task-resource-configmap.yaml
13+
14+
broker:
15+
create: true
16+
googlepubsub:
17+
projectId: ${GCP_PROJECT_ID}
18+
subscriptionId: ${NAMESPACE}-clusters-${ADAPTER_NAME}
19+
topic: ${NAMESPACE}-clusters
20+
deadLetterTopic: ${NAMESPACE}-clusters-dlq
21+
createTopicIfMissing: ${ADAPTER_GOOGLEPUBSUB_CREATE_TOPIC_IF_MISSING}
22+
createSubscriptionIfMissing: ${ADAPTER_GOOGLEPUBSUB_CREATE_SUBSCRIPTION_IF_MISSING}
23+
24+
image:
25+
registry: ${IMAGE_REGISTRY}
26+
repository: ${ADAPTER_IMAGE_REPO}
27+
pullPolicy: Always
28+
tag: ${ADAPTER_IMAGE_TAG}
29+
30+
rbac:
31+
resources:
32+
- namespaces
33+
- configmaps

0 commit comments

Comments
 (0)