From 3a6753610370a4d05db2a08040f17d53d31d2f66 Mon Sep 17 00:00:00 2001 From: Philip Laine Date: Thu, 29 Aug 2024 15:55:27 +0200 Subject: [PATCH] refactor: move table printing out of pkg Signed-off-by: Philip Laine --- src/cmd/common/table.go | 170 ++++++++++++++++++++++++++++++++- src/cmd/connect.go | 3 +- src/cmd/initialize.go | 16 +++- src/cmd/package.go | 18 +++- src/cmd/tools/zarf.go | 20 ++-- src/pkg/cluster/state.go | 18 +++- src/pkg/cluster/state_test.go | 9 +- src/pkg/message/connect.go | 27 ------ src/pkg/message/credentials.go | 150 ----------------------------- src/pkg/message/message.go | 28 ------ src/pkg/packager/common.go | 4 + src/pkg/packager/deploy.go | 54 +++-------- 12 files changed, 243 insertions(+), 274 deletions(-) delete mode 100644 src/pkg/message/connect.go delete mode 100644 src/pkg/message/credentials.go diff --git a/src/cmd/common/table.go b/src/cmd/common/table.go index ee5f9ee190..cb04acf0bc 100644 --- a/src/cmd/common/table.go +++ b/src/cmd/common/table.go @@ -6,14 +6,46 @@ package common import ( "fmt" "path/filepath" + "strings" "github.com/defenseunicorns/pkg/helpers/v2" "github.com/fatih/color" + "github.com/pterm/pterm" + "github.com/zarf-dev/zarf/src/pkg/cluster" "github.com/zarf-dev/zarf/src/pkg/lint" "github.com/zarf-dev/zarf/src/pkg/message" + "github.com/zarf-dev/zarf/src/types" ) +// Table prints a padded table containing the specified header and data +func Table(header []string, data [][]string) { + pterm.Println() + + // To avoid side effects make copies of the header and data before adding padding + headerCopy := make([]string, len(header)) + copy(headerCopy, header) + dataCopy := make([][]string, len(data)) + copy(dataCopy, data) + if len(headerCopy) > 0 { + headerCopy[0] = fmt.Sprintf(" %s", headerCopy[0]) + } + + table := pterm.TableData{ + headerCopy, + } + + for _, row := range dataCopy { + if len(row) > 0 { + row[0] = fmt.Sprintf(" %s", row[0]) + } + table = append(table, pterm.TableData{row}...) + } + + //nolint:errcheck // never returns an error + pterm.DefaultTable.WithHasHeader().WithData(table).Render() +} + // PrintFindings prints the findings in the LintError as a table. func PrintFindings(lintErr *lint.LintError) { mapOfFindingsByPath := lint.GroupFindingsByPath(lintErr.Findings, lintErr.PackageName) @@ -41,7 +73,7 @@ func PrintFindings(lintErr *lint.LintError) { packagePathFromUser = filepath.Join(lintErr.BaseDir, findings[0].PackagePathOverride) } message.Notef("Linting package %q at %s", findings[0].PackageNameOverride, packagePathFromUser) - message.Table([]string{"Type", "Path", "Message"}, lintData) + Table([]string{"Type", "Path", "Message"}, lintData) } } @@ -51,3 +83,139 @@ func colorWrap(str string, attr color.Attribute) string { } return fmt.Sprintf("\x1b[%dm%s\x1b[0m", attr, str) } + +// PrintConnectStringTable prints a table of connect strings. +func PrintConnectStringTable(connectStrings types.ConnectStrings) { + if len(connectStrings) == 0 { + return + } + + connectData := [][]string{} + for name, connect := range connectStrings { + name = fmt.Sprintf("zarf connect %s", name) + connectData = append(connectData, []string{name, connect.Description}) + } + header := []string{"Connect Command", "Description"} + Table(header, connectData) +} + +// PrintCredentialTable displays credentials in a table +func PrintCredentialTable(state *types.ZarfState, componentsToDeploy []types.DeployedComponent) { + if len(componentsToDeploy) == 0 { + componentsToDeploy = []types.DeployedComponent{{Name: "git-server"}} + } + + // Pause the logfile's output to avoid credentials being printed to the log file + // if logFile != nil { + // logFile.Pause() + // defer logFile.Resume() + // } + + loginData := [][]string{} + if state.RegistryInfo.IsInternal() { + loginData = append(loginData, + []string{"Registry", state.RegistryInfo.PushUsername, state.RegistryInfo.PushPassword, "zarf connect registry", cluster.RegistryKey}, + []string{"Registry (read-only)", state.RegistryInfo.PullUsername, state.RegistryInfo.PullPassword, "zarf connect registry", cluster.RegistryReadKey}, + ) + } + + for _, component := range componentsToDeploy { + // Show message if including git-server + if component.Name == "git-server" { + loginData = append(loginData, + []string{"Git", state.GitServer.PushUsername, state.GitServer.PushPassword, "zarf connect git", cluster.GitKey}, + []string{"Git (read-only)", state.GitServer.PullUsername, state.GitServer.PullPassword, "zarf connect git", cluster.GitReadKey}, + []string{"Artifact Token", state.ArtifactServer.PushUsername, state.ArtifactServer.PushToken, "zarf connect git", cluster.ArtifactKey}, + ) + } + } + + if len(loginData) > 0 { + header := []string{"Application", "Username", "Password", "Connect", "Get-Creds Key"} + Table(header, loginData) + } +} + +// PrintComponentCredential displays credentials for a single component +func PrintComponentCredential(state *types.ZarfState, componentName string) { + switch strings.ToLower(componentName) { + case cluster.GitKey: + message.Notef("Git Server push password (username: %s):", state.GitServer.PushUsername) + fmt.Println(state.GitServer.PushPassword) + case cluster.GitReadKey: + message.Notef("Git Server (read-only) password (username: %s):", state.GitServer.PullUsername) + fmt.Println(state.GitServer.PullPassword) + case cluster.ArtifactKey: + message.Notef("Artifact Server token (username: %s):", state.ArtifactServer.PushUsername) + fmt.Println(state.ArtifactServer.PushToken) + case cluster.RegistryKey: + message.Notef("Image Registry password (username: %s):", state.RegistryInfo.PushUsername) + fmt.Println(state.RegistryInfo.PushPassword) + case cluster.RegistryReadKey: + message.Notef("Image Registry (read-only) password (username: %s):", state.RegistryInfo.PullUsername) + fmt.Println(state.RegistryInfo.PullPassword) + default: + message.Warn("Unknown component: " + componentName) + } +} + +// PrintCredentialUpdates displays credentials that will be updated +func PrintCredentialUpdates(oldState *types.ZarfState, newState *types.ZarfState, services []string) { + for _, service := range services { + message.HorizontalRule() + + switch service { + case cluster.RegistryKey: + oR := oldState.RegistryInfo + nR := newState.RegistryInfo + message.Title("Registry", "the information used to interact with Zarf's container image registry") + pterm.Println() + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("URL Address"), compareStrings(oR.Address, nR.Address, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Username"), compareStrings(oR.PushUsername, nR.PushUsername, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Password"), compareStrings(oR.PushPassword, nR.PushPassword, true)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Pull Username"), compareStrings(oR.PullUsername, nR.PullUsername, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Pull Password"), compareStrings(oR.PullPassword, nR.PullPassword, true)) + case cluster.GitKey: + oG := oldState.GitServer + nG := newState.GitServer + message.Title("Git Server", "the information used to interact with Zarf's GitOps Git Server") + pterm.Println() + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("URL Address"), compareStrings(oG.Address, nG.Address, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Username"), compareStrings(oG.PushUsername, nG.PushUsername, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Password"), compareStrings(oG.PushPassword, nG.PushPassword, true)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Pull Username"), compareStrings(oG.PullUsername, nG.PullUsername, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Pull Password"), compareStrings(oG.PullPassword, nG.PullPassword, true)) + case cluster.ArtifactKey: + oA := oldState.ArtifactServer + nA := newState.ArtifactServer + message.Title("Artifact Server", "the information used to interact with Zarf's Artifact Server") + pterm.Println() + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("URL Address"), compareStrings(oA.Address, nA.Address, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Username"), compareStrings(oA.PushUsername, nA.PushUsername, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Token"), compareStrings(oA.PushToken, nA.PushToken, true)) + case cluster.AgentKey: + oT := oldState.AgentTLS + nT := newState.AgentTLS + message.Title("Agent TLS", "the certificates used to connect to Zarf's Agent") + pterm.Println() + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Certificate Authority"), compareStrings(string(oT.CA), string(nT.CA), true)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Public Certificate"), compareStrings(string(oT.Cert), string(nT.Cert), true)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Private Key"), compareStrings(string(oT.Key), string(nT.Key), true)) + } + } + + pterm.Println() +} + +func compareStrings(old string, new string, secret bool) string { + if new == old { + if secret { + return "**sanitized** (unchanged)" + } + return fmt.Sprintf("%s (unchanged)", old) + } + if secret { + return fmt.Sprintf("%s -> %s", pterm.FgRed.Sprint("**existing (sanitized)**"), pterm.FgGreen.Sprint("**replacement (sanitized)**")) + } + return fmt.Sprintf("%s -> %s", pterm.FgRed.Sprint(old), pterm.FgGreen.Sprint(new)) +} diff --git a/src/cmd/connect.go b/src/cmd/connect.go index 8600d703a3..eb36bb4b62 100644 --- a/src/cmd/connect.go +++ b/src/cmd/connect.go @@ -9,6 +9,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/cluster" "github.com/zarf-dev/zarf/src/pkg/message" @@ -98,7 +99,7 @@ var connectListCmd = &cobra.Command{ if err != nil { return err } - message.PrintConnectStringTable(connections) + common.PrintConnectStringTable(connections) return nil }, } diff --git a/src/cmd/initialize.go b/src/cmd/initialize.go index 4d1c61363b..a43fbb29e1 100644 --- a/src/cmd/initialize.go +++ b/src/cmd/initialize.go @@ -69,10 +69,24 @@ var initCmd = &cobra.Command{ } defer pkgClient.ClearTempPaths() - err = pkgClient.Deploy(cmd.Context()) + deployedComponents, err := pkgClient.Deploy(cmd.Context()) if err != nil { return err } + + // Don't print if cluster is not configured + cluster := pkgClient.GetCluster() + if cluster == nil { + return nil + } + + // Grab a fresh copy of the state to print the most up-to-date version of the creds + latestState, err := cluster.LoadZarfState(cmd.Context()) + if err != nil { + return err + } + common.PrintCredentialTable(latestState, deployedComponents) + return nil }, } diff --git a/src/cmd/package.go b/src/cmd/package.go index a40439d53f..5a377da794 100644 --- a/src/cmd/package.go +++ b/src/cmd/package.go @@ -96,11 +96,21 @@ var packageDeployCmd = &cobra.Command{ } defer pkgClient.ClearTempPaths() - ctx := cmd.Context() - - if err := pkgClient.Deploy(ctx); err != nil { + deployedComponents, err := pkgClient.Deploy(cmd.Context()) + if err != nil { return fmt.Errorf("failed to deploy package: %w", err) } + + connectStrings := types.ConnectStrings{} + for _, comp := range deployedComponents { + for _, chart := range comp.InstalledCharts { + for k, v := range chart.ConnectStrings { + connectStrings[k] = v + } + } + } + common.PrintConnectStringTable(connectStrings) + return nil }, } @@ -193,7 +203,7 @@ var packageListCmd = &cobra.Command{ } header := []string{"Package", "Version", "Components"} - message.Table(header, packageData) + common.Table(header, packageData) // Print out any unmarshalling errors if err != nil { diff --git a/src/cmd/tools/zarf.go b/src/cmd/tools/zarf.go index 5a9cd11718..770905861c 100644 --- a/src/cmd/tools/zarf.go +++ b/src/cmd/tools/zarf.go @@ -74,9 +74,9 @@ var getCredsCmd = &cobra.Command{ if len(args) > 0 { // If a component name is provided, only show that component's credentials - message.PrintComponentCredential(state, args[0]) + common.PrintComponentCredential(state, args[0]) } else { - message.PrintCredentialTable(state, nil) + common.PrintCredentialTable(state, nil) } return nil }, @@ -90,7 +90,7 @@ var updateCredsCmd = &cobra.Command{ Aliases: []string{"uc"}, Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - validKeys := []string{message.RegistryKey, message.GitKey, message.ArtifactKey, message.AgentKey} + validKeys := []string{cluster.RegistryKey, cluster.GitKey, cluster.ArtifactKey, cluster.AgentKey} if len(args) == 0 { args = validKeys } else { @@ -122,7 +122,7 @@ var updateCredsCmd = &cobra.Command{ return fmt.Errorf("unable to update Zarf credentials: %w", err) } - message.PrintCredentialUpdates(oldState, newState, args) + common.PrintCredentialUpdates(oldState, newState, args) confirm := config.CommonOptions.Confirm @@ -139,13 +139,13 @@ var updateCredsCmd = &cobra.Command{ if confirm { // Update registry and git pull secrets - if slices.Contains(args, message.RegistryKey) { + if slices.Contains(args, cluster.RegistryKey) { err := c.UpdateZarfManagedImageSecrets(ctx, newState) if err != nil { return err } } - if slices.Contains(args, message.GitKey) { + if slices.Contains(args, cluster.GitKey) { err := c.UpdateZarfManagedGitSecrets(ctx, newState) if err != nil { return err @@ -159,7 +159,7 @@ var updateCredsCmd = &cobra.Command{ } // Update artifact token (if internal) - if slices.Contains(args, message.ArtifactKey) && newState.ArtifactServer.PushToken == "" && newState.ArtifactServer.IsInternal() && internalGitServerExists { + if slices.Contains(args, cluster.ArtifactKey) && newState.ArtifactServer.PushToken == "" && newState.ArtifactServer.IsInternal() && internalGitServerExists { newState.ArtifactServer.PushToken, err = c.UpdateInternalArtifactServerToken(ctx, oldState.GitServer) if err != nil { return fmt.Errorf("unable to create the new Gitea artifact token: %w", err) @@ -175,20 +175,20 @@ var updateCredsCmd = &cobra.Command{ // Update Zarf 'init' component Helm releases if present h := helm.NewClusterOnly(&types.PackagerConfig{}, template.GetZarfVariableConfig(), newState, c) - if slices.Contains(args, message.RegistryKey) && newState.RegistryInfo.IsInternal() { + if slices.Contains(args, cluster.RegistryKey) && newState.RegistryInfo.IsInternal() { err = h.UpdateZarfRegistryValues(ctx) 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()) } } - if slices.Contains(args, message.GitKey) && newState.GitServer.IsInternal() && internalGitServerExists { + if slices.Contains(args, cluster.GitKey) && newState.GitServer.IsInternal() && internalGitServerExists { err := c.UpdateInternalGitServerSecret(cmd.Context(), oldState.GitServer, newState.GitServer) if err != nil { return fmt.Errorf("unable to update Zarf Git Server values: %w", err) } } - if slices.Contains(args, message.AgentKey) { + if slices.Contains(args, cluster.AgentKey) { err = h.UpdateZarfAgentValues(ctx) if err != nil { // Warn if we couldn't actually update the agent (it might not be installed and we should try to continue) diff --git a/src/pkg/cluster/state.go b/src/pkg/cluster/state.go index 3c31ccf128..634fb551af 100644 --- a/src/pkg/cluster/state.go +++ b/src/pkg/cluster/state.go @@ -283,11 +283,21 @@ func (c *Cluster) SaveZarfState(ctx context.Context, state *types.ZarfState) err return nil } +// Common constants for printing credentials +const ( + RegistryKey = "registry" + RegistryReadKey = "registry-readonly" + GitKey = "git" + GitReadKey = "git-readonly" + ArtifactKey = "artifact" + AgentKey = "agent" +) + // MergeZarfState merges init options for provided services into the provided state to create a new state struct func MergeZarfState(oldState *types.ZarfState, initOptions types.ZarfInitOptions, services []string) (*types.ZarfState, error) { newState := *oldState var err error - if slices.Contains(services, message.RegistryKey) { + if slices.Contains(services, RegistryKey) { // TODO: Replace use of reflections with explicit setting newState.RegistryInfo = helpers.MergeNonZero(newState.RegistryInfo, initOptions.RegistryInfo) @@ -303,7 +313,7 @@ func MergeZarfState(oldState *types.ZarfState, initOptions types.ZarfInitOptions } } } - if slices.Contains(services, message.GitKey) { + if slices.Contains(services, GitKey) { // TODO: Replace use of reflections with explicit setting newState.GitServer = helpers.MergeNonZero(newState.GitServer, initOptions.GitServer) @@ -319,7 +329,7 @@ func MergeZarfState(oldState *types.ZarfState, initOptions types.ZarfInitOptions } } } - if slices.Contains(services, message.ArtifactKey) { + if slices.Contains(services, ArtifactKey) { // TODO: Replace use of reflections with explicit setting newState.ArtifactServer = helpers.MergeNonZero(newState.ArtifactServer, initOptions.ArtifactServer) @@ -328,7 +338,7 @@ func MergeZarfState(oldState *types.ZarfState, initOptions types.ZarfInitOptions newState.ArtifactServer.PushToken = "" } } - if slices.Contains(services, message.AgentKey) { + if slices.Contains(services, AgentKey) { agentTLS, err := pki.GeneratePKI(config.ZarfAgentHost) if err != nil { return nil, err diff --git a/src/pkg/cluster/state_test.go b/src/pkg/cluster/state_test.go index 1575528f4a..a46746f6b4 100644 --- a/src/pkg/cluster/state_test.go +++ b/src/pkg/cluster/state_test.go @@ -18,7 +18,6 @@ import ( "github.com/defenseunicorns/pkg/helpers/v2" - "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/pkg/pki" "github.com/zarf-dev/zarf/src/types" ) @@ -304,7 +303,7 @@ func TestMergeZarfStateRegistry(t *testing.T) { oldState := &types.ZarfState{ RegistryInfo: tt.oldRegistry, } - newState, err := MergeZarfState(oldState, types.ZarfInitOptions{RegistryInfo: tt.initRegistry}, []string{message.RegistryKey}) + newState, err := MergeZarfState(oldState, types.ZarfInitOptions{RegistryInfo: tt.initRegistry}, []string{RegistryKey}) require.NoError(t, err) require.Equal(t, tt.expectedRegistry.PushUsername, newState.RegistryInfo.PushUsername) require.Equal(t, tt.expectedRegistry.PullUsername, newState.RegistryInfo.PullUsername) @@ -382,7 +381,7 @@ func TestMergeZarfStateGit(t *testing.T) { oldState := &types.ZarfState{ GitServer: tt.oldGitServer, } - newState, err := MergeZarfState(oldState, types.ZarfInitOptions{GitServer: tt.initGitServer}, []string{message.GitKey}) + newState, err := MergeZarfState(oldState, types.ZarfInitOptions{GitServer: tt.initGitServer}, []string{GitKey}) require.NoError(t, err) require.Equal(t, tt.expectedGitServer.PushUsername, newState.GitServer.PushUsername) require.Equal(t, tt.expectedGitServer.PullUsername, newState.GitServer.PullUsername) @@ -469,7 +468,7 @@ func TestMergeZarfStateArtifact(t *testing.T) { oldState := &types.ZarfState{ ArtifactServer: tt.oldArtifactServer, } - newState, err := MergeZarfState(oldState, types.ZarfInitOptions{ArtifactServer: tt.initArtifactServer}, []string{message.ArtifactKey}) + newState, err := MergeZarfState(oldState, types.ZarfInitOptions{ArtifactServer: tt.initArtifactServer}, []string{ArtifactKey}) require.NoError(t, err) require.Equal(t, tt.expectedArtifactServer, newState.ArtifactServer) }) @@ -484,7 +483,7 @@ func TestMergeZarfStateAgent(t *testing.T) { oldState := &types.ZarfState{ AgentTLS: agentTLS, } - newState, err := MergeZarfState(oldState, types.ZarfInitOptions{}, []string{message.AgentKey}) + newState, err := MergeZarfState(oldState, types.ZarfInitOptions{}, []string{AgentKey}) require.NoError(t, err) require.NotEqual(t, oldState.AgentTLS, newState.AgentTLS) } diff --git a/src/pkg/message/connect.go b/src/pkg/message/connect.go deleted file mode 100644 index 499a9e3602..0000000000 --- a/src/pkg/message/connect.go +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2021-Present The Zarf Authors - -// Package message provides a rich set of functions for displaying messages to the user. -package message - -import ( - "fmt" - - "github.com/zarf-dev/zarf/src/types" -) - -// PrintConnectStringTable prints a table of connect strings. -func PrintConnectStringTable(connectStrings types.ConnectStrings) { - if len(connectStrings) > 0 { - connectData := [][]string{} - // Loop over each connectStrings and convert to a string matrix - for name, connect := range connectStrings { - name = fmt.Sprintf("zarf connect %s", name) - connectData = append(connectData, []string{name, connect.Description}) - } - - // Create the table output with the data - header := []string{"Connect Command", "Description"} - Table(header, connectData) - } -} diff --git a/src/pkg/message/credentials.go b/src/pkg/message/credentials.go deleted file mode 100644 index a0fc97537c..0000000000 --- a/src/pkg/message/credentials.go +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2021-Present The Zarf Authors - -// Package message provides a rich set of functions for displaying messages to the user. -package message - -import ( - "fmt" - "strings" - - "github.com/pterm/pterm" - "github.com/zarf-dev/zarf/src/types" -) - -// Common constants for printing credentials -const ( - RegistryKey = "registry" - RegistryReadKey = "registry-readonly" - GitKey = "git" - GitReadKey = "git-readonly" - ArtifactKey = "artifact" - AgentKey = "agent" -) - -// PrintCredentialTable displays credentials in a table -func PrintCredentialTable(state *types.ZarfState, componentsToDeploy []types.DeployedComponent) { - if len(componentsToDeploy) == 0 { - componentsToDeploy = []types.DeployedComponent{{Name: "git-server"}} - } - - // Pause the logfile's output to avoid credentials being printed to the log file - if logFile != nil { - logFile.Pause() - defer logFile.Resume() - } - - loginData := [][]string{} - if state.RegistryInfo.IsInternal() { - loginData = append(loginData, - []string{"Registry", state.RegistryInfo.PushUsername, state.RegistryInfo.PushPassword, "zarf connect registry", RegistryKey}, - []string{"Registry (read-only)", state.RegistryInfo.PullUsername, state.RegistryInfo.PullPassword, "zarf connect registry", RegistryReadKey}, - ) - } - - for _, component := range componentsToDeploy { - // Show message if including git-server - if component.Name == "git-server" { - loginData = append(loginData, - []string{"Git", state.GitServer.PushUsername, state.GitServer.PushPassword, "zarf connect git", GitKey}, - []string{"Git (read-only)", state.GitServer.PullUsername, state.GitServer.PullPassword, "zarf connect git", GitReadKey}, - []string{"Artifact Token", state.ArtifactServer.PushUsername, state.ArtifactServer.PushToken, "zarf connect git", ArtifactKey}, - ) - } - } - - if len(loginData) > 0 { - header := []string{"Application", "Username", "Password", "Connect", "Get-Creds Key"} - Table(header, loginData) - } -} - -// PrintComponentCredential displays credentials for a single component -func PrintComponentCredential(state *types.ZarfState, componentName string) { - switch strings.ToLower(componentName) { - case GitKey: - Notef("Git Server push password (username: %s):", state.GitServer.PushUsername) - fmt.Println(state.GitServer.PushPassword) - case GitReadKey: - Notef("Git Server (read-only) password (username: %s):", state.GitServer.PullUsername) - fmt.Println(state.GitServer.PullPassword) - case ArtifactKey: - Notef("Artifact Server token (username: %s):", state.ArtifactServer.PushUsername) - fmt.Println(state.ArtifactServer.PushToken) - case RegistryKey: - Notef("Image Registry password (username: %s):", state.RegistryInfo.PushUsername) - fmt.Println(state.RegistryInfo.PushPassword) - case RegistryReadKey: - Notef("Image Registry (read-only) password (username: %s):", state.RegistryInfo.PullUsername) - fmt.Println(state.RegistryInfo.PullPassword) - default: - Warn("Unknown component: " + componentName) - } -} - -// PrintCredentialUpdates displays credentials that will be updated -func PrintCredentialUpdates(oldState *types.ZarfState, newState *types.ZarfState, services []string) { - // Pause the logfile's output to avoid credentials being printed to the log file - if logFile != nil { - logFile.Pause() - defer logFile.Resume() - } - - for _, service := range services { - HorizontalRule() - - switch service { - case RegistryKey: - oR := oldState.RegistryInfo - nR := newState.RegistryInfo - Title("Registry", "the information used to interact with Zarf's container image registry") - pterm.Println() - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("URL Address"), compareStrings(oR.Address, nR.Address, false)) - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Username"), compareStrings(oR.PushUsername, nR.PushUsername, false)) - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Password"), compareStrings(oR.PushPassword, nR.PushPassword, true)) - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Pull Username"), compareStrings(oR.PullUsername, nR.PullUsername, false)) - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Pull Password"), compareStrings(oR.PullPassword, nR.PullPassword, true)) - case GitKey: - oG := oldState.GitServer - nG := newState.GitServer - Title("Git Server", "the information used to interact with Zarf's GitOps Git Server") - pterm.Println() - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("URL Address"), compareStrings(oG.Address, nG.Address, false)) - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Username"), compareStrings(oG.PushUsername, nG.PushUsername, false)) - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Password"), compareStrings(oG.PushPassword, nG.PushPassword, true)) - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Pull Username"), compareStrings(oG.PullUsername, nG.PullUsername, false)) - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Pull Password"), compareStrings(oG.PullPassword, nG.PullPassword, true)) - case ArtifactKey: - oA := oldState.ArtifactServer - nA := newState.ArtifactServer - Title("Artifact Server", "the information used to interact with Zarf's Artifact Server") - pterm.Println() - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("URL Address"), compareStrings(oA.Address, nA.Address, false)) - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Username"), compareStrings(oA.PushUsername, nA.PushUsername, false)) - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Token"), compareStrings(oA.PushToken, nA.PushToken, true)) - case AgentKey: - oT := oldState.AgentTLS - nT := newState.AgentTLS - Title("Agent TLS", "the certificates used to connect to Zarf's Agent") - pterm.Println() - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Certificate Authority"), compareStrings(string(oT.CA), string(nT.CA), true)) - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Public Certificate"), compareStrings(string(oT.Cert), string(nT.Cert), true)) - pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Private Key"), compareStrings(string(oT.Key), string(nT.Key), true)) - } - } - - pterm.Println() -} - -func compareStrings(old string, new string, secret bool) string { - if new == old { - if secret { - return "**sanitized** (unchanged)" - } - return fmt.Sprintf("%s (unchanged)", old) - } - if secret { - return fmt.Sprintf("%s -> %s", pterm.FgRed.Sprint("**existing (sanitized)**"), pterm.FgGreen.Sprint("**replacement (sanitized)**")) - } - return fmt.Sprintf("%s -> %s", pterm.FgRed.Sprint(old), pterm.FgGreen.Sprint(new)) -} diff --git a/src/pkg/message/message.go b/src/pkg/message/message.go index 0eea8b326c..69a2f16b68 100644 --- a/src/pkg/message/message.go +++ b/src/pkg/message/message.go @@ -247,34 +247,6 @@ func Paragraphn(n int, format string, a ...any) string { return strings.Join(formattedLines, "\n") } -// Table prints a padded table containing the specified header and data -func Table(header []string, data [][]string) { - pterm.Println() - - // To avoid side effects make copies of the header and data before adding padding - headerCopy := make([]string, len(header)) - copy(headerCopy, header) - dataCopy := make([][]string, len(data)) - copy(dataCopy, data) - if len(headerCopy) > 0 { - headerCopy[0] = fmt.Sprintf(" %s", headerCopy[0]) - } - - table := pterm.TableData{ - headerCopy, - } - - for _, row := range dataCopy { - if len(row) > 0 { - row[0] = fmt.Sprintf(" %s", row[0]) - } - table = append(table, pterm.TableData{row}...) - } - - //nolint:errcheck // never returns an error - pterm.DefaultTable.WithHasHeader().WithData(table).Render() -} - func debugPrinter(offset int, a ...any) { printer := pterm.Debug.WithShowLineNumber(logLevel > 2).WithLineNumberOffset(offset) now := time.Now().Format(time.RFC3339) diff --git a/src/pkg/packager/common.go b/src/pkg/packager/common.go index 1325a70b46..399810fc11 100644 --- a/src/pkg/packager/common.go +++ b/src/pkg/packager/common.go @@ -116,6 +116,10 @@ func New(cfg *types.PackagerConfig, mods ...Modifier) (*Packager, error) { return pkgr, nil } +func (p *Packager) GetCluster() *cluster.Cluster { + return p.cluster +} + // ClearTempPaths removes the temp directory and any files within it. func (p *Packager) ClearTempPaths() { // Remove the temp directory, but don't throw an error if it fails diff --git a/src/pkg/packager/deploy.go b/src/pkg/packager/deploy.go index 35e34b020c..1bf38c6a77 100644 --- a/src/pkg/packager/deploy.go +++ b/src/pkg/packager/deploy.go @@ -58,7 +58,7 @@ func (p *Packager) resetRegistryHPA(ctx context.Context) { } // Deploy attempts to deploy the given PackageConfig. -func (p *Packager) Deploy(ctx context.Context) error { +func (p *Packager) Deploy(ctx context.Context) ([]types.DeployedComponent, error) { isInteractive := !config.CommonOptions.Confirm deployFilter := filters.Combine( @@ -71,48 +71,48 @@ func (p *Packager) Deploy(ctx context.Context) error { filter := filters.Empty() pkg, loadWarnings, err := p.source.LoadPackage(ctx, p.layout, filter, true) if err != nil { - return fmt.Errorf("unable to load the package: %w", err) + return nil, fmt.Errorf("unable to load the package: %w", err) } p.cfg.Pkg = pkg warnings = append(warnings, loadWarnings...) } else { pkg, loadWarnings, err := p.source.LoadPackage(ctx, p.layout, deployFilter, true) if err != nil { - return fmt.Errorf("unable to load the package: %w", err) + return nil, fmt.Errorf("unable to load the package: %w", err) } p.cfg.Pkg = pkg warnings = append(warnings, loadWarnings...) if err := p.populatePackageVariableConfig(); err != nil { - return fmt.Errorf("unable to set the active variables: %w", err) + return nil, fmt.Errorf("unable to set the active variables: %w", err) } } validateWarnings, err := validateLastNonBreakingVersion(config.CLIVersion, p.cfg.Pkg.Build.LastNonBreakingVersion) if err != nil { - return err + return nil, err } warnings = append(warnings, validateWarnings...) sbomViewFiles, sbomWarnings, err := p.layout.SBOMs.StageSBOMViewFiles() if err != nil { - return err + return nil, err } warnings = append(warnings, sbomWarnings...) // Confirm the overall package deployment if !p.confirmAction(config.ZarfDeployStage, warnings, sbomViewFiles) { - return fmt.Errorf("deployment cancelled") + return nil, fmt.Errorf("deployment cancelled") } if isInteractive { p.cfg.Pkg.Components, err = deployFilter.Apply(p.cfg.Pkg) if err != nil { - return err + return nil, err } // Set variables and prompt if --confirm is not set if err := p.populatePackageVariableConfig(); err != nil { - return fmt.Errorf("unable to set the active variables: %w", err) + return nil, fmt.Errorf("unable to set the active variables: %w", err) } } @@ -123,7 +123,7 @@ func (p *Packager) Deploy(ctx context.Context) error { // Get a list of all the components we are deploying and actually deploy them deployedComponents, err := p.deployComponents(ctx) if err != nil { - return err + return nil, err } if len(deployedComponents) == 0 { message.Warn("No components were selected for deployment. Inspect the package to view the available components and select components interactively or by name with \"--components\"") @@ -132,12 +132,7 @@ func (p *Packager) Deploy(ctx context.Context) error { // Notify all the things about the successful deployment message.Successf("Zarf deployment complete") - err = p.printTablesForDeployment(ctx, deployedComponents) - if err != nil { - return err - } - - return nil + return deployedComponents, nil } // deployComponents loops through a list of ZarfComponents and deploys them. @@ -777,33 +772,6 @@ func (p *Packager) installChartAndManifests(ctx context.Context, componentPaths return installedCharts, nil } -func (p *Packager) printTablesForDeployment(ctx context.Context, componentsToDeploy []types.DeployedComponent) error { - // If not init config, print the application connection table - if !p.cfg.Pkg.IsInitConfig() { - connectStrings := types.ConnectStrings{} - for _, comp := range componentsToDeploy { - for _, chart := range comp.InstalledCharts { - for k, v := range chart.ConnectStrings { - connectStrings[k] = v - } - } - } - message.PrintConnectStringTable(connectStrings) - return nil - } - // Don't print if cluster is not configured - if p.cluster == nil { - return nil - } - // Grab a fresh copy of the state to print the most up-to-date version of the creds - latestState, err := p.cluster.LoadZarfState(ctx) - if err != nil { - return err - } - message.PrintCredentialTable(latestState, componentsToDeploy) - return nil -} - // ServiceInfoFromServiceURL takes a serviceURL and parses it to find the service info for connecting to the cluster. The string is expected to follow the following format: // Example serviceURL: http://{SERVICE_NAME}.{NAMESPACE}.svc.cluster.local:{PORT}. func serviceInfoFromServiceURL(serviceURL string) (string, string, int, error) {