Skip to content

Commit

Permalink
Merge pull request #5117 from fluxcd/debug-kc
Browse files Browse the repository at this point in the history
Implement `flux debug kustomization` command
  • Loading branch information
stefanprodan authored Jan 6, 2025
2 parents b45a8b5 + 5208515 commit b232bbe
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 9 deletions.
18 changes: 9 additions & 9 deletions cmd/flux/debug_helmrelease.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,19 @@ 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
# 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
}
Expand All @@ -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")
}

Expand All @@ -86,10 +87,9 @@ 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("---")
}
return nil
}

if debugHelmReleaseArgs.showValues {
Expand Down
16 changes: 16 additions & 0 deletions cmd/flux/debug_helmrelease_test.go
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down
134 changes: 134 additions & 0 deletions cmd/flux/debug_kustomization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
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"
"errors"
"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),
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
}

type debugKustomizationFlags struct {
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.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
}

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 {
// 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", "")
}
}

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
}
71 changes: 71 additions & 0 deletions cmd/flux/debug_kustomization_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}
}
16 changes: 16 additions & 0 deletions cmd/flux/export_test.go
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down
1 change: 1 addition & 0 deletions cmd/flux/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ func resetCmdArgs() {
}
envsubstArgs = envsubstFlags{}
debugHelmReleaseArgs = debugHelmReleaseFlags{}
debugKustomizationArgs = debugKustomizationFlags{}
}

func isChangeError(err error) bool {
Expand Down
1 change: 1 addition & 0 deletions cmd/flux/testdata/debug_helmrelease/status.golden.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# Status documentation: https://fluxcd.io/flux/components/helm/helmreleases/#helmrelease-status
observedGeneration: -1
63 changes: 63 additions & 0 deletions cmd/flux/testdata/debug_kustomization/objects.yaml
Original file line number Diff line number Diff line change
@@ -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"
2 changes: 2 additions & 0 deletions cmd/flux/testdata/debug_kustomization/status.golden.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Status documentation: https://fluxcd.io/flux/components/kustomize/kustomizations/#kustomization-status
observedGeneration: -1
3 changes: 3 additions & 0 deletions cmd/flux/testdata/debug_kustomization/vars-from.golden.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
TEST_CM=cm
TEST_OVERRIDE=secret
TEST_SECRET=secret
4 changes: 4 additions & 0 deletions cmd/flux/testdata/debug_kustomization/vars.golden.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
TEST_CM=cm
TEST_INLINE=in-line
TEST_OVERRIDE=in-line
TEST_SECRET=secret
Loading

0 comments on commit b232bbe

Please sign in to comment.