Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
70b932b
fix returning empty server array in case the response does not return…
j1n-o9r Apr 13, 2026
14cca95
adapted outputResult signature to comply to linter
j1n-o9r Apr 13, 2026
972a75e
switched to getter for retrieving the items
j1n-o9r Apr 13, 2026
7f78cf8
adapted printing behavior to align with expected behavior
j1n-o9r Apr 13, 2026
936bb00
change printing to defined output instead of stderr
j1n-o9r Apr 13, 2026
07bb839
adapted network list command to align to expectations
j1n-o9r Apr 13, 2026
f015fae
remove unnecessary normalization of nil value in response items after…
j1n-o9r Apr 13, 2026
7fb466a
adapted network-area list command to align to expectations
j1n-o9r Apr 13, 2026
2e13f98
removed redundant check
j1n-o9r Apr 13, 2026
fd6ccb0
adapted affinity list command to align to expectations
j1n-o9r Apr 15, 2026
6752d88
adapted image list command to align to expectations
j1n-o9r Apr 15, 2026
3f6a8be
adapted key pair list command to align to expectations
j1n-o9r Apr 15, 2026
8e34092
fixed debug print in key pair list command
j1n-o9r Apr 15, 2026
51fb3e1
move project label retrieval
j1n-o9r Apr 15, 2026
4661dc5
adapted public ip list command to align to expectations
j1n-o9r Apr 15, 2026
e0db653
adapted security group list command to align to expectations
j1n-o9r Apr 15, 2026
b3afc4b
added missing --limit flag for list security groups command
j1n-o9r Apr 15, 2026
f8d2829
adapted volumes list command to align to expectations
j1n-o9r Apr 15, 2026
bf4718f
adapted volume snapshots list command to align to expectations
j1n-o9r Apr 15, 2026
a888af1
adapted volume performance class list command to align to expectations
j1n-o9r Apr 15, 2026
02db11e
adapted volume backups list command to align to expectations
j1n-o9r Apr 15, 2026
42eafa4
fixed order of params
j1n-o9r Apr 15, 2026
48e9f80
adapted network area network ranges list command to align to expectat…
j1n-o9r Apr 15, 2026
b663d3d
adapted network area routes list command to align to expectations
j1n-o9r Apr 15, 2026
777630a
adapted security group rules list command to align to expectations
j1n-o9r Apr 15, 2026
8885e05
adapted server machine-types list command to align to expectations
j1n-o9r Apr 20, 2026
e72a115
adapted server network-interfaces list command to align to expectations
j1n-o9r Apr 20, 2026
4083b93
adapted server service accounts list command to align to expectations
j1n-o9r Apr 20, 2026
ee1b411
adapted server volumes list command to align to expectations
j1n-o9r Apr 20, 2026
9b8c40d
removed unnecessary check in public ip
j1n-o9r Apr 22, 2026
de440de
regenerate docs
j1n-o9r Apr 22, 2026
f25cdc0
fixed linter issue
j1n-o9r Apr 22, 2026
2a3e7ee
Update internal/cmd/key-pair/list/list.go
j1n-o9r Apr 22, 2026
884f8c7
(fix): remove mistakenly added project label from key pairs list command
j1n-o9r Apr 22, 2026
d24108d
(fix): made orgLabel in model from pointer to string for not having t…
j1n-o9r Apr 22, 2026
dac4b2a
(fix): remove unnecessarily declaring variable with explicit statement
j1n-o9r Apr 22, 2026
3e80ea6
(fix): made OrganizationId and NetworkAreaId in model from pointer to…
j1n-o9r Apr 22, 2026
68c108c
(fix): changed print statement to not contain abbreviation
j1n-o9r Apr 22, 2026
81e4c72
(fix): added test suggestion
j1n-o9r Apr 22, 2026
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
11 changes: 9 additions & 2 deletions docs/stackit_security-group_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,25 @@ stackit security-group list [flags]
### Examples

