Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
863828c
feat(cdn): add cdn client, config, list command
cgoetz-inovex Nov 19, 2025
f198a95
fix(cdn): generate docs
cgoetz-inovex Nov 19, 2025
d5f4304
fix(cdn) fix linting issues
cgoetz-inovex Nov 19, 2025
8c71293
Update internal/cmd/beta/cdn/distribution/list/list.go
cgoetz-inovex Nov 19, 2025
82f7d49
Update internal/cmd/beta/cdn/distribution/list/list.go
cgoetz-inovex Nov 19, 2025
a87cd60
fix(cdn): rename sortBy params, use EnumSliceToStringSlice
cgoetz-inovex Nov 20, 2025
42d1196
fix(cdn): make `testNextPageID`, `testID` and `testStatus` constant
cgoetz-inovex Nov 20, 2025
0d7729a
fix(cdn): add cdn subcommand to beta, generate docs
cgoetz-inovex Nov 20, 2025
c3095c5
Merge branch 'main' into feat/STACKITCLI-70-add-cdn-distribution-beta…
cgoetz-inovex Nov 24, 2025
7e50687
feat(cdn) add limit flag to distribution list
cgoetz-inovex Dec 1, 2025
9e049a0
feat(cdn): add distribution create cmd
cgoetz-inovex Nov 20, 2025
cbf7a9f
fix(cdn): generate distribution create docs
cgoetz-inovex Nov 20, 2025
7296ee0
squash this
cgoetz-inovex Nov 21, 2025
1235792
feat(cdn) implement cdn distribution create/delete/describe/update
cgoetz-inovex Dec 1, 2025
568659c
squash
cgoetz-inovex Dec 1, 2025
a183846
squash
cgoetz-inovex Dec 1, 2025
4f4e0b1
fix(cdn) regenerate docs
cgoetz-inovex Dec 1, 2025
dbfea3d
fix(cdn) linting issues
cgoetz-inovex Dec 1, 2025
9d512ea
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/sec…
dependabot[bot] Nov 25, 2025
cd065fd
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/ser…
dependabot[bot] Nov 25, 2025
c5d46b4
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/ske…
dependabot[bot] Nov 25, 2025
50ebc7c
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/ser…
dependabot[bot] Nov 25, 2025
13cb16c
chore(deps): bump renovatebot/github-action from 44.0.3 to 44.0.4 (#1…
dependabot[bot] Nov 25, 2025
b950c06
feat(kms) describe key, keyring, wrappingkey (#1107)
cgoetz-inovex Nov 25, 2025
0b693ec
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/rab…
dependabot[bot] Nov 25, 2025
d29f75a
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/alb…
dependabot[bot] Nov 25, 2025
e95f987
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/mon…
dependabot[bot] Nov 26, 2025
be30de5
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/ser…
dependabot[bot] Nov 26, 2025
f6a0b1a
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/ser…
dependabot[bot] Nov 27, 2025
8faa48d
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/dns…
dependabot[bot] Nov 27, 2025
8cda53b
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/iaa…
dependabot[bot] Nov 27, 2025
4ae1761
fix(alb): print valid JSON/YAML output for list cmds (#1045)
rubenhoenle Nov 27, 2025
0edc3c7
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/mar…
dependabot[bot] Nov 28, 2025
f0a12ee
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/red…
dependabot[bot] Nov 28, 2025
1580200
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/run…
dependabot[bot] Nov 28, 2025
3cc971e
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/aut…
dependabot[bot] Nov 28, 2025
784d53a
chore: increase linter timeout (#1131)
marceljk Nov 28, 2025
97cde20
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/int…
dependabot[bot] Nov 28, 2025
9ccaaf8
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/loa…
dependabot[bot] Nov 28, 2025
e93e347
fix: log browser url to stderr instead of stdout (#1136)
marceljk Nov 28, 2025
589ce8f
fix(deps): update module github.com/goccy/go-yaml to v1.19.0 (#1137)
stackit-pipeline Dec 1, 2025
0908904
feat(mongodbflex): add readAnyDatabase and stackitAdmin roles for use…
PietvanderMeulen Dec 1, 2025
2b014bc
fix: refresh token flow uses x-www-form encoding (#1135)
JorTurFer Dec 1, 2025
fb6d721
chore(deps): update renovatebot/github-action action to v44.0.5 (#1141)
stackit-pipeline Dec 2, 2025
a228bda
chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/sql…
dependabot[bot] Dec 2, 2025
883acc3
feat(cdn): add cdn client, config, list command
cgoetz-inovex Nov 19, 2025
5241898
feat(cdn) implement cdn distribution create/delete/describe/update
cgoetz-inovex Dec 1, 2025
180fa38
Merge branch 'main' into feat/STACKITCLI-70-add-cdn-distribution-beta…
cgoetz-inovex Dec 2, 2025
90f05e9
fix(cdn) manual merge fixes
cgoetz-inovex Dec 2, 2025
76d91fb
fix(cdn) review fixes
cgoetz-inovex Dec 2, 2025
3d0f111
fix(cdn) replace utils.Min usage with builtin min
cgoetz-inovex Dec 2, 2025
d6160f1
Update internal/cmd/beta/cdn/distribution/list/list.go
cgoetz-inovex Dec 5, 2025
b2a1c41
fix(cdn) apply review comments
cgoetz-inovex Dec 5, 2025
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
1 change: 1 addition & 0 deletions docs/stackit_config_set.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ stackit config set [flags]
```
--allowed-url-domain string Domain name, used for the verification of the URLs that are given in the custom identity provider endpoint and "STACKIT curl" command
--authorization-custom-endpoint string Authorization API base URL, used in calls to this API
--cdn-custom-endpoint string CDN API base URL, used in calls to this API
--dns-custom-endpoint string DNS API base URL, used in calls to this API
-h, --help Help for "stackit config set"
--iaas-custom-endpoint string IaaS API base URL, used in calls to this API
Expand Down
1 change: 1 addition & 0 deletions docs/stackit_config_unset.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ stackit config unset [flags]
--allowed-url-domain Domain name, used for the verification of the URLs that are given in the IDP endpoint and curl commands. If unset, defaults to stackit.cloud
--async Configuration option to run commands asynchronously
--authorization-custom-endpoint Authorization API base URL. If unset, uses the default base URL
--cdn-custom-endpoint Custom CDN endpoint URL. If unset, uses the default base URL
--dns-custom-endpoint DNS API base URL. If unset, uses the default base URL
-h, --help Help for "stackit config unset"
--iaas-custom-endpoint IaaS API base URL. If unset, uses the default base URL
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/stackitcloud/stackit-sdk-go/core v0.19.0
github.com/stackitcloud/stackit-sdk-go/services/alb v0.7.1
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0
github.com/stackitcloud/stackit-sdk-go/services/cdn v1.6.0
github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1
github.com/stackitcloud/stackit-sdk-go/services/git v0.9.0
github.com/stackitcloud/stackit-sdk-go/services/iaas v1.2.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,8 @@ github.com/stackitcloud/stackit-sdk-go/services/alb v0.7.1 h1:DaJkEN/6l+AJEQ3Dr+
github.com/stackitcloud/stackit-sdk-go/services/alb v0.7.1/go.mod h1:SzA+UsSNv4D9IvNT7hwYPewgAvUgj5WXIU2tZ0XaMBI=
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0 h1:7ZKd3b+E/R4TEVShLTXxx5FrsuDuJBOyuVOuKTMa4mo=
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0/go.mod h1:/FoXa6hF77Gv8brrvLBCKa5ie1Xy9xn39yfHwaln9Tw=
github.com/stackitcloud/stackit-sdk-go/services/cdn v1.6.0 h1:Q+qIdejeMsYMkbtVoI9BpGlKGdSVFRBhH/zj44SP8TM=
github.com/stackitcloud/stackit-sdk-go/services/cdn v1.6.0/go.mod h1:YGadfhuy8yoseczTxF7vN4t9ES2WxGQr0Pug14ii7y4=
github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1 h1:CnhAMLql0MNmAeq4roQKN8OpSKX4FSgTU6Eu6detB4I=
github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1/go.mod h1:7Bx85knfNSBxulPdJUFuBePXNee3cO+sOTYnUG6M+iQ=
github.com/stackitcloud/stackit-sdk-go/services/git v0.9.0 h1:zuoJnsLnjxdQcQbs7gUXYzrN0Ip5NXj+6LFBp1EO6cg=
Expand Down
25 changes: 25 additions & 0 deletions internal/cmd/beta/cdn/cdn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package cdn

import (
"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/cdn/distribution"
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
)

func NewCmd(params *params.CmdParams) *cobra.Command {
cmd := &cobra.Command{
Use: "cdn",
Short: "Manage CDN resources",
Long: "Manage the lifecycle of CDN resources.",
Args: args.NoArgs,
Run: utils.CmdHelp,
}
addSubcommands(cmd, params)
return cmd
}

func addSubcommands(cmd *cobra.Command, params *params.CmdParams) {
cmd.AddCommand(distribution.NewCommand(params))
}
24 changes: 24 additions & 0 deletions internal/cmd/beta/cdn/distribution/distribution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package distribution

import (
"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/cdn/distribution/list"
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
)

func NewCommand(params *params.CmdParams) *cobra.Command {
cmd := &cobra.Command{
Use: "distribution",
Short: "Manage CDN distributions",
Long: "Manage the lifecycle of CDN distributions.",
Args: cobra.NoArgs,
Run: utils.CmdHelp,
}
addSubcommands(cmd, params)
return cmd
}

func addSubcommands(cmd *cobra.Command, params *params.CmdParams) {
cmd.AddCommand(list.NewCmd(params))
}
173 changes: 173 additions & 0 deletions internal/cmd/beta/cdn/distribution/list/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package list

import (
"context"
"fmt"
"strings"

"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
"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/flags"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-cli/internal/pkg/services/cdn/client"
"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
"github.com/stackitcloud/stackit-sdk-go/services/cdn"
)

type inputModel struct {
*globalflags.GlobalFlagModel
SortBy string
}

const (
sortByFlag = "sort-by"
)

func NewCmd(params *params.CmdParams) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List CDN distributions",
Long: "List all CDN distributions in your account.",
Args: args.NoArgs,
Example: examples.Build(
examples.NewExample(
`List all CDN distributions`,
`$ stackit beta dns distribution list`,
),
examples.NewExample(
`List all CDN distributions sorted by id`,
`$ stackit beta dns distribution list --sort-by=id`,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background() // should this be cancellable?

model, err := parseInput(params.Printer, cmd, args)
if err != nil {
return err
}

apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
if err != nil {
return err
}

distributions, err := fetchDistributions(ctx, model, apiClient)
if err != nil {
return fmt.Errorf("fetch distributions: %w", err)
}

return outputResult(params.Printer, model.OutputFormat, distributions)
},
}

configureFlags(cmd)
return cmd
}

var sortByFlagOptions = []string{"id", "created", "updated", "origin-url", "status"}
Copy link
Member

Choose a reason for hiding this comment

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

Please directly use directly the API values: "id" "updatedAt" "createdAt" "originUrl" "status" "originUrlRelated"

This was wrong in the Jira story.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe there's also a enum generated in the SDK which holds these values. Then this would be the way to go because it will be kept up-to-date in the future without any efforts on our side.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Searched for sortBy and originUrl etc. could not find an enum for this. The spec looks like it should generate an enum: https://github.com/stackitcloud/stackit-api-specifications/blob/83214868e6dbdc53d053cc07654542d8f34b5491/services/cdn/v1beta2/cdn.json#L1486

How could I start to investigate this further?


func configureFlags(cmd *cobra.Command) {
// same default as apiClient
cmd.Flags().Var(flags.EnumFlag(false, "created", sortByFlagOptions...), sortByFlag, fmt.Sprintf("Sort entries by a specific field, one of %q", sortByFlagOptions))
}

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

model := inputModel{
GlobalFlagModel: globalFlags,
SortBy: flags.FlagWithDefaultToStringValue(p, cmd, sortByFlag),
}

p.DebugInputModel(model)
return &model, nil
}

func buildRequest(ctx context.Context, model *inputModel, apiClient *cdn.APIClient, nextPageID cdn.ListDistributionsResponseGetNextPageIdentifierAttributeType) cdn.ApiListDistributionsRequest {
req := apiClient.ListDistributions(ctx, model.GlobalFlagModel.ProjectId)
req = req.SortBy(toAPISortBy(model.SortBy))
req = req.PageSize(100)
if nextPageID != nil {
req = req.PageIdentifier(*nextPageID)
}
return req
}

func toAPISortBy(sortBy string) string {
switch sortBy {
case "id":
return "id"
case "created":
return "createdAt"
case "updated":
return "updatedAt"
case "origin-url":
return "originUrl"
case "status":
return "status"
default:
panic("invalid sortBy value, programmer error")
}
}

func outputResult(p *print.Printer, outputFormat string, distributions []cdn.Distribution) error {
if distributions == nil {
distributions = make([]cdn.Distribution, 0) // otherwise prints null in json output
Copy link
Member

Choose a reason for hiding this comment

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

That's a good catch indeed! Any chance we can solve this in a central place for all commands?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice idea, I'll investigate this a bit, don't know much about go reflection yet.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With reflection I'd add this in Printer.OutputResult():

	val := reflect.ValueOf(output)
	if val.IsNil() {
		// when passing a nil slice or map, JSON and YAML marshal to "null"
		// so we need to create an empty slice or map to have "[]" or "{}" output instead
		switch val.Kind() {
		case reflect.Slice:
			output = make([]any, 0)
		case reflect.Map:
			output = make(map[any]any)
		default:
			// do nothing
		}
	}

The linked document suggests type switches, which would not work here.

Another solution without reflection, that comes to mind, would be to introduce specialized OutputResult functions, like OutputResultSlice/Map.

Copy link
Member

Choose a reason for hiding this comment

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

👍

Could be an option, please feel free to create a Jira ticket and propose a solution 😊

}
return p.OutputResult(outputFormat, distributions, func() error {
if len(distributions) == 0 {
p.Outputln("No CDN distributions found")
return nil
}

table := tables.NewTable()
table.SetHeader("ID", "REGIONS", "STATUS")
for i := range distributions {
d := &distributions[i]
regions := make([]string, 0, len(*d.Config.Regions))
for _, r := range *d.Config.Regions {
regions = append(regions, string(r))
}
joinedRegions := strings.Join(regions, ", ")
table.AddRow(
utils.PtrString(d.Id),
joinedRegions,
utils.PtrString(d.Status),
)
}
err := table.Display(p)
if err != nil {
return fmt.Errorf("render table: %w", err)
}
return nil
})
}

func fetchDistributions(ctx context.Context, model *inputModel, apiClient *cdn.APIClient) ([]cdn.Distribution, error) {
var nextPageID cdn.ListDistributionsResponseGetNextPageIdentifierAttributeType
var distributions []cdn.Distribution
for {
request := buildRequest(ctx, model, apiClient, nextPageID)
response, err := request.Execute()
if err != nil {
return nil, fmt.Errorf("list distributions: %w", err)
}
nextPageID = response.NextPageIdentifier
if response.Distributions != nil {
distributions = append(distributions, *response.Distributions...)
}
if nextPageID == nil {
break
}
}
return distributions, nil
}
Loading
Loading