diff --git a/src/pkg/message/message.go b/src/pkg/message/message.go index 713bb90cbe..c3eab711bf 100644 --- a/src/pkg/message/message.go +++ b/src/pkg/message/message.go @@ -7,6 +7,7 @@ package message import ( "encoding/json" "fmt" + "io" "net/http" "os" "runtime/debug" @@ -59,6 +60,11 @@ func (d *DebugWriter) Write(raw []byte) (int, error) { } func init() { + InitializePTerm(os.Stderr) +} + +// InitializePTerm sets the default styles and output for pterm. +func InitializePTerm(w io.Writer) { pterm.ThemeDefault.SuccessMessageStyle = *pterm.NewStyle(pterm.FgLightGreen) // Customize default error. pterm.Success.Prefix = pterm.Prefix{ @@ -73,7 +79,7 @@ func init() { Text: " •", } - pterm.SetDefaultOutput(os.Stderr) + pterm.SetDefaultOutput(w) } // UseLogFile wraps a given file in a PausableWriter diff --git a/src/pkg/packager/common.go b/src/pkg/packager/common.go index 938167e52c..eddce07a53 100644 --- a/src/pkg/packager/common.go +++ b/src/pkg/packager/common.go @@ -212,7 +212,7 @@ func (p *Packager) attemptClusterChecks(ctx context.Context) (err error) { // Check for any breaking changes between the initialized Zarf version and this CLI if existingInitPackage, _ := p.cluster.GetDeployedPackage(ctx, "init"); existingInitPackage != nil { // Use the build version instead of the metadata since this will support older Zarf versions - deprecated.PrintBreakingChanges(existingInitPackage.Data.Build.Version) + deprecated.PrintBreakingChanges(os.Stderr, existingInitPackage.Data.Build.Version, config.CLIVersion) } spinner.Success() diff --git a/src/pkg/packager/deprecated/common.go b/src/pkg/packager/deprecated/common.go index 3583eac9a4..4867ac52b2 100644 --- a/src/pkg/packager/deprecated/common.go +++ b/src/pkg/packager/deprecated/common.go @@ -6,24 +6,33 @@ package deprecated import ( "fmt" + "io" "strings" "slices" "github.com/Masterminds/semver/v3" - "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/types" "github.com/pterm/pterm" ) -// BreakingChange represents a breaking change that happened on a specified Zarf version +// BreakingChange represents a breaking change that happened on a specified Zarf version. type BreakingChange struct { version *semver.Version title string mitigation string } +// String returns the string representation of the BreakingChange. +func (bc BreakingChange) String() string { + return fmt.Sprintf("%s\n\n - %s\n %s\n", + pterm.Bold.Sprintf(bc.title), + pterm.Bold.Sprint("Mitigation:"), + strings.ReplaceAll(message.Paragraphn(96, "%s", pterm.FgLightCyan.Sprint(bc.mitigation)), "\n", "\n "), + ) +} + // List of migrations tracked in the zarf.yaml build data. const ( // This should be updated when a breaking change is introduced to the Zarf package structure. See: https://github.com/defenseunicorns/zarf/releases/tag/v0.27.0 @@ -32,15 +41,6 @@ const ( PluralizeSetVariable = "pluralize-set-variable" ) -// List of breaking changes to warn the user of. -var breakingChanges = []BreakingChange{ - { - version: semver.New(0, 26, 0, "", ""), - title: "Zarf container images are now mutated based on tag instead of repository name.", - mitigation: "Reinitialize the cluster using v0.26.0 or later and redeploy existing packages to update the image references (you can view existing packages with 'zarf package list' and view cluster images with 'zarf tools registry catalog').", - }, -} - // MigrateComponent runs all migrations on a component. // Build should be empty on package create, but include just in case someone copied a zarf.yaml from a zarf package. func MigrateComponent(build types.ZarfBuildData, component types.ZarfComponent) (migratedComponent types.ZarfComponent, warnings []string) { @@ -77,47 +77,50 @@ func MigrateComponent(build types.ZarfBuildData, component types.ZarfComponent) return migratedComponent, warnings } -// PrintBreakingChanges prints the breaking changes between the provided version and the current CLIVersion -func PrintBreakingChanges(deployedZarfVersion string) { +// PrintBreakingChanges prints the breaking changes between the provided version and the current CLIVersion. +func PrintBreakingChanges(w io.Writer, deployedZarfVersion, cliVersion string) { deployedSemver, err := semver.NewVersion(deployedZarfVersion) if err != nil { message.Debugf("Unable to check for breaking changes between Zarf versions") return } - applicableBreakingChanges := []BreakingChange{} + // List of breaking changes to warn the user of. + var breakingChanges = []BreakingChange{ + { + version: semver.MustParse("0.26.0"), + title: "Zarf container images are now mutated based on tag instead of repository name.", + mitigation: "Reinitialize the cluster using v0.26.0 or later and redeploy existing packages to update the image references (you can view existing packages with 'zarf package list' and view cluster images with 'zarf tools registry catalog').", + }, + } - // Calculate the applicable breaking changes + // Calculate the applicable breaking changes. + var applicableBreakingChanges []BreakingChange for _, breakingChange := range breakingChanges { if deployedSemver.LessThan(breakingChange.version) { applicableBreakingChanges = append(applicableBreakingChanges, breakingChange) } } - if len(applicableBreakingChanges) > 0 { - // Print header information - message.HorizontalRule() - message.Title("Potential Breaking Changes", "breaking changes that may cause issues with this package") - - // Print information about the versions - format := pterm.FgYellow.Sprint("CLI version ") + "%s" + pterm.FgYellow.Sprint(" is being used to deploy to a cluster that was initialized with ") + - "%s" + pterm.FgYellow.Sprint(". Between these versions there are the following breaking changes to consider:") - cliVersion := pterm.Bold.Sprintf(config.CLIVersion) - deployedVersion := pterm.Bold.Sprintf(deployedZarfVersion) - message.Warnf(format, cliVersion, deployedVersion) - - // Print each applicable breaking change - for idx, applicableBreakingChange := range applicableBreakingChanges { - titleFormat := pterm.Bold.Sprintf("\n %d. ", idx+1) + "%s" - - pterm.Printfln(titleFormat, applicableBreakingChange.title) + if len(applicableBreakingChanges) == 0 { + return + } - mitigationText := message.Paragraphn(96, "%s", pterm.FgLightCyan.Sprint(applicableBreakingChange.mitigation)) + // Print header information + message.HorizontalRule() + message.Title("Potential Breaking Changes", "breaking changes that may cause issues with this package") - pterm.Printfln("\n - %s", pterm.Bold.Sprint("Mitigation:")) - pterm.Printfln(" %s", strings.ReplaceAll(mitigationText, "\n", "\n ")) - } + // Print information about the versions + format := pterm.FgYellow.Sprint("CLI version ") + "%s" + pterm.FgYellow.Sprint(" is being used to deploy to a cluster that was initialized with ") + + "%s" + pterm.FgYellow.Sprint(". Between these versions there are the following breaking changes to consider:") + cliVersion = pterm.Bold.Sprintf(cliVersion) + deployedZarfVersion = pterm.Bold.Sprintf(deployedZarfVersion) + message.Warnf(format, cliVersion, deployedZarfVersion) - message.HorizontalRule() + // Print each applicable breaking change + for i, applicableBreakingChange := range applicableBreakingChanges { + fmt.Fprintf(w, "\n %d. %s", i+1, applicableBreakingChange.String()) } + + message.HorizontalRule() } diff --git a/src/pkg/packager/deprecated/common_test.go b/src/pkg/packager/deprecated/common_test.go new file mode 100644 index 0000000000..6dec381554 --- /dev/null +++ b/src/pkg/packager/deprecated/common_test.go @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +// Package deprecated handles package deprecations and migrations +package deprecated + +import ( + "bytes" + "testing" + + "github.com/Masterminds/semver/v3" + "github.com/defenseunicorns/zarf/src/pkg/message" + "github.com/stretchr/testify/require" +) + +func TestPrintBreakingChanges(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + deployedVersion string + cliVersion string + breakingChanges []BreakingChange + }{ + { + name: "No breaking changes", + deployedVersion: "0.26.0", + cliVersion: "0.26.0", + breakingChanges: []BreakingChange{}, + }, + { + name: "agent breaking change", + deployedVersion: "0.25.0", + cliVersion: "0.26.0", + breakingChanges: []BreakingChange{ + { + version: semver.MustParse("0.26.0"), + title: "Zarf container images are now mutated based on tag instead of repository name.", + mitigation: "Reinitialize the cluster using v0.26.0 or later and redeploy existing packages to update the image references (you can view existing packages with 'zarf package list' and view cluster images with 'zarf tools registry catalog').", + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var output bytes.Buffer + message.InitializePTerm(&output) + PrintBreakingChanges(&output, tt.deployedVersion, tt.cliVersion) + for _, bc := range tt.breakingChanges { + require.Contains(t, output.String(), bc.String()) + } + t.Log(output.String()) + }) + } +} diff --git a/src/test/e2e/30_config_file_test.go b/src/test/e2e/29_config_file_test.go similarity index 100% rename from src/test/e2e/30_config_file_test.go rename to src/test/e2e/29_config_file_test.go diff --git a/src/test/e2e/29_mismatched_checks_test.go b/src/test/e2e/29_mismatched_checks_test.go deleted file mode 100644 index fef9a413dc..0000000000 --- a/src/test/e2e/29_mismatched_checks_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2021-Present The Zarf Authors - -// Package test provides e2e tests for Zarf. -package test - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "path/filepath" - "testing" - - "github.com/defenseunicorns/zarf/src/types" - "github.com/stretchr/testify/require" -) - -// TestMismatchedVersions ensures that zarf produces a warning -// when the initialized version of Zarf doesn't match the current CLI -func TestMismatchedVersions(t *testing.T) { - t.Log("E2E: Mismatched versions") - e2e.SetupWithCluster(t) - - var ( - expectedWarningMessage = "Potential Breaking Changes" - ) - - // Get the current init package secret - initPkg := types.DeployedPackage{} - base64Pkg, _, err := e2e.Kubectl("get", "secret", "zarf-package-init", "-n", "zarf", "-o", "jsonpath={.data.data}") - require.NoError(t, err) - jsonPkg, err := base64.StdEncoding.DecodeString(base64Pkg) - require.NoError(t, err) - fmt.Println(string(jsonPkg)) - err = json.Unmarshal(jsonPkg, &initPkg) - require.NoError(t, err) - - // Edit the build data to trigger the breaking change check - initPkg.Data.Build.Version = "v0.25.0" - - // Delete the package secret - _, _, err = e2e.Kubectl("delete", "secret", "zarf-package-init", "-n", "zarf") - require.NoError(t, err) - - // Create a new secret with the modified data - jsonPkgModified, err := json.Marshal(initPkg) - require.NoError(t, err) - _, _, err = e2e.Kubectl("create", "secret", "generic", "zarf-package-init", "-n", "zarf", fmt.Sprintf("--from-literal=data=%s", string(jsonPkgModified))) - require.NoError(t, err) - - path := filepath.Join("build", fmt.Sprintf("zarf-package-dos-games-%s-1.0.0.tar.zst", e2e.Arch)) - - // Deploy the games package - stdOut, stdErr, err := e2e.Zarf("package", "deploy", path, "--confirm") - require.NoError(t, err, stdOut, stdErr) - require.Contains(t, stdErr, expectedWarningMessage) - - // Remove the games package - stdOut, stdErr, err = e2e.Zarf("package", "remove", "dos-games", "--confirm") - require.NoError(t, err, stdOut, stdErr) - - // Reset the package secret - _, _, err = e2e.Kubectl("delete", "secret", "zarf-package-init", "-n", "zarf") - require.NoError(t, err) - _, _, err = e2e.Kubectl("create", "secret", "generic", "zarf-package-init", "-n", "zarf", fmt.Sprintf("--from-literal=data=%s", string(jsonPkg))) - require.NoError(t, err) -} diff --git a/src/test/e2e/31_component_action_cluster_test.go b/src/test/e2e/30_component_action_cluster_test.go similarity index 100% rename from src/test/e2e/31_component_action_cluster_test.go rename to src/test/e2e/30_component_action_cluster_test.go diff --git a/src/test/e2e/32_checksum_and_signature_test.go b/src/test/e2e/31_checksum_and_signature_test.go similarity index 100% rename from src/test/e2e/32_checksum_and_signature_test.go rename to src/test/e2e/31_checksum_and_signature_test.go diff --git a/src/test/e2e/33_component_webhooks_test.go b/src/test/e2e/32_component_webhooks_test.go similarity index 100% rename from src/test/e2e/33_component_webhooks_test.go rename to src/test/e2e/32_component_webhooks_test.go diff --git a/src/test/e2e/34_manifest_with_symlink_test.go b/src/test/e2e/33_manifest_with_symlink_test.go similarity index 100% rename from src/test/e2e/34_manifest_with_symlink_test.go rename to src/test/e2e/33_manifest_with_symlink_test.go diff --git a/src/test/e2e/35_custom_init_package_test.go b/src/test/e2e/34_custom_init_package_test.go similarity index 100% rename from src/test/e2e/35_custom_init_package_test.go rename to src/test/e2e/34_custom_init_package_test.go diff --git a/src/test/e2e/36_custom_retries_test.go b/src/test/e2e/35_custom_retries_test.go similarity index 100% rename from src/test/e2e/36_custom_retries_test.go rename to src/test/e2e/35_custom_retries_test.go diff --git a/src/test/e2e/37_pod_without_labels_test.go b/src/test/e2e/36_pod_without_labels_test.go similarity index 100% rename from src/test/e2e/37_pod_without_labels_test.go rename to src/test/e2e/36_pod_without_labels_test.go