Skip to content

Commit c9f99b7

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

File tree

7 files changed

+386
-0
lines changed

7 files changed

+386
-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: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package list
2+
3+
import (
4+
"github.com/google/go-cmp/cmp"
5+
"github.com/google/uuid"
6+
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
7+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
9+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
10+
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
11+
"testing"
12+
)
13+
14+
func TestParseInput(t *testing.T) {
15+
projectId := uuid.New().String()
16+
tests := []struct {
17+
description string
18+
globalFlags map[string]string
19+
expectedModel *inputModel
20+
isValid bool
21+
}{
22+
{
23+
description: "valid project id",
24+
globalFlags: map[string]string{
25+
"project-id": projectId,
26+
},
27+
expectedModel: &inputModel{
28+
GlobalFlagModel: &globalflags.GlobalFlagModel{
29+
ProjectId: projectId,
30+
Verbosity: globalflags.InfoVerbosity,
31+
},
32+
},
33+
isValid: true,
34+
},
35+
{
36+
description: "missing project id",
37+
globalFlags: map[string]string{},
38+
expectedModel: nil,
39+
isValid: false,
40+
},
41+
}
42+
43+
for _, tt := range tests {
44+
t.Run(tt.description, func(t *testing.T) {
45+
p := print.NewPrinter()
46+
cmd := NewCmd(&params.CmdParams{Printer: p})
47+
err := globalflags.Configure(cmd.Flags())
48+
if err != nil {
49+
t.Fatal(err)
50+
}
51+
52+
for flag, value := range tt.globalFlags {
53+
if err := cmd.Flags().Set(flag, value); err != nil {
54+
t.Fatalf("Failed to set global flag %s: %v", flag, err)
55+
}
56+
}
57+
58+
model, err := parseInput(p, cmd)
59+
if !tt.isValid && err == nil {
60+
t.Fatalf("parseInput() error = %v, wantErr %v", err, !tt.isValid)
61+
}
62+
63+
if tt.isValid {
64+
if diff := cmp.Diff(model, tt.expectedModel); diff != "" {
65+
t.Fatalf("Model mismatch (-want +got):\n%s", diff)
66+
}
67+
}
68+
})
69+
}
70+
}
71+
72+
func TestOutputResult(t *testing.T) {
73+
tests := []struct {
74+
name string
75+
outputFormat string
76+
networkList iaas.PublicNetworkListResponse
77+
expectedOutput string
78+
wantErr bool
79+
}{
80+
{
81+
name: "JSON output single",
82+
outputFormat: "json",
83+
networkList: iaas.PublicNetworkListResponse{
84+
Items: &[]iaas.PublicNetwork{
85+
{Cidr: utils.Ptr("192.168.0.0/24")},
86+
},
87+
},
88+
wantErr: false,
89+
},
90+
{
91+
name: "JSON output multiple",
92+
outputFormat: "json",
93+
networkList: iaas.PublicNetworkListResponse{
94+
Items: &[]iaas.PublicNetwork{
95+
{Cidr: utils.Ptr("192.168.0.0/24")},
96+
{Cidr: utils.Ptr("192.167.0.0/24")},
97+
},
98+
},
99+
wantErr: false,
100+
},
101+
{
102+
name: "YAML output single",
103+
outputFormat: "yaml",
104+
networkList: iaas.PublicNetworkListResponse{
105+
Items: &[]iaas.PublicNetwork{
106+
{Cidr: utils.Ptr("192.168.0.0/24")},
107+
},
108+
},
109+
wantErr: false,
110+
},
111+
{
112+
name: "YAML output multiple",
113+
outputFormat: "yaml",
114+
networkList: iaas.PublicNetworkListResponse{
115+
Items: &[]iaas.PublicNetwork{
116+
{Cidr: utils.Ptr("192.168.0.0/24")},
117+
{Cidr: utils.Ptr("192.167.0.0/24")},
118+
},
119+
},
120+
wantErr: false,
121+
},
122+
{
123+
name: "pretty output single",
124+
outputFormat: "pretty",
125+
networkList: iaas.PublicNetworkListResponse{
126+
Items: &[]iaas.PublicNetwork{
127+
{Cidr: utils.Ptr("192.168.0.0/24")},
128+
},
129+
},
130+
wantErr: false,
131+
},
132+
{
133+
name: "pretty output multiple",
134+
outputFormat: "pretty",
135+
networkList: iaas.PublicNetworkListResponse{
136+
Items: &[]iaas.PublicNetwork{
137+
{Cidr: utils.Ptr("192.168.0.0/24")},
138+
{Cidr: utils.Ptr("192.167.0.0/24")},
139+
},
140+
},
141+
wantErr: false,
142+
},
143+
{
144+
name: "default output",
145+
outputFormat: "",
146+
networkList: iaas.PublicNetworkListResponse{
147+
Items: &[]iaas.PublicNetwork{
148+
{Cidr: utils.Ptr("192.168.0.0/24")},
149+
},
150+
},
151+
wantErr: false,
152+
},
153+
}
154+
155+
for _, tt := range tests {
156+
t.Run(tt.name, func(t *testing.T) {
157+
p := print.NewPrinter()
158+
p.Cmd = NewCmd(&params.CmdParams{Printer: p})
159+
err := outputResult(p, tt.outputFormat, tt.networkList)
160+
if (err != nil) != tt.wantErr {
161+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
162+
}
163+
})
164+
}
165+
}

0 commit comments

Comments
 (0)