Skip to content

Commit 48010fe

Browse files
Onboard Load Balancer generate-payload command (#250)
* onboard load-balancer generate-payload command * add go mod * generate docs * remove version from the default payload which will be used for create commands * Update description Co-authored-by: Vicente Pinto <vicente.pinto@freiheit.com> * Update description Co-authored-by: Vicente Pinto <vicente.pinto@freiheit.com> * split create and update payloads * move default create payload to a const * delete unused utils files * update docs * remove redundant var declaration --------- Co-authored-by: Vicente Pinto <vicente.pinto@freiheit.com>
1 parent e7fc0fe commit 48010fe

5 files changed

Lines changed: 459 additions & 2 deletions

File tree

docs/stackit_load-balancer.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ stackit load-balancer [flags]
2929
### SEE ALSO
3030

3131
* [stackit](./stackit.md) - Manage STACKIT resources using the command line
32+
* [stackit load-balancer generate-payload](./stackit_load-balancer_generate-payload.md) - Generates a payload to create/update a Load Balancer
3233

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
## stackit load-balancer generate-payload
2+
3+
Generates a payload to create/update a Load Balancer
4+
5+
### Synopsis
6+
7+
Generates a JSON payload with values to be used as --payload input for load balancer creation or update.
8+
See https://docs.api.stackit.cloud/documentation/load-balancer/version/v1#tag/Load-Balancer/operation/APIService_CreateLoadBalancer for information regarding the payload structure.
9+
10+
```
11+
stackit load-balancer generate-payload [flags]
12+
```
13+
14+
### Examples
15+
16+
```
17+
Generate a payload, and adapt it with custom values for the different configuration options
18+
$ stackit load-balancer generate-payload > ./payload.json
19+
<Modify payload in file, if needed>
20+
$ stackit load-balancer create --payload @./payload.json
21+
22+
Generate a payload with values of an existing load balancer, and adapt it with custom values for the different configuration options
23+
$ stackit load-balancer generate-payload --instance-name my-lb > ./payload.json
24+
<Modify payload in file>
25+
$ stackit load-balancer update my-lb --payload @./payload.json
26+
```
27+
28+
### Options
29+
30+
```
31+
-h, --help Help for "stackit load-balancer generate-payload"
32+
-n, --instance-name string If set, generates the payload with the current values of the given load balancer. If unset, generates the payload with empty values
33+
```
34+
35+
### Options inherited from parent commands
36+
37+
```
38+
-y, --assume-yes If set, skips all confirmation prompts
39+
--async If set, runs the command asynchronously
40+
-o, --output-format string Output format, one of ["json" "pretty" "none"]
41+
-p, --project-id string Project ID
42+
--verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
43+
```
44+
45+
### SEE ALSO
46+
47+
* [stackit load-balancer](./stackit_load-balancer.md) - Provides functionality for Load Balancer
48+
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package generatepayload
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
9+
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
10+
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
14+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/load-balancer/client"
15+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
16+
"github.com/stackitcloud/stackit-sdk-go/services/loadbalancer"
17+
18+
"github.com/spf13/cobra"
19+
)
20+
21+
const (
22+
instanceNameFlag = "instance-name"
23+
)
24+
25+
type inputModel struct {
26+
*globalflags.GlobalFlagModel
27+
InstanceName *string
28+
}
29+
30+
var (
31+
defaultPayloadListener = &loadbalancer.Listener{
32+
DisplayName: utils.Ptr(""),
33+
Name: utils.Ptr(""),
34+
Port: utils.Ptr(int64(0)),
35+
Protocol: utils.Ptr(""),
36+
ServerNameIndicators: &[]loadbalancer.ServerNameIndicator{
37+
{
38+
Name: utils.Ptr(""),
39+
},
40+
},
41+
TargetPool: utils.Ptr(""),
42+
Tcp: &loadbalancer.OptionsTCP{
43+
IdleTimeout: utils.Ptr(""),
44+
},
45+
Udp: &loadbalancer.OptionsUDP{
46+
IdleTimeout: utils.Ptr(""),
47+
},
48+
}
49+
50+
defaultPayloadNetwork = &loadbalancer.Network{
51+
NetworkId: utils.Ptr(""),
52+
Role: utils.Ptr(""),
53+
}
54+
55+
defaultPayloadTargetPool = &loadbalancer.TargetPool{
56+
ActiveHealthCheck: &loadbalancer.ActiveHealthCheck{
57+
HealthyThreshold: utils.Ptr(int64(0)),
58+
Interval: utils.Ptr(""),
59+
IntervalJitter: utils.Ptr(""),
60+
Timeout: utils.Ptr(""),
61+
UnhealthyThreshold: utils.Ptr(int64(0)),
62+
},
63+
Name: utils.Ptr(""),
64+
SessionPersistence: &loadbalancer.SessionPersistence{
65+
UseSourceIpAddress: utils.Ptr(false),
66+
},
67+
TargetPort: utils.Ptr(int64(0)),
68+
Targets: &[]loadbalancer.Target{
69+
{
70+
DisplayName: utils.Ptr(""),
71+
Ip: utils.Ptr(""),
72+
},
73+
},
74+
}
75+
76+
DefaultCreateLoadBalancerPayload = loadbalancer.CreateLoadBalancerPayload{
77+
ExternalAddress: utils.Ptr(""),
78+
Listeners: &[]loadbalancer.Listener{
79+
*defaultPayloadListener,
80+
},
81+
Name: utils.Ptr(""),
82+
Networks: &[]loadbalancer.Network{
83+
*defaultPayloadNetwork,
84+
},
85+
Options: &loadbalancer.LoadBalancerOptions{
86+
AccessControl: &loadbalancer.LoadbalancerOptionAccessControl{
87+
AllowedSourceRanges: &[]string{
88+
"",
89+
},
90+
},
91+
EphemeralAddress: utils.Ptr(false),
92+
Observability: &loadbalancer.LoadbalancerOptionObservability{
93+
Logs: &loadbalancer.LoadbalancerOptionLogs{
94+
CredentialsRef: utils.Ptr(""),
95+
PushUrl: utils.Ptr(""),
96+
},
97+
Metrics: &loadbalancer.LoadbalancerOptionMetrics{
98+
CredentialsRef: utils.Ptr(""),
99+
PushUrl: utils.Ptr(""),
100+
},
101+
},
102+
PrivateNetworkOnly: utils.Ptr(false),
103+
},
104+
PrivateAddress: utils.Ptr(""),
105+
TargetPools: &[]loadbalancer.TargetPool{
106+
*defaultPayloadTargetPool,
107+
},
108+
}
109+
)
110+
111+
func NewCmd(p *print.Printer) *cobra.Command {
112+
cmd := &cobra.Command{
113+
Use: "generate-payload",
114+
Short: "Generates a payload to create/update a Load Balancer",
115+
Long: fmt.Sprintf("%s\n%s",
116+
"Generates a JSON payload with values to be used as --payload input for load balancer creation or update.",
117+
"See https://docs.api.stackit.cloud/documentation/load-balancer/version/v1#tag/Load-Balancer/operation/APIService_CreateLoadBalancer for information regarding the payload structure.",
118+
),
119+
Args: args.NoArgs,
120+
Example: examples.Build(
121+
examples.NewExample(
122+
`Generate a payload, and adapt it with custom values for the different configuration options`,
123+
`$ stackit load-balancer generate-payload > ./payload.json`,
124+
`<Modify payload in file, if needed>`,
125+
`$ stackit load-balancer create --payload @./payload.json`),
126+
examples.NewExample(
127+
`Generate a payload with values of an existing load balancer, and adapt it with custom values for the different configuration options`,
128+
`$ stackit load-balancer generate-payload --instance-name my-lb > ./payload.json`,
129+
`<Modify payload in file>`,
130+
`$ stackit load-balancer update my-lb --payload @./payload.json`),
131+
),
132+
RunE: func(cmd *cobra.Command, args []string) error {
133+
ctx := context.Background()
134+
model, err := parseInput(p, cmd)
135+
if err != nil {
136+
return err
137+
}
138+
139+
// Configure API client
140+
apiClient, err := client.ConfigureClient(p)
141+
if err != nil {
142+
return err
143+
}
144+
145+
if model.InstanceName == nil {
146+
createPayload := DefaultCreateLoadBalancerPayload
147+
return outputCreateResult(p, &createPayload)
148+
}
149+
150+
req := buildRequest(ctx, model, apiClient)
151+
resp, err := req.Execute()
152+
if err != nil {
153+
return fmt.Errorf("read load balancer: %w", err)
154+
}
155+
updatePayload := &loadbalancer.UpdateLoadBalancerPayload{
156+
ExternalAddress: resp.ExternalAddress,
157+
Listeners: resp.Listeners,
158+
Name: resp.Name,
159+
Networks: resp.Networks,
160+
Options: resp.Options,
161+
PrivateAddress: resp.PrivateAddress,
162+
TargetPools: resp.TargetPools,
163+
Version: resp.Version,
164+
}
165+
return outputUpdateResult(p, updatePayload)
166+
},
167+
}
168+
configureFlags(cmd)
169+
return cmd
170+
}
171+
172+
func configureFlags(cmd *cobra.Command) {
173+
cmd.Flags().StringP(instanceNameFlag, "n", "", "If set, generates the payload with the current values of the given load balancer. If unset, generates the payload with empty values")
174+
}
175+
176+
func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
177+
globalFlags := globalflags.Parse(p, cmd)
178+
179+
instanceName := flags.FlagToStringPointer(p, cmd, instanceNameFlag)
180+
// If instanceName is provided, projectId is needed as well
181+
if instanceName != nil && globalFlags.ProjectId == "" {
182+
return nil, &errors.ProjectIdError{}
183+
}
184+
185+
return &inputModel{
186+
GlobalFlagModel: globalFlags,
187+
InstanceName: instanceName,
188+
}, nil
189+
}
190+
191+
func buildRequest(ctx context.Context, model *inputModel, apiClient *loadbalancer.APIClient) loadbalancer.ApiGetLoadBalancerRequest {
192+
req := apiClient.GetLoadBalancer(ctx, model.ProjectId, *model.InstanceName)
193+
return req
194+
}
195+
196+
func outputCreateResult(p *print.Printer, payload *loadbalancer.CreateLoadBalancerPayload) error {
197+
payloadBytes, err := json.MarshalIndent(*payload, "", " ")
198+
if err != nil {
199+
return fmt.Errorf("marshal create load balancer payload: %w", err)
200+
}
201+
p.Outputln(string(payloadBytes))
202+
203+
return nil
204+
}
205+
206+
func outputUpdateResult(p *print.Printer, payload *loadbalancer.UpdateLoadBalancerPayload) error {
207+
payloadBytes, err := json.MarshalIndent(*payload, "", " ")
208+
if err != nil {
209+
return fmt.Errorf("marshal update load balancer payload: %w", err)
210+
}
211+
p.Outputln(string(payloadBytes))
212+
213+
return nil
214+
}

0 commit comments

Comments
 (0)