cmd/werf/helm/helm.go

Summary

Maintainability
C
1 day
Test Coverage
B
87%
package helm

import (
    "context"
    "fmt"
    "os"
    "path/filepath"
    "time"

    "github.com/spf13/cobra"
    helm_v3 "helm.sh/helm/v3/cmd/helm"
    "helm.sh/helm/v3/pkg/action"
    "helm.sh/helm/v3/pkg/chart"
    "helm.sh/helm/v3/pkg/chart/loader"

    "github.com/werf/kubedog/pkg/kube"
    "github.com/werf/werf/v2/cmd/werf/common"
    helm2 "github.com/werf/werf/v2/cmd/werf/docs/replacers/helm"
    helm_secret_decrypt "github.com/werf/werf/v2/cmd/werf/helm/secret/decrypt"
    helm_secret_encrypt "github.com/werf/werf/v2/cmd/werf/helm/secret/encrypt"
    helm_secret_file_decrypt "github.com/werf/werf/v2/cmd/werf/helm/secret/file/decrypt"
    helm_secret_file_edit "github.com/werf/werf/v2/cmd/werf/helm/secret/file/edit"
    helm_secret_file_encrypt "github.com/werf/werf/v2/cmd/werf/helm/secret/file/encrypt"
    helm_secret_generate_secret_key "github.com/werf/werf/v2/cmd/werf/helm/secret/generate_secret_key"
    helm_secret_rotate_secret_key "github.com/werf/werf/v2/cmd/werf/helm/secret/rotate_secret_key"
    helm_secret_values_decrypt "github.com/werf/werf/v2/cmd/werf/helm/secret/values/decrypt"
    helm_secret_values_edit "github.com/werf/werf/v2/cmd/werf/helm/secret/values/edit"
    helm_secret_values_encrypt "github.com/werf/werf/v2/cmd/werf/helm/secret/values/encrypt"
    "github.com/werf/werf/v2/pkg/deploy/helm"
    "github.com/werf/werf/v2/pkg/deploy/helm/chart_extender"
    "github.com/werf/werf/v2/pkg/deploy/helm/chart_extender/helpers"
    "github.com/werf/werf/v2/pkg/werf"
)

var _commonCmdData common.CmdData

func IsHelm3Mode() bool {
    return os.Getenv("WERF_HELM3_MODE") == "1"
}

