From 837f2caad21adb7024e8f4cd27c3689de85571c0 Mon Sep 17 00:00:00 2001 From: Austin Abro <37223396+AustinAbro321@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:31:26 -0500 Subject: [PATCH] feat: introduce slog for zarf tools (#3212) Signed-off-by: Austin Abro --- src/cmd/root.go | 1 + src/cmd/tools/crane.go | 97 +++++++++++++---------------- src/cmd/tools/helm.go | 2 + src/cmd/tools/kubectl.go | 2 + src/cmd/tools/zarf.go | 92 +++++++++++++++++++++++++-- src/config/lang/english.go | 4 -- src/internal/packager/helm/chart.go | 2 + src/pkg/cluster/secrets.go | 5 ++ 8 files changed, 143 insertions(+), 62 deletions(-) diff --git a/src/cmd/root.go b/src/cmd/root.go index 922a015126..5acfc9c278 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -95,6 +95,7 @@ func preRun(cmd *cobra.Command, _ []string) error { var disableMessage bool if LogFormat != "" { disableMessage = true + skipLogFile = true ctx := logger.WithLoggingEnabled(ctx, true) cmd.SetContext(ctx) } diff --git a/src/cmd/tools/crane.go b/src/cmd/tools/crane.go index dd76955339..972a81bffc 100644 --- a/src/cmd/tools/crane.go +++ b/src/cmd/tools/crane.go @@ -5,6 +5,7 @@ package tools import ( + "context" "errors" "fmt" "os" @@ -20,7 +21,7 @@ import ( "github.com/zarf-dev/zarf/src/config/lang" "github.com/zarf-dev/zarf/src/internal/packager/images" "github.com/zarf-dev/zarf/src/pkg/cluster" - "github.com/zarf-dev/zarf/src/pkg/message" + "github.com/zarf-dev/zarf/src/pkg/logger" "github.com/zarf-dev/zarf/src/pkg/transform" "github.com/zarf-dev/zarf/src/types" ) @@ -39,6 +40,11 @@ func init() { Aliases: []string{"r", "crane"}, Short: lang.CmdToolsRegistryShort, PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { + // TODO (@austinabro321) once the code in cmd is simplified, we should change this to respect + // the log-format flag + l := logger.Default() + ctx := logger.WithContext(cmd.Context(), l) + cmd.SetContext(ctx) // The crane options loading here comes from the rootCmd of crane craneOptions = append(craneOptions, crane.WithContext(cmd.Context())) // TODO(jonjohnsonjr): crane.Verbose option? @@ -51,7 +57,6 @@ func init() { if ndlayers { craneOptions = append(craneOptions, crane.WithNondistributable()) } - var err error var v1Platform *v1.Platform if platform != "all" { @@ -111,19 +116,19 @@ func zarfCraneCatalog(cranePlatformOptions *[]crane.Option) *cobra.Command { originalCatalogFn := craneCatalog.RunE craneCatalog.RunE = func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + l := logger.From(cmd.Context()) if len(args) > 0 { return originalCatalogFn(cmd, args) } - message.Note(lang.CmdToolsRegistryZarfState) + l.Info("retrieving registry information from Zarf state") c, err := cluster.NewCluster() if err != nil { return err } - ctx := cmd.Context() - zarfState, err := c.LoadZarfState(ctx) if err != nil { return err @@ -139,7 +144,6 @@ func zarfCraneCatalog(cranePlatformOptions *[]crane.Option) *cobra.Command { *cranePlatformOptions = append(*cranePlatformOptions, authOption) if tunnel != nil { - message.Notef(lang.CmdToolsRegistryTunnel, registryEndpoint, zarfState.RegistryInfo.Address) defer tunnel.Close() return tunnel.Wrap(func() error { return originalCatalogFn(cmd, []string{registryEndpoint}) }) } @@ -160,6 +164,8 @@ func zarfCraneInternalWrapper(commandToWrap func(*[]crane.Option) *cobra.Command originalListFn := wrappedCommand.RunE wrappedCommand.RunE = func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + l := logger.From(ctx) if len(args) < imageNameArgumentIndex+1 { return errors.New("not have enough arguments specified for this command") } @@ -170,13 +176,11 @@ func zarfCraneInternalWrapper(commandToWrap func(*[]crane.Option) *cobra.Command return originalListFn(cmd, args) } - message.Note(lang.CmdToolsRegistryZarfState) - - ctx := cmd.Context() + l.Info("retrieving registry information from Zarf state") zarfState, err := c.LoadZarfState(ctx) if err != nil { - message.Warnf("could not get Zarf state from Kubernetes cluster, continuing without state information %s", err.Error()) + l.Warn("could not get Zarf state from Kubernetes cluster, continuing without state information", "error", err.Error()) return originalListFn(cmd, args) } @@ -195,7 +199,7 @@ func zarfCraneInternalWrapper(commandToWrap func(*[]crane.Option) *cobra.Command *cranePlatformOptions = append(*cranePlatformOptions, authOption) if tunnel != nil { - message.Notef(lang.CmdToolsRegistryTunnel, tunnel.Endpoint(), zarfState.RegistryInfo.Address) + l.Info("opening a tunnel to the Zarf registry", "local-endpoint", tunnel.Endpoint(), "cluster-address", zarfState.RegistryInfo.Address) defer tunnel.Close() @@ -219,6 +223,7 @@ func pruneImages(cmd *cobra.Command, _ []string) error { } ctx := cmd.Context() + l := logger.From(ctx) zarfState, err := c.LoadZarfState(ctx) if err != nil { @@ -237,19 +242,19 @@ func pruneImages(cmd *cobra.Command, _ []string) error { } if tunnel != nil { - message.Notef(lang.CmdToolsRegistryTunnel, registryEndpoint, zarfState.RegistryInfo.Address) + l.Info("opening a tunnel to the Zarf registry", "local-endpoint", tunnel.Endpoint(), "cluster-address", zarfState.RegistryInfo.Address) defer tunnel.Close() - return tunnel.Wrap(func() error { return doPruneImagesForPackages(zarfState, zarfPackages, registryEndpoint) }) + return tunnel.Wrap(func() error { return doPruneImagesForPackages(ctx, zarfState, zarfPackages, registryEndpoint) }) } - return doPruneImagesForPackages(zarfState, zarfPackages, registryEndpoint) + return doPruneImagesForPackages(ctx, zarfState, zarfPackages, registryEndpoint) } -func doPruneImagesForPackages(zarfState *types.ZarfState, zarfPackages []types.DeployedPackage, registryEndpoint string) error { +func doPruneImagesForPackages(ctx context.Context, zarfState *types.ZarfState, zarfPackages []types.DeployedPackage, registryEndpoint string) error { + l := logger.From(ctx) authOption := images.WithPushAuth(zarfState.RegistryInfo) - spinner := message.NewProgressSpinner(lang.CmdToolsRegistryPruneLookup) - defer spinner.Stop() + l.Info("finding images to prune") // Determine which image digests are currently used by Zarf packages pkgImages := map[string]bool{} @@ -278,8 +283,6 @@ func doPruneImagesForPackages(zarfState *types.ZarfState, zarfPackages []types.D } } - spinner.Updatef(lang.CmdToolsRegistryPruneCatalog) - // Find which images and tags are in the registry currently imageCatalog, err := crane.Catalog(registryEndpoint, authOption) if err != nil { @@ -302,8 +305,6 @@ func doPruneImagesForPackages(zarfState *types.ZarfState, zarfPackages []types.D } } - spinner.Updatef(lang.CmdToolsRegistryPruneCalculate) - // Figure out which images are in the registry but not needed by packages imageDigestsToPrune := map[string]bool{} for digestRef, digest := range referenceToDigest { @@ -317,44 +318,36 @@ func doPruneImagesForPackages(zarfState *types.ZarfState, zarfPackages []types.D } } - spinner.Success() + if len(imageDigestsToPrune) == 0 { + l.Info("there are no images to prune") + return nil + } - if len(imageDigestsToPrune) > 0 { - message.Note(lang.CmdToolsRegistryPruneImageList) + l.Info("the following image digests will be pruned from the registry:") + for digestRef := range imageDigestsToPrune { + l.Info(digestRef) + } - for digestRef := range imageDigestsToPrune { - message.Info(digestRef) + confirm := config.CommonOptions.Confirm + if !confirm { + prompt := &survey.Confirm{ + Message: "continue with image prune?", } - - confirm := config.CommonOptions.Confirm - - if confirm { - message.Note(lang.CmdConfirmProvided) - } else { - prompt := &survey.Confirm{ - Message: lang.CmdConfirmContinue, - } - if err := survey.AskOne(prompt, &confirm); err != nil { - return fmt.Errorf("confirm selection canceled: %w", err) - } + if err := survey.AskOne(prompt, &confirm); err != nil { + return fmt.Errorf("confirm selection canceled: %w", err) } - if confirm { - spinner := message.NewProgressSpinner(lang.CmdToolsRegistryPruneDelete) - defer spinner.Stop() + } + if confirm { + l.Info("pruning images") - // Delete the digest references that are to be pruned - for digestRef := range imageDigestsToPrune { - err = crane.Delete(digestRef, authOption) - if err != nil { - return err - } + // Delete the digest references that are to be pruned + for digestRef := range imageDigestsToPrune { + err = crane.Delete(digestRef, authOption) + if err != nil { + return err } - - spinner.Success() + l.Debug("image pruned", "name", digestRef) } - } else { - message.Note(lang.CmdToolsRegistryPruneNoImages) } - return nil } diff --git a/src/cmd/tools/helm.go b/src/cmd/tools/helm.go index 9f367b694e..b6c08e316c 100644 --- a/src/cmd/tools/helm.go +++ b/src/cmd/tools/helm.go @@ -7,6 +7,7 @@ package tools import ( "os" + "github.com/zarf-dev/zarf/src/pkg/logger" "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/cmd/tools/helm" @@ -29,6 +30,7 @@ func init() { helmCmd, err := helm.NewRootCmd(actionConfig, os.Stdout, helmArgs) if err != nil { message.Debug("Failed to initialize helm command", "error", err) + logger.Default().Debug("failed to initialize helm command", "error", err) } helmCmd.Short = lang.CmdToolsHelmShort helmCmd.Long = lang.CmdToolsHelmLong diff --git a/src/cmd/tools/kubectl.go b/src/cmd/tools/kubectl.go index bccabe89f1..4054a4e3e6 100644 --- a/src/cmd/tools/kubectl.go +++ b/src/cmd/tools/kubectl.go @@ -10,6 +10,7 @@ import ( "github.com/spf13/cobra" "github.com/zarf-dev/zarf/src/cmd/common" "github.com/zarf-dev/zarf/src/config/lang" + "github.com/zarf-dev/zarf/src/pkg/logger" "github.com/zarf-dev/zarf/src/pkg/message" kubeCLI "k8s.io/component-base/cli" kubeCmd "k8s.io/kubectl/pkg/cmd" @@ -33,6 +34,7 @@ func init() { if err := kubeCLI.RunNoErrOutput(kubectlCmd); err != nil { // @todo(jeff-mccoy) - Kubectl gets mad about being a subcommand. message.Debug(err) + logger.Default().Debug(err.Error()) } } diff --git a/src/cmd/tools/zarf.go b/src/cmd/tools/zarf.go index d33af49cdf..3f967a8510 100644 --- a/src/cmd/tools/zarf.go +++ b/src/cmd/tools/zarf.go @@ -10,6 +10,7 @@ import ( "fmt" "os" "slices" + "strings" "github.com/AlecAivazis/survey/v2" "github.com/sigstore/cosign/v2/pkg/cosign" @@ -24,6 +25,7 @@ import ( "github.com/zarf-dev/zarf/src/internal/packager/helm" "github.com/zarf-dev/zarf/src/internal/packager/template" "github.com/zarf-dev/zarf/src/pkg/cluster" + "github.com/zarf-dev/zarf/src/pkg/logger" "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/pkg/packager/sources" "github.com/zarf-dev/zarf/src/pkg/pki" @@ -35,6 +37,15 @@ var subAltNames []string var outputDirectory string var updateCredsInitOpts types.ZarfInitOptions +const ( + registryKey = "registry" + registryReadKey = "registry-readonly" + gitKey = "git" + gitReadKey = "git-readonly" + artifactKey = "artifact" + agentKey = "agent" +) + var deprecatedGetGitCredsCmd = &cobra.Command{ Use: "get-git-password", Hidden: true, @@ -74,6 +85,8 @@ var getCredsCmd = &cobra.Command{ if len(args) > 0 { // If a component name is provided, only show that component's credentials + // Printing both the pterm output and slogger for now + printComponentCredential(ctx, state, args[0]) message.PrintComponentCredential(state, args[0]) } else { message.PrintCredentialTable(state, nil) @@ -101,6 +114,7 @@ var updateCredsCmd = &cobra.Command{ } ctx := cmd.Context() + l := logger.From(ctx) timeoutCtx, cancel := context.WithTimeout(ctx, cluster.DefaultTimeout) defer cancel() @@ -122,13 +136,13 @@ var updateCredsCmd = &cobra.Command{ return fmt.Errorf("unable to update Zarf credentials: %w", err) } + // Printing both the pterm output and slogger for now message.PrintCredentialUpdates(oldState, newState, args) + printCredentialUpdates(ctx, oldState, newState, args) confirm := config.CommonOptions.Confirm - if confirm { - message.Note(lang.CmdToolsUpdateCredsConfirmProvided) - } else { + if !confirm { prompt := &survey.Confirm{ Message: lang.CmdToolsUpdateCredsConfirmContinue, } @@ -180,6 +194,7 @@ var updateCredsCmd = &cobra.Command{ if err != nil { // Warn if we couldn't actually update the registry (it might not be installed and we should try to continue) message.Warnf(lang.CmdToolsUpdateCredsUnableUpdateRegistry, err.Error()) + l.Warn("unable to update Zarf Registry values", "error", err.Error()) } } if slices.Contains(args, message.GitKey) && newState.GitServer.IsInternal() && internalGitServerExists { @@ -193,6 +208,7 @@ var updateCredsCmd = &cobra.Command{ if err != nil { // Warn if we couldn't actually update the agent (it might not be installed and we should try to continue) message.Warnf(lang.CmdToolsUpdateCredsUnableUpdateAgent, err.Error()) + l.Warn("unable to update Zarf Agent TLS secrets", "error", err.Error()) } } } @@ -200,16 +216,76 @@ var updateCredsCmd = &cobra.Command{ }, } +func printComponentCredential(ctx context.Context, state *types.ZarfState, componentName string) { + // TODO (@austinabro321) when we move over to the new logger, we can should add fmt.Println calls + // to this function as they will be removed from message.PrintComponentCredential + l := logger.From(ctx) + switch strings.ToLower(componentName) { + case gitKey: + l.Info("Git server push password", "username", state.GitServer.PushUsername) + case gitReadKey: + l.Info("Git server (read-only) password", "username", state.GitServer.PullUsername) + case artifactKey: + l.Info("artifact server token", "username", state.ArtifactServer.PushUsername) + case registryKey: + l.Info("image registry password", "username", state.RegistryInfo.PushUsername) + case registryReadKey: + l.Info("image registry (read-only) password", "username", state.RegistryInfo.PullUsername) + default: + l.Warn("unknown component", "component", componentName) + } +} + +func printCredentialUpdates(ctx context.Context, oldState *types.ZarfState, newState *types.ZarfState, services []string) { + // Pause the logfile's output to avoid credentials being printed to the log file + l := logger.From(ctx) + l.Info("--- printing credential updates. Sensitive values will be redacted ---") + for _, service := range services { + switch service { + case registryKey: + oR := oldState.RegistryInfo + nR := newState.RegistryInfo + l.Info("registry URL address", "existing", oR.Address, "replacement", nR.Address) + l.Info("registry push username", "existing", oR.PushUsername, "replacement", nR.PushUsername) + l.Info("registry push password", "changed", !(oR.PushPassword == nR.PushPassword)) + l.Info("registry pull username", "existing", oR.PullUsername, "replacement", nR.PullUsername) + l.Info("registry pull password", "changed", !(oR.PullPassword == nR.PullPassword)) + case gitKey: + oG := oldState.GitServer + nG := newState.GitServer + l.Info("Git server URL address", "existing", oG.Address, "replacement", nG.Address) + l.Info("Git server push username", "existing", oG.PushUsername, "replacement", nG.PushUsername) + l.Info("Git server push password", "changed", !(oG.PushPassword == nG.PushPassword)) + l.Info("Git server pull username", "existing", oG.PullUsername, "replacement", nG.PullUsername) + l.Info("Git server pull password", "changed", !(oG.PullPassword == nG.PullPassword)) + case artifactKey: + oA := oldState.ArtifactServer + nA := newState.ArtifactServer + l.Info("artifact server URL address", "existing", oA.Address, "replacement", nA.Address) + l.Info("artifact server push username", "existing", oA.PushUsername, "replacement", nA.PushUsername) + l.Info("artifact server push token", "changed", !(oA.PushToken == nA.PushToken)) + case agentKey: + oT := oldState.AgentTLS + nT := newState.AgentTLS + l.Info("agent certificate authority", "changed", !(string(oT.CA) == string(nT.CA))) + l.Info("agent public certificate", "changed", !(string(oT.Cert) == string(nT.Cert))) + l.Info("agent private key", "changed", !(string(oT.Key) == string(nT.Key))) + } + } +} + var clearCacheCmd = &cobra.Command{ Use: "clear-cache", Aliases: []string{"c"}, Short: lang.CmdToolsClearCacheShort, - RunE: func(_ *cobra.Command, _ []string) error { + RunE: func(cmd *cobra.Command, _ []string) error { + l := logger.From(cmd.Context()) cachePath, err := config.GetAbsCachePath() if err != nil { return err } message.Notef(lang.CmdToolsClearCacheDir, cachePath) + l.Info("clearing cache", "path", cachePath) if err := os.RemoveAll(cachePath); err != nil { return fmt.Errorf("unable to clear the cache directory %s: %w", cachePath, err) } @@ -242,7 +318,7 @@ var generatePKICmd = &cobra.Command{ Aliases: []string{"pki"}, Short: lang.CmdToolsGenPkiShort, Args: cobra.ExactArgs(1), - RunE: func(_ *cobra.Command, args []string) error { + RunE: func(cmd *cobra.Command, args []string) error { pki, err := pki.GeneratePKI(args[0], subAltNames...) if err != nil { return err @@ -257,6 +333,7 @@ var generatePKICmd = &cobra.Command{ return err } message.Successf(lang.CmdToolsGenPkiSuccess, args[0]) + logger.From(cmd.Context()).Info("successfully created a chain of trust", "host", args[0]) return nil }, } @@ -265,7 +342,7 @@ var generateKeyCmd = &cobra.Command{ Use: "gen-key", Aliases: []string{"key"}, Short: lang.CmdToolsGenKeyShort, - RunE: func(_ *cobra.Command, _ []string) error { + RunE: func(cmd *cobra.Command, _ []string) error { // Utility function to prompt the user for the password to the private key passwordFunc := func(bool) ([]byte, error) { // perform the first prompt @@ -329,6 +406,9 @@ var generateKeyCmd = &cobra.Command{ } message.Successf(lang.CmdToolsGenKeySuccess, prvKeyFileName, pubKeyFileName) + logger.From(cmd.Context()).Info("Successfully generated key pair", + "private-key-path", prvKeyExistsErr, + "public-key-path", pubKeyFileName) return nil }, } diff --git a/src/config/lang/english.go b/src/config/lang/english.go index d8c0f7694e..2d848a6c1a 100644 --- a/src/config/lang/english.go +++ b/src/config/lang/english.go @@ -36,10 +36,6 @@ const ( // Zarf CLI commands. const ( - // common command language - CmdConfirmProvided = "Confirm flag specified, continuing without prompting." - CmdConfirmContinue = "Continue with these changes?" - // root zarf command RootCmdShort = "DevSecOps for Airgap" RootCmdLong = "Zarf eliminates the complexity of air gap software delivery for Kubernetes clusters and cloud native workloads\n" + diff --git a/src/internal/packager/helm/chart.go b/src/internal/packager/helm/chart.go index 59454057bf..e000045650 100644 --- a/src/internal/packager/helm/chart.go +++ b/src/internal/packager/helm/chart.go @@ -242,6 +242,8 @@ func (h *Helm) RemoveChart(ctx context.Context, namespace string, name string, s func (h *Helm) UpdateReleaseValues(ctx context.Context, updatedValues map[string]interface{}) error { spinner := message.NewProgressSpinner("Updating values for helm release %s", h.chart.ReleaseName) defer spinner.Stop() + l := logger.From(ctx) + l.Debug("updating values for helm release", "name", h.chart.ReleaseName) err := h.createActionConfig(ctx, h.chart.Namespace, spinner) if err != nil { diff --git a/src/pkg/cluster/secrets.go b/src/pkg/cluster/secrets.go index aa49d00c9e..5cfaf30880 100644 --- a/src/pkg/cluster/secrets.go +++ b/src/pkg/cluster/secrets.go @@ -16,6 +16,7 @@ import ( v1ac "k8s.io/client-go/applyconfigurations/core/v1" "github.com/zarf-dev/zarf/src/config" + "github.com/zarf-dev/zarf/src/pkg/logger" "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/types" ) @@ -93,6 +94,7 @@ func (c *Cluster) GenerateGitPullCreds(namespace, name string, gitServerInfo typ // UpdateZarfManagedImageSecrets updates all Zarf-managed image secrets in all namespaces based on state func (c *Cluster) UpdateZarfManagedImageSecrets(ctx context.Context, state *types.ZarfState) error { + l := logger.From(ctx) spinner := message.NewProgressSpinner("Updating existing Zarf-managed image secrets") defer spinner.Stop() @@ -117,6 +119,7 @@ func (c *Cluster) UpdateZarfManagedImageSecrets(ctx context.Context, state *type if err != nil { return err } + l.Info("applying Zarf managed registry secret for namespace", "name", namespace.Name) spinner.Updatef("Updating existing Zarf-managed image secret for namespace: '%s'", namespace.Name) _, err = c.Clientset.CoreV1().Secrets(*newRegistrySecret.Namespace).Apply(ctx, newRegistrySecret, metav1.ApplyOptions{Force: true, FieldManager: FieldManagerName}) if err != nil { @@ -132,6 +135,7 @@ func (c *Cluster) UpdateZarfManagedImageSecrets(ctx context.Context, state *type func (c *Cluster) UpdateZarfManagedGitSecrets(ctx context.Context, state *types.ZarfState) error { spinner := message.NewProgressSpinner("Updating existing Zarf-managed git secrets") defer spinner.Stop() + l := logger.From(ctx) namespaceList, err := c.Clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) if err != nil { @@ -151,6 +155,7 @@ func (c *Cluster) UpdateZarfManagedGitSecrets(ctx context.Context, state *types. } newGitSecret := c.GenerateGitPullCreds(namespace.Name, config.ZarfGitServerSecretName, state.GitServer) spinner.Updatef("Updating existing Zarf-managed git secret for namespace: %s", namespace.Name) + l.Info("applying Zarf managed git secret for namespace", "name", namespace.Name) _, err = c.Clientset.CoreV1().Secrets(*newGitSecret.Namespace).Apply(ctx, newGitSecret, metav1.ApplyOptions{Force: true, FieldManager: FieldManagerName}) if err != nil { return err