Skip to content

Commit 10126de

Browse files
committed
feat: provide functionality to list public ip-ranges
1 parent 9896dba commit 10126de

File tree

7 files changed

+387
-0
lines changed

7 files changed

+387
-0
lines changed

docs/stackit_public-ip.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ stackit public-ip [flags]
3535
* [stackit public-ip delete](./stackit_public-ip_delete.md) - Deletes a Public IP
3636
* [stackit public-ip describe](./stackit_public-ip_describe.md) - Shows details of a Public IP
3737
* [stackit public-ip disassociate](./stackit_public-ip_disassociate.md) - Disassociates a Public IP from a network interface or a virtual IP
38+
* [stackit public-ip ip-ranges](./stackit_public-ip_ip-ranges.md) - Provides functionality for STACKIT Cloud public ip-ranges
3839
* [stackit public-ip list](./stackit_public-ip_list.md) - Lists all Public IPs of a project
3940
* [stackit public-ip update](./stackit_public-ip_update.md) - Updates a Public IP
4041

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
## stackit public-ip ip-ranges
2+
3+
Provides functionality for STACKIT Cloud public ip-ranges
4+
5+
### Synopsis
6+
7+
Provides functionality for STACKIT Cloud public ip-ranges
8+
9+
```
10+
stackit public-ip ip-ranges [flags]
11+
```
12+
13+
### Options
14+
15+
```
16+
-h, --help Help for "stackit public-ip ip-ranges"
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" "none" "yaml"]
25+
-p, --project-id string Project ID
26+
--region string Target region for region-specific requests
27+
--verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
28+
```
29+
30+
### SEE ALSO
31+
32+
* [stackit public-ip](./stackit_public-ip.md) - Provides functionality for public IPs
33+
* [stackit public-ip ip-ranges list](./stackit_public-ip_ip-ranges_list.md) - Lists all STACKIT cloud public IP ranges.
34+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## stackit public-ip ip-ranges list
2+
3+
Lists all STACKIT cloud public IP ranges.
4+
5+
### Synopsis
6+
7+
Lists all STACKIT cloud public IP ranges.
8+
9+
```
10+
stackit public-ip ip-ranges list [flags]
11+
```
12+
13+
### Examples
14+
15+
```
16+
Lists all STACKIT cloud public IP ranges
17+
$ stackit public-ip ranges list
18+
```
19+
20+
### Options
21+
22+
```
23+
-h, --help Help for "stackit public-ip ip-ranges list"
24+
```
25+
26+
### Options inherited from parent commands
27+
28+
```
29+
-y, --assume-yes If set, skips all confirmation prompts
30+
--async If set, runs the command asynchronously
31+
-o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"]
32+
-p, --project-id string Project ID
33+
--region string Target region for region-specific requests
34+
--verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
35+
```
36+
37+
### SEE ALSO
38+
39+
* [stackit public-ip ip-ranges](./stackit_public-ip_ip-ranges.md) - Provides functionality for STACKIT Cloud public ip-ranges
40+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package ipranges
2+
3+
import (
4+
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
5+
"github.com/stackitcloud/stackit-cli/internal/cmd/public-ip/ip-ranges/list"
6+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
7+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
8+
9+
"github.com/spf13/cobra"
10+
)
11+
12+
func NewCmd(params *params.CmdParams) *cobra.Command {
13+
cmd := &cobra.Command{
14+
Use: "ip-ranges",
15+
Short: "Provides functionality for STACKIT Cloud public ip-ranges",
16+
Long: "Provides functionality for STACKIT Cloud public ip-ranges",
17+
Args: args.NoArgs,
18+
Run: utils.CmdHelp,
19+
}
20+
addSubcommands(cmd, params)
21+
return cmd
22+
}
23+
24+
func addSubcommands(cmd *cobra.Command, params *params.CmdParams) {
25+
cmd.AddCommand(list.NewCmd(params))
26+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package list
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"strings"
8+
9+
"github.com/goccy/go-yaml"
10+
"github.com/spf13/cobra"
11+
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
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/globalflags"
16+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
17+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client"
18+
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
19+
)
20+
21+
type inputModel struct {
22+
*globalflags.GlobalFlagModel
23+
}
24+
25+
func NewCmd(params *params.CmdParams) *cobra.Command {
26+
cmd := &cobra.Command{
27+
Use: "list",
28+
Short: "Lists all STACKIT cloud public IP ranges.",
29+
Long: "Lists all STACKIT cloud public IP ranges.",
30+
Args: args.NoArgs,
31+
Example: examples.Build(
32+
examples.NewExample(
33+
`Lists all STACKIT cloud public IP ranges`,
34+
"$ stackit public-ip ranges list",
35+
),
36+
),
37+
RunE: func(cmd *cobra.Command, _ []string) error {
38+
ctx := context.Background()
39+
model, err := parseInput(params.Printer, cmd)
40+
if err != nil {
41+
return err
42+
}
43+
44+
// Configure API client
45+
apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
46+
if err != nil {
47+
return err
48+
}
49+
50+
// Call API
51+
req := apiClient.ListPublicIPRanges(ctx)
52+
resp, err := req.Execute()
53+
54+
if err != nil {
55+
return fmt.Errorf("list public ip ranges s: %w", err)
56+
}
57+
58+
if resp.Items == nil || len(*resp.Items) == 0 {
59+
params.Printer.Info("No public IP ranges found\n")
60+
return nil
61+
}
62+
63+
return outputResult(params.Printer, model.OutputFormat, *resp)
64+
},
65+
}
66+
return cmd
67+
}
68+
69+
func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
70+
globalFlags := globalflags.Parse(p, cmd)
71+
if globalFlags.ProjectId == "" {
72+
return nil, &errors.ProjectIdError{}
73+
}
74+
75+
model := inputModel{GlobalFlagModel: globalFlags}
76+
77+
if p.IsVerbosityDebug() {
78+
modelStr, err := print.BuildDebugStrFromInputModel(model)
79+
if err != nil {
80+
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
81+
} else {
82+
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
83+
}
84+
}
85+
86+
return &model, nil
87+
}
88+
89+
func outputResult(p *print.Printer, outputFormat string, networkListResponse iaas.PublicNetworkListResponse) error {
90+
switch outputFormat {
91+
case print.JSONOutputFormat:
92+
details, err := json.MarshalIndent(networkListResponse, "", " ")
93+
if err != nil {
94+
return fmt.Errorf("marshal public IP: %w", err)
95+
}
96+
p.Outputln(string(details))
97+
98+
return nil
99+
case print.YAMLOutputFormat:
100+
details, err := yaml.MarshalWithOptions(networkListResponse, yaml.IndentSequence(true), yaml.UseJSONMarshaler())
101+
if err != nil {
102+
return fmt.Errorf("marshal public IP: %w", err)
103+
}
104+
p.Outputln(string(details))
105+
106+
return nil
107+
default:
108+
var publicIps []string
109+
for _, item := range *networkListResponse.Items {
110+
if item.Cidr != nil || *item.Cidr != "" {
111+
publicIps = append(publicIps, *item.Cidr)
112+
}
113+
}
114+
p.Outputln(strings.Join(publicIps, ","))
115+
116+
return nil
117+
}
118+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package list
2+
3+
import (
4+
"testing"
5+
6+
"github.com/google/go-cmp/cmp"
7+
"github.com/google/uuid"
8+
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
9+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
10+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
12+
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
13+
)
14+
15+
func TestParseInput(t *testing.T) {
16+
projectId := uuid.New().String()
17+
tests := []struct {
18+
description string
19+
globalFlags map[string]string
20+
expectedModel *inputModel
21+
isValid bool
22+
}{
23+
{
24+
description: "valid project id",
25+
globalFlags: map[string]string{
26+
"project-id": projectId,
27+
},
28+
expectedModel: &inputModel{
29+
GlobalFlagModel: &globalflags.GlobalFlagModel{
30+
ProjectId: projectId,
31+
Verbosity: globalflags.InfoVerbosity,
32+
},
33+
},
34+
isValid: true,
35+
},
36+
{
37+
description: "missing project id",
38+
globalFlags: map[string]string{},
39+
expectedModel: nil,
40+
isValid: false,
41+
},
42+
}
43+
44+
for _, tt := range tests {
45+
t.Run(tt.description, func(t *testing.T) {
46+
p := print.NewPrinter()
47+
cmd := NewCmd(&params.CmdParams{Printer: p})
48+
err := globalflags.Configure(cmd.Flags())
49+
if err != nil {
50+
t.Fatal(err)
51+
}
52+
53+
for flag, value := range tt.globalFlags {
54+
if err := cmd.Flags().Set(flag, value); err != nil {
55+
t.Fatalf("Failed to set global flag %s: %v", flag, err)
56+
}
57+
}
58+
59+
model, err := parseInput(p, cmd)
60+
if !tt.isValid && err == nil {
61+
t.Fatalf("parseInput() error = %v, wantErr %v", err, !tt.isValid)
62+
}
63+
64+
if tt.isValid {
65+
if diff := cmp.Diff(model, tt.expectedModel); diff != "" {
66+
t.Fatalf("Model mismatch (-want +got):\n%s", diff)
67+
}
68+
}
69+
})
70+
}
71+
}
72+
73+
func TestOutputResult(t *testing.T) {
74+
tests := []struct {
75+
name string
76+
outputFormat string
77+
networkList iaas.PublicNetworkListResponse
78+
expectedOutput string
79+
wantErr bool
80+
}{
81+
{
82+
name: "JSON output single",
83+
outputFormat: "json",
84+
networkList: iaas.PublicNetworkListResponse{
85+
Items: &[]iaas.PublicNetwork{
86+
{Cidr: utils.Ptr("192.168.0.0/24")},
87+
},
88+
},
89+
wantErr: false,
90+
},
91+
{
92+
name: "JSON output multiple",
93+
outputFormat: "json",
94+
networkList: iaas.PublicNetworkListResponse{
95+
Items: &[]iaas.PublicNetwork{
96+
{Cidr: utils.Ptr("192.168.0.0/24")},
97+
{Cidr: utils.Ptr("192.167.0.0/24")},
98+
},
99+
},
100+
wantErr: false,
101+
},
102+
{
103+
name: "YAML output single",
104+
outputFormat: "yaml",
105+
networkList: iaas.PublicNetworkListResponse{
106+
Items: &[]iaas.PublicNetwork{
107+
{Cidr: utils.Ptr("192.168.0.0/24")},
108+
},
109+
},
110+
wantErr: false,
111+
},
112+
{
113+
name: "YAML output multiple",
114+
outputFormat: "yaml",
115+
networkList: iaas.PublicNetworkListResponse{
116+
Items: &[]iaas.PublicNetwork{
117+
{Cidr: utils.Ptr("192.168.0.0/24")},
118+
{Cidr: utils.Ptr("192.167.0.0/24")},
119+
},
120+
},
121+
wantErr: false,
122+
},
123+
{
124+
name: "pretty output single",
125+
outputFormat: "pretty",
126+
networkList: iaas.PublicNetworkListResponse{
127+
Items: &[]iaas.PublicNetwork{
128+
{Cidr: utils.Ptr("192.168.0.0/24")},
129+
},
130+
},
131+
wantErr: false,
132+
},
133+
{
134+
name: "pretty output multiple",
135+
outputFormat: "pretty",
136+
networkList: iaas.PublicNetworkListResponse{
137+
Items: &[]iaas.PublicNetwork{
138+
{Cidr: utils.Ptr("192.168.0.0/24")},
139+
{Cidr: utils.Ptr("192.167.0.0/24")},
140+
},
141+
},
142+
wantErr: false,
143+
},
144+
{
145+
name: "default output",
146+
outputFormat: "",
147+
networkList: iaas.PublicNetworkListResponse{
148+
Items: &[]iaas.PublicNetwork{
149+
{Cidr: utils.Ptr("192.168.0.0/24")},
150+
},
151+
},
152+
wantErr: false,
153+
},
154+
}
155+
156+
for _, tt := range tests {
157+
t.Run(tt.name, func(t *testing.T) {
158+
p := print.NewPrinter()
159+
p.Cmd = NewCmd(&params.CmdParams{Printer: p})
160+
err := outputResult(p, tt.outputFormat, tt.networkList)
161+
if (err != nil) != tt.wantErr {
162+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
163+
}
164+
})
165+
}
166+
}

0 commit comments

Comments
 (0)