func NewCmd(ctx context.Context) (*cobra.Command, error) {
    var namespace string
    ctx = common.NewContextWithCmdData(ctx, &_commonCmdData)
    cmd := common.SetCommandContext(ctx, &cobra.Command{
        Use:          "helm",
        Short:        "Manage application deployment with helm",
        SilenceUsage: true,
    })

    registryClient, err := common.NewHelmRegistryClientWithoutInit(ctx)
    if err != nil {
        return nil, fmt.Errorf("unable to create helm registry client: %w", err)
    }

    actionConfig := new(action.Configuration)
    actionConfig.RegistryClient = registryClient

    wc := chart_extender.NewWerfChartStub(ctx, false)

    loader.GlobalLoadOptions = &loader.LoadOptions{
        ChartExtender:               wc,
        SubchartExtenderFactoryFunc: func() chart.ChartExtender { return chart_extender.NewWerfChartStub(ctx, false) },
    }

    os.Setenv("HELM_EXPERIMENTAL_OCI", "1")

    cmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", *helm_v3.Settings.GetNamespaceP(), "namespace scope for this request")
    common.SetupTmpDir(&_commonCmdData, cmd, common.SetupTmpDirOptions{})
    common.SetupHomeDir(&_commonCmdData, cmd, common.SetupHomeDirOptions{})
    common.SetupKubeConfig(&_commonCmdData, cmd)
    common.SetupKubeConfigBase64(&_commonCmdData, cmd)
    common.SetupKubeContext(&_commonCmdData, cmd)
    common.SetupStatusProgressPeriod(&_commonCmdData, cmd)
    common.SetupHooksStatusProgressPeriod(&_commonCmdData, cmd)
    common.SetupReleasesHistoryMax(&_commonCmdData, cmd)
    common.SetupLogOptions(&_commonCmdData, cmd)
    common.SetupInsecureHelmDependencies(&_commonCmdData, cmd, false)
    common.SetupDockerConfig(&_commonCmdData, cmd, "")

    dependencyCmd := helm_v3.NewDependencyCmd(actionConfig, os.Stdout)
    for _, depCmd := range dependencyCmd.Commands() {
        if depCmd.Name() == "update" {
            oldRunE := depCmd.RunE
            depCmd.RunE = func(cmd *cobra.Command, args []string) error {
                if err := oldRunE(cmd, args); err != nil {
                    return err
                }

                chartpath := "."
                if len(args) > 0 {
                    chartpath = filepath.Clean(args[0])
                }

                ch, err := loader.LoadDir(chartpath)
                if err != nil {
                    return fmt.Errorf("error loading chart %q: %w", chartpath, err)
                }

                return chart_extender.CopyChartDependenciesIntoCache(cmd.Context(), chartpath, ch)
            }
        }
    }

    cmd.AddCommand(
        helm2.ReplaceHelmUninstallDocs(helm_v3.NewUninstallCmd(actionConfig, os.Stdout, helm_v3.UninstallCmdOptions{
            StagesSplitter: helm.NewStagesSplitter(),
        })),
        helm2.ReplaceHelmDependencyDocs(dependencyCmd),
        helm2.ReplaceHelmGetDocs(helm_v3.NewGetCmd(actionConfig, os.Stdout)),
        helm2.ReplaceHelmHistoryDocs(helm_v3.NewHistoryCmd(actionConfig, os.Stdout)),
        NewLintCmd(actionConfig, wc),
        helm2.ReplaceHelmListDocs(helm_v3.NewListCmd(actionConfig, os.Stdout)),
        NewTemplateCmd(actionConfig, wc, &namespace),
        helm2.ReplaceHelmRepoDocs(helm_v3.NewRepoCmd(os.Stdout)),
        helm2.ReplaceHelmRollbackDocs(helm_v3.NewRollbackCmd(actionConfig, os.Stdout, helm_v3.RollbackCmdOptions{
            StagesSplitter:              helm.NewStagesSplitter(),
            StagesExternalDepsGenerator: helm.NewStagesExternalDepsGenerator(&actionConfig.RESTClientGetter, &namespace),
        })),
        NewInstallCmd(actionConfig, wc, &namespace),
        helm2.ReplaceHelmUpgradeDocs(NewUpgradeCmd(actionConfig, wc, &namespace)),
        helm2.ReplaceHelmCreateDocs(helm_v3.NewCreateCmd(os.Stdout)),
        helm2.ReplaceHelmEnvDocs(helm_v3.NewEnvCmd(os.Stdout)),
        helm2.ReplaceHelmPackageDocs(helm_v3.NewPackageCmd(actionConfig, os.Stdout)),
        helm2.ReplaceHelmPluginDocs(helm_v3.NewPluginCmd(os.Stdout)),
        helm2.ReplaceHelmPullDocs(helm_v3.NewPullCmd(actionConfig, os.Stdout)),
        helm2.ReplaceHelmSearchDocs(helm_v3.NewSearchCmd(os.Stdout)),
        helm2.ReplaceHelmShowDocs(helm_v3.NewShowCmd(actionConfig, os.Stdout)),
        helm2.ReplaceHelmStatusDocs(helm_v3.NewStatusCmd(actionConfig, os.Stdout)),
        helm_v3.NewTestCmd(actionConfig, os.Stdout),
        helm2.ReplaceHelmVerifyDocs(helm_v3.NewVerifyCmd(os.Stdout)),
        helm2.ReplaceHelmVersionDocs(helm_v3.NewVersionCmd(os.Stdout)),
        secretCmd(ctx),
        NewGetAutogeneratedValuesCmd(ctx),
        NewGetNamespaceCmd(ctx),
        NewGetReleaseCmd(ctx),
        helm_v3.NewRegistryCmd(actionConfig, os.Stdout),
    )

    if IsHelm3Mode() {
        helm_v3.LoadPlugins(cmd, os.Stdout)
    } else {
        func() {
            if len(os.Args) > 1 {
                saveArgs := os.Args
                os.Args = os.Args[1:]
                defer func() {
                    os.Args = saveArgs
                }()
            }

            helm_v3.LoadPlugins(cmd, os.Stdout)
        }()
    }

    commandsQueue := []*cobra.Command{cmd}
    for len(commandsQueue) > 0 {
        cmd := commandsQueue[0]
        commandsQueue = commandsQueue[1:]

        commandsQueue = append(commandsQueue, cmd.Commands()...)

        if cmd.Runnable() {
            oldRunE := cmd.RunE
            cmd.RunE = nil

            oldRun := cmd.Run
            cmd.Run = nil

            cmd.RunE = func(cmd *cobra.Command, args []string) error {
                // NOTE: Common init block for all runnable commands.
                ctx := cmd.Context()

                if err := werf.Init(*_commonCmdData.TmpDir, *_commonCmdData.HomeDir); err != nil {
                    return err
                }

                if err := common.ProcessLogOptions(&_commonCmdData); err != nil {
                    common.PrintHelp(cmd)
                    return err
                }

                os.Setenv("WERF_HELM3_MODE", "1")
                os.Setenv("HELM_NAMESPACE", namespace)

                stubCommitDate := time.Unix(0, 0)

                if vals, err := helpers.GetServiceValues(ctx, "PROJECT", "REPO", nil, helpers.ServiceValuesOptions{
                    Namespace:  namespace,
                    IsStub:     true,
                    CommitHash: "COMMIT_HASH",
                    CommitDate: &stubCommitDate,
                }); err != nil {
                    return fmt.Errorf("error creating service values: %w", err)
                } else {
                    wc.SetStubServiceValues(vals)
                }

                common.InitHelmRegistryClient(registryClient, *_commonCmdData.DockerConfig, *_commonCmdData.InsecureHelmDependencies)

                common.SetupOndemandKubeInitializer(*_commonCmdData.KubeContext, *_commonCmdData.KubeConfig, *_commonCmdData.KubeConfigBase64, *_commonCmdData.KubeConfigPathMergeList)

                helm.InitActionConfig(ctx, common.GetOndemandKubeInitializer(), namespace, helm_v3.Settings, actionConfig, helm.InitActionConfigOptions{
                    StatusProgressPeriod:      time.Duration(*_commonCmdData.StatusProgressPeriodSeconds) * time.Second,
                    HooksStatusProgressPeriod: time.Duration(*_commonCmdData.HooksStatusProgressPeriodSeconds) * time.Second,
                    KubeConfigOptions: kube.KubeConfigOptions{
                        Context:             *_commonCmdData.KubeContext,
                        ConfigPath:          *_commonCmdData.KubeConfig,
                        ConfigPathMergeList: *_commonCmdData.KubeConfigPathMergeList,
                        ConfigDataBase64:    *_commonCmdData.KubeConfigBase64,
                    },
                    ReleasesHistoryMax: *_commonCmdData.ReleasesHistoryMax,
                })

                if oldRunE != nil {
                    return oldRunE(cmd, args)
                } else if oldRun != nil {
                    oldRun(cmd, args)
                    return nil
                } else {
                    panic(fmt.Sprintf("unexpected command %q, please report bug to the https://github.com/werf/werf", cmd.Name()))
                }
            }
        }
    }

    if len(os.Args) > 1 {
        cmd.PersistentFlags().ParseErrorsWhitelist.UnknownFlags = true
        cmd.PersistentFlags().Parse(os.Args[1:])
    }

    return cmd, nil
}

func secretCmd(ctx context.Context) *cobra.Command {
    cmd := common.SetCommandContext(ctx, &cobra.Command{
        Use:   "secret",
        Short: "Work with secrets",
    })

    fileCmd := common.SetCommandContext(ctx, &cobra.Command{
        Use:   "file",
        Short: "Work with secret files",
    })

    fileCmd.AddCommand(
        helm_secret_file_encrypt.NewCmd(ctx),
        helm_secret_file_decrypt.NewCmd(ctx),
        helm_secret_file_edit.NewCmd(ctx),
    )

    valuesCmd := common.SetCommandContext(ctx, &cobra.Command{
        Use:   "values",
        Short: "Work with secret values files",
    })

    valuesCmd.AddCommand(
        helm_secret_values_encrypt.NewCmd(ctx),
        helm_secret_values_decrypt.NewCmd(ctx),
        helm_secret_values_edit.NewCmd(ctx),
    )

    cmd.AddCommand(
        fileCmd,
        valuesCmd,
        helm_secret_generate_secret_key.NewCmd(ctx),
        helm_secret_encrypt.NewCmd(ctx),
        helm_secret_decrypt.NewCmd(ctx),
        helm_secret_rotate_secret_key.NewCmd(ctx),
    )

    return cmd
}