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

Implement flux debug helmrelease command

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
This commit is contained in:
Stefan Prodan
2024-12-10 16:23:26 +02:00
parent aa9de2c6bc
commit 120ec049f9
10 changed files with 309 additions and 2 deletions

31
cmd/flux/debug.go Normal file
View File

@@ -0,0 +1,31 @@
/*
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 (
"github.com/spf13/cobra"
)
var debugCmd = &cobra.Command{
Use: "debug",
Short: "Debug a flux resource",
Long: `The debug command can be used to troubleshoot failing resource reconciliations.`,
}
func init() {
rootCmd.AddCommand(debugCmd)
}

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"
helmv2 "github.com/fluxcd/helm-controller/api/v2"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/chartutil"
"github.com/go-logr/logr"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/v2/internal/utils"
)
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.`,
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),
}
type debugHelmReleaseFlags struct {
name string
showStatus bool
showValues bool
}
var debugHelmReleaseArgs debugHelmReleaseFlags
func init() {
debugHelmReleaseCmd.Flags().BoolVar(&debugHelmReleaseArgs.showStatus, "show-status", false, "print the status of the Helm release")
debugHelmReleaseCmd.Flags().BoolVar(&debugHelmReleaseArgs.showValues, "show-values", false, "print the final values of the Helm release")
debugCmd.AddCommand(debugHelmReleaseCmd)
}
func debugHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
name := args[0]
if debugHelmReleaseArgs.showStatus == false && debugHelmReleaseArgs.showValues == false {
return fmt.Errorf("either --show-status or --show-values must be set")
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
if err != nil {
return err
}
hr := &helmv2.HelmRelease{}
hrName := types.NamespacedName{Namespace: *kubeconfigArgs.Namespace, Name: name}
if err := kubeClient.Get(ctx, hrName, hr); err != nil {
return err
}
if debugHelmReleaseArgs.showStatus {
status, err := yaml.Marshal(hr.Status)
if err != nil {
return err
}
rootCmd.Print(string(status))
if debugHelmReleaseArgs.showValues {
rootCmd.Println("---")
}
}
if debugHelmReleaseArgs.showValues {
// TODO(stefan): remove the mapping when helm-controller/api v1.2.0 has been released
var valuesRefs []meta.ValuesReference
for _, source := range hr.Spec.ValuesFrom {
valuesRefs = append(valuesRefs, meta.ValuesReference{
Kind: source.Kind,
Name: source.Name,
ValuesKey: source.ValuesKey,
Optional: source.Optional,
})
}
finalValues, err := chartutil.ChartValuesFromReferences(ctx,
logr.Discard(),
kubeClient,
hr.GetNamespace(),
hr.GetValues(),
valuesRefs...)
if err != nil {
return err
}
values, err := yaml.Marshal(finalValues)
if err != nil {
return err
}
rootCmd.Print(string(values))
}
return nil
}

View File

@@ -0,0 +1,55 @@
//go:build unit
// +build unit
package main
import (
"testing"
)
func TestDebugHelmRelease(t *testing.T) {
namespace := allocateNamespace("debug")
objectFile := "testdata/debug_helmrelease/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 helmrelease test-values-inline --show-status --show-values=false",
"testdata/debug_helmrelease/status.golden.yaml",
tmpl,
},
{
"debug values",
"debug helmrelease test-values-inline --show-values --show-status=false",
"testdata/debug_helmrelease/values-inline.golden.yaml",
tmpl,
},
{
"debug values from",
"debug helmrelease test-values-from --show-values --show-status=false",
"testdata/debug_helmrelease/values-from.golden.yaml",
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

@@ -469,6 +469,7 @@ func resetCmdArgs() {
output: "yaml",
}
envsubstArgs = envsubstFlags{}
debugHelmReleaseArgs = debugHelmReleaseFlags{}
}
func isChangeError(err error) bool {

View File

@@ -0,0 +1,63 @@
apiVersion: v1
kind: Namespace
metadata:
name: {{ .fluxns }}
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: test-values-inline
namespace: {{ .fluxns }}
spec:
chartRef:
kind: OCIRepository
name: podinfo
interval: 5m0s
values:
image:
repository: stefanprodan/podinfo
tag: 5.0.0
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: test-values-from
namespace: {{ .fluxns }}
spec:
chartRef:
kind: OCIRepository
name: podinfo
interval: 5m0s
values:
image:
repository: stefanprodan/podinfo
tag: 5.0.0
valuesFrom:
- kind: ConfigMap
name: test
- kind: Secret
name: test
valuesKey: secrets.yaml
- kind: ConfigMap
name: none
optional: true
---
apiVersion: v1
kind: ConfigMap
metadata:
name: test
namespace: {{ .fluxns }}
data:
values.yaml: |
cm: "test"
override: "cm"
---
apiVersion: v1
kind: Secret
metadata:
name: test
namespace: {{ .fluxns }}
stringData:
secrets.yaml: |
secret: "test"
override: "secret"

View File

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

View File

@@ -0,0 +1,6 @@
cm: test
image:
repository: stefanprodan/podinfo
tag: 5.0.0
override: secret
secret: test

View File

@@ -0,0 +1,3 @@
image:
repository: stefanprodan/podinfo
tag: 5.0.0