From c645c8011f40d8faf76e7a0f096d137b28efd501 Mon Sep 17 00:00:00 2001 From: Alejandro Acevedo Date: Thu, 23 Jul 2026 14:43:44 +0200 Subject: [PATCH 1/4] STAC-23211: Accept specific stackpack version during provisioning --- cmd/stackpack/stackpack_install.go | 13 +++++++-- cmd/stackpack/stackpack_install_test.go | 28 +++++++++++++++++++ generated/stackstate_api/api/openapi.yaml | 11 ++++++-- generated/stackstate_api/api_stackpack.go | 12 ++++++++ .../docs/PresentationHighlight.md | 9 ++++-- generated/stackstate_api/docs/StackpackApi.md | 6 ++-- .../model_presentation_highlight.go | 27 +++++++++++------- stackstate_openapi/openapi_version | 2 +- 8 files changed, 89 insertions(+), 19 deletions(-) diff --git a/cmd/stackpack/stackpack_install.go b/cmd/stackpack/stackpack_install.go index 39b81b25..69b4e4f8 100644 --- a/cmd/stackpack/stackpack_install.go +++ b/cmd/stackpack/stackpack_install.go @@ -16,6 +16,7 @@ import ( type InstallArgs struct { Name string UnlockedStrategy string + Version string Params map[string]string Wait bool Timeout time.Duration @@ -33,7 +34,10 @@ func StackpackInstallCommand(cli *di.Deps) *cobra.Command { sts stackpack install --name example -p "full_name=First Last" -p URL=https://stackstate.com # install and wait for completion -sts stackpack install --name kubernetes -p cluster_name=production --wait`, +sts stackpack install --name kubernetes -p cluster_name=production --wait + +# install a specific (older) version, e.g. to downgrade +sts stackpack install --name example --stackpack-version 1.2.3 -p "full_name=First Last"`, RunE: cli.CmdRunEWithApi(RunStackpackInstallCommand(args)), } common.AddRequiredNameFlagVar(cmd, &args.Name, "Name of the StackPack") @@ -44,6 +48,7 @@ sts stackpack install --name kubernetes -p cluster_name=production --wait`, "Strategy use to upgrade StackPack instance"+ fmt.Sprintf(" (must be { %s })", strings.Join(UnlockedStrategyChoices, " | ")), ) + cmd.Flags().StringVar(&args.Version, StackpackVersionFlag, "", "Install a specific version of the StackPack, for example to downgrade (the StackPack must not already be installed at a different version)") cmd.Flags().StringToStringVarP(&args.Params, ParameterFlag, "p", args.Params, "List of parameters of the form \"key=value\"") cmd.Flags().BoolVar(&args.Wait, "wait", false, "Wait for installation to complete") cmd.Flags().DurationVar(&args.Timeout, "timeout", DefaultTimeout, "Timeout for waiting") @@ -57,7 +62,11 @@ func RunStackpackInstallCommand(args *InstallArgs) di.CmdWithApiFn { api *stackstate_api.APIClient, serverInfo *stackstate_api.ServerInfo, ) common.CLIError { - instance, resp, err := api.StackpackApi.ProvisionDetails(cli.Context, args.Name).RequestBody(args.Params).Unlocked(args.UnlockedStrategy).Execute() + apiRequest := api.StackpackApi.ProvisionDetails(cli.Context, args.Name).RequestBody(args.Params).Unlocked(args.UnlockedStrategy) + if args.Version != "" { + apiRequest = apiRequest.Version(args.Version) + } + instance, resp, err := apiRequest.Execute() if err != nil { return common.NewResponseError(err, resp) } diff --git a/cmd/stackpack/stackpack_install_test.go b/cmd/stackpack/stackpack_install_test.go index 1cf1114c..f00aa689 100644 --- a/cmd/stackpack/stackpack_install_test.go +++ b/cmd/stackpack/stackpack_install_test.go @@ -112,6 +112,34 @@ func TestStackpackInstallNoParameters(t *testing.T) { ) } +func TestStackpackInstallWithVersion(t *testing.T) { + cli, cmd := setupStackPackInstallCmd(t) + + di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "install", "--name", "zabbix", + "--stackpack-version", "3.2.0", + ) + + strategyFlag := FailStrategy + versionFlag := "3.2.0" + assert.Equal(t, + stackstate_api.ProvisionDetailsCall{ + PstackPackName: "zabbix", + Punlocked: &strategyFlag, + Pversion: &versionFlag, + PrequestBody: &map[string]string{}, + }, + (*cli.MockClient.ApiMocks.StackpackApi.ProvisionDetailsCalls)[0], + ) +} + +func TestStackpackInstallWithoutVersion(t *testing.T) { + cli, cmd := setupStackPackInstallCmd(t) + + di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "install", "--name", "zabbix") + + assert.Nil(t, (*cli.MockClient.ApiMocks.StackpackApi.ProvisionDetailsCalls)[0].Pversion) +} + func TestStackpackInstallPrintsToJson(t *testing.T) { cli, cmd := setupStackPackInstallCmd(t) diff --git a/generated/stackstate_api/api/openapi.yaml b/generated/stackstate_api/api/openapi.yaml index 08823744..c6ad0ba1 100644 --- a/generated/stackstate_api/api/openapi.yaml +++ b/generated/stackstate_api/api/openapi.yaml @@ -413,6 +413,14 @@ paths: required: true schema: type: string + - description: "Optional version string (e.g. '1.2.3'). If not provided, the\ + \ latest version is used. When provisioning, the StackPack cannot already\ + \ be installed at a different version." + in: query + name: version + required: false + schema: + type: string requestBody: content: application/json: @@ -14857,7 +14865,7 @@ components: properties: _type: enum: - - ErrorPresentationComponentLink + - ErrorComponentPresentationLink type: string linkId: description: Identity key for this link. @@ -20781,7 +20789,6 @@ components: $ref: '#/components/schemas/PresentationHighlightMetricsSection' type: array required: - - fields - title type: object PresentationHighlightField: diff --git a/generated/stackstate_api/api_stackpack.go b/generated/stackstate_api/api_stackpack.go index e9360c33..427053a8 100644 --- a/generated/stackstate_api/api_stackpack.go +++ b/generated/stackstate_api/api_stackpack.go @@ -348,6 +348,7 @@ type ApiProvisionDetailsRequest struct { ApiService StackpackApi stackPackName string unlocked *string + version *string requestBody *map[string]string } @@ -356,6 +357,12 @@ func (r ApiProvisionDetailsRequest) Unlocked(unlocked string) ApiProvisionDetail return r } +// Optional version string (e.g. '1.2.3'). If not provided, the latest version is used. When provisioning, the StackPack cannot already be installed at a different version. +func (r ApiProvisionDetailsRequest) Version(version string) ApiProvisionDetailsRequest { + r.version = &version + return r +} + func (r ApiProvisionDetailsRequest) RequestBody(requestBody map[string]string) ApiProvisionDetailsRequest { r.requestBody = &requestBody return r @@ -409,6 +416,9 @@ func (a *StackpackApiService) ProvisionDetailsExecute(r ApiProvisionDetailsReque } localVarQueryParams.Add("unlocked", parameterToString(*r.unlocked, "")) + if r.version != nil { + localVarQueryParams.Add("version", parameterToString(*r.version, "")) + } // to determine the Content-Type header localVarHTTPContentTypes := []string{"application/json"} @@ -2033,6 +2043,7 @@ type ProvisionDetailsMockResponse struct { type ProvisionDetailsCall struct { PstackPackName string Punlocked *string + Pversion *string PrequestBody *map[string]string } @@ -2048,6 +2059,7 @@ func (mock StackpackApiMock) ProvisionDetailsExecute(r ApiProvisionDetailsReques p := ProvisionDetailsCall{ PstackPackName: r.stackPackName, Punlocked: r.unlocked, + Pversion: r.version, PrequestBody: r.requestBody, } *mock.ProvisionDetailsCalls = append(*mock.ProvisionDetailsCalls, p) diff --git a/generated/stackstate_api/docs/PresentationHighlight.md b/generated/stackstate_api/docs/PresentationHighlight.md index 71f10046..293aabd4 100644 --- a/generated/stackstate_api/docs/PresentationHighlight.md +++ b/generated/stackstate_api/docs/PresentationHighlight.md @@ -5,7 +5,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **Title** | **string** | | -**Fields** | [**[]PresentationHighlightField**](PresentationHighlightField.md) | | +**Fields** | Pointer to [**[]PresentationHighlightField**](PresentationHighlightField.md) | | [optional] **Provisioning** | Pointer to [**PresentationHighlightProvisioning**](PresentationHighlightProvisioning.md) | | [optional] **RelatedResources** | Pointer to [**[]PresentationRelatedResource**](PresentationRelatedResource.md) | | [optional] **Links** | Pointer to [**[]PresentationLink**](PresentationLink.md) | | [optional] @@ -16,7 +16,7 @@ Name | Type | Description | Notes ### NewPresentationHighlight -`func NewPresentationHighlight(title string, fields []PresentationHighlightField, ) *PresentationHighlight` +`func NewPresentationHighlight(title string, ) *PresentationHighlight` NewPresentationHighlight instantiates a new PresentationHighlight object This constructor will assign default values to properties that have it defined, @@ -70,6 +70,11 @@ and a boolean to check if the value has been set. SetFields sets Fields field to given value. +### HasFields + +`func (o *PresentationHighlight) HasFields() bool` + +HasFields returns a boolean if a field has been set. ### GetProvisioning diff --git a/generated/stackstate_api/docs/StackpackApi.md b/generated/stackstate_api/docs/StackpackApi.md index 48cbda85..12fa4045 100644 --- a/generated/stackstate_api/docs/StackpackApi.md +++ b/generated/stackstate_api/docs/StackpackApi.md @@ -92,7 +92,7 @@ Name | Type | Description | Notes ## ProvisionDetails -> ProvisionResponse ProvisionDetails(ctx, stackPackName).Unlocked(unlocked).RequestBody(requestBody).Execute() +> ProvisionResponse ProvisionDetails(ctx, stackPackName).Unlocked(unlocked).Version(version).RequestBody(requestBody).Execute() Provision API @@ -113,11 +113,12 @@ import ( func main() { stackPackName := "stackPackName_example" // string | unlocked := "unlocked_example" // string | + version := "version_example" // string | Optional version string (e.g. '1.2.3'). If not provided, the latest version is used. When provisioning, the StackPack cannot already be installed at a different version. (optional) requestBody := map[string]string{"key": "Inner_example"} // map[string]string | (optional) configuration := openapiclient.NewConfiguration() apiClient := openapiclient.NewAPIClient(configuration) - resp, r, err := apiClient.StackpackApi.ProvisionDetails(context.Background(), stackPackName).Unlocked(unlocked).RequestBody(requestBody).Execute() + resp, r, err := apiClient.StackpackApi.ProvisionDetails(context.Background(), stackPackName).Unlocked(unlocked).Version(version).RequestBody(requestBody).Execute() if err != nil { fmt.Fprintf(os.Stderr, "Error when calling `StackpackApi.ProvisionDetails``: %v\n", err) fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) @@ -144,6 +145,7 @@ Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- **unlocked** | **string** | | + **version** | **string** | Optional version string (e.g. '1.2.3'). If not provided, the latest version is used. When provisioning, the StackPack cannot already be installed at a different version. | **requestBody** | **map[string]string** | | ### Return type diff --git a/generated/stackstate_api/model_presentation_highlight.go b/generated/stackstate_api/model_presentation_highlight.go index 54d80273..d410790c 100644 --- a/generated/stackstate_api/model_presentation_highlight.go +++ b/generated/stackstate_api/model_presentation_highlight.go @@ -18,7 +18,7 @@ import ( // PresentationHighlight Highlight presentation definition. The `fields` define the fields to show in the highlight page. If multiple ComponentPresentations match, fields are merged by `fieldId` according to binding rank. Related resources follow the same merge semantics using `resourceId` as the identity key. Links follow the same merge semantics using `linkId` as the identity key. type PresentationHighlight struct { Title string `json:"title" yaml:"title"` - Fields []PresentationHighlightField `json:"fields" yaml:"fields"` + Fields []PresentationHighlightField `json:"fields,omitempty" yaml:"fields,omitempty"` Provisioning *PresentationHighlightProvisioning `json:"provisioning,omitempty" yaml:"provisioning,omitempty"` RelatedResources []PresentationRelatedResource `json:"relatedResources,omitempty" yaml:"relatedResources,omitempty"` Links []PresentationLink `json:"links,omitempty" yaml:"links,omitempty"` @@ -30,10 +30,9 @@ type PresentationHighlight struct { // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewPresentationHighlight(title string, fields []PresentationHighlightField) *PresentationHighlight { +func NewPresentationHighlight(title string) *PresentationHighlight { this := PresentationHighlight{} this.Title = title - this.Fields = fields return &this } @@ -69,26 +68,34 @@ func (o *PresentationHighlight) SetTitle(v string) { o.Title = v } -// GetFields returns the Fields field value +// GetFields returns the Fields field value if set, zero value otherwise. func (o *PresentationHighlight) GetFields() []PresentationHighlightField { - if o == nil { + if o == nil || o.Fields == nil { var ret []PresentationHighlightField return ret } - return o.Fields } -// GetFieldsOk returns a tuple with the Fields field value +// GetFieldsOk returns a tuple with the Fields field value if set, nil otherwise // and a boolean to check if the value has been set. func (o *PresentationHighlight) GetFieldsOk() ([]PresentationHighlightField, bool) { - if o == nil { + if o == nil || o.Fields == nil { return nil, false } return o.Fields, true } -// SetFields sets field value +// HasFields returns a boolean if a field has been set. +func (o *PresentationHighlight) HasFields() bool { + if o != nil && o.Fields != nil { + return true + } + + return false +} + +// SetFields gets a reference to the given []PresentationHighlightField and assigns it to the Fields field. func (o *PresentationHighlight) SetFields(v []PresentationHighlightField) { o.Fields = v } @@ -258,7 +265,7 @@ func (o PresentationHighlight) MarshalJSON() ([]byte, error) { if true { toSerialize["title"] = o.Title } - if true { + if o.Fields != nil { toSerialize["fields"] = o.Fields } if o.Provisioning != nil { diff --git a/stackstate_openapi/openapi_version b/stackstate_openapi/openapi_version index 302bee1c..1782a5bb 100644 --- a/stackstate_openapi/openapi_version +++ b/stackstate_openapi/openapi_version @@ -1 +1 @@ -9c9f7edc3fc4bf1b8f62092b80f6ed50fcd8be2e \ No newline at end of file +3889f3a46f6a2405349b35221fb60aad09eafe14 \ No newline at end of file From 96ff9955750b7f0e8cc6c3b24621c3a722e8c486 Mon Sep 17 00:00:00 2001 From: Alejandro Acevedo Date: Thu, 23 Jul 2026 17:53:14 +0200 Subject: [PATCH 2/4] STAC-23211: Implement downgrade stackpack command --- cmd/stackpack.go | 1 + cmd/stackpack/stackpack_downgrade.go | 125 ++++++++++ cmd/stackpack/stackpack_downgrade_test.go | 80 +++++++ generated/stackstate_api/README.md | 1 + generated/stackstate_api/api/openapi.yaml | 34 +++ generated/stackstate_api/api_stackpack.go | 213 ++++++++++++++++++ generated/stackstate_api/docs/StackpackApi.md | 73 ++++++ stackstate_openapi/openapi_version | 2 +- 8 files changed, 528 insertions(+), 1 deletion(-) create mode 100644 cmd/stackpack/stackpack_downgrade.go create mode 100644 cmd/stackpack/stackpack_downgrade_test.go diff --git a/cmd/stackpack.go b/cmd/stackpack.go index 47b40f92..a3db63cd 100644 --- a/cmd/stackpack.go +++ b/cmd/stackpack.go @@ -25,6 +25,7 @@ func StackPackCommand(cli *di.Deps) *cobra.Command { cmd.AddCommand(stackpack.StackpackListParameterCommand(cli)) cmd.AddCommand(stackpack.StackpackUninstallCommand(cli)) cmd.AddCommand(stackpack.StackpackUpgradeCommand(cli)) + cmd.AddCommand(stackpack.StackpackDowngradeCommand(cli)) cmd.AddCommand(stackpack.StackpackConfirmManualStepsCommand(cli)) cmd.AddCommand(stackpack.StackpackDescribeCommand(cli)) cmd.AddCommand(stackpack.StackpackListVersionsCommand(cli)) diff --git a/cmd/stackpack/stackpack_downgrade.go b/cmd/stackpack/stackpack_downgrade.go new file mode 100644 index 00000000..6b4d22be --- /dev/null +++ b/cmd/stackpack/stackpack_downgrade.go @@ -0,0 +1,125 @@ +package stackpack + +import ( + "fmt" + "time" + + "github.com/spf13/cobra" + "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" +) + +type DowngradeArgs struct { + TypeName string + Version string + Wait bool + Timeout time.Duration +} + +func StackpackDowngradeCommand(cli *di.Deps) *cobra.Command { + args := &DowngradeArgs{} + cmd := &cobra.Command{ + Use: "downgrade", + Short: "Downgrade a StackPack to an older version", + Long: "Downgrade all instances of a StackPack to an older version. " + + "Only supported for StackPacks 2.0 and the target version must be lower than the currently installed version. " + + "To move a StackPacks 1.0 instance to an older version, uninstall it and install the desired version instead.", + Example: `# downgrade a StackPack to an older version +sts stackpack downgrade --name kubernetes --stackpack-version 1.2.3 + +# downgrade and wait for completion +sts stackpack downgrade --name kubernetes --stackpack-version 1.2.3 --wait`, + RunE: cli.CmdRunEWithApi(RunStackpackDowngradeCommand(args)), + } + common.AddRequiredNameFlagVar(cmd, &args.TypeName, "Name of the StackPack") + cmd.Flags().StringVar(&args.Version, StackpackVersionFlag, "", "Version to downgrade to") + cmd.MarkFlagRequired(StackpackVersionFlag) + cmd.Flags().BoolVar(&args.Wait, "wait", false, "Wait for downgrade to complete") + cmd.Flags().DurationVar(&args.Timeout, "timeout", DefaultTimeout, "Timeout for waiting") + return cmd +} + +func RunStackpackDowngradeCommand(args *DowngradeArgs) di.CmdWithApiFn { + return func( + cmd *cobra.Command, + cli *di.Deps, + api *stackstate_api.APIClient, + serverInfo *stackstate_api.ServerInfo, + ) common.CLIError { + _, resp, err := api.StackpackApi.DowngradeStackPack(cli.Context, args.TypeName). + Version(args.Version). + Execute() + if err != nil { + return common.NewResponseError(err, resp) + } + + if args.Wait { + if !cli.IsJson() { + cli.Printer.PrintLn("Waiting for downgrade to complete...") + } + + waiter := NewOperationWaiter(cli, api) + waitErr := waiter.WaitForCompletion(WaitOptions{ + StackPackName: args.TypeName, + Timeout: args.Timeout, + PollInterval: DefaultPollInterval, + }) + if waitErr != nil { + return common.NewRuntimeError(waitErr) + } + + stackPackList, cliErr := fetchAllStackPacks(cli, api) + if cliErr != nil { + return cliErr + } + + finalStackPack, err := findStackPackByName(stackPackList, args.TypeName) + if err != nil { + return common.NewNotFoundError(err) + } + + if cli.IsJson() { + cli.Printer.PrintJson(map[string]interface{}{ + "stackpack": finalStackPack, + "status": "completed", + "current-version": finalStackPack.GetVersion(), + }) + } else { + cli.Printer.Success("StackPack downgrade completed successfully") + + data := make([][]interface{}, 0) + for _, config := range finalStackPack.GetConfigurations() { + lastUpdateTime := time.UnixMilli(config.GetLastUpdateTimestamp()) + data = append(data, []interface{}{ + config.GetId(), + finalStackPack.GetName(), + config.GetStatus(), + config.GetStackPackVersion(), + lastUpdateTime, + }) + } + + cli.Printer.Table( + printer.TableData{ + Header: []string{"id", "name", "status", "version", "last updated"}, + Data: data, + MissingTableDataMsg: printer.NotFoundMsg{Types: "configurations for " + args.TypeName}, + }, + ) + } + } else { + if cli.IsJson() { + cli.Printer.PrintJson(map[string]interface{}{ + "success": true, + "target-version": args.Version, + }) + } else { + cli.Printer.Success(fmt.Sprintf("Successfully triggered downgrade of %s to %s", args.TypeName, args.Version)) + } + } + + return nil + } +} diff --git a/cmd/stackpack/stackpack_downgrade_test.go b/cmd/stackpack/stackpack_downgrade_test.go new file mode 100644 index 00000000..3f21b28b --- /dev/null +++ b/cmd/stackpack/stackpack_downgrade_test.go @@ -0,0 +1,80 @@ +package stackpack + +import ( + "testing" + + "github.com/spf13/cobra" + "github.com/stackvista/stackstate-cli/generated/stackstate_api" + "github.com/stackvista/stackstate-cli/internal/di" + "github.com/stretchr/testify/assert" +) + +var ( + downgradeVersion = "1.2.3" +) + +func setupStackPackDowngradeCmd(t *testing.T) (*di.MockDeps, *cobra.Command) { + cli := di.NewMockDeps(t) + cmd := StackpackDowngradeCommand(&cli.Deps) + cli.MockClient.ApiMocks.StackpackApi.DowngradeStackPackResponse.Result = successfulResponseResult + return &cli, cmd +} + +func TestStackpackDowngradePrintToTable(t *testing.T) { + cli, cmd := setupStackPackDowngradeCmd(t) + di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "downgrade", "--name", "zabbix", + "--stackpack-version", downgradeVersion, + ) + + assert.Equal(t, + []stackstate_api.DowngradeStackPackCall{{ + PstackPackName: "zabbix", + Pversion: &downgradeVersion, + }}, + *cli.MockClient.ApiMocks.StackpackApi.DowngradeStackPackCalls) + assert.True(t, cli.MockPrinter.HasNonJsonCalls) + assert.Equal(t, []string{"Successfully triggered downgrade of zabbix to 1.2.3"}, *cli.MockPrinter.SuccessCalls) +} + +func TestStackpackDowngradePrintToJson(t *testing.T) { + cli, cmd := setupStackPackDowngradeCmd(t) + di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "downgrade", "--name", "zabbix", + "--stackpack-version", downgradeVersion, "-o", "json", + ) + + assert.Equal(t, + []stackstate_api.DowngradeStackPackCall{{ + PstackPackName: "zabbix", + Pversion: &downgradeVersion, + }}, + *cli.MockClient.ApiMocks.StackpackApi.DowngradeStackPackCalls) + + expectedJsonCalls := []map[string]interface{}{{ + "success": true, + "target-version": downgradeVersion, + }} + assert.Equal(t, expectedJsonCalls, *cli.MockPrinter.PrintJsonCalls) +} + +func TestStackpackDowngradeRequiresVersion(t *testing.T) { + cli := di.NewMockDeps(t) + cmd := StackpackDowngradeCommand(&cli.Deps) + + _, err := di.ExecuteCommandWithContext(&cli.Deps, cmd, "downgrade", "--name", "zabbix") + + assert.Error(t, err) + assert.Contains(t, err.Error(), "stackpack-version") +} + +func TestStackpackDowngradeHasWaitFlags(t *testing.T) { + cli := di.NewMockDeps(t) + cmd := StackpackDowngradeCommand(&cli.Deps) + + waitFlag := cmd.Flags().Lookup("wait") + assert.NotNil(t, waitFlag, "wait flag should exist") + assert.Equal(t, "false", waitFlag.DefValue, "wait flag default should be false") + + timeoutFlag := cmd.Flags().Lookup("timeout") + assert.NotNil(t, timeoutFlag, "timeout flag should exist") + assert.Equal(t, "1m0s", timeoutFlag.DefValue, "timeout flag default should be 1m0s") +} diff --git a/generated/stackstate_api/README.md b/generated/stackstate_api/README.md index df60a585..e2e211b0 100644 --- a/generated/stackstate_api/README.md +++ b/generated/stackstate_api/README.md @@ -226,6 +226,7 @@ Class | Method | HTTP request | Description *ServiceTokenApi* | [**GetServiceTokens**](docs/ServiceTokenApi.md#getservicetokens) | **Get** /security/tokens | Get service tokens *SnapshotApi* | [**QuerySnapshot**](docs/SnapshotApi.md#querysnapshot) | **Post** /snapshot | Query topology snapshot *StackpackApi* | [**ConfirmManualSteps**](docs/StackpackApi.md#confirmmanualsteps) | **Post** /stackpack/{stackPackName}/confirm-manual-steps/{stackPackInstanceId} | Confirm manual steps +*StackpackApi* | [**DowngradeStackPack**](docs/StackpackApi.md#downgradestackpack) | **Post** /stackpack/{stackPackName}/downgrade | Downgrade API *StackpackApi* | [**ProvisionDetails**](docs/StackpackApi.md#provisiondetails) | **Post** /stackpack/{stackPackName}/provision | Provision API *StackpackApi* | [**ProvisionUninstall**](docs/StackpackApi.md#provisionuninstall) | **Post** /stackpack/{stackPackName}/deprovision/{stackPackInstanceId} | Provision API *StackpackApi* | [**StackPackDeleteVersion**](docs/StackpackApi.md#stackpackdeleteversion) | **Delete** /stackpack/{stackPackName}/versions/{version} | Delete a StackPack version diff --git a/generated/stackstate_api/api/openapi.yaml b/generated/stackstate_api/api/openapi.yaml index c6ad0ba1..2875de92 100644 --- a/generated/stackstate_api/api/openapi.yaml +++ b/generated/stackstate_api/api/openapi.yaml @@ -516,6 +516,40 @@ paths: summary: Upgrade API tags: - stackpack + /stackpack/{stackPackName}/downgrade: + post: + description: Downgrade a StackPack to an older version. Only supported for StackPacks + 2.0; the target version must be lower than the currently installed version. + operationId: downgradeStackPack + parameters: + - in: path + name: stackPackName + required: true + schema: + type: string + - description: Version string (e.g. '1.2.3') to downgrade to. Must be lower + than the currently installed version. + in: query + name: version + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + type: string + description: successful + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/GenericErrorsResponse' + description: Error when handling the request on the server side. + summary: Downgrade API + tags: + - stackpack /stackpack/{stackPackName}/versions: delete: description: "Delete versions of a StackPack. Versions currently in use are\ diff --git a/generated/stackstate_api/api_stackpack.go b/generated/stackstate_api/api_stackpack.go index 427053a8..63c05397 100644 --- a/generated/stackstate_api/api_stackpack.go +++ b/generated/stackstate_api/api_stackpack.go @@ -39,6 +39,21 @@ type StackpackApi interface { // @return string ConfirmManualStepsExecute(r ApiConfirmManualStepsRequest) (string, *http.Response, error) + /* + DowngradeStackPack Downgrade API + + Downgrade a StackPack to an older version. Only supported for StackPacks 2.0; the target version must be lower than the currently installed version. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param stackPackName + @return ApiDowngradeStackPackRequest + */ + DowngradeStackPack(ctx context.Context, stackPackName string) ApiDowngradeStackPackRequest + + // DowngradeStackPackExecute executes the request + // @return string + DowngradeStackPackExecute(r ApiDowngradeStackPackRequest) (string, *http.Response, error) + /* ProvisionDetails Provision API @@ -343,6 +358,172 @@ func (a *StackpackApiService) ConfirmManualStepsExecute(r ApiConfirmManualStepsR return localVarReturnValue, localVarHTTPResponse, nil } +type ApiDowngradeStackPackRequest struct { + ctx context.Context + ApiService StackpackApi + stackPackName string + version *string +} + +// Version string (e.g. '1.2.3') to downgrade to. Must be lower than the currently installed version. +func (r ApiDowngradeStackPackRequest) Version(version string) ApiDowngradeStackPackRequest { + r.version = &version + return r +} + +func (r ApiDowngradeStackPackRequest) Execute() (string, *http.Response, error) { + return r.ApiService.DowngradeStackPackExecute(r) +} + +/* +DowngradeStackPack Downgrade API + +Downgrade a StackPack to an older version. Only supported for StackPacks 2.0; the target version must be lower than the currently installed version. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param stackPackName + @return ApiDowngradeStackPackRequest +*/ +func (a *StackpackApiService) DowngradeStackPack(ctx context.Context, stackPackName string) ApiDowngradeStackPackRequest { + return ApiDowngradeStackPackRequest{ + ApiService: a, + ctx: ctx, + stackPackName: stackPackName, + } +} + +// Execute executes the request +// +// @return string +func (a *StackpackApiService) DowngradeStackPackExecute(r ApiDowngradeStackPackRequest) (string, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue string + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "StackpackApiService.DowngradeStackPack") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/stackpack/{stackPackName}/downgrade" + localVarPath = strings.Replace(localVarPath, "{"+"stackPackName"+"}", url.PathEscape(parameterToString(r.stackPackName, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.version == nil { + return localVarReturnValue, nil, reportError("version is required and must be specified") + } + + localVarQueryParams.Add("version", parameterToString(*r.version, "")) + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["ApiToken"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-API-Token"] = key + } + } + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["ServiceBearer"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-API-ServiceBearer"] = key + } + } + } + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + if apiKey, ok := auth["ServiceToken"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + localVarHeaderParams["X-API-Key"] = key + } + } + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = ioutil.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 500 { + var v GenericErrorsResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + type ApiProvisionDetailsRequest struct { ctx context.Context ApiService StackpackApi @@ -1960,6 +2141,8 @@ func (a *StackpackApiService) UpgradeStackPackExecute(r ApiUpgradeStackPackReque type StackpackApiMock struct { ConfirmManualStepsCalls *[]ConfirmManualStepsCall ConfirmManualStepsResponse ConfirmManualStepsMockResponse + DowngradeStackPackCalls *[]DowngradeStackPackCall + DowngradeStackPackResponse DowngradeStackPackMockResponse ProvisionDetailsCalls *[]ProvisionDetailsCall ProvisionDetailsResponse ProvisionDetailsMockResponse ProvisionUninstallCalls *[]ProvisionUninstallCall @@ -1982,6 +2165,7 @@ type StackpackApiMock struct { func NewStackpackApiMock() StackpackApiMock { xConfirmManualStepsCalls := make([]ConfirmManualStepsCall, 0) + xDowngradeStackPackCalls := make([]DowngradeStackPackCall, 0) xProvisionDetailsCalls := make([]ProvisionDetailsCall, 0) xProvisionUninstallCalls := make([]ProvisionUninstallCall, 0) xStackPackDeleteVersionCalls := make([]StackPackDeleteVersionCall, 0) @@ -1993,6 +2177,7 @@ func NewStackpackApiMock() StackpackApiMock { xUpgradeStackPackCalls := make([]UpgradeStackPackCall, 0) return StackpackApiMock{ ConfirmManualStepsCalls: &xConfirmManualStepsCalls, + DowngradeStackPackCalls: &xDowngradeStackPackCalls, ProvisionDetailsCalls: &xProvisionDetailsCalls, ProvisionUninstallCalls: &xProvisionUninstallCalls, StackPackDeleteVersionCalls: &xStackPackDeleteVersionCalls, @@ -2034,6 +2219,34 @@ func (mock StackpackApiMock) ConfirmManualStepsExecute(r ApiConfirmManualStepsRe return mock.ConfirmManualStepsResponse.Result, mock.ConfirmManualStepsResponse.Response, mock.ConfirmManualStepsResponse.Error } +type DowngradeStackPackMockResponse struct { + Result string + Response *http.Response + Error error +} + +type DowngradeStackPackCall struct { + PstackPackName string + Pversion *string +} + +func (mock StackpackApiMock) DowngradeStackPack(ctx context.Context, stackPackName string) ApiDowngradeStackPackRequest { + return ApiDowngradeStackPackRequest{ + ApiService: mock, + ctx: ctx, + stackPackName: stackPackName, + } +} + +func (mock StackpackApiMock) DowngradeStackPackExecute(r ApiDowngradeStackPackRequest) (string, *http.Response, error) { + p := DowngradeStackPackCall{ + PstackPackName: r.stackPackName, + Pversion: r.version, + } + *mock.DowngradeStackPackCalls = append(*mock.DowngradeStackPackCalls, p) + return mock.DowngradeStackPackResponse.Result, mock.DowngradeStackPackResponse.Response, mock.DowngradeStackPackResponse.Error +} + type ProvisionDetailsMockResponse struct { Result ProvisionResponse Response *http.Response diff --git a/generated/stackstate_api/docs/StackpackApi.md b/generated/stackstate_api/docs/StackpackApi.md index 12fa4045..990863dd 100644 --- a/generated/stackstate_api/docs/StackpackApi.md +++ b/generated/stackstate_api/docs/StackpackApi.md @@ -5,6 +5,7 @@ All URIs are relative to *http://localhost* Method | HTTP request | Description ------------- | ------------- | ------------- [**ConfirmManualSteps**](StackpackApi.md#ConfirmManualSteps) | **Post** /stackpack/{stackPackName}/confirm-manual-steps/{stackPackInstanceId} | Confirm manual steps +[**DowngradeStackPack**](StackpackApi.md#DowngradeStackPack) | **Post** /stackpack/{stackPackName}/downgrade | Downgrade API [**ProvisionDetails**](StackpackApi.md#ProvisionDetails) | **Post** /stackpack/{stackPackName}/provision | Provision API [**ProvisionUninstall**](StackpackApi.md#ProvisionUninstall) | **Post** /stackpack/{stackPackName}/deprovision/{stackPackInstanceId} | Provision API [**StackPackDeleteVersion**](StackpackApi.md#StackPackDeleteVersion) | **Delete** /stackpack/{stackPackName}/versions/{version} | Delete a StackPack version @@ -90,6 +91,78 @@ Name | Type | Description | Notes [[Back to README]](../README.md) +## DowngradeStackPack + +> string DowngradeStackPack(ctx, stackPackName).Version(version).Execute() + +Downgrade API + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "./openapi" +) + +func main() { + stackPackName := "stackPackName_example" // string | + version := "version_example" // string | Version string (e.g. '1.2.3') to downgrade to. Must be lower than the currently installed version. + + configuration := openapiclient.NewConfiguration() + apiClient := openapiclient.NewAPIClient(configuration) + resp, r, err := apiClient.StackpackApi.DowngradeStackPack(context.Background(), stackPackName).Version(version).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `StackpackApi.DowngradeStackPack``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + // response from `DowngradeStackPack`: string + fmt.Fprintf(os.Stdout, "Response from `StackpackApi.DowngradeStackPack`: %v\n", resp) +} +``` + +### Path Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- +**ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. +**stackPackName** | **string** | | + +### Other Parameters + +Other parameters are passed through a pointer to a apiDowngradeStackPackRequest struct via the builder pattern + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + **version** | **string** | Version string (e.g. '1.2.3') to downgrade to. Must be lower than the currently installed version. | + +### Return type + +**string** + +### Authorization + +[ApiToken](../README.md#ApiToken), [ServiceBearer](../README.md#ServiceBearer), [ServiceToken](../README.md#ServiceToken) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + + ## ProvisionDetails > ProvisionResponse ProvisionDetails(ctx, stackPackName).Unlocked(unlocked).Version(version).RequestBody(requestBody).Execute() diff --git a/stackstate_openapi/openapi_version b/stackstate_openapi/openapi_version index 1782a5bb..136c0993 100644 --- a/stackstate_openapi/openapi_version +++ b/stackstate_openapi/openapi_version @@ -1 +1 @@ -3889f3a46f6a2405349b35221fb60aad09eafe14 \ No newline at end of file +c422b1e0a911adccd77f979c713b670a2ec0bd8c \ No newline at end of file From b76a41ee12f1a3671f355ff0c917317125a63bda Mon Sep 17 00:00:00 2001 From: Alejandro Acevedo Date: Mon, 27 Jul 2026 09:55:27 +0200 Subject: [PATCH 3/4] STAC-23211: Fix lint --- cmd/stackpack/common.go | 58 +++++++++++++++++++++++++++ cmd/stackpack/stackpack_downgrade.go | 55 +------------------------- cmd/stackpack/stackpack_upgrade.go | 59 +--------------------------- 3 files changed, 61 insertions(+), 111 deletions(-) diff --git a/cmd/stackpack/common.go b/cmd/stackpack/common.go index 6e501b46..c9901340 100644 --- a/cmd/stackpack/common.go +++ b/cmd/stackpack/common.go @@ -10,6 +10,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 ( @@ -118,6 +119,63 @@ func (w *OperationWaiter) WaitForCompletion(options WaitOptions) error { } } +// waitAndDisplayResult waits for a StackPack operation to complete, then displays the final status. +// operationLabel is used in progress/success messages (e.g. "upgrade" or "downgrade"). +func waitAndDisplayResult(cli *di.Deps, api *stackstate_api.APIClient, stackPackName string, timeout time.Duration, operationLabel string) common.CLIError { + if !cli.IsJson() { + cli.Printer.PrintLn("Waiting for " + operationLabel + " to complete...") + } + + waiter := NewOperationWaiter(cli, api) + if waitErr := waiter.WaitForCompletion(WaitOptions{ + StackPackName: stackPackName, + Timeout: timeout, + PollInterval: DefaultPollInterval, + }); waitErr != nil { + return common.NewRuntimeError(waitErr) + } + + stackPackList, cliErr := fetchAllStackPacks(cli, api) + if cliErr != nil { + return cliErr + } + + finalStackPack, err := findStackPackByName(stackPackList, stackPackName) + if err != nil { + return common.NewNotFoundError(err) + } + + if cli.IsJson() { + cli.Printer.PrintJson(map[string]interface{}{ + "stackpack": finalStackPack, + "status": "completed", + "current-version": finalStackPack.GetVersion(), + }) + } else { + cli.Printer.Success("StackPack " + operationLabel + " completed successfully") + + data := make([][]interface{}, 0) + for _, config := range finalStackPack.GetConfigurations() { + lastUpdateTime := time.UnixMilli(config.GetLastUpdateTimestamp()) + data = append(data, []interface{}{ + config.GetId(), + finalStackPack.GetName(), + config.GetStatus(), + config.GetStackPackVersion(), + lastUpdateTime, + }) + } + + cli.Printer.Table(printer.TableData{ + Header: []string{"id", "name", "status", "version", "last updated"}, + Data: data, + MissingTableDataMsg: printer.NotFoundMsg{Types: "configurations for " + stackPackName}, + }) + } + + return nil +} + func findStackPackByName(stacks []stackstate_api.FullStackPack, name string) (stackstate_api.FullStackPack, error) { for _, v := range stacks { if v.GetName() == name { diff --git a/cmd/stackpack/stackpack_downgrade.go b/cmd/stackpack/stackpack_downgrade.go index 6b4d22be..3fb2c885 100644 --- a/cmd/stackpack/stackpack_downgrade.go +++ b/cmd/stackpack/stackpack_downgrade.go @@ -8,7 +8,6 @@ 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" ) type DowngradeArgs struct { @@ -35,7 +34,7 @@ sts stackpack downgrade --name kubernetes --stackpack-version 1.2.3 --wait`, } common.AddRequiredNameFlagVar(cmd, &args.TypeName, "Name of the StackPack") cmd.Flags().StringVar(&args.Version, StackpackVersionFlag, "", "Version to downgrade to") - cmd.MarkFlagRequired(StackpackVersionFlag) + cmd.MarkFlagRequired(StackpackVersionFlag) //nolint:errcheck cmd.Flags().BoolVar(&args.Wait, "wait", false, "Wait for downgrade to complete") cmd.Flags().DurationVar(&args.Timeout, "timeout", DefaultTimeout, "Timeout for waiting") return cmd @@ -56,59 +55,9 @@ func RunStackpackDowngradeCommand(args *DowngradeArgs) di.CmdWithApiFn { } if args.Wait { - if !cli.IsJson() { - cli.Printer.PrintLn("Waiting for downgrade to complete...") - } - - waiter := NewOperationWaiter(cli, api) - waitErr := waiter.WaitForCompletion(WaitOptions{ - StackPackName: args.TypeName, - Timeout: args.Timeout, - PollInterval: DefaultPollInterval, - }) - if waitErr != nil { - return common.NewRuntimeError(waitErr) - } - - stackPackList, cliErr := fetchAllStackPacks(cli, api) - if cliErr != nil { + if cliErr := waitAndDisplayResult(cli, api, args.TypeName, args.Timeout, "downgrade"); cliErr != nil { return cliErr } - - finalStackPack, err := findStackPackByName(stackPackList, args.TypeName) - if err != nil { - return common.NewNotFoundError(err) - } - - if cli.IsJson() { - cli.Printer.PrintJson(map[string]interface{}{ - "stackpack": finalStackPack, - "status": "completed", - "current-version": finalStackPack.GetVersion(), - }) - } else { - cli.Printer.Success("StackPack downgrade completed successfully") - - data := make([][]interface{}, 0) - for _, config := range finalStackPack.GetConfigurations() { - lastUpdateTime := time.UnixMilli(config.GetLastUpdateTimestamp()) - data = append(data, []interface{}{ - config.GetId(), - finalStackPack.GetName(), - config.GetStatus(), - config.GetStackPackVersion(), - lastUpdateTime, - }) - } - - cli.Printer.Table( - printer.TableData{ - Header: []string{"id", "name", "status", "version", "last updated"}, - Data: data, - MissingTableDataMsg: printer.NotFoundMsg{Types: "configurations for " + args.TypeName}, - }, - ) - } } else { if cli.IsJson() { cli.Printer.PrintJson(map[string]interface{}{ diff --git a/cmd/stackpack/stackpack_upgrade.go b/cmd/stackpack/stackpack_upgrade.go index 588556df..c1d85da8 100644 --- a/cmd/stackpack/stackpack_upgrade.go +++ b/cmd/stackpack/stackpack_upgrade.go @@ -11,7 +11,6 @@ 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" ) var ( @@ -73,66 +72,10 @@ func RunStackpackUpgradeCommand(args *UpgradeArgs) di.CmdWithApiFn { return common.NewResponseError(err, resp) } - // Wait functionality: monitor upgrade until completion if args.Wait { - if !cli.IsJson() { - cli.Printer.PrintLn("Waiting for upgrade to complete...") - } - - // Use OperationWaiter to poll until all configurations are upgraded - waiter := NewOperationWaiter(cli, api) - waitErr := waiter.WaitForCompletion(WaitOptions{ - StackPackName: args.TypeName, - Timeout: args.Timeout, - PollInterval: DefaultPollInterval, - }) - if waitErr != nil { - // Use NewRuntimeError to avoid showing usage on operation failures - return common.NewRuntimeError(waitErr) - } - - // Re-fetch final status for display after successful completion - stackPackList, cliErr := fetchAllStackPacks(cli, api) - if cliErr != nil { + if cliErr := waitAndDisplayResult(cli, api, args.TypeName, args.Timeout, "upgrade"); cliErr != nil { return cliErr } - - finalStackPack, err := findStackPackByName(stackPackList, args.TypeName) - if err != nil { - return common.NewNotFoundError(err) - } - - // Display final status - if cli.IsJson() { - cli.Printer.PrintJson(map[string]interface{}{ - "stackpack": finalStackPack, - "status": "completed", - "current-version": finalStackPack.GetVersion(), - }) - } else { - cli.Printer.Success("StackPack upgrade completed successfully") - - // Show configurations status - data := make([][]interface{}, 0) - for _, config := range finalStackPack.GetConfigurations() { - lastUpdateTime := time.UnixMilli(config.GetLastUpdateTimestamp()) - data = append(data, []interface{}{ - config.GetId(), - finalStackPack.GetName(), - config.GetStatus(), - config.GetStackPackVersion(), - lastUpdateTime, - }) - } - - cli.Printer.Table( - printer.TableData{ - Header: []string{"id", "name", "status", "version", "last updated"}, - Data: data, - MissingTableDataMsg: printer.NotFoundMsg{Types: "configurations for " + args.TypeName}, - }, - ) - } } else { if cli.IsJson() { cli.Printer.PrintJson(map[string]interface{}{ From 070b9ad305304b53a629a8c2bd15607e122cc926 Mon Sep 17 00:00:00 2001 From: Alejandro Acevedo Date: Mon, 27 Jul 2026 13:34:00 +0200 Subject: [PATCH 4/4] STAC-23211: Bump openapi --- stackstate_openapi/openapi_version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stackstate_openapi/openapi_version b/stackstate_openapi/openapi_version index 136c0993..7853d414 100644 --- a/stackstate_openapi/openapi_version +++ b/stackstate_openapi/openapi_version @@ -1 +1 @@ -c422b1e0a911adccd77f979c713b670a2ec0bd8c \ No newline at end of file +3c7d65fd15dfbf8ad4e503bb2ef99c1e58eadcbf \ No newline at end of file