1
0
mirror of synced 2026-02-06 19:05:55 +00:00

Implement flux debug kustomization command

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
This commit is contained in:
Stefan Prodan
2024-12-13 12:21:41 +02:00
parent 19568eb94e
commit 5b740c45d1
7 changed files with 268 additions and 0 deletions

View File

@@ -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
}

View File

@@ -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)
})
}
}

View File

@@ -470,6 +470,7 @@ func resetCmdArgs() {
}
envsubstArgs = envsubstFlags{}
debugHelmReleaseArgs = debugHelmReleaseFlags{}
debugKustomizationArgs = debugKustomizationFlags{}
}
func isChangeError(err error) bool {

View File

@@ -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"

View File

@@ -0,0 +1 @@
observedGeneration: -1

View File

@@ -0,0 +1,3 @@
TEST_CM=cm
TEST_OVERRIDE=secret
TEST_SECRET=secret

View File

@@ -0,0 +1,4 @@
TEST_CM=cm
TEST_INLINE=in-line
TEST_OVERRIDE=in-line
TEST_SECRET=secret