From 5cf0dcc77d568cb34ecf71dc39f995adbb594426 Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Fri, 13 Dec 2024 10:53:39 +0200 Subject: [PATCH 1/6] Add preview note to `debug hr` Signed-off-by: Stefan Prodan --- cmd/flux/debug_helmrelease.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/flux/debug_helmrelease.go b/cmd/flux/debug_helmrelease.go index 9997eeb844..7a5c934df5 100644 --- a/cmd/flux/debug_helmrelease.go +++ b/cmd/flux/debug_helmrelease.go @@ -35,8 +35,8 @@ var debugHelmReleaseCmd = &cobra.Command{ Use: "helmrelease [name]", Aliases: []string{"hr"}, Short: "Debug a HelmRelease resource", - Long: `The debug helmrelease command can be used to troubleshoot failing Helm release reconciliations. -WARNING: This command will print sensitive information if Kubernetes Secrets are referenced in the HelmRelease .spec.valuesFrom field.`, + Long: withPreviewNote(`The debug helmrelease command can be used to troubleshoot failing Helm release reconciliations. +WARNING: This command will print sensitive information if Kubernetes Secrets are referenced in the HelmRelease .spec.valuesFrom field.`), Example: ` # Print the status of a Helm release flux debug hr podinfo --show-status From 19568eb94e439cbbb3d7cf3242cb09ab2659ce76 Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Fri, 13 Dec 2024 10:54:07 +0200 Subject: [PATCH 2/6] Add missing copyright headers Signed-off-by: Stefan Prodan --- cmd/flux/debug_helmrelease_test.go | 16 ++++++++++++++++ cmd/flux/export_test.go | 16 ++++++++++++++++ cmd/flux/trace_test.go | 16 ++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/cmd/flux/debug_helmrelease_test.go b/cmd/flux/debug_helmrelease_test.go index cb65df41bd..a9de934418 100644 --- a/cmd/flux/debug_helmrelease_test.go +++ b/cmd/flux/debug_helmrelease_test.go @@ -1,6 +1,22 @@ //go:build unit // +build unit +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package main import ( diff --git a/cmd/flux/export_test.go b/cmd/flux/export_test.go index 5488305b18..2329b9227b 100644 --- a/cmd/flux/export_test.go +++ b/cmd/flux/export_test.go @@ -1,6 +1,22 @@ //go:build unit // +build unit +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package main import ( diff --git a/cmd/flux/trace_test.go b/cmd/flux/trace_test.go index a4126a4a1b..0f11f6da5d 100644 --- a/cmd/flux/trace_test.go +++ b/cmd/flux/trace_test.go @@ -1,6 +1,22 @@ //go:build unit // +build unit +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package main import ( From 5b740c45d19faf5f2cfe5456d792b9c3125a0111 Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Fri, 13 Dec 2024 12:21:41 +0200 Subject: [PATCH 3/6] Implement `flux debug kustomization` command Signed-off-by: Stefan Prodan --- cmd/flux/debug_kustomization.go | 125 ++++++++++++++++++ cmd/flux/debug_kustomization_test.go | 71 ++++++++++ cmd/flux/main_test.go | 1 + .../testdata/debug_kustomization/objects.yaml | 63 +++++++++ .../debug_kustomization/status.golden.yaml | 1 + .../debug_kustomization/vars-from.golden.env | 3 + .../debug_kustomization/vars.golden.env | 4 + 7 files changed, 268 insertions(+) create mode 100644 cmd/flux/debug_kustomization.go create mode 100644 cmd/flux/debug_kustomization_test.go create mode 100644 cmd/flux/testdata/debug_kustomization/objects.yaml create mode 100644 cmd/flux/testdata/debug_kustomization/status.golden.yaml create mode 100644 cmd/flux/testdata/debug_kustomization/vars-from.golden.env create mode 100644 cmd/flux/testdata/debug_kustomization/vars.golden.env diff --git a/cmd/flux/debug_kustomization.go b/cmd/flux/debug_kustomization.go new file mode 100644 index 0000000000..1036600580 --- /dev/null +++ b/cmd/flux/debug_kustomization.go @@ -0,0 +1,125 @@ +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "fmt" + "sort" + "strings" + + kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" + "github.com/fluxcd/pkg/kustomize" + "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/yaml" + + "github.com/fluxcd/flux2/v2/internal/utils" +) + +var debugKustomizationCmd = &cobra.Command{ + Use: "kustomization [name]", + Aliases: []string{"ks"}, + Short: "Debug a Flux Kustomization resource", + Long: withPreviewNote(`The debug kustomization command can be used to troubleshoot failing Flux Kustomization reconciliations. +WARNING: This command will print sensitive information if Kubernetes Secrets are referenced in the Kustomization .spec.postBuild.substituteFrom field.`), + Example: ` # Print the status of a Flux Kustomization + flux debug ks podinfo --show-status + + # Export the final variables used for post-build substitutions composed from referred ConfigMaps and Secrets + flux debug ks podinfo --show-vars > vars.env`, + RunE: debugKustomizationCmdRun, + Args: cobra.ExactArgs(1), +} + +type debugKustomizationFlags struct { + name string + showStatus bool + showVars bool +} + +var debugKustomizationArgs debugKustomizationFlags + +func init() { + debugKustomizationCmd.Flags().BoolVar(&debugKustomizationArgs.showStatus, "show-status", false, "print the status of the Flux Kustomization") + debugKustomizationCmd.Flags().BoolVar(&debugKustomizationArgs.showVars, "show-vars", false, "print the final vars of the Flux Kustomization in dot env format") + debugCmd.AddCommand(debugKustomizationCmd) +} + +func debugKustomizationCmdRun(cmd *cobra.Command, args []string) error { + name := args[0] + + if (!debugKustomizationArgs.showStatus && !debugKustomizationArgs.showVars) || + (debugKustomizationArgs.showStatus && debugKustomizationArgs.showVars) { + return fmt.Errorf("either --show-status or --show-vars must be set") + } + + ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) + defer cancel() + + kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions) + if err != nil { + return err + } + + ks := &kustomizev1.Kustomization{} + ksName := types.NamespacedName{Namespace: *kubeconfigArgs.Namespace, Name: name} + if err := kubeClient.Get(ctx, ksName, ks); err != nil { + return err + } + + if debugKustomizationArgs.showStatus { + status, err := yaml.Marshal(ks.Status) + if err != nil { + return err + } + rootCmd.Print(string(status)) + return nil + } + + if debugKustomizationArgs.showVars { + ksObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ks) + if err != nil { + return err + } + + finalVars, err := kustomize.LoadVariables(ctx, kubeClient, unstructured.Unstructured{Object: ksObj}) + if err != nil { + return err + } + + if len(ks.Spec.PostBuild.Substitute) > 0 { + for k, v := range ks.Spec.PostBuild.Substitute { + finalVars[k] = strings.ReplaceAll(v, "\n", "") + } + } + + keys := make([]string, 0, len(finalVars)) + for k := range finalVars { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, k := range keys { + rootCmd.Println(k + "=" + finalVars[k]) + } + } + + return nil +} diff --git a/cmd/flux/debug_kustomization_test.go b/cmd/flux/debug_kustomization_test.go new file mode 100644 index 0000000000..d3742ed36e --- /dev/null +++ b/cmd/flux/debug_kustomization_test.go @@ -0,0 +1,71 @@ +//go:build unit +// +build unit + +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "testing" +) + +func TestDebugKustomization(t *testing.T) { + namespace := allocateNamespace("debug") + + objectFile := "testdata/debug_kustomization/objects.yaml" + tmpl := map[string]string{ + "fluxns": namespace, + } + testEnv.CreateObjectFile(objectFile, tmpl, t) + + cases := []struct { + name string + arg string + goldenFile string + tmpl map[string]string + }{ + { + "debug status", + "debug ks test --show-status --show-vars=false", + "testdata/debug_kustomization/status.golden.yaml", + tmpl, + }, + { + "debug vars", + "debug ks test --show-vars --show-status=false", + "testdata/debug_kustomization/vars.golden.env", + tmpl, + }, + { + "debug vars from", + "debug ks test-from --show-vars --show-status=false", + "testdata/debug_kustomization/vars-from.golden.env", + tmpl, + }, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + cmd := cmdTestCase{ + args: tt.arg + " -n=" + namespace, + assert: assertGoldenTemplateFile(tt.goldenFile, tmpl), + } + + cmd.runTestCmd(t) + }) + } +} diff --git a/cmd/flux/main_test.go b/cmd/flux/main_test.go index e814c5854e..30673b1f89 100644 --- a/cmd/flux/main_test.go +++ b/cmd/flux/main_test.go @@ -470,6 +470,7 @@ func resetCmdArgs() { } envsubstArgs = envsubstFlags{} debugHelmReleaseArgs = debugHelmReleaseFlags{} + debugKustomizationArgs = debugKustomizationFlags{} } func isChangeError(err error) bool { diff --git a/cmd/flux/testdata/debug_kustomization/objects.yaml b/cmd/flux/testdata/debug_kustomization/objects.yaml new file mode 100644 index 0000000000..b724d7d32c --- /dev/null +++ b/cmd/flux/testdata/debug_kustomization/objects.yaml @@ -0,0 +1,63 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .fluxns }} +--- +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: test + namespace: {{ .fluxns }} +spec: + sourceRef: + kind: GitRepository + name: test + interval: 1m + path: "./" + prune: true + postBuild: + substitute: + TEST_OVERRIDE: "in-line" + TEST_INLINE: "in-line" + substituteFrom: + - kind: ConfigMap + name: test + - kind: Secret + name: test +--- +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: test-from + namespace: {{ .fluxns }} +spec: + sourceRef: + kind: GitRepository + name: test + interval: 1m + path: "./" + prune: true + postBuild: + substituteFrom: + - kind: ConfigMap + name: test + - kind: Secret + name: test +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test + namespace: {{ .fluxns }} +data: + TEST_OVERRIDE: "cm" + TEST_CM: "cm" +--- +apiVersion: v1 +kind: Secret +metadata: + name: test + namespace: {{ .fluxns }} +stringData: + TEST_OVERRIDE: "secret" + TEST_SECRET: "secret" diff --git a/cmd/flux/testdata/debug_kustomization/status.golden.yaml b/cmd/flux/testdata/debug_kustomization/status.golden.yaml new file mode 100644 index 0000000000..8f988e76af --- /dev/null +++ b/cmd/flux/testdata/debug_kustomization/status.golden.yaml @@ -0,0 +1 @@ +observedGeneration: -1 diff --git a/cmd/flux/testdata/debug_kustomization/vars-from.golden.env b/cmd/flux/testdata/debug_kustomization/vars-from.golden.env new file mode 100644 index 0000000000..f69b74a9dd --- /dev/null +++ b/cmd/flux/testdata/debug_kustomization/vars-from.golden.env @@ -0,0 +1,3 @@ +TEST_CM=cm +TEST_OVERRIDE=secret +TEST_SECRET=secret diff --git a/cmd/flux/testdata/debug_kustomization/vars.golden.env b/cmd/flux/testdata/debug_kustomization/vars.golden.env new file mode 100644 index 0000000000..9d9f18a077 --- /dev/null +++ b/cmd/flux/testdata/debug_kustomization/vars.golden.env @@ -0,0 +1,4 @@ +TEST_CM=cm +TEST_INLINE=in-line +TEST_OVERRIDE=in-line +TEST_SECRET=secret From 9b1b5e8a51de06331d129f77ce8787d265d07043 Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Fri, 13 Dec 2024 18:40:02 +0200 Subject: [PATCH 4/6] Add name completion to debug commands Signed-off-by: Stefan Prodan --- cmd/flux/debug_helmrelease.go | 6 +++--- cmd/flux/debug_kustomization.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/flux/debug_helmrelease.go b/cmd/flux/debug_helmrelease.go index 7a5c934df5..4f6a9d8a21 100644 --- a/cmd/flux/debug_helmrelease.go +++ b/cmd/flux/debug_helmrelease.go @@ -42,12 +42,12 @@ WARNING: This command will print sensitive information if Kubernetes Secrets are # Export the final values of a Helm release composed from referred ConfigMaps and Secrets flux debug hr podinfo --show-values > values.yaml`, - RunE: debugHelmReleaseCmdRun, - Args: cobra.ExactArgs(1), + RunE: debugHelmReleaseCmdRun, + Args: cobra.ExactArgs(1), + ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)), } type debugHelmReleaseFlags struct { - name string showStatus bool showValues bool } diff --git a/cmd/flux/debug_kustomization.go b/cmd/flux/debug_kustomization.go index 1036600580..860505643f 100644 --- a/cmd/flux/debug_kustomization.go +++ b/cmd/flux/debug_kustomization.go @@ -44,12 +44,12 @@ WARNING: This command will print sensitive information if Kubernetes Secrets are # Export the final variables used for post-build substitutions composed from referred ConfigMaps and Secrets flux debug ks podinfo --show-vars > vars.env`, - RunE: debugKustomizationCmdRun, - Args: cobra.ExactArgs(1), + RunE: debugKustomizationCmdRun, + Args: cobra.ExactArgs(1), + ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)), } type debugKustomizationFlags struct { - name string showStatus bool showVars bool } From 928ea24bcb58ce940c14b2946bf0770295e74345 Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Wed, 18 Dec 2024 00:50:02 +0200 Subject: [PATCH 5/6] Add links to status docs in `flux debug` commands Signed-off-by: Stefan Prodan --- cmd/flux/debug_helmrelease.go | 1 + cmd/flux/debug_kustomization.go | 9 +++++++++ cmd/flux/testdata/debug_helmrelease/status.golden.yaml | 1 + cmd/flux/testdata/debug_kustomization/status.golden.yaml | 1 + 4 files changed, 12 insertions(+) diff --git a/cmd/flux/debug_helmrelease.go b/cmd/flux/debug_helmrelease.go index 4f6a9d8a21..0bde73bc4e 100644 --- a/cmd/flux/debug_helmrelease.go +++ b/cmd/flux/debug_helmrelease.go @@ -86,6 +86,7 @@ func debugHelmReleaseCmdRun(cmd *cobra.Command, args []string) error { if err != nil { return err } + rootCmd.Println("# Status documentation: https://fluxcd.io/flux/components/helm/helmreleases/#helmrelease-status") rootCmd.Print(string(status)) if debugHelmReleaseArgs.showValues { rootCmd.Println("---") diff --git a/cmd/flux/debug_kustomization.go b/cmd/flux/debug_kustomization.go index 860505643f..026b150c20 100644 --- a/cmd/flux/debug_kustomization.go +++ b/cmd/flux/debug_kustomization.go @@ -18,6 +18,7 @@ package main import ( "context" + "errors" "fmt" "sort" "strings" @@ -89,11 +90,16 @@ func debugKustomizationCmdRun(cmd *cobra.Command, args []string) error { if err != nil { return err } + rootCmd.Println("# Status documentation: https://fluxcd.io/flux/components/kustomize/kustomizations/#kustomization-status") rootCmd.Print(string(status)) return nil } if debugKustomizationArgs.showVars { + if ks.Spec.PostBuild == nil { + return errors.New("no post build substitutions found") + } + ksObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ks) if err != nil { return err @@ -106,6 +112,9 @@ func debugKustomizationCmdRun(cmd *cobra.Command, args []string) error { if len(ks.Spec.PostBuild.Substitute) > 0 { for k, v := range ks.Spec.PostBuild.Substitute { + // Remove new lines from the values as they are not supported. + // Replicates the controller behavior from + // https://github.com/fluxcd/pkg/blob/main/kustomize/kustomize_varsub.go finalVars[k] = strings.ReplaceAll(v, "\n", "") } } diff --git a/cmd/flux/testdata/debug_helmrelease/status.golden.yaml b/cmd/flux/testdata/debug_helmrelease/status.golden.yaml index 8f988e76af..14c35c7692 100644 --- a/cmd/flux/testdata/debug_helmrelease/status.golden.yaml +++ b/cmd/flux/testdata/debug_helmrelease/status.golden.yaml @@ -1 +1,2 @@ +# Status documentation: https://fluxcd.io/flux/components/helm/helmreleases/#helmrelease-status observedGeneration: -1 diff --git a/cmd/flux/testdata/debug_kustomization/status.golden.yaml b/cmd/flux/testdata/debug_kustomization/status.golden.yaml index 8f988e76af..a71799aa84 100644 --- a/cmd/flux/testdata/debug_kustomization/status.golden.yaml +++ b/cmd/flux/testdata/debug_kustomization/status.golden.yaml @@ -1 +1,2 @@ +# Status documentation: https://fluxcd.io/flux/components/kustomize/kustomizations/#kustomization-status observedGeneration: -1 From 52085156047292ae035b1cd1451ece68d24d070c Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Mon, 23 Dec 2024 17:44:33 +0200 Subject: [PATCH 6/6] Make `flux debug hr` single flag selection required Signed-off-by: Stefan Prodan --- cmd/flux/debug_helmrelease.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cmd/flux/debug_helmrelease.go b/cmd/flux/debug_helmrelease.go index 0bde73bc4e..d87d815542 100644 --- a/cmd/flux/debug_helmrelease.go +++ b/cmd/flux/debug_helmrelease.go @@ -63,7 +63,8 @@ func init() { func debugHelmReleaseCmdRun(cmd *cobra.Command, args []string) error { name := args[0] - if debugHelmReleaseArgs.showStatus == false && debugHelmReleaseArgs.showValues == false { + if (!debugHelmReleaseArgs.showStatus && !debugHelmReleaseArgs.showValues) || + (debugHelmReleaseArgs.showStatus && debugHelmReleaseArgs.showValues) { return fmt.Errorf("either --show-status or --show-values must be set") } @@ -88,9 +89,7 @@ func debugHelmReleaseCmdRun(cmd *cobra.Command, args []string) error { } rootCmd.Println("# Status documentation: https://fluxcd.io/flux/components/helm/helmreleases/#helmrelease-status") rootCmd.Print(string(status)) - if debugHelmReleaseArgs.showValues { - rootCmd.Println("---") - } + return nil } if debugHelmReleaseArgs.showValues {