Skip to content

Commit 7dd2f50

Browse files
committed
feat: support for quota listing
1 parent c5b3de6 commit 7dd2f50

File tree

8 files changed

+462
-1
lines changed

8 files changed

+462
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Below you can find a list of the STACKIT services already available in the CLI (
7676
| Service | CLI Commands | Status |
7777
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
7878
| Observability | `observability` | :white_check_mark: |
79-
| Infrastructure as a Service (IaaS) | `beta network-area` <br/> `beta network` <br/> `beta volume` <br/> `beta network-interface` <br/> `beta public-ip` <br/> `beta security-group` <br/> `beta key-pair` <br/> `beta image` | :white_check_mark: (beta)|
79+
| Infrastructure as a Service (IaaS) | `beta network-area` <br/> `beta network` <br/> `beta volume` <br/> `beta network-interface` <br/> `beta public-ip` <br/> `beta security-group` <br/> `beta key-pair` <br/> `beta image` <br/> `beta quota` | :white_check_mark: (beta)|
8080
| Authorization | `project`, `organization` | :white_check_mark: |
8181
| DNS | `dns` | :white_check_mark: |
8282
| Kubernetes Engine (SKE) | `ske` | :white_check_mark: |

docs/stackit_beta.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ stackit beta [flags]
4747
* [stackit beta network-area](./stackit_beta_network-area.md) - Provides functionality for STACKIT Network Area (SNA)
4848
* [stackit beta network-interface](./stackit_beta_network-interface.md) - Provides functionality for network interfaces
4949
* [stackit beta public-ip](./stackit_beta_public-ip.md) - Provides functionality for public IPs
50+
* [stackit beta quota](./stackit_beta_quota.md) - Manage server quotas
5051
* [stackit beta security-group](./stackit_beta_security-group.md) - Manage security groups
5152
* [stackit beta server](./stackit_beta_server.md) - Provides functionality for servers
5253
* [stackit beta sqlserverflex](./stackit_beta_sqlserverflex.md) - Provides functionality for SQLServer Flex

docs/stackit_beta_quota.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
## stackit beta quota
2+
3+
Manage server quotas
4+
5+
### Synopsis
6+
7+
Manage the lifecycle of server quotas.
8+
9+
```
10+
stackit beta quota [flags]
11+
```
12+
13+
### Options
14+
15+
```
16+
-h, --help Help for "stackit beta quota"
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 beta](./stackit_beta.md) - Contains beta STACKIT CLI commands
33+
* [stackit beta quota list](./stackit_beta_quota_list.md) - Lists quotas
34+

docs/stackit_beta_quota_list.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## stackit beta quota list
2+
3+
Lists quotas
4+
5+
### Synopsis
6+
7+
Lists server quotas.
8+
9+
```
10+
stackit beta quota list [flags]
11+
```
12+
13+
### Examples
14+
15+
```
16+
List available quotas
17+
$ stackit beta quota list
18+
```
19+
20+
### Options
21+
22+
```
23+
-h, --help Help for "stackit beta quota 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 beta quota](./stackit_beta_quota.md) - Manage server quotas
40+

internal/cmd/beta/beta.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
networkArea "github.com/stackitcloud/stackit-cli/internal/cmd/beta/network-area"
1010
networkinterface "github.com/stackitcloud/stackit-cli/internal/cmd/beta/network-interface"
1111
publicip "github.com/stackitcloud/stackit-cli/internal/cmd/beta/public-ip"
12+
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/quota"
1213
securitygroup "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group"
1314
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/server"
1415
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/sqlserverflex"
@@ -54,4 +55,5 @@ func addSubcommands(cmd *cobra.Command, p *print.Printer) {
5455
cmd.AddCommand(securitygroup.NewCmd(p))
5556
cmd.AddCommand(keypair.NewCmd(p))
5657
cmd.AddCommand(image.NewCmd(p))
58+
cmd.AddCommand(quota.NewCmd(p))
5759
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package list
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"strconv"
8+
9+
"github.com/goccy/go-yaml"
10+
"github.com/spf13/cobra"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
14+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
15+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
16+
"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
17+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client"
18+
"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
19+
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
20+
)
21+
22+
type inputModel struct {
23+
*globalflags.GlobalFlagModel
24+
}
25+
26+
func NewCmd(p *print.Printer) *cobra.Command {
27+
cmd := &cobra.Command{
28+
Use: "list",
29+
Short: "Lists quotas",
30+
Long: "Lists server quotas.",
31+
Args: args.NoArgs,
32+
Example: examples.Build(
33+
examples.NewExample(
34+
`List available quotas`,
35+
`$ stackit beta quota list`,
36+
),
37+
),
38+
RunE: func(cmd *cobra.Command, _ []string) error {
39+
ctx := context.Background()
40+
model, err := parseInput(p, cmd)
41+
if err != nil {
42+
return err
43+
}
44+
45+
// Configure API client
46+
apiClient, err := client.ConfigureClient(p)
47+
if err != nil {
48+
return err
49+
}
50+
51+
projectLabel, err := projectname.GetProjectName(ctx, p, cmd)
52+
if err != nil {
53+
p.Debug(print.ErrorLevel, "get project name: %v", err)
54+
projectLabel = model.ProjectId
55+
}
56+
57+
// Call API
58+
request := buildRequest(ctx, model, apiClient)
59+
60+
response, err := request.Execute()
61+
if err != nil {
62+
return fmt.Errorf("list quotas: %w", err)
63+
}
64+
65+
if items := response.Quotas; items == nil {
66+
p.Info("No quotas found for project %q", projectLabel)
67+
} else {
68+
if err := outputResult(p, model.OutputFormat, items); err != nil {
69+
return fmt.Errorf("output quotas: %w", err)
70+
}
71+
}
72+
73+
return nil
74+
},
75+
}
76+
77+
configureFlags(cmd)
78+
return cmd
79+
}
80+
81+
func configureFlags(_ *cobra.Command) {
82+
}
83+
84+
func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
85+
globalFlags := globalflags.Parse(p, cmd)
86+
if globalFlags.ProjectId == "" {
87+
return nil, &errors.ProjectIdError{}
88+
}
89+
90+
model := inputModel{
91+
GlobalFlagModel: globalFlags,
92+
}
93+
94+
if p.IsVerbosityDebug() {
95+
modelStr, err := print.BuildDebugStrFromInputModel(model)
96+
if err != nil {
97+
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
98+
} else {
99+
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
100+
}
101+
}
102+
103+
return &model, nil
104+
}
105+
106+
func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiListQuotasRequest {
107+
request := apiClient.ListQuotas(ctx, model.ProjectId)
108+
109+
return request
110+
}
111+
112+
func outputResult(p *print.Printer, outputFormat string, quotas *iaas.QuotaList) error {
113+
switch outputFormat {
114+
case print.JSONOutputFormat:
115+
details, err := json.MarshalIndent(quotas, "", " ")
116+
if err != nil {
117+
return fmt.Errorf("marshal quota list: %w", err)
118+
}
119+
p.Outputln(string(details))
120+
121+
return nil
122+
case print.YAMLOutputFormat:
123+
details, err := yaml.MarshalWithOptions(quotas, yaml.IndentSequence(true))
124+
if err != nil {
125+
return fmt.Errorf("marshal quota list: %w", err)
126+
}
127+
p.Outputln(string(details))
128+
129+
return nil
130+
131+
default:
132+
table := tables.NewTable()
133+
table.SetHeader("NAME", "LIMIT", "USAGE", "PERCENT")
134+
if val := quotas.BackupGigabytes; val != nil {
135+
table.AddRow("Backup Gigabytes", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val.GetLimit(), val.GetUsage()))
136+
}
137+
if val := quotas.Backups; val != nil {
138+
table.AddRow("Backups", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val.GetLimit(), val.GetUsage()))
139+
}
140+
if val := quotas.Gigabytes; val != nil {
141+
table.AddRow("Gigabytes", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val.GetLimit(), val.GetUsage()))
142+
}
143+
if val := quotas.Networks; val != nil {
144+
table.AddRow("Networks", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val.GetLimit(), val.GetUsage()))
145+
}
146+
if val := quotas.Nics; val != nil {
147+
table.AddRow("Nics", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val.GetLimit(), val.GetUsage()))
148+
}
149+
if val := quotas.PublicIps; val != nil {
150+
table.AddRow("Public Ips", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val.GetLimit(), val.GetUsage()))
151+
}
152+
if val := quotas.Ram; val != nil {
153+
table.AddRow("Ram", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val.GetLimit(), val.GetUsage()))
154+
}
155+
if val := quotas.SecurityGroupRules; val != nil {
156+
table.AddRow("Security Group Rules", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val.GetLimit(), val.GetUsage()))
157+
}
158+
if val := quotas.SecurityGroups; val != nil {
159+
table.AddRow("Security Groups", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val.GetLimit(), val.GetUsage()))
160+
}
161+
if val := quotas.Snapshots; val != nil {
162+
table.AddRow("Snapshots", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val.GetLimit(), val.GetUsage()))
163+
}
164+
if val := quotas.Vcpu; val != nil {
165+
table.AddRow("Vcpu", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val.GetLimit(), val.GetUsage()))
166+
}
167+
if val := quotas.Volumes; val != nil {
168+
table.AddRow("Volumes", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val.GetLimit(), val.GetUsage()))
169+
}
170+
err := table.Display(p)
171+
if err != nil {
172+
return fmt.Errorf("render table: %w", err)
173+
}
174+
175+
return nil
176+
}
177+
}
178+
179+
func conv(n *int64) string {
180+
if n != nil {
181+
return strconv.FormatInt(*n, 10)
182+
}
183+
return "n/a"
184+
}
185+
186+
func percentage(a, b *int64) string {
187+
if a != nil && b != nil {
188+
return fmt.Sprintf("%3.1f%%", 100.0/float64(*a)*float64(*b))
189+
}
190+
return "n/a"
191+
}

0 commit comments

Comments
 (0)