From 5eed3d77f2d5703c0910958771b56f7aa25143a1 Mon Sep 17 00:00:00 2001 From: matheuscscp <2975506+matheuscscp@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:53:47 +0000 Subject: [PATCH 1/4] Update fluxcd/pkg dependencies Signed-off-by: GitHub --- api/go.mod | 2 +- api/go.sum | 4 ++-- go.mod | 6 +++--- go.sum | 12 ++++++------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/api/go.mod b/api/go.mod index 595021e1..6e819ec9 100644 --- a/api/go.mod +++ b/api/go.mod @@ -3,7 +3,7 @@ module github.com/fluxcd/kustomize-controller/api go 1.25.0 require ( - github.com/fluxcd/pkg/apis/kustomize v1.16.0 + github.com/fluxcd/pkg/apis/kustomize v1.17.0 github.com/fluxcd/pkg/apis/meta v1.26.0 k8s.io/apiextensions-apiserver v0.35.2 k8s.io/apimachinery v0.35.2 diff --git a/api/go.sum b/api/go.sum index 3103a058..3380d993 100644 --- a/api/go.sum +++ b/api/go.sum @@ -4,8 +4,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fluxcd/pkg/apis/kustomize v1.16.0 h1:PhWXEhqQqsisIpwp1/wHvTvo+MO+GGzsBPoN0ZnRE3Y= -github.com/fluxcd/pkg/apis/kustomize v1.16.0/go.mod h1:IZOy4CCtR/hxMGb7erK1RfbGnczVv4/dRBoVD37AywI= +github.com/fluxcd/pkg/apis/kustomize v1.17.0 h1:xINP8vW0c6Iz99AFzNX5gFkb8I2QYVCzEaU8HfvajNM= +github.com/fluxcd/pkg/apis/kustomize v1.17.0/go.mod h1:IZOy4CCtR/hxMGb7erK1RfbGnczVv4/dRBoVD37AywI= github.com/fluxcd/pkg/apis/meta v1.26.0 h1:dxP1FfBpTCYso6odzRcltVnnRuBb2VyhhgV0VX9YbUE= github.com/fluxcd/pkg/apis/meta v1.26.0/go.mod h1:c7o6mJGLCMvNrfdinGZehkrdZuFT9vZdZNrn66DtVD0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= diff --git a/go.mod b/go.mod index e18b45f9..1fc467fd 100644 --- a/go.mod +++ b/go.mod @@ -22,13 +22,13 @@ require ( github.com/fluxcd/kustomize-controller/api v1.8.0 github.com/fluxcd/pkg/apis/acl v0.9.0 github.com/fluxcd/pkg/apis/event v0.25.0 - github.com/fluxcd/pkg/apis/kustomize v1.16.0 + github.com/fluxcd/pkg/apis/kustomize v1.17.0 github.com/fluxcd/pkg/apis/meta v1.26.0 github.com/fluxcd/pkg/auth v0.41.0 github.com/fluxcd/pkg/cache v0.13.0 github.com/fluxcd/pkg/http/fetch v0.24.0 - github.com/fluxcd/pkg/kustomize v1.30.0 - github.com/fluxcd/pkg/runtime v0.103.0 + github.com/fluxcd/pkg/kustomize v1.31.0 + github.com/fluxcd/pkg/runtime v0.104.0 github.com/fluxcd/pkg/ssa v0.71.0 github.com/fluxcd/pkg/tar v1.1.0 github.com/fluxcd/pkg/testserver v0.13.0 diff --git a/go.sum b/go.sum index bd34a67b..2306b03a 100644 --- a/go.sum +++ b/go.sum @@ -209,8 +209,8 @@ github.com/fluxcd/pkg/apis/acl v0.9.0 h1:wBpgsKT+jcyZEcM//OmZr9RiF8klL3ebrDp2u2T github.com/fluxcd/pkg/apis/acl v0.9.0/go.mod h1:TttNS+gocsGLwnvmgVi3/Yscwqrjc17+vhgYfqkfrV4= github.com/fluxcd/pkg/apis/event v0.25.0 h1:zdwytvDhG+fk+Ywl5DOtv7TklkrVgM21WHm1f+YhleE= github.com/fluxcd/pkg/apis/event v0.25.0/go.mod h1:TlK8HWYrTwl0raqBRC+ROoNpYW5fdVnwcwOBOx5Kzw8= -github.com/fluxcd/pkg/apis/kustomize v1.16.0 h1:PhWXEhqQqsisIpwp1/wHvTvo+MO+GGzsBPoN0ZnRE3Y= -github.com/fluxcd/pkg/apis/kustomize v1.16.0/go.mod h1:IZOy4CCtR/hxMGb7erK1RfbGnczVv4/dRBoVD37AywI= +github.com/fluxcd/pkg/apis/kustomize v1.17.0 h1:xINP8vW0c6Iz99AFzNX5gFkb8I2QYVCzEaU8HfvajNM= +github.com/fluxcd/pkg/apis/kustomize v1.17.0/go.mod h1:IZOy4CCtR/hxMGb7erK1RfbGnczVv4/dRBoVD37AywI= github.com/fluxcd/pkg/apis/meta v1.26.0 h1:dxP1FfBpTCYso6odzRcltVnnRuBb2VyhhgV0VX9YbUE= github.com/fluxcd/pkg/apis/meta v1.26.0/go.mod h1:c7o6mJGLCMvNrfdinGZehkrdZuFT9vZdZNrn66DtVD0= github.com/fluxcd/pkg/auth v0.41.0 h1:7NaaPN03ginRUUA928n7hiRJoBoMrF/Prl0AtDlLXBQ= @@ -221,10 +221,10 @@ github.com/fluxcd/pkg/envsubst v1.6.0 h1:up1bcNAQ6cCm1OA2aWlcERZqRhXE/V/QasHht5/ github.com/fluxcd/pkg/envsubst v1.6.0/go.mod h1:c3a8DYI855sZUubHFYQbjfjop6Wu4/zg1cLyf7SnCes= github.com/fluxcd/pkg/http/fetch v0.24.0 h1:helmjE86zZG6UTcWDV0IxkKpDKWh96FKeGDSz+W1ZcM= github.com/fluxcd/pkg/http/fetch v0.24.0/go.mod h1:4gGHbadYT5PpVELFfzPx+3FrXSJupcTxu4qciYsFZas= -github.com/fluxcd/pkg/kustomize v1.30.0 h1:WVufDRbpgCTy+bR+ALVtKFfFpKXVAIEIlEceJbA210M= -github.com/fluxcd/pkg/kustomize v1.30.0/go.mod h1:LBo4uYZdnPFQnCBg1qnPtSpntK6XJbybVSovdH+IxZw= -github.com/fluxcd/pkg/runtime v0.103.0 h1:J5y5GPhWdkyqIUBlaI1FP2N02TtZmsjbWhhZubuTSFk= -github.com/fluxcd/pkg/runtime v0.103.0/go.mod h1:mbo2f3azo3yVQgm7XZGxQB6/2zvzQ5Wgtd8TjRRwwAw= +github.com/fluxcd/pkg/kustomize v1.31.0 h1:VjzSzQsysvnQJ//wbcLERtW3GwXZSIR52ChwbBaaIIU= +github.com/fluxcd/pkg/kustomize v1.31.0/go.mod h1:0PmQOAJ1gselt1DrJ1pmoJg37LPDACiSU9bbqZ7pysk= +github.com/fluxcd/pkg/runtime v0.104.0 h1:6isoyeCvYTXRozknz87pU/Z1eWdm4Hi+ojZFwcwnaqs= +github.com/fluxcd/pkg/runtime v0.104.0/go.mod h1:I7KymH0BM5YUSd68ohjm/RTLXGkw59IXFuu8ISLbLvs= github.com/fluxcd/pkg/sourceignore v0.17.0 h1:Z72nruRMhC15zIEpWoDrAcJcJ1El6QDnP/aRDfE4WOA= github.com/fluxcd/pkg/sourceignore v0.17.0/go.mod h1:3e/VmYLId0pI/H5sK7W9Ibif+j0Ahns9RxNjDMtTTfY= github.com/fluxcd/pkg/ssa v0.71.0 h1:xEGkY4OLo0tODJxjh3TX/lzxeqAiwjRDyKZ7dakz5Sc= From f65f7f4e7d91b3e20ed51601963ee314ec350ab5 Mon Sep 17 00:00:00 2001 From: Matheus Pimenta Date: Tue, 21 Apr 2026 16:56:05 +0100 Subject: [PATCH 2/4] Regenerate CRD Signed-off-by: Matheus Pimenta --- config/crd/bases/kustomize.toolkit.fluxcd.io_kustomizations.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/config/crd/bases/kustomize.toolkit.fluxcd.io_kustomizations.yaml b/config/crd/bases/kustomize.toolkit.fluxcd.io_kustomizations.yaml index ccda40e1..beeff81f 100644 --- a/config/crd/bases/kustomize.toolkit.fluxcd.io_kustomizations.yaml +++ b/config/crd/bases/kustomize.toolkit.fluxcd.io_kustomizations.yaml @@ -202,7 +202,6 @@ spec: required: - apiVersion - current - - kind type: object type: array healthChecks: From 1a6cacc0d53e0b9f4c21b1922a74adf4bfd33dae Mon Sep 17 00:00:00 2001 From: Matheus Pimenta Date: Tue, 21 Apr 2026 17:05:42 +0100 Subject: [PATCH 3/4] Update API docs for CEL health checks Signed-off-by: Matheus Pimenta --- docs/spec/v1/kustomizations.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/spec/v1/kustomizations.md b/docs/spec/v1/kustomizations.md index ff9a7eaa..bd6e6f69 100644 --- a/docs/spec/v1/kustomizations.md +++ b/docs/spec/v1/kustomizations.md @@ -355,8 +355,12 @@ health checks on custom resources. This is done through Common Expression Language (CEL) expressions. This field accepts a list of objects with the following fields: -- `apiVersion`: The API version of the custom resource. Required. -- `kind`: The kind of the custom resource. Required. +- `apiVersion`: The API version of the custom resource. Required. Only the + group portion is used for matching; the version is ignored, so the same + entry applies to every served version of the resource. +- `kind`: The kind of the custom resource. Optional. When omitted, the entry + applies to all kinds under the given `apiVersion`'s group. An entry with a + specific `kind` takes precedence over a group-only entry for that same kind. - `current`: A required CEL expression that returns `true` if the resource is ready. - `inProgress`: An optional CEL expression that returns `true` if the resource is still being reconciled. From 5cc19aa4f96006236b1c094680f1ba1bd431b9f6 Mon Sep 17 00:00:00 2001 From: Matheus Pimenta Date: Tue, 21 Apr 2026 17:17:46 +0100 Subject: [PATCH 4/4] Add tests for empty kind in CEL healthchecks Signed-off-by: Matheus Pimenta --- .../controller/kustomization_wait_test.go | 188 ++++++++++++++++++ 1 file changed, 188 insertions(+) diff --git a/internal/controller/kustomization_wait_test.go b/internal/controller/kustomization_wait_test.go index ae90540f..024b36b4 100644 --- a/internal/controller/kustomization_wait_test.go +++ b/internal/controller/kustomization_wait_test.go @@ -640,3 +640,191 @@ spec: g.Expect(cancelEvent.Message).To(ContainSubstring("Health checks canceled")) g.Expect(cancelEvent.Message).To(ContainSubstring("GitRepository")) } + +func TestKustomizationReconciler_HealthCheckExprs_GroupOnly(t *testing.T) { + g := NewWithT(t) + id := "cel-grp-" + randStringRunes(5) + revision := "v1.0.0" + resultK := &kustomizev1.Kustomization{} + timeout := 60 * time.Second + + err := createNamespace(id) + g.Expect(err).NotTo(HaveOccurred(), "failed to create test namespace") + + err = createKubeConfigSecret(id) + g.Expect(err).NotTo(HaveOccurred(), "failed to create kubeconfig secret") + + // Unique group per test run to avoid CRD name collisions within the shared envtest. + group := fmt.Sprintf("%s.flux-test.io", id) + fooCRName := "foo-" + randStringRunes(5) + barCRName := "bar-" + randStringRunes(5) + + // Two cluster-scoped CRDs in the same group, plus one CR of each kind. + // The CEL expression reads `spec.ready`, applied via SSA from the artifact. + buildFiles := func(barReady bool) []testserver.File { + return []testserver.File{ + { + Name: "crd-foo.yaml", + Body: fmt.Sprintf(`--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: foos.%[1]s +spec: + group: %[1]s + names: + kind: Foo + listKind: FooList + plural: foos + singular: foo + scope: Cluster + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + ready: + type: boolean +`, group), + }, + { + Name: "crd-bar.yaml", + Body: fmt.Sprintf(`--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: bars.%[1]s +spec: + group: %[1]s + names: + kind: Bar + listKind: BarList + plural: bars + singular: bar + scope: Cluster + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + ready: + type: boolean +`, group), + }, + { + Name: "foo.yaml", + Body: fmt.Sprintf(`--- +apiVersion: %[1]s/v1 +kind: Foo +metadata: + name: %[2]s +spec: + ready: true +`, group, fooCRName), + }, + { + Name: "bar.yaml", + Body: fmt.Sprintf(`--- +apiVersion: %[1]s/v1 +kind: Bar +metadata: + name: %[2]s +spec: + ready: %[3]t +`, group, barCRName, barReady), + }, + } + } + + artifact, err := testServer.ArtifactFromFiles(buildFiles(true)) + g.Expect(err).NotTo(HaveOccurred()) + + repositoryName := types.NamespacedName{ + Name: fmt.Sprintf("grp-%s", randStringRunes(5)), + Namespace: id, + } + + err = applyGitRepository(repositoryName, artifact, revision) + g.Expect(err).NotTo(HaveOccurred()) + + kustomization := &kustomizev1.Kustomization{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("grp-%s", randStringRunes(5)), + Namespace: id, + }, + Spec: kustomizev1.KustomizationSpec{ + Interval: metav1.Duration{Duration: 2 * time.Minute}, + Path: "./", + KubeConfig: &meta.KubeConfigReference{ + SecretRef: &meta.SecretKeyReference{ + Name: "kubeconfig", + }, + }, + SourceRef: kustomizev1.CrossNamespaceSourceReference{ + Name: repositoryName.Name, + Namespace: repositoryName.Namespace, + Kind: sourcev1.GitRepositoryKind, + }, + Prune: true, + Wait: true, + // Single group-only healthcheck (empty Kind) that must be applied + // to both Foo and Bar custom resources. + HealthCheckExprs: []kustomize.CustomHealthCheck{{ + APIVersion: group + "/v1", + HealthCheckExpressions: kustomize.HealthCheckExpressions{ + Current: "has(spec.ready) && spec.ready == true", + }, + }}, + }, + } + + g.Expect(k8sClient.Create(context.Background(), kustomization)).To(Succeed()) + + t.Run("group-only healthcheck succeeds for both kinds", func(t *testing.T) { + g.Eventually(func() bool { + _ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK) + return isReconcileSuccess(resultK) + }, timeout, time.Second).Should(BeTrue()) + logStatus(t, resultK) + + g.Expect(conditions.IsTrue(resultK, meta.HealthyCondition)).To(BeTrue()) + g.Expect(conditions.GetReason(resultK, meta.HealthyCondition)).To(BeIdenticalTo(meta.SucceededReason)) + }) + + t.Run("reports unhealthy when one kind stops satisfying the group expression", func(t *testing.T) { + badArtifact, err := testServer.ArtifactFromFiles(buildFiles(false)) + g.Expect(err).NotTo(HaveOccurred()) + + err = applyGitRepository(repositoryName, badArtifact, "v1.0.1") + g.Expect(err).NotTo(HaveOccurred()) + + // Shorten healthcheck timeout so the failure surfaces quickly. + g.Eventually(func() error { + _ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK) + resultK.Spec.Timeout = &metav1.Duration{Duration: 5 * time.Second} + return k8sClient.Update(context.Background(), resultK) + }, timeout, time.Second).Should(BeNil()) + + g.Eventually(func() bool { + _ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK) + return conditions.IsFalse(resultK, meta.HealthyCondition) && + conditions.GetReason(resultK, meta.HealthyCondition) == meta.HealthCheckFailedReason + }, timeout, time.Second).Should(BeTrue()) + + msg := conditions.GetMessage(resultK, meta.HealthyCondition) + g.Expect(msg).To(ContainSubstring("Bar")) + g.Expect(msg).To(ContainSubstring(barCRName)) + }) +}