Skip to content
Open
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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ require (
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v1.15.0
github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.24.2
github.com/stackitcloud/stackit-sdk-go/services/runcommand v1.10.1
github.com/stackitcloud/stackit-sdk-go/services/sca v0.1.0
github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.19.1
github.com/stackitcloud/stackit-sdk-go/services/serverbackup v1.7.1
github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.5.5
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,8 @@ github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.24.2 h1:4UxxJ
github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.24.2/go.mod h1:NEz3f+GV5G++BE9/MmZCsXJyCih7jtg0pZuSyG2sLEs=
github.com/stackitcloud/stackit-sdk-go/services/runcommand v1.10.1 h1:+0dl8HjYKEdhUZEr/7WcugPhNoZzf9PBnsQWJBDLtaY=
github.com/stackitcloud/stackit-sdk-go/services/runcommand v1.10.1/go.mod h1:fQdqal+Jduk/DY5UUHCefHn5wsRN6uROEFA/O7HObig=
github.com/stackitcloud/stackit-sdk-go/services/sca v0.1.0 h1:P8ubDswgf9cXmIpRWIhaHePZYak7UIYTFF3eL4Qw9v8=
github.com/stackitcloud/stackit-sdk-go/services/sca v0.1.0/go.mod h1:SB1HA3WwYYOBbWjZ/LhZR/SKoY+3qLJFXWNea5S/Vgg=
github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.19.1 h1:Mym5vqa3YaF42LbqlVXDFYf/evsNU76l0wpUPGEu+0U=
github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.19.1/go.mod h1:oFbKTkCFtp/RVjTeuv5caSuuEnf3/JG8zRDB4nbLYes=
github.com/stackitcloud/stackit-sdk-go/services/serverbackup v1.7.1 h1:lU6kQgv2tWLKDkhBRhS3v9BSFoUwHAcUOkHEa52kPVA=
Expand Down
40 changes: 40 additions & 0 deletions internal/cmd/alpha/alpha.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package alpha

import (
"fmt"

"github.com/stackitcloud/stackit-cli/internal/cmd/alpha/sca"
"github.com/stackitcloud/stackit-cli/internal/pkg/types"

"github.com/stackitcloud/stackit-cli/internal/pkg/args"
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"

"github.com/spf13/cobra"
)

func NewCmd(params *types.CmdParams) *cobra.Command {
cmd := &cobra.Command{
Use: "alpha",
Short: "Contains alpha STACKIT CLI commands",
Long: fmt.Sprintf("%s\n%s",
"Contains beta STACKIT CLI commands.",
"The commands under this group are still in an alpha state, and functionality may be incomplete or have breaking changes."),
Args: args.NoArgs,
Run: utils.CmdHelp,
Example: examples.Build(
examples.NewExample(
"See the currently available beta commands",
"$ stackit alpha --help"),
examples.NewExample(
"Execute a alpha command",
"$ stackit alpha MY_COMMAND"),
),
}
addSubcommands(cmd, params)
return cmd
}

func addSubcommands(cmd *cobra.Command, params *types.CmdParams) {
cmd.AddCommand(sca.NewCmd(params))
}
40 changes: 40 additions & 0 deletions internal/cmd/alpha/sca/application/application.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package application

import (
"github.com/spf13/cobra"

"github.com/stackitcloud/stackit-cli/internal/cmd/alpha/sca/application/create"
createfrompayload "github.com/stackitcloud/stackit-cli/internal/cmd/alpha/sca/application/create-from-payload"
"github.com/stackitcloud/stackit-cli/internal/cmd/alpha/sca/application/delete"
"github.com/stackitcloud/stackit-cli/internal/cmd/alpha/sca/application/describe"
generatepayload "github.com/stackitcloud/stackit-cli/internal/cmd/alpha/sca/application/generate-payload"
"github.com/stackitcloud/stackit-cli/internal/cmd/alpha/sca/application/list"
"github.com/stackitcloud/stackit-cli/internal/cmd/alpha/sca/application/update"
updatefrompayload "github.com/stackitcloud/stackit-cli/internal/cmd/alpha/sca/application/update-from-payload"
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
"github.com/stackitcloud/stackit-cli/internal/pkg/types"
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
)

func NewCmd(params *types.CmdParams) *cobra.Command {
cmd := &cobra.Command{
Use: "application",
Short: "Provides functionality for SCA applications",
Long: "Provides functionality for STACKIT Container Applications (SCA) cluster.",
Args: args.NoArgs,
Run: utils.CmdHelp,
}
addSubcommands(cmd, params)
return cmd
}

func addSubcommands(cmd *cobra.Command, params *types.CmdParams) {
cmd.AddCommand(list.NewCmd(params))
cmd.AddCommand(describe.NewCmd(params))
cmd.AddCommand(create.NewCmd(params))
cmd.AddCommand(createfrompayload.NewCmd(params))
cmd.AddCommand(delete.NewCmd(params))
cmd.AddCommand(generatepayload.NewCmd(params))
cmd.AddCommand(update.NewCmd(params))
cmd.AddCommand(updatefrompayload.NewCmd(params))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package createfrompayload

import (
"context"
"encoding/json"
"fmt"

"github.com/spf13/cobra"
sca "github.com/stackitcloud/stackit-sdk-go/services/sca/v1alphaapi"
"github.com/stackitcloud/stackit-sdk-go/services/sca/v1alphaapi/wait"

"github.com/stackitcloud/stackit-cli/internal/pkg/args"
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-cli/internal/pkg/services/sca/client"
"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
"github.com/stackitcloud/stackit-cli/internal/pkg/types"
)

const (
environmentIDFlag = "environment-id"
nameFlag = "name"
payloadFlag = "payload"
)

type inputModel struct {
*globalflags.GlobalFlagModel
EnvironmentID string
Payload *sca.CreateApplicationPayload
}

func NewCmd(params *types.CmdParams) *cobra.Command {
cmd := &cobra.Command{
Use: "create-from-payload",
Short: "Create a SCA application from payload",
Long: fmt.Sprintf("%s\n%s\n%s",
"Create a STACKIT Container Applications (SCA) application from payload.",
`The payload can be provided as a JSON string or a file path prefixed with "@".`,
"See https://docs.api.stackit.cloud/documentation/sca/version/v1alpha#tag/Applications/operation/Applications_CreateApplication for information regarding the payload structure.",
),
Args: args.NoArgs,
Example: examples.Build(
examples.NewExample(
`Create a SCA application using an API payload sourced from the file "./payload.json"`,
"$ stackit alpha sca cluster create-from-payload --name application-name --payload @./payload.json"),
examples.NewExample(
`Create a SCA application using an API payload provided as a JSON string`,
`$ stackit alpha sca cluster create-from-payload --name application-name --payload "{...}"`),
examples.NewExample(
`Generate a payload with default values, and adapt it with custom values for the different configuration options`,
`$ stackit alpha sca application generate-payload --file-path ./payload.json`,
`<Modify payload in file, if needed>`,
`$ stackit alpha sca application create-from-payload --name application-name --payload @./payload.json`),
),
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := context.Background()

model, err := parseInput(params.Printer, cmd, nil)
if err != nil {
return err
}

// Configure API client
apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
if err != nil {
return err
}

globalFlags := globalflags.Parse(params.Printer, cmd)
if globalFlags.ProjectId == "" {
return &errors.ProjectIdError{}
}

// Call API
req := buildRequest(ctx, model, apiClient)
resp, err := req.Execute()
if err != nil {
return fmt.Errorf("create application: %w", err)
}

if !model.Async {
err := spinner.Run(params.Printer, fmt.Sprintf("Creating application with id %q", resp.GetId()), func() error {
_, err := wait.CreateApplicationWaitHandler(ctx, apiClient.DefaultAPI, model.ProjectId, model.EnvironmentID, resp.GetId()).WaitWithContext(ctx)
return err
})
if err != nil {
return fmt.Errorf("wait for application creation: %w", err)
}
}

return outputResult(params.Printer, model, resp)
},
}

configureFlags(cmd)
return cmd
}

func buildRequest(ctx context.Context, model *inputModel, apiClient *sca.APIClient) sca.ApiCreateApplicationRequest {
return apiClient.DefaultAPI.CreateApplication(ctx, model.ProjectId, model.EnvironmentID).
CreateApplicationPayload(*model.Payload)
}

func outputResult(p *print.Printer, model *inputModel, application *sca.Application) error {
if application == nil {
return fmt.Errorf("create application response is empty")
}

return p.OutputResult(model.OutputFormat, application, func() error {
operationState := "Created"
if model.Async {
operationState = "Triggered creation of"
}
p.Outputf("%s application for environment %s. Application ID: %s\n", operationState, application.GetEnvironmentId(), application.GetId())
return nil
})
}

func configureFlags(cmd *cobra.Command) {
cmd.Flags().Var(flags.UUIDFlag(), environmentIDFlag, "Environment ID (uses default environment if not set)")
cmd.Flags().String(nameFlag, "", "Application display name")
cmd.Flags().Var(flags.ReadFromFileFlag(), payloadFlag, `Request payload (JSON). Can be a string or a file path, if prefixed with "@" (example: @./payload.json). If unset, will use a default payload (you can check it by running "stackit sca application generate-payload")`)

cobra.CheckErr(flags.MarkFlagsRequired(cmd, nameFlag))
}

func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) {
globalFlags := globalflags.Parse(p, cmd)
if globalFlags.ProjectId == "" {
return nil, &errors.ProjectIdError{}
}

environmentID := flags.FlagToStringValue(p, cmd, environmentIDFlag)
if environmentID == "" {
environmentID = globalFlags.ProjectId
}

payloadValue := flags.FlagToStringPointer(p, cmd, payloadFlag)
var payload *sca.CreateApplicationPayload
if payloadValue != nil {
payload = &sca.CreateApplicationPayload{}
err := json.Unmarshal([]byte(*payloadValue), payload)
if err != nil {
return nil, fmt.Errorf("encode payload: %w", err)
}
}

if payload == nil {
return nil, &errors.FlagValidationError{
Flag: payloadFlag,
Details: "Received empty payload",
}
}
Comment on lines +141 to +156

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs state default payload is used when none is set - this is not the case


payload.DisplayName = flags.FlagToStringValue(p, cmd, nameFlag)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

causes nil pointer dereference when no paylaod is set


model := inputModel{
GlobalFlagModel: globalFlags,
EnvironmentID: environmentID,
Payload: payload,
}

p.DebugInputModel(model)
return &model, nil
}
Loading