Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Below you can find a list of the STACKIT services already available in the CLI (
| Service | CLI Commands | Status |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| Observability | `observability` | :white_check_mark: |
| 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)|
| 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)|
| Authorization | `project`, `organization` | :white_check_mark: |
| DNS | `dns` | :white_check_mark: |
| Kubernetes Engine (SKE) | `ske` | :white_check_mark: |
Expand Down
1 change: 1 addition & 0 deletions docs/stackit_beta.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ stackit beta [flags]
* [stackit beta network-area](./stackit_beta_network-area.md) - Provides functionality for STACKIT Network Area (SNA)
* [stackit beta network-interface](./stackit_beta_network-interface.md) - Provides functionality for network interfaces
* [stackit beta public-ip](./stackit_beta_public-ip.md) - Provides functionality for public IPs
* [stackit beta quota](./stackit_beta_quota.md) - Manage server quotas
* [stackit beta security-group](./stackit_beta_security-group.md) - Manage security groups
* [stackit beta server](./stackit_beta_server.md) - Provides functionality for servers
* [stackit beta sqlserverflex](./stackit_beta_sqlserverflex.md) - Provides functionality for SQLServer Flex
Expand Down
34 changes: 34 additions & 0 deletions docs/stackit_beta_quota.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## stackit beta quota

Manage server quotas

### Synopsis

Manage the lifecycle of server quotas.

```
stackit beta quota [flags]
```

### Options

```
-h, --help Help for "stackit beta quota"
```

### Options inherited from parent commands

```
-y, --assume-yes If set, skips all confirmation prompts
--async If set, runs the command asynchronously
-o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"]
-p, --project-id string Project ID
--region string Target region for region-specific requests
--verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
```

### SEE ALSO

* [stackit beta](./stackit_beta.md) - Contains beta STACKIT CLI commands
* [stackit beta quota list](./stackit_beta_quota_list.md) - Lists quotas

40 changes: 40 additions & 0 deletions docs/stackit_beta_quota_list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## stackit beta quota list

Lists quotas

### Synopsis

Lists server quotas.

```
stackit beta quota list [flags]
```

### Examples

```
List available quotas
$ stackit beta quota list
```

### Options

```
-h, --help Help for "stackit beta quota list"
```

### Options inherited from parent commands

```
-y, --assume-yes If set, skips all confirmation prompts
--async If set, runs the command asynchronously
-o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"]
-p, --project-id string Project ID
--region string Target region for region-specific requests
--verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
```

### SEE ALSO

* [stackit beta quota](./stackit_beta_quota.md) - Manage server quotas

2 changes: 2 additions & 0 deletions internal/cmd/beta/beta.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
networkArea "github.com/stackitcloud/stackit-cli/internal/cmd/beta/network-area"
networkinterface "github.com/stackitcloud/stackit-cli/internal/cmd/beta/network-interface"
publicip "github.com/stackitcloud/stackit-cli/internal/cmd/beta/public-ip"
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/quota"
securitygroup "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group"
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/server"
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/sqlserverflex"
Expand Down Expand Up @@ -54,4 +55,5 @@ func addSubcommands(cmd *cobra.Command, p *print.Printer) {
cmd.AddCommand(securitygroup.NewCmd(p))
cmd.AddCommand(keypair.NewCmd(p))
cmd.AddCommand(image.NewCmd(p))
cmd.AddCommand(quota.NewCmd(p))
}
194 changes: 194 additions & 0 deletions internal/cmd/beta/quota/list/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package list

import (
"context"
"encoding/json"
"fmt"
"strconv"

"github.com/goccy/go-yaml"
"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
"github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client"
"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
)

type inputModel struct {
*globalflags.GlobalFlagModel
}

func NewCmd(p *print.Printer) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "Lists quotas",
Long: "Lists server quotas.",
Comment thread
bahkauv70 marked this conversation as resolved.
Outdated
Args: args.NoArgs,
Example: examples.Build(
examples.NewExample(
`List available quotas`,
`$ stackit beta quota list`,
),
),
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := context.Background()
model, err := parseInput(p, cmd)
if err != nil {
return err
}

