Skip to content

Commit a668fe3

Browse files
committed
feat(alb): added target pool update
1 parent 4e74953 commit a668fe3

File tree

7 files changed

+525
-3
lines changed

7 files changed

+525
-3
lines changed

internal/cmd/beta/alb/alb.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/alb/delete"
66
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/alb/describe"
77
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/alb/list"
8+
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/alb/pool"
89
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/alb/template"
910
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/alb/update"
1011
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
@@ -35,5 +36,6 @@ func addSubcommands(cmd *cobra.Command, p *print.Printer) {
3536
update.NewCmd(p),
3637
describe.NewCmd(p),
3738
delete.NewCmd(p),
39+
pool.NewCmd(p),
3840
)
3941
}

internal/cmd/beta/alb/pool/pool.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package pool
2+
3+
import (
4+
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/alb/pool/update"
5+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
6+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
7+
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
9+
10+
"github.com/spf13/cobra"
11+
)
12+
13+
func NewCmd(p *print.Printer) *cobra.Command {
14+
cmd := &cobra.Command{
15+
Use: "pool",
16+
Short: "Manages target pools for application loadbalancers",
17+
Long: "Manage the lifecycle of target pools for application loadbalancers.",
18+
Args: args.NoArgs,
19+
Run: utils.CmdHelp,
20+
}
21+
addSubcommands(cmd, p)
22+
return cmd
23+
}
24+
25+
func addSubcommands(cmd *cobra.Command, p *print.Printer) {
26+
cmd.AddCommand(update.NewCmd(p))
27+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"activeHealthCheck": {
3+
"healthyThreshold": 1,
4+
"httpHealthChecks": {
5+
"okStatuses": [
6+
"string"
7+
],
8+
"path": "string"
9+
},
10+
"interval": "3s",
11+
"intervalJitter": "3s",
12+
"timeout": "3s",
13+
"unhealthyThreshold": 1
14+
},
15+
"name": "my-target-pool",
16+
"targetPort": 5732,
17+
"targets": [
18+
{
19+
"displayName": "my-target",
20+
"ip": "192.0.2.5"
21+
}
22+
],
23+
"tlsConfig": {
24+
"customCa": "string",
25+
"enabled": true,
26+
"skipCertificateValidation": true
27+
}
28+
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package update
2+
3+
import (
4+
"bufio"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
"os"
9+
"strings"
10+
11+
"github.com/goccy/go-yaml"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
14+
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
15+
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
16+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
17+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
18+
"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
19+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/alb/client"
20+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
21+
22+
"github.com/spf13/cobra"
23+
"github.com/stackitcloud/stackit-sdk-go/services/alb"
24+
)
25+
26+
const (
27+
configurationFlag = "configuration"
28+
albNameFlag = "name"
29+
poolNameFlag = "pool"
30+
)
31+
32+
type inputModel struct {
33+
*globalflags.GlobalFlagModel
34+
Configuration *string
35+
AlbName *string
36+
Poolname *string
37+
}
38+
39+
func NewCmd(p *print.Printer) *cobra.Command {
40+
cmd := &cobra.Command{
41+
Use: "update",
42+
Short: "Updates an application target pool",
43+
Long: "Updates an application target pool.",
44+
Args: args.NoArgs,
45+
Example: examples.Build(
46+
examples.NewExample(
47+
`Update an application target pool from a configuration file`,
48+
"$ stackit beta alb update --configuration my-target pool.json"),
49+
),
50+
RunE: func(cmd *cobra.Command, _ []string) error {
51+
ctx := context.Background()
52+
model, err := parseInput(p, cmd)
53+
if err != nil {
54+
return err
55+
}
56+
57+
// Configure API client
58+
apiClient, err := client.ConfigureClient(p)
59+
if err != nil {
60+
return err
61+
}
62+
63+
projectLabel, err := projectname.GetProjectName(ctx, p, cmd)
64+
if err != nil {
65+
p.Debug(print.ErrorLevel, "get project name: %v", err)
66+
projectLabel = model.ProjectId
67+
}
68+
69+
if !model.AssumeYes {
70+
prompt := fmt.Sprintf("Are you sure you want to update an application target pool for project %q?", projectLabel)
71+
err = p.PromptForConfirmation(prompt)
72+
if err != nil {
73+
return err
74+
}
75+
}
76+
77+
// Call API
78+
req, err := buildRequest(ctx, model, apiClient)
79+
if err != nil {
80+
return err
81+
}
82+
resp, err := req.Execute()
83+
if err != nil {
84+
return fmt.Errorf("update application target pool: %w", err)
85+
}
86+
87+
return outputResult(p, model, projectLabel, resp)
88+
},
89+
}
90+
configureFlags(cmd)
91+
return cmd
92+
}
93+
94+
func configureFlags(cmd *cobra.Command) {
95+
cmd.Flags().StringP(configurationFlag, "c", "", "filename of the input configuration file")
96+
cmd.Flags().StringP(albNameFlag, "n", "", "name of the target pool name to update")
97+
cmd.Flags().StringP(poolNameFlag, "t", "", "name of the target pool to update")
98+
err := flags.MarkFlagsRequired(cmd, configurationFlag, albNameFlag, poolNameFlag)
99+
cobra.CheckErr(err)
100+
}
101+
102+
func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
103+
globalFlags := globalflags.Parse(p, cmd)
104+
if globalFlags.ProjectId == "" {
105+
return nil, &errors.ProjectIdError{}
106+
}
107+
108+
model := inputModel{
109+
GlobalFlagModel: globalFlags,
110+
Configuration: flags.FlagToStringPointer(p, cmd, configurationFlag),
111+
AlbName: flags.FlagToStringPointer(p, cmd, albNameFlag),
112+
Poolname: flags.FlagToStringPointer(p, cmd, poolNameFlag),
113+
}
114+
115+
if p.IsVerbosityDebug() {
116+
modelStr, err := print.BuildDebugStrFromInputModel(model)
117+
if err != nil {
118+
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
119+
} else {
120+
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
121+
}
122+
}
123+
124+
return &model, nil
125+
}
126+
127+
func buildRequest(ctx context.Context, model *inputModel, apiClient *alb.APIClient) (req alb.ApiUpdateTargetPoolRequest, err error) {
128+
payload, err := readPayload(ctx, model)
129+
if err != nil {
130+
return req, err
131+
}
132+
req = apiClient.UpdateTargetPool(ctx, model.ProjectId, model.Region, *model.AlbName, *model.Poolname)
133+
return req.UpdateTargetPoolPayload(payload), nil
134+
}
135+
136+
func readPayload(_ context.Context, model *inputModel) (payload alb.UpdateTargetPoolPayload, err error) {
137+
if model.Configuration == nil {
138+
return payload, fmt.Errorf("no configuration file defined")
139+
}
140+
file, err := os.Open(*model.Configuration)
141+
if err != nil {
142+
return payload, fmt.Errorf("cannot open configuration file %q: %w", *model.Configuration, err)
143+
}
144+
defer file.Close() // nolint:errcheck // at this point close errors are not relevant anymore
145+
146+
if strings.HasSuffix(*model.Configuration, ".yaml") {
147+
decoder := yaml.NewDecoder(bufio.NewReader(file), yaml.UseJSONUnmarshaler())
148+
if err := decoder.Decode(&payload); err != nil {
149+
return payload, fmt.Errorf("cannot deserialize yaml configuration from %q: %w", *model.Configuration, err)
150+
}
151+
} else if strings.HasSuffix(*model.Configuration, ".json") {
152+
decoder := json.NewDecoder(bufio.NewReader(file))
153+
if err := decoder.Decode(&payload); err != nil {
154+
return payload, fmt.Errorf("cannot deserialize json configuration from %q: %w", *model.Configuration, err)
155+
}
156+
} else {
157+
return payload, fmt.Errorf("cannot determine configuration fileformat of %q by extension. Must be '.json' or '.yaml'", *model.Configuration)
158+
}
159+
160+
return payload, nil
161+
}
162+
163+
func outputResult(p *print.Printer, model *inputModel, projectLabel string, resp *alb.TargetPool) error {
164+
if resp == nil {
165+
return fmt.Errorf("update target pool response is empty")
166+
}
167+
switch model.OutputFormat {
168+
case print.JSONOutputFormat:
169+
details, err := json.MarshalIndent(resp, "", " ")
170+
if err != nil {
171+
return fmt.Errorf("marshal target pool: %w", err)
172+
}
173+
p.Outputln(string(details))
174+
175+
return nil
176+
case print.YAMLOutputFormat:
177+
details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler())
178+
if err != nil {
179+
return fmt.Errorf("marshal target pool: %w", err)
180+
}
181+
p.Outputln(string(details))
182+
183+
return nil
184+
default:
185+
operationState := "Updated"
186+
if model.Async {
187+
operationState = "Triggered update of"
188+
}
189+
p.Outputf("%s application target pool for %q. Name: %s\n", operationState, projectLabel, utils.PtrString(resp.Name))
190+
return nil
191+
}
192+
}

0 commit comments

Comments
 (0)