```
List all groups
Lists all security groups
$ stackit security-group list

List groups with labels
Lists security groups with labels
$ stackit security-group list --label-selector label1=value1,label2=value2

Lists all security groups in JSON format
$ stackit security-group list --output-format json

Lists up to 10 security groups
$ stackit security-group list --limit 10
```

### Options

```
-h, --help Help for "stackit security-group list"
--label-selector string Filter by label
--limit int Maximum number of entries to list
```

### Options inherited from parent commands
Expand Down
29 changes: 16 additions & 13 deletions internal/cmd/affinity-groups/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
"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"
)

Expand Down Expand Up @@ -63,16 +64,19 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
if err != nil {
return fmt.Errorf("list affinity groups: %w", err)
}
items := result.GetItems()

if items := result.Items; items != nil {
if model.Limit != nil && len(*items) > int(*model.Limit) {
*items = (*items)[:*model.Limit]
}
return outputResult(params.Printer, *model, *items)
projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd)
if err != nil {
params.Printer.Debug(print.ErrorLevel, "get project name: %v", err)
projectLabel = model.ProjectId
}

params.Printer.Outputln("No affinity groups found")
return nil
// Truncate Output
if model.Limit != nil && len(items) > int(*model.Limit) {
items = items[:*model.Limit]
}
return outputResult(params.Printer, model.OutputFormat, projectLabel, items)
},
}
configureFlags(cmd)
Expand Down Expand Up @@ -110,13 +114,12 @@ func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel,
return &model, nil
}