// Configure API client
apiClient, err := client.ConfigureClient(p)
if err != nil {
return err
}

projectLabel, err := projectname.GetProjectName(ctx, p, cmd)
if err != nil {
p.Debug(print.ErrorLevel, "get project name: %v", err)
projectLabel = model.ProjectId
}

// Call API
request := buildRequest(ctx, model, apiClient)

response, err := request.Execute()
if err != nil {
return fmt.Errorf("list quotas: %w", err)
}

if items := response.Quotas; items == nil {
p.Info("No quotas found for project %q", projectLabel)
} else {
if err := outputResult(p, model.OutputFormat, items); err != nil {
return fmt.Errorf("output quotas: %w", err)
}
}

return nil
},
}

configureFlags(cmd)
return cmd
}

func configureFlags(_ *cobra.Command) {
Comment thread
bahkauv70 marked this conversation as resolved.
Outdated
}

func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
globalFlags := globalflags.Parse(p, cmd)
if globalFlags.ProjectId == "" {
return nil, &errors.ProjectIdError{}
}

model := inputModel{
GlobalFlagModel: globalFlags,
}

if p.IsVerbosityDebug() {
modelStr, err := print.BuildDebugStrFromInputModel(model)
if err != nil {
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
} else {
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
}
}

return &model, nil
}

func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiListQuotasRequest {
request := apiClient.ListQuotas(ctx, model.ProjectId)

return request
}

func outputResult(p *print.Printer, outputFormat string, quotas *iaas.QuotaList) error {
switch outputFormat {
case print.JSONOutputFormat:
details, err := json.MarshalIndent(quotas, "", " ")
if err != nil {
return fmt.Errorf("marshal quota list: %w", err)
}
p.Outputln(string(details))

return nil
case print.YAMLOutputFormat:
details, err := yaml.MarshalWithOptions(quotas, yaml.IndentSequence(true), yaml.UseJSONMarshaler())
if err != nil {
return fmt.Errorf("marshal quota list: %w", err)
}
p.Outputln(string(details))

return nil

default:
table := tables.NewTable()
table.SetHeader("NAME", "LIMIT", "USAGE", "PERCENT")
if val := quotas.BackupGigabytes; val != nil {
table.AddRow("Backup Gigabytes", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val))
}
if val := quotas.Backups; val != nil {
table.AddRow("Backups", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val))
}
if val := quotas.Gigabytes; val != nil {
table.AddRow("Gigabytes", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val))
Comment thread
bahkauv70 marked this conversation as resolved.
Outdated
}
if val := quotas.Networks; val != nil {
table.AddRow("Networks", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val))
}
if val := quotas.Nics; val != nil {
table.AddRow("Nics", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val))
}
if val := quotas.PublicIps; val != nil {
table.AddRow("Public Ips", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val))
}
if val := quotas.Ram; val != nil {
table.AddRow("Ram", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val))
Comment thread
bahkauv70 marked this conversation as resolved.
Outdated
}
if val := quotas.SecurityGroupRules; val != nil {
table.AddRow("Security Group Rules", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val))
}
if val := quotas.SecurityGroups; val != nil {
table.AddRow("Security Groups", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val))
}
if val := quotas.Snapshots; val != nil {
table.AddRow("Snapshots", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val))
}
if val := quotas.Vcpu; val != nil {
table.AddRow("Vcpu", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val))
}
if val := quotas.Volumes; val != nil {
table.AddRow("Volumes", conv(val.GetLimit()), conv(val.GetUsage()), percentage(val))
}
err := table.Display(p)
if err != nil {
return fmt.Errorf("render table: %w", err)
}

return nil
}
}

func conv(n *int64) string {
if n != nil {
return strconv.FormatInt(*n, 10)
}
return "n/a"
}

func percentage(val interface {
GetLimit() *int64
GetUsage() *int64
}) string {
if a, b := val.GetLimit(), val.GetUsage(); a != nil && b != nil {
return fmt.Sprintf("%3.1f%%", 100.0/float64(*a)*float64(*b))
}
return "n/a"
}
Loading