Skip to content

Commit 668a4b3

Browse files
authored
Onboard Argus (instance): create command and utils (#133)
* Onboard Argus instance: create command init * create command: complete functionality and start testing * create command: added testing for utils * update utils error message * Add custom Argus error and fix create command testing * fix error formatting in utils * fix error formatting
1 parent 72b837c commit 668a4b3

10 files changed

Lines changed: 961 additions & 0 deletions

File tree

docs/stackit_argus.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ stackit argus [flags]
2828
### SEE ALSO
2929

3030
* [stackit](./stackit.md) - Manage STACKIT resources using the command line
31+
* [stackit argus instance](./stackit_argus_instance.md) - Provides functionality for Argus instances
3132
* [stackit argus plans](./stackit_argus_plans.md) - Lists all Argus service plans
3233

docs/stackit_argus_instance.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## stackit argus instance
2+
3+
Provides functionality for Argus instances
4+
5+
### Synopsis
6+
7+
Provides functionality for Argus instances.
8+
9+
```
10+
stackit argus instance [flags]
11+
```
12+
13+
### Options
14+
15+
```
16+
-h, --help Help for "stackit argus instance"
17+
```
18+
19+
### Options inherited from parent commands
20+
21+
```
22+
-y, --assume-yes If set, skips all confirmation prompts
23+
--async If set, runs the command asynchronously
24+
-o, --output-format string Output format, one of ["json" "pretty"]
25+
-p, --project-id string Project ID
26+
```
27+
28+
### SEE ALSO
29+
30+
* [stackit argus](./stackit_argus.md) - Provides functionality for Argus
31+
* [stackit argus instance create](./stackit_argus_instance_create.md) - Creates an Argus instance
32+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
## stackit argus instance create
2+
3+
Creates an Argus instance
4+
5+
### Synopsis
6+
7+
Creates an Argus instance.
8+
9+
```
10+
stackit argus instance create [flags]
11+
```
12+
13+
### Examples
14+
15+
```
16+
Create an Argus instance with name "my-instance" and specify plan by name
17+
$ stackit argus instance create --name my-instance --plan-name Monitoring-Starter-EU01
18+
19+
Create an Argus instance with name "my-instance" and specify plan by ID
20+
$ stackit argus instance create --name my-instance --plan-id xxx
21+
```
22+
23+
### Options
24+
25+
```
26+
-h, --help Help for "stackit argus instance create"
27+
-n, --name string Instance name
28+
--plan-id string Plan ID
29+
--plan-name string Plan name
30+
```
31+
32+
### Options inherited from parent commands
33+
34+
```
35+
-y, --assume-yes If set, skips all confirmation prompts
36+
--async If set, runs the command asynchronously
37+
-o, --output-format string Output format, one of ["json" "pretty"]
38+
-p, --project-id string Project ID
39+
```
40+
41+
### SEE ALSO
42+
43+
* [stackit argus instance](./stackit_argus_instance.md) - Provides functionality for Argus instances
44+

internal/cmd/argus/argus.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package argus
22

33
import (
4+
"github.com/stackitcloud/stackit-cli/internal/cmd/argus/instance"
45
"github.com/stackitcloud/stackit-cli/internal/cmd/argus/plans"
56
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
67
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
@@ -22,4 +23,5 @@ func NewCmd() *cobra.Command {
2223

2324
func addSubcommands(cmd *cobra.Command) {
2425
cmd.AddCommand(plans.NewCmd())
26+
cmd.AddCommand(instance.NewCmd())
2527
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package create
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
9+
"github.com/stackitcloud/stackit-cli/internal/pkg/confirm"
10+
cliErr "github.com/stackitcloud/stackit-cli/internal/pkg/errors"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
14+
"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
15+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/argus/client"
16+
argusUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/argus/utils"
17+
"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
18+
19+
"github.com/spf13/cobra"
20+
"github.com/stackitcloud/stackit-sdk-go/services/argus"
21+
"github.com/stackitcloud/stackit-sdk-go/services/argus/wait"
22+
)
23+
24+
const (
25+
instanceNameFlag = "name"
26+
planIdFlag = "plan-id"
27+
planNameFlag = "plan-name"
28+
)
29+
30+
type inputModel struct {
31+
*globalflags.GlobalFlagModel
32+
PlanName string
33+
34+
InstanceName *string
35+
PlanId *string
36+
}
37+
38+
func NewCmd() *cobra.Command {
39+
cmd := &cobra.Command{
40+
Use: "create",
41+
Short: "Creates an Argus instance",
42+
Long: "Creates an Argus instance.",
43+
Args: args.NoArgs,
44+
Example: examples.Build(
45+
examples.NewExample(
46+
`Create an Argus instance with name "my-instance" and specify plan by name`,
47+
"$ stackit argus instance create --name my-instance --plan-name Monitoring-Starter-EU01"),
48+
examples.NewExample(
49+
`Create an Argus instance with name "my-instance" and specify plan by ID`,
50+
"$ stackit argus instance create --name my-instance --plan-id xxx"),
51+
),
52+
RunE: func(cmd *cobra.Command, args []string) error {
53+
ctx := context.Background()
54+
model, err := parseInput(cmd)
55+
if err != nil {
56+
return err
57+
}
58+
59+
// Configure API client
60+
apiClient, err := client.ConfigureClient(cmd)
61+
if err != nil {
62+
return err
63+
}
64+
65+
projectLabel, err := projectname.GetProjectName(ctx, cmd)
66+
if err != nil {
67+
projectLabel = model.ProjectId
68+
}
69+
70+
if !model.AssumeYes {
71+
prompt := fmt.Sprintf("Are you sure you want to create an Argus instance for project %q?", projectLabel)
72+
err = confirm.PromptForConfirmation(cmd, prompt)
73+
if err != nil {
74+
return err
75+
}
76+
}
77+
78+
// Call API
79+
req, err := buildRequest(ctx, model, apiClient)
80+
if err != nil {
81+
var argusInvalidPlanError *cliErr.ArgusInvalidPlanError
82+
if !errors.As(err, &argusInvalidPlanError) {
83+
return fmt.Errorf("build Argus instance creation request: %w", err)
84+
}
85+
return err
86+
}
87+
resp, err := req.Execute()
88+
if err != nil {
89+
return fmt.Errorf("create Argus instance: %w", err)
90+
}
91+
instanceId := *resp.InstanceId
92+
93+
// Wait for async operation, if async mode not enabled
94+
if !model.Async {
95+
s := spinner.New(cmd)
96+
s.Start("Creating instance")
97+
_, err = wait.CreateInstanceWaitHandler(ctx, apiClient, instanceId, model.ProjectId).WaitWithContext(ctx)
98+
if err != nil {
99+
return fmt.Errorf("wait for Argus instance creation: %w", err)
100+
}
101+
s.Stop()
102+
}
103+
104+
operationState := "Created"
105+
if model.Async {
106+
operationState = "Triggered creation of"
107+
}
108+
cmd.Printf("%s instance for project %q. Instance ID: %s\n", operationState, projectLabel, instanceId)
109+
return nil
110+
},
111+
}
112+
configureFlags(cmd)
113+
return cmd
114+
}
115+
116+
func configureFlags(cmd *cobra.Command) {
117+
cmd.Flags().StringP(instanceNameFlag, "n", "", "Instance name")
118+
cmd.Flags().Var(flags.UUIDFlag(), planIdFlag, "Plan ID")
119+
cmd.Flags().String(planNameFlag, "", "Plan name")
120+
121+
err := flags.MarkFlagsRequired(cmd, instanceNameFlag)
122+
cobra.CheckErr(err)
123+
}
124+
125+
func parseInput(cmd *cobra.Command) (*inputModel, error) {
126+
globalFlags := globalflags.Parse(cmd)
127+
if globalFlags.ProjectId == "" {
128+
return nil, &cliErr.ProjectIdError{}
129+
}
130+
131+
planId := flags.FlagToStringPointer(cmd, planIdFlag)
132+
planName := flags.FlagToStringValue(cmd, planNameFlag)
133+
134+
if planId == nil && (planName == "") {
135+
return nil, &cliErr.ArgusInputPlanError{
136+
Cmd: cmd,
137+
}
138+
}
139+
if planId != nil && (planName != "") {
140+
return nil, &cliErr.ArgusInputPlanError{
141+
Cmd: cmd,
142+
}
143+
}
144+
145+
return &inputModel{
146+
GlobalFlagModel: globalFlags,
147+
InstanceName: flags.FlagToStringPointer(cmd, instanceNameFlag),
148+
PlanId: planId,
149+
PlanName: planName,
150+
}, nil
151+
}
152+
153+
type argusClient interface {
154+
CreateInstance(ctx context.Context, projectId string) argus.ApiCreateInstanceRequest
155+
ListPlansExecute(ctx context.Context, projectId string) (*argus.PlansResponse, error)
156+
}
157+
158+
func buildRequest(ctx context.Context, model *inputModel, apiClient argusClient) (argus.ApiCreateInstanceRequest, error) {
159+
req := apiClient.CreateInstance(ctx, model.ProjectId)
160+
161+
var planId *string
162+
var err error
163+
164+
plans, err := apiClient.ListPlansExecute(ctx, model.ProjectId)
165+
if err != nil {
166+
return req, fmt.Errorf("get Argus plans: %w", err)
167+
}
168+
169+
if model.PlanId == nil {
170+
planId, err = argusUtils.LoadPlanId(model.PlanName, plans)
171+
if err != nil {
172+
var argusInvalidPlanError *cliErr.ArgusInvalidPlanError
173+
if !errors.As(err, &argusInvalidPlanError) {
174+
return req, fmt.Errorf("load plan ID: %w", err)
175+
}
176+
return req, err
177+
}
178+
} else {
179+
err := argusUtils.ValidatePlanId(*model.PlanId, plans)
180+
if err != nil {
181+
return req, err
182+
}
183+
planId = model.PlanId
184+
}
185+
186+
req = req.CreateInstancePayload(argus.CreateInstancePayload{
187+
Name: model.InstanceName,
188+
PlanId: planId,
189+
})
190+
return req, nil
191+
}

0 commit comments

Comments
 (0)