Implement flux debug helmrelease command
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
This commit is contained in:
31
cmd/flux/debug.go
Normal file
31
cmd/flux/debug.go
Normal 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)
|
||||
}
|
||||
125
cmd/flux/debug_helmrelease.go
Normal file
125
cmd/flux/debug_helmrelease.go
Normal 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
|
||||
}
|
||||
55
cmd/flux/debug_helmrelease_test.go
Normal file
55
cmd/flux/debug_helmrelease_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -469,6 +469,7 @@ func resetCmdArgs() {
|
||||
output: "yaml",
|
||||
}
|
||||
envsubstArgs = envsubstFlags{}
|
||||
debugHelmReleaseArgs = debugHelmReleaseFlags{}
|
||||
}
|
||||
|
||||
func isChangeError(err error) bool {
|
||||
|
||||
63
cmd/flux/testdata/debug_helmrelease/objects.yaml
vendored
Normal file
63
cmd/flux/testdata/debug_helmrelease/objects.yaml
vendored
Normal 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"
|
||||
1
cmd/flux/testdata/debug_helmrelease/status.golden.yaml
vendored
Normal file
1
cmd/flux/testdata/debug_helmrelease/status.golden.yaml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
observedGeneration: -1
|
||||
6
cmd/flux/testdata/debug_helmrelease/values-from.golden.yaml
vendored
Normal file
6
cmd/flux/testdata/debug_helmrelease/values-from.golden.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
cm: test
|
||||
image:
|
||||
repository: stefanprodan/podinfo
|
||||
tag: 5.0.0
|
||||
override: secret
|
||||
secret: test
|
||||
3
cmd/flux/testdata/debug_helmrelease/values-inline.golden.yaml
vendored
Normal file
3
cmd/flux/testdata/debug_helmrelease/values-inline.golden.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
image:
|
||||
repository: stefanprodan/podinfo
|
||||
tag: 5.0.0
|
||||
Reference in New Issue
Block a user