Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
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
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 cdn distribution list`,
),
examples.NewExample(
`List all CDN distributions sorted by id`,
`$ stackit beta cdn 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
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
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
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.

}
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")
Copy link
Author

Choose a reason for hiding this comment

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

@rubenhoenle, when using the table output we only get these columns. When using the JSON format we would get all fields of cdn.Distribution. Should I introduce here custom struct that only contains ID, REGIONS and STATUS in JSON?

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