Skip to content

Commit 5cc19aa

Browse files
committed
Add tests for empty kind in CEL healthchecks
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
1 parent 1a6cacc commit 5cc19aa

1 file changed

Lines changed: 188 additions & 0 deletions

File tree

internal/controller/kustomization_wait_test.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,3 +640,191 @@ spec:
640640
g.Expect(cancelEvent.Message).To(ContainSubstring("Health checks canceled"))
641641
g.Expect(cancelEvent.Message).To(ContainSubstring("GitRepository"))
642642
}
643+
644+
func TestKustomizationReconciler_HealthCheckExprs_GroupOnly(t *testing.T) {
645+
g := NewWithT(t)
646+
id := "cel-grp-" + randStringRunes(5)
647+
revision := "v1.0.0"
648+
resultK := &kustomizev1.Kustomization{}
649+
timeout := 60 * time.Second
650+
651+
err := createNamespace(id)
652+
g.Expect(err).NotTo(HaveOccurred(), "failed to create test namespace")
653+
654+
err = createKubeConfigSecret(id)
655+
g.Expect(err).NotTo(HaveOccurred(), "failed to create kubeconfig secret")
656+
657+
// Unique group per test run to avoid CRD name collisions within the shared envtest.
658+
group := fmt.Sprintf("%s.flux-test.io", id)
659+
fooCRName := "foo-" + randStringRunes(5)
660+
barCRName := "bar-" + randStringRunes(5)
661+
662+
// Two cluster-scoped CRDs in the same group, plus one CR of each kind.
663+
// The CEL expression reads `spec.ready`, applied via SSA from the artifact.
664+
buildFiles := func(barReady bool) []testserver.File {
665+
return []testserver.File{
666+
{
667+
Name: "crd-foo.yaml",
668+
Body: fmt.Sprintf(`---
669+
apiVersion: apiextensions.k8s.io/v1
670+
kind: CustomResourceDefinition
671+
metadata:
672+
name: foos.%[1]s
673+
spec:
674+
group: %[1]s
675+
names:
676+
kind: Foo
677+
listKind: FooList
678+
plural: foos
679+
singular: foo
680+
scope: Cluster
681+
versions:
682+
- name: v1
683+
served: true
684+
storage: true
685+
schema:
686+
openAPIV3Schema:
687+
type: object
688+
properties:
689+
spec:
690+
type: object
691+
properties:
692+
ready:
693+
type: boolean
694+
`, group),
695+
},
696+
{
697+
Name: "crd-bar.yaml",
698+
Body: fmt.Sprintf(`---
699+
apiVersion: apiextensions.k8s.io/v1
700+
kind: CustomResourceDefinition
701+
metadata:
702+
name: bars.%[1]s
703+
spec:
704+
group: %[1]s
705+
names:
706+
kind: Bar
707+
listKind: BarList
708+
plural: bars
709+
singular: bar
710+
scope: Cluster
711+
versions:
712+
- name: v1
713+
served: true
714+
storage: true
715+
schema:
716+
openAPIV3Schema:
717+
type: object
718+
properties:
719+
spec:
720+
type: object
721+
properties:
722+
ready:
723+
type: boolean
724+
`, group),
725+
},
726+
{
727+
Name: "foo.yaml",
728+
Body: fmt.Sprintf(`---
729+
apiVersion: %[1]s/v1
730+
kind: Foo
731+
metadata:
732+
name: %[2]s
733+
spec:
734+
ready: true
735+
`, group, fooCRName),
736+
},
737+
{
738+
Name: "bar.yaml",
739+
Body: fmt.Sprintf(`---
740+
apiVersion: %[1]s/v1
741+
kind: Bar
742+
metadata:
743+
name: %[2]s
744+
spec:
745+
ready: %[3]t
746+
`, group, barCRName, barReady),
747+
},
748+
}
749+
}
750+
751+
artifact, err := testServer.ArtifactFromFiles(buildFiles(true))
752+
g.Expect(err).NotTo(HaveOccurred())
753+
754+
repositoryName := types.NamespacedName{
755+
Name: fmt.Sprintf("grp-%s", randStringRunes(5)),
756+
Namespace: id,
757+
}
758+
759+
err = applyGitRepository(repositoryName, artifact, revision)
760+
g.Expect(err).NotTo(HaveOccurred())
761+
762+
kustomization := &kustomizev1.Kustomization{
763+
ObjectMeta: metav1.ObjectMeta{
764+
Name: fmt.Sprintf("grp-%s", randStringRunes(5)),
765+
Namespace: id,
766+
},
767+
Spec: kustomizev1.KustomizationSpec{
768+
Interval: metav1.Duration{Duration: 2 * time.Minute},
769+
Path: "./",
770+
KubeConfig: &meta.KubeConfigReference{
771+
SecretRef: &meta.SecretKeyReference{
772+
Name: "kubeconfig",
773+
},
774+
},
775+
SourceRef: kustomizev1.CrossNamespaceSourceReference{
776+
Name: repositoryName.Name,
777+
Namespace: repositoryName.Namespace,
778+
Kind: sourcev1.GitRepositoryKind,
779+
},
780+
Prune: true,
781+
Wait: true,
782+
// Single group-only healthcheck (empty Kind) that must be applied
783+
// to both Foo and Bar custom resources.
784+
HealthCheckExprs: []kustomize.CustomHealthCheck{{
785+
APIVersion: group + "/v1",
786+
HealthCheckExpressions: kustomize.HealthCheckExpressions{
787+
Current: "has(spec.ready) && spec.ready == true",
788+
},
789+
}},
790+
},
791+
}
792+
793+
g.Expect(k8sClient.Create(context.Background(), kustomization)).To(Succeed())
794+
795+
t.Run("group-only healthcheck succeeds for both kinds", func(t *testing.T) {
796+
g.Eventually(func() bool {
797+
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
798+
return isReconcileSuccess(resultK)
799+
}, timeout, time.Second).Should(BeTrue())
800+
logStatus(t, resultK)
801+
802+
g.Expect(conditions.IsTrue(resultK, meta.HealthyCondition)).To(BeTrue())
803+
g.Expect(conditions.GetReason(resultK, meta.HealthyCondition)).To(BeIdenticalTo(meta.SucceededReason))
804+
})
805+
806+
t.Run("reports unhealthy when one kind stops satisfying the group expression", func(t *testing.T) {
807+
badArtifact, err := testServer.ArtifactFromFiles(buildFiles(false))
808+
g.Expect(err).NotTo(HaveOccurred())
809+
810+
err = applyGitRepository(repositoryName, badArtifact, "v1.0.1")
811+
g.Expect(err).NotTo(HaveOccurred())
812+
813+
// Shorten healthcheck timeout so the failure surfaces quickly.
814+
g.Eventually(func() error {
815+
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
816+
resultK.Spec.Timeout = &metav1.Duration{Duration: 5 * time.Second}
817+
return k8sClient.Update(context.Background(), resultK)
818+
}, timeout, time.Second).Should(BeNil())
819+
820+
g.Eventually(func() bool {
821+
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
822+
return conditions.IsFalse(resultK, meta.HealthyCondition) &&
823+
conditions.GetReason(resultK, meta.HealthyCondition) == meta.HealthCheckFailedReason
824+
}, timeout, time.Second).Should(BeTrue())
825+
826+
msg := conditions.GetMessage(resultK, meta.HealthyCondition)
827+
g.Expect(msg).To(ContainSubstring("Bar"))
828+
g.Expect(msg).To(ContainSubstring(barCRName))
829+
})
830+
}

0 commit comments

Comments
 (0)