Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/otelcomponentmapping/otelcomponentmapping_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func OtelComponentMappingDeleteCommand(deps *di.Deps) *cobra.Command {
cmd := &cobra.Command{
Use: "delete",
Short: "Delete an OTel Component Mapping by identifier (URN)",
Long: "Delete an OTel Component Mapping by identifier (URN)",
Long: "Delete an OTel Component Mapping by identifier (URN).",
Example: `# delete a component mapping by identifier
sts otel-component-mapping delete --identifier urn:stackpack:stackpack-name:shared:otel-component-mapping:service`,
RunE: deps.CmdRunEWithApi(RunDeleteComponentMappingCommand(args)),
Expand Down
18 changes: 4 additions & 14 deletions cmd/stackpack.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
package cmd

import (
"os"

"github.com/spf13/cobra"
"github.com/stackvista/stackstate-cli/cmd/stackpack"
"github.com/stackvista/stackstate-cli/internal/di"
)

const (
experimentalStackpackEnvVar = "STS_EXPERIMENTAL_STACKPACKS"
)

func StackPackCommand(cli *di.Deps) *cobra.Command {
cmd := &cobra.Command{
Use: "stackpack",
Expand All @@ -31,14 +25,10 @@ func StackPackCommand(cli *di.Deps) *cobra.Command {
cmd.AddCommand(stackpack.StackpackListVersionsCommand(cli))
cmd.AddCommand(stackpack.StackpackDeleteVersionCommand(cli))
cmd.AddCommand(stackpack.StackpackDeleteVersionsCommand(cli))

// The not-production-ready commands
if os.Getenv(experimentalStackpackEnvVar) != "" {
cmd.AddCommand(stackpack.StackpackScaffoldCommand(cli))
cmd.AddCommand(stackpack.StackpackPackageCommand(cli))
cmd.AddCommand(stackpack.StackpackTestDeployCommand(cli))
cmd.AddCommand(stackpack.StackpackValidateCommand(cli))
}
cmd.AddCommand(stackpack.StackpackScaffoldCommand(cli))
cmd.AddCommand(stackpack.StackpackPackageCommand(cli))
cmd.AddCommand(stackpack.StackpackTestDeployCommand(cli))
cmd.AddCommand(stackpack.StackpackValidateCommand(cli))

return cmd
}
4 changes: 2 additions & 2 deletions cmd/stackpack/stackpack_package.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ sts stackpack package --force`,
RunE: cli.CmdRunE(RunStackpackPackageCommand(args)),
}

cmd.Flags().StringVarP(&args.StackpackDir, "stackpack-directory", "d", "", "Path to stackpack directory (defaults to current directory)")
cmd.Flags().StringVarP(&args.ArchiveFile, "archive-file", "f", "", "Path to the .sts file to create (defaults to <stackpack_name>-<version>.sts in current directory)")
cmd.Flags().StringVarP(&args.StackpackDir, "directory", "d", "", "Path to stackpack directory (defaults to current directory)")
cmd.Flags().StringVarP(&args.ArchiveFile, "file", "f", "", "Path to the .sts file to create (defaults to <stackpack_name>-<version>.sts in current directory)")
cmd.Flags().BoolVar(&args.Force, "force", false, "Overwrite existing .sts file without prompting")

return cmd
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/stackvista/stackstate-cli/generated/stackstate_api"
"github.com/stackvista/stackstate-cli/internal/common"
"github.com/stackvista/stackstate-cli/internal/di"
"github.com/stackvista/stackstate-cli/internal/printer"
)

const (
Expand Down Expand Up @@ -59,7 +60,7 @@ sts stackpack test-deploy -d ./my-stackpack --yes`,
RunE: cli.CmdRunEWithApi(RunStackpackTestDeployCommand(args)),
}

cmd.Flags().StringVarP(&args.StackpackDir, "stackpack-directory", "d", "", "Path to stackpack directory (defaults to current directory)")
cmd.Flags().StringVarP(&args.StackpackDir, "directory", "d", "", "Path to stackpack directory (defaults to current directory)")
cmd.Flags().StringToStringVarP(&args.Params, ParameterFlag, "p", args.Params, "List of parameters of the form \"key=value\"")
cmd.Flags().BoolVarP(&args.Yes, "yes", "y", false, "Skip confirmation prompt before upload")

Expand All @@ -76,11 +77,6 @@ func RunStackpackTestDeployCommand(args *TestDeployArgs) di.CmdWithApiFn {
api *stackstate_api.APIClient,
serverInfo *stackstate_api.ServerInfo,
) common.CLIError {
// Warn if JSON output is requested - not meaningful for test-deploy command
if cli.IsJson() {
cli.Printer.PrintLn("Warning: JSON output format is not meaningful for the test-deploy command, proceeding with text output")
}

// Set default stackpack directory
if args.StackpackDir == "" {
currentDir, err := os.Getwd()
Expand All @@ -98,20 +94,20 @@ func RunStackpackTestDeployCommand(args *TestDeployArgs) di.CmdWithApiFn {
return common.NewRuntimeError(fmt.Errorf("failed to parse %s: %w", stackpackConfigFile, err))
}

cli.Printer.Success("Starting stackpack test-deploy sequence...")
cli.Printer.PrintLn(fmt.Sprintf(" Stackpack: %s (current version: %s)", originalInfo.Name, originalInfo.Version))
cli.Printer.PrintLn("")
printSuccess(cli, "Starting stackpack test-deploy sequence...")
printMsg(cli, fmt.Sprintf(" Stackpack: %s (current version: %s)", originalInfo.Name, originalInfo.Version))
printMsg(cli, "")

// Step 1: Check installed version and determine base version for snapshot
cli.Printer.PrintLn("Step 1/5: Checking installed version...")
printMsg(cli, "Step 1/5: Checking installed version...")
installedVersion, err := getInstalledStackpackVersion(cli, api, originalInfo.Name)
if err != nil {
return common.NewRuntimeError(fmt.Errorf("failed to check installed stackpack version: %w", err))
}

baseVersionForSnapshot := originalInfo.Version
if installedVersion != "" {
cli.Printer.PrintLn(fmt.Sprintf(" Found installed version: %s", installedVersion))
printMsg(cli, fmt.Sprintf(" Found installed version: %s", installedVersion))

// Compare base versions (strip cli-test suffix from installed version for comparison)
installedBaseVersion := installedVersion
Expand All @@ -129,15 +125,15 @@ func RunStackpackTestDeployCommand(args *TestDeployArgs) di.CmdWithApiFn {
baseComparison := compareVersions(installedBaseVersion, originalInfo.Version)
if baseComparison > 0 || (baseComparison == 0 && strings.Contains(installedVersion, "-cli-test.")) {
baseVersionForSnapshot = installedVersion
cli.Printer.PrintLn(fmt.Sprintf(" Using installed version as base: %s", baseVersionForSnapshot))
printMsg(cli, fmt.Sprintf(" Using installed version as base: %s", baseVersionForSnapshot))
} else if baseComparison < 0 {
cli.Printer.PrintLn(fmt.Sprintf(" Using local version as base (higher than installed): %s", baseVersionForSnapshot))
printMsg(cli, fmt.Sprintf(" Using local version as base (higher than installed): %s", baseVersionForSnapshot))
}
}

// Step 2: Create temporary directory and copy stackpack
cli.Printer.PrintLn("")
cli.Printer.PrintLn("Step 2/5: Creating temporary copy for testing...")
printMsg(cli, "")
printMsg(cli, "Step 2/5: Creating temporary copy for testing...")

tempDir, err := os.MkdirTemp("", "stackpack-test-*")
if err != nil {
Expand All @@ -147,30 +143,30 @@ func RunStackpackTestDeployCommand(args *TestDeployArgs) di.CmdWithApiFn {
// Ensure cleanup of temporary directory
defer func() {
if removeErr := os.RemoveAll(tempDir); removeErr != nil {
cli.Printer.PrintLn(fmt.Sprintf("Warning: Failed to cleanup temporary directory: %v", removeErr))
printMsg(cli, fmt.Sprintf("Warning: Failed to cleanup temporary directory: %v", removeErr))
}
}()

tempStackpackDir := filepath.Join(tempDir, "stackpack")
if err := copyDirectory(args.StackpackDir, tempStackpackDir); err != nil {
return common.NewRuntimeError(fmt.Errorf("failed to copy stackpack to temporary directory: %w", err))
}
cli.Printer.Success("Temporary copy created")
printSuccess(cli, "Temporary copy created")

// Step 3: Update version in temporary copy
cli.Printer.PrintLn("")
cli.Printer.PrintLn("Step 3/5: Bumping version for testing...")
printMsg(cli, "")
printMsg(cli, "Step 3/5: Bumping version for testing...")

tempConfigPath := filepath.Join(tempStackpackDir, stackpackConfigFile)
newVersion, err := bumpSnapshotVersionWithBase(tempConfigPath, baseVersionForSnapshot)
if err != nil {
return common.NewRuntimeError(fmt.Errorf("failed to bump version: %w", err))
}
cli.Printer.Success(fmt.Sprintf("Version bumped to: %s", newVersion))
printSuccess(cli, fmt.Sprintf("Version bumped to: %s", newVersion))

// Step 4: Package stackpack from temporary directory
cli.Printer.PrintLn("")
cli.Printer.PrintLn("Step 4/5: Packaging stackpack...")
printMsg(cli, "")
printMsg(cli, "Step 4/5: Packaging stackpack...")
packageArgs := &PackageArgs{
StackpackDir: tempStackpackDir, // Use temporary directory
Force: true, // Always overwrite for testing
Expand All @@ -183,27 +179,27 @@ func RunStackpackTestDeployCommand(args *TestDeployArgs) di.CmdWithApiFn {
if err := runPackageStep(cli, packageArgs); err != nil {
return err
}
cli.Printer.Success("Stackpack packaged successfully")
printSuccess(cli, "Stackpack packaged successfully")

// Step 5: Confirm upload (if needed) and execute upload/install workflow
if !args.Yes {
cli.Printer.PrintLn("")
if !args.Yes && !cli.IsJson() {
Comment thread
fvlankvelt marked this conversation as resolved.
printMsg(cli, "")
if !confirmUpload(cli, packageArgs.ArchiveFile) {
return common.NewRuntimeError(fmt.Errorf("upload cancelled by user"))
}
}

// Upload stackpack
cli.Printer.PrintLn("")
cli.Printer.PrintLn("Step 5/5: Uploading and installing/upgrading stackpack...")
printMsg(cli, "")
printMsg(cli, "Step 5/5: Uploading and installing/upgrading stackpack...")
uploadArgs := &UploadArgs{
FilePath: packageArgs.ArchiveFile,
}

if err := runUploadStep(cli, api, serverInfo, uploadArgs); err != nil {
return err
}
cli.Printer.Success("Stackpack uploaded successfully")
printSuccess(cli, "Stackpack uploaded successfully")

// Install or upgrade stackpack based on installation status
if installedVersion != "" {
Expand All @@ -217,7 +213,7 @@ func RunStackpackTestDeployCommand(args *TestDeployArgs) di.CmdWithApiFn {
if err := runUpgradeStep(cli, api, serverInfo, upgradeArgs); err != nil {
return err
}
cli.Printer.Success("Stackpack upgraded successfully")
printSuccess(cli, "Stackpack upgraded successfully")
} else {
installArgs := &InstallArgs{
Name: originalInfo.Name,
Expand All @@ -230,15 +226,20 @@ func RunStackpackTestDeployCommand(args *TestDeployArgs) di.CmdWithApiFn {
if err := runInstallStep(cli, api, serverInfo, installArgs); err != nil {
return err
}
cli.Printer.Success("Stackpack installed successfully")
printSuccess(cli, "Stackpack installed successfully")
}

cli.Printer.PrintLn("")
cli.Printer.Success("🎉 Test-deploy sequence completed successfully!")
printMsg(cli, "")
printSuccess(cli, "🎉 Test-deploy sequence completed successfully!")

// Clean up .sts file
if err := os.Remove(packageArgs.ArchiveFile); err != nil {
cli.Printer.PrintLn(fmt.Sprintf("Note: Could not clean up .sts file %s: %v", packageArgs.ArchiveFile, err))
printMsg(cli, fmt.Sprintf("Note: Could not clean up .sts file %s: %v", packageArgs.ArchiveFile, err))
} else if cli.IsJson() {
cli.Printer.PrintJson(map[string]interface{}{
"stackpack": originalInfo.Name,
"version": newVersion,
})
}

return nil
Expand Down Expand Up @@ -315,8 +316,8 @@ func confirmUpload(cli *di.Deps, zipFile string) bool {
serverURL = cli.CurrentContext.URL
}

cli.Printer.PrintLn(fmt.Sprintf("⚠️ This will upload '%s' to SUSE Observability server:", filepath.Base(zipFile)))
cli.Printer.PrintLn(fmt.Sprintf(" Server: %s", serverURL))
printMsg(cli, fmt.Sprintf("⚠️ This will upload '%s' to SUSE Observability server:", filepath.Base(zipFile)))
printMsg(cli, fmt.Sprintf(" Server: %s", serverURL))
fmt.Print(" Continue? (y/N): ")

reader := bufio.NewReader(os.Stdin)
Expand All @@ -332,33 +333,50 @@ func confirmUpload(cli *di.Deps, zipFile string) bool {
// runPackageStep executes the package command logic
func runPackageStep(cli *di.Deps, args *PackageArgs) common.CLIError {
// Reuse the existing package command logic
packageCmd := &cobra.Command{}
packageFn := RunStackpackPackageCommand(args)
return packageFn(cli, packageCmd)
return runCmd(cli, func(ctx *di.Deps) common.CLIError {
packageCmd := &cobra.Command{}
packageFn := RunStackpackPackageCommand(args)
return packageFn(ctx, packageCmd)
})
}

// runUploadStep executes the upload command logic
func runUploadStep(cli *di.Deps, api *stackstate_api.APIClient, serverInfo *stackstate_api.ServerInfo, args *UploadArgs) common.CLIError {
// Reuse the existing upload command logic
uploadCmd := &cobra.Command{}
uploadFn := RunStackpackUploadCommand(args)
return uploadFn(uploadCmd, cli, api, serverInfo)
return runCmd(cli, func(ctx *di.Deps) common.CLIError {
// Reuse the existing upload command logic
uploadCmd := &cobra.Command{}
uploadFn := RunStackpackUploadCommand(args)
return uploadFn(uploadCmd, ctx, api, serverInfo)
})
}

// runInstallStep executes the install command logic
func runInstallStep(cli *di.Deps, api *stackstate_api.APIClient, serverInfo *stackstate_api.ServerInfo, args *InstallArgs) common.CLIError {
// Reuse the existing install command logic
installCmd := &cobra.Command{}
installFn := RunStackpackInstallCommand(args)
return installFn(installCmd, cli, api, serverInfo)
return runCmd(cli, func(ctx *di.Deps) common.CLIError {
// Reuse the existing install command logic
installCmd := &cobra.Command{}
installFn := RunStackpackInstallCommand(args)
return installFn(installCmd, ctx, api, serverInfo)
})
}

// runUpgradeStep executes the upgrade command logic
func runUpgradeStep(cli *di.Deps, api *stackstate_api.APIClient, serverInfo *stackstate_api.ServerInfo, args *UpgradeArgs) common.CLIError {
// Reuse the existing upgrade command logic
upgradeCmd := &cobra.Command{}
upgradeFn := RunStackpackUpgradeCommand(args)
return upgradeFn(upgradeCmd, cli, api, serverInfo)
return runCmd(cli, func(ctx *di.Deps) common.CLIError {
// Reuse the existing upgrade command logic
upgradeCmd := &cobra.Command{}
upgradeFn := RunStackpackUpgradeCommand(args)
return upgradeFn(upgradeCmd, ctx, api, serverInfo)
})
}

func runCmd(cli *di.Deps, f func(ctx *di.Deps) common.CLIError) common.CLIError {
oldPr := cli.Printer
pr := printer.NewMockPrinter(nil)
cli.Printer = &pr
err := f(cli)
cli.Printer = oldPr
return err
}

// getInstalledStackpackVersion checks if a stackpack is installed and returns its version
Expand Down Expand Up @@ -489,3 +507,15 @@ func updateVersionInYaml(configPath, newVersion string) error {

return nil
}

func printMsg(cli *di.Deps, msg string) {
if !cli.IsJson() {
cli.Printer.PrintLn(msg)
}
}

func printSuccess(cli *di.Deps, msg string) {
if !cli.IsJson() {
cli.Printer.Success(msg)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestStackpackTestDeployCommand_FlagsAndStructure(t *testing.T) {
// Test flags exist
flags := cmd.Flags()

stackpackDirFlag := flags.Lookup("stackpack-directory")
stackpackDirFlag := flags.Lookup("directory")
require.NotNil(t, stackpackDirFlag)
assert.Equal(t, "d", stackpackDirFlag.Shorthand)

Expand Down
14 changes: 6 additions & 8 deletions cmd/stackpack/stackpack_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,17 @@ This command validates a stackpack by uploading it to the server.
- If a directory is provided, it is automatically packaged into a .sts file before uploading
- If a .sts file is provided, it is uploaded directly

Exactly one of --stackpack-directory or --stackpack-file must be specified.

This command is experimental and requires STS_EXPERIMENTAL_STACKPACKS environment variable to be set.`,
Exactly one of --directory or --file must be specified.`,
Example: `# Validate a stackpack directory (automatically packaged)
sts stackpack validate --stackpack-directory ./my-stackpack
Comment thread
fvlankvelt marked this conversation as resolved.
sts stackpack validate --directory ./my-stackpack

# Validate a pre-packaged .sts file
sts stackpack validate --stackpack-file ./my-stackpack.sts`,
sts stackpack validate --file ./my-stackpack.sts`,
RunE: cli.CmdRunEWithApi(RunStackpackValidateCommand(args)),
}

cmd.Flags().StringVarP(&args.StackpackDir, "stackpack-directory", "d", "", "Path to stackpack directory")
cmd.Flags().StringVarP(&args.StackpackFile, "stackpack-file", "f", "", "Path to .sts file")
cmd.Flags().StringVarP(&args.StackpackDir, "directory", "d", "", "Path to stackpack directory")
cmd.Flags().StringVarP(&args.StackpackFile, "file", "f", "", "Path to .sts file")

return cmd
}
Expand All @@ -61,7 +59,7 @@ func RunStackpackValidateCommand(args *ValidateArgs) di.CmdWithApiFn {
// Validate exactly one of directory or file is set
if (args.StackpackDir == "" && args.StackpackFile == "") ||
(args.StackpackDir != "" && args.StackpackFile != "") {
return common.NewCLIArgParseError(fmt.Errorf("exactly one of --stackpack-directory or --stackpack-file must be specified"))
return common.NewCLIArgParseError(fmt.Errorf("exactly one of --directory or --file must be specified"))
}

// Prepare file to validate - if directory is provided, package it first
Expand Down
Loading
Loading