func outputResult(p *print.Printer, model inputModel, items []iaas.AffinityGroup) error {
var outputFormat string
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change this and the method signature?

Copy link
Copy Markdown
Author

@j1n-o9r j1n-o9r Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looked weird at first glance that the signature differed from all other outputResult functions. I also could not see a benefit from passing the model into the function. Furthermore I also think the state that is checked in the function will not happen, since if the input model will be nil and the outputResult function cannot be called. But maybe I am overseeing something here. Should I change it to the old signature?

if model.GlobalFlagModel != nil {
outputFormat = model.OutputFormat
}

func outputResult(p *print.Printer, outputFormat, projectLabel string, items []iaas.AffinityGroup) error {
return p.OutputResult(outputFormat, items, func() error {
if len(items) == 0 {
p.Outputf("No affinity groups found for project %q\n", projectLabel)
return nil
}
table := tables.NewTable()
table.SetHeader("ID", "NAME", "POLICY")
for _, item := range items {
Expand Down
27 changes: 22 additions & 5 deletions internal/cmd/affinity-groups/list/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,23 +141,40 @@ func TestBuildRequest(t *testing.T) {
}

func TestOutputResult(t *testing.T) {
type args struct {
outputFormat string
projectLabel string
instances []iaas.AffinityGroup
}
tests := []struct {
description string
model inputModel
response []iaas.AffinityGroup
args args
isValid bool
}{
{
description: "empty",
model: inputModel{},
response: []iaas.AffinityGroup{},
args: args{},
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You changed that testcase with that change. The response parameter was an empty slice before, now it's nil.

Valid testcase, but be aware what you're doing here. As commented above the question is why you changed the method signature at all? 😄

Copy link
Copy Markdown
Author

@j1n-o9r j1n-o9r Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for reminding that the case differs, honestly I don't know if I was aware of that when editing but I am now. 😄

I would suggest maybe having both cases tested.

isValid: true,
},
{
description: "empty slice",
args: args{
instances: []iaas.AffinityGroup{},
},
isValid: true,
},
{
description: "empty affinity group in slice",
args: args{
instances: []iaas.AffinityGroup{{}},
},
isValid: true,
},
}
params := testparams.NewTestParams()
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
err := outputResult(params.Printer, tt.model, tt.response)
err := outputResult(params.Printer, tt.args.outputFormat, tt.args.projectLabel, tt.args.instances)
if err != nil {
if !tt.isValid {
return
Expand Down
23 changes: 12 additions & 11 deletions internal/cmd/image/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,18 @@ func NewCmd(params *types.CmdParams) *cobra.Command {

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

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

if items := response.GetItems(); len(items) == 0 {
params.Printer.Info("No images found for project %q", projectLabel)
} else {
if model.Limit != nil && len(items) > int(*model.Limit) {
items = (items)[:*model.Limit]
}
if err := outputResult(params.Printer, model.OutputFormat, items); err != nil {
return fmt.Errorf("output images: %w", err)
}
// Truncate output
if model.Limit != nil && len(items) > int(*model.Limit) {
items = (items)[:*model.Limit]
}
if err := outputResult(params.Printer, model.OutputFormat, projectLabel, items); err != nil {
return fmt.Errorf("output images: %w", err)
}

return nil
Expand Down Expand Up @@ -149,8 +146,12 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APICli
return request
}

func outputResult(p *print.Printer, outputFormat string, items []iaas.Image) error {
func outputResult(p *print.Printer, outputFormat, projectLabel string, items []iaas.Image) error {
return p.OutputResult(outputFormat, items, func() error {
if len(items) == 0 {
p.Outputf("No images found for project %q\n", projectLabel)
return nil
}
table := tables.NewTable()
table.SetHeader("ID", "NAME", "OS", "ARCHITECTURE", "DISTRIBUTION", "VERSION", "SCOPE", "OWNER", "LABELS")
for i := range items {
Expand Down
3 changes: 2 additions & 1 deletion internal/cmd/image/list/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ func TestBuildRequest(t *testing.T) {
func Test_outputResult(t *testing.T) {
type args struct {
outputFormat string
projectLabel string
items []iaas.Image
}
tests := []struct {
Expand Down Expand Up @@ -215,7 +216,7 @@ func Test_outputResult(t *testing.T) {
params := testparams.NewTestParams()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := outputResult(params.Printer, tt.args.outputFormat, tt.args.items); (err != nil) != tt.wantErr {
if err := outputResult(params.Printer, tt.args.outputFormat, tt.args.projectLabel, tt.args.items); (err != nil) != tt.wantErr {
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
}
})
Expand Down
12 changes: 7 additions & 5 deletions internal/cmd/key-pair/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,9 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
return fmt.Errorf("list key pairs: %w", err)
}

if resp.Items == nil || len(*resp.Items) == 0 {
params.Printer.Info("No key pairs found\n")
return nil
}
items := resp.GetItems()

items := *resp.Items
// Truncate output
if model.Limit != nil && len(items) > int(*model.Limit) {
items = items[:*model.Limit]
}
Expand Down Expand Up @@ -130,6 +127,11 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APICli

func outputResult(p *print.Printer, outputFormat string, keyPairs []iaas.Keypair) error {
return p.OutputResult(outputFormat, keyPairs, func() error {
if len(keyPairs) == 0 {
p.Outputf("No key pairs found\n")
return nil
}

table := tables.NewTable()
table.SetHeader("KEY PAIR NAME", "LABELS", "FINGERPRINT", "CREATED AT", "UPDATED AT")

Expand Down
46 changes: 25 additions & 21 deletions internal/cmd/network-area/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const (
type inputModel struct {
*globalflags.GlobalFlagModel
Limit *int64
OrganizationId *string
OrganizationId string
LabelSelector *string
}

Expand Down Expand Up @@ -80,31 +80,30 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
return fmt.Errorf("list network areas: %w", err)
}

if resp.Items == nil || len(*resp.Items) == 0 {
var orgLabel string
rmApiClient, err := rmClient.ConfigureClient(params.Printer, params.CliVersion)
if err == nil {
orgLabel, err = rmUtils.GetOrganizationName(ctx, rmApiClient, *model.OrganizationId)
if err != nil {
params.Printer.Debug(print.ErrorLevel, "get organization name: %v", err)
orgLabel = *model.OrganizationId
} else if orgLabel == "" {
orgLabel = *model.OrganizationId
}
} else {
params.Printer.Debug(print.ErrorLevel, "configure resource manager client: %v", err)
items := resp.GetItems()

var orgLabel string
rmApiClient, err := rmClient.ConfigureClient(params.Printer, params.CliVersion)
if err == nil {
orgLabel, err = rmUtils.GetOrganizationName(ctx, rmApiClient, model.OrganizationId)
if err != nil {
params.Printer.Debug(print.ErrorLevel, "get organization name: %v", err)
orgLabel = model.OrganizationId
}
params.Printer.Info("No STACKIT Network Areas found for organization %q\n", orgLabel)
return nil
} else {
params.Printer.Debug(print.ErrorLevel, "configure resource manager client: %v", err)
}

if orgLabel == "" {
orgLabel = model.OrganizationId
}

// Truncate output
items := *resp.Items
if model.Limit != nil && len(items) > int(*model.Limit) {
items = items[:*model.Limit]
}

return outputResult(params.Printer, model.OutputFormat, items)
return outputResult(params.Printer, model.OutputFormat, orgLabel, items)
},
}
configureFlags(cmd)
Expand Down Expand Up @@ -133,7 +132,7 @@ func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel,
model := inputModel{
GlobalFlagModel: globalFlags,
Limit: limit,
OrganizationId: flags.FlagToStringPointer(p, cmd, organizationIdFlag),
OrganizationId: flags.FlagToStringValue(p, cmd, organizationIdFlag),
LabelSelector: flags.FlagToStringPointer(p, cmd, labelSelectorFlag),
}

Expand All @@ -142,15 +141,20 @@ func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel,
}

func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiListNetworkAreasRequest {
req := apiClient.ListNetworkAreas(ctx, *model.OrganizationId)
req := apiClient.ListNetworkAreas(ctx, model.OrganizationId)
if model.LabelSelector != nil {
req = req.LabelSelector(*model.LabelSelector)
}
return req
}

func outputResult(p *print.Printer, outputFormat string, networkAreas []iaas.NetworkArea) error {
func outputResult(p *print.Printer, outputFormat, orgLabel string, networkAreas []iaas.NetworkArea) error {
return p.OutputResult(outputFormat, networkAreas, func() error {
if len(networkAreas) == 0 {
p.Outputf("No STACKIT Network Areas found for organization %q\n", orgLabel)
return nil
}

table := tables.NewTable()
table.SetHeader("ID", "Name", "# Attached Projects")

Expand Down
5 changes: 3 additions & 2 deletions internal/cmd/network-area/list/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func fixtureInputModel(mods ...func(model *inputModel)) *inputModel {
GlobalFlagModel: &globalflags.GlobalFlagModel{
Verbosity: globalflags.VerbosityDefault,
},
OrganizationId: &testOrganizationId,
OrganizationId: testOrganizationId,
Limit: utils.Ptr(int64(10)),
LabelSelector: utils.Ptr(testLabelSelector),
}
Expand Down Expand Up @@ -167,6 +167,7 @@ func TestBuildRequest(t *testing.T) {
func TestOutputResult(t *testing.T) {
type args struct {
outputFormat string
orgLabel string
networkAreas []iaas.NetworkArea
}
tests := []struct {
Expand Down Expand Up @@ -197,7 +198,7 @@ func TestOutputResult(t *testing.T) {
params := testparams.NewTestParams()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := outputResult(params.Printer, tt.args.outputFormat, tt.args.networkAreas); (err != nil) != tt.wantErr {
if err := outputResult(params.Printer, tt.args.outputFormat, tt.args.orgLabel, tt.args.networkAreas); (err != nil) != tt.wantErr {
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
}
})
Expand Down
Loading