Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add cdn operations #460

Merged
merged 11 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## [v6.8.0]
- Added support for CDN operations

## [v6.7.8] (October 2024)

### Added
Expand Down
19 changes: 19 additions & 0 deletions commands/cdn/cdn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package cdn

import (
"github.com/ionos-cloud/ionosctl/v6/commands/cdn/distribution"
"github.com/ionos-cloud/ionosctl/v6/internal/core"
"github.com/spf13/cobra"
)

func Command() *core.Command {
cmd := &core.Command{
Command: &cobra.Command{
Use: "cdn",
Short: "The sub-commands of the 'cdn' resource help manage CDN distributions",
TraverseChildren: true,
},
}
cmd.AddCommand(distribution.Command())
return cmd
}
37 changes: 37 additions & 0 deletions commands/cdn/completer/completer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package completer

import (
"context"
"github.com/ionos-cloud/ionosctl/v6/internal/client"
"github.com/ionos-cloud/ionosctl/v6/pkg/functional"
cdn "github.com/ionos-cloud/sdk-go-cdn"
)

// DistributionsProperty returns a list of properties of all distributions matching the given filters
func DistributionsProperty[V any](f func(cdn.Distribution) V, fs ...Filter) []V {
recs, err := Distributions(fs...)
if err != nil {
return nil
}
return functional.Map(*recs.Items, f)
}

// Distributions returns all distributions matching the given filters
func Distributions(fs ...Filter) (cdn.Distributions, error) {
req := client.Must().CDNClient.DistributionsApi.DistributionsGet(context.Background())
for _, f := range fs {
var err error
req, err = f(req)
if err != nil {
return cdn.Distributions{}, err
}
}

ls, _, err := req.Execute()
if err != nil {
return cdn.Distributions{}, err
}
return ls, nil
}

type Filter func(request cdn.ApiDistributionsGetRequest) (cdn.ApiDistributionsGetRequest, error)
113 changes: 113 additions & 0 deletions commands/cdn/distribution/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package distribution

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

"github.com/ionos-cloud/ionosctl/v6/internal/constants"
"github.com/ionos-cloud/ionosctl/v6/pkg/pointer"
"github.com/ionos-cloud/ionosctl/v6/pkg/uuidgen"

cdn "github.com/ionos-cloud/sdk-go-cdn"

"github.com/ionos-cloud/ionosctl/v6/internal/client"
"github.com/spf13/viper"

"github.com/ionos-cloud/ionosctl/v6/internal/core"
)

func Create() *core.Command {
cmd := core.NewCommand(context.Background(), nil, core.CommandBuilder{
Copy link
Contributor

@avirtopeanu-ionos avirtopeanu-ionos Oct 29, 2024

Choose a reason for hiding this comment

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

Is there a reason not to use NewCommandWithJsonProperties ?

The user might not know exactly what json we expect here for --routing-rules.

NewCommandWithJsonProperties adds two flags --json-properties and --json-properties-example. The latter if set, it prints an example json (which for example could be piped to a .json file), and exits, so the user can use that as a sort of guide

Copy link
Contributor Author

Choose a reason for hiding this comment

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

JsonProperties is kind of vague. Having the custom name is more unambiguous. We can have a routing-rules example if we want.

Copy link
Contributor

@avirtopeanu-ionos avirtopeanu-ionos Oct 29, 2024

Choose a reason for hiding this comment

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

sure, then let's do that

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe it might actually be more appropriate to have the create command use a json properties file to create the object. From what I can tell, the routing rules have quite a complex structure and it might be easier for the user to just use a properties file.

Namespace: "cdn",
Resource: "distribution",
Verb: "create",
Aliases: []string{"c", "post"},
ShortDesc: "Create a CDN distribution. Wiki: https://docs.ionos.com/cloud/network-services/cdn/dcd-how-tos/create-cdn-distribution",
Example: "ionosctl cdn ds create --domain foo-bar.com --certificate-id id --routing-rules rules.json",
PreCmdRun: func(c *core.PreCommandConfig) error {
if err := core.CheckRequiredFlags(c.Command, c.NS, constants.FlagCDNDistributionDomain, constants.FlagCDNDistributionRoutingRules); err != nil {
return err
}

return nil
},
CmdRun: func(c *core.CommandConfig) error {
input := &cdn.DistributionProperties{}
if err := setPropertiesFromFlags(c, input); err != nil {
return err
}

id := uuidgen.Must()
res, _, err := client.Must().CDNClient.DistributionsApi.DistributionsPut(context.Background(), id).
DistributionUpdate(cdn.DistributionUpdate{
Id: &id,
Properties: input,
}).Execute()
if err != nil {
return err
}

return printDistribution(c, res)
},
InitClient: true,
})

cmd.Command.SilenceUsage = true
cmd.Command.Flags().SortFlags = false
return addDistributionCreateFlags(cmd)
}

func addDistributionCreateFlags(cmd *core.Command) *core.Command {
cmd.AddStringFlag(constants.FlagCDNDistributionDomain, "", "", "The domain of the distribution")
cmd.AddStringFlag(constants.FlagCDNDistributionCertificateID, "", "", "The ID of the certificate")
cmd.AddStringFlag(constants.FlagCDNDistributionRoutingRules, "", "", "The routing rules of the distribution. JSON string or file path of routing rules")
return cmd
}

func setPropertiesFromFlags(c *core.CommandConfig, p *cdn.DistributionProperties) error {
if fn := core.GetFlagName(c.NS, constants.FlagCDNDistributionDomain); viper.IsSet(fn) {
p.Domain = pointer.From(viper.GetString(fn))
}

if fn := core.GetFlagName(c.NS, constants.FlagCDNDistributionCertificateID); viper.IsSet(fn) {
p.CertificateId = pointer.From(viper.GetString(fn))
}

if fn := core.GetFlagName(c.NS, constants.FlagCDNDistributionRoutingRules); viper.IsSet(fn) {
rr := viper.GetString(fn)
data, err := getRoutingRulesData(rr)
if err != nil {
return fmt.Errorf("error reading routing rules file: %s", err)
}

rules, err := getRoutingRulesFromJSON(data)
if err != nil {
return fmt.Errorf("error parsing routing rules: %s", err)
}
p.RoutingRules = rules
}

return nil
}

func getRoutingRulesFromJSON(data []byte) (*[]cdn.RoutingRule, error) {
var rr []cdn.RoutingRule
err := json.Unmarshal(data, &rr)
if err != nil {
return nil, err
}
return &rr, nil
}

func getRoutingRulesData(input string) ([]byte, error) {
switch _, err := os.Stat(input); {
case err == nil:
return os.ReadFile(input)
case os.IsNotExist(err):
return []byte(input), nil
default:
return nil, err
}
}
62 changes: 62 additions & 0 deletions commands/cdn/distribution/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package distribution

import (
"context"
"fmt"
"github.com/ionos-cloud/ionosctl/v6/commands/cdn/completer"
"github.com/ionos-cloud/ionosctl/v6/internal/client"
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
"github.com/ionos-cloud/ionosctl/v6/pkg/confirm"
cdn "github.com/ionos-cloud/sdk-go-cdn"
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/ionos-cloud/ionosctl/v6/internal/core"
)

func Delete() *core.Command {
cmd := core.NewCommand(context.Background(), nil, core.CommandBuilder{
Namespace: "cdn",
Resource: "distribution",
Verb: "delete",
Aliases: []string{"del", "d"},
ShortDesc: "Delete a distribution",
Example: `ionosctl cdn ds delete --distribution-id ID`,
PreCmdRun: func(c *core.PreCommandConfig) error {
if err := core.CheckRequiredFlags(c.Command, c.NS, constants.FlagCDNDistributionID); err != nil {
return err
}

return nil
},
CmdRun: func(c *core.CommandConfig) error {
distributionID := viper.GetString(core.GetFlagName(c.NS, constants.FlagCDNDistributionID))
d, _, err := client.Must().CDNClient.DistributionsApi.DistributionsFindById(context.Background(), distributionID).Execute()
if err != nil {
return fmt.Errorf("distribution not found: %w", err)
}

yes := confirm.FAsk(c.Command.Command.InOrStdin(), fmt.Sprintf("Are you sure you want to delete distribution %s for domain %s", *d.Id, *d.Properties.Domain),
viper.GetBool(constants.ArgForce))
if !yes {
return fmt.Errorf("user cancelled deletion")
}

_, err = client.Must().CDNClient.DistributionsApi.DistributionsDelete(context.Background(), *d.Id).Execute()
return err
},
InitClient: true,
})

cmd.AddStringFlag(constants.FlagCDNDistributionID, constants.FlagIdShort, "", "The ID of the distribution you want to retrieve", core.RequiredFlagOption())
_ = cmd.Command.RegisterFlagCompletionFunc(constants.FlagCDNDistributionID, func(c *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return completer.DistributionsProperty(func(r cdn.Distribution) string {
return *r.Id
}), cobra.ShellCompDirectiveNoFileComp
})

cmd.Command.SilenceUsage = true
cmd.Command.Flags().SortFlags = false

return cmd
}
37 changes: 37 additions & 0 deletions commands/cdn/distribution/dsitribution.go
digna-ionos marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package distribution

import (
"github.com/ionos-cloud/ionosctl/v6/commands/cdn/distribution/routingrules"
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
"github.com/ionos-cloud/ionosctl/v6/internal/core"
"github.com/ionos-cloud/ionosctl/v6/internal/printer/tabheaders"
"github.com/spf13/cobra"
)

var (
allCols = []string{"Id", "Domain", "CertificateId", "State"}
defaultCols = []string{"Id", "Domain", "CertificateId", "State"}
)

func Command() *core.Command {
cmd := &core.Command{
Command: &cobra.Command{
Use: "distribution",
Short: "The sub-commands of 'ionosctl cdn distribution' allow you to manage CDN distributions",
Aliases: []string{"ds"},
TraverseChildren: true,
},
}
cmd.Command.PersistentFlags().StringSlice(constants.ArgCols, nil, tabheaders.ColsMessage(allCols))
_ = cmd.Command.RegisterFlagCompletionFunc(constants.ArgCols, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return allCols, cobra.ShellCompDirectiveNoFileComp
})

cmd.AddCommand(List())
cmd.AddCommand(FindByID())
cmd.AddCommand(Delete())
cmd.AddCommand(Create())
cmd.AddCommand(Update())
cmd.AddCommand(routingrules.Root())
return cmd
}
64 changes: 64 additions & 0 deletions commands/cdn/distribution/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package distribution

import (
"context"
"fmt"
"github.com/ionos-cloud/ionosctl/v6/commands/cdn/completer"
cdn "github.com/ionos-cloud/sdk-go-cdn"
"github.com/spf13/cobra"

"github.com/ionos-cloud/ionosctl/v6/internal/client"
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
"github.com/ionos-cloud/ionosctl/v6/internal/core"
"github.com/ionos-cloud/ionosctl/v6/internal/printer/json2table/jsonpaths"
"github.com/ionos-cloud/ionosctl/v6/internal/printer/jsontabwriter"
"github.com/ionos-cloud/ionosctl/v6/internal/printer/tabheaders"
"github.com/spf13/viper"
)

func FindByID() *core.Command {
cmd := core.NewCommand(context.Background(), nil, core.CommandBuilder{
Namespace: "cdn",
Resource: "distribution",
Verb: "get",
Aliases: []string{"g"},
ShortDesc: "Retrieve a distribution",
Example: "ionosctl cdn ds get --distribution-id ID",
PreCmdRun: func(c *core.PreCommandConfig) error {
if err := core.CheckRequiredFlags(c.Command, c.NS, constants.FlagCDNDistributionID); err != nil {
return err
}

return nil
},
CmdRun: func(c *core.CommandConfig) error {
distributionID := viper.GetString(core.GetFlagName(c.NS, constants.FlagCDNDistributionID))
r, _, err := client.Must().CDNClient.DistributionsApi.DistributionsFindById(context.Background(),
distributionID,
).Execute()
if err != nil {
return err
}

cols, _ := c.Command.Command.Flags().GetStringSlice(constants.ArgCols)
out, err := jsontabwriter.GenerateOutput("", jsonpaths.CDNDistribution, r,
tabheaders.GetHeadersAllDefault(defaultCols, cols))
if err != nil {
return err
}

fmt.Fprintf(c.Command.Command.OutOrStdout(), out)
return nil
},
InitClient: true,
})
cmd.AddStringFlag(constants.FlagCDNDistributionID, constants.FlagIdShort, "", "The ID of the distribution you want to retrieve", core.RequiredFlagOption())
_ = cmd.Command.RegisterFlagCompletionFunc(constants.FlagCDNDistributionID, func(c *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return completer.DistributionsProperty(func(r cdn.Distribution) string {
return *r.Id
}), cobra.ShellCompDirectiveNoFileComp
})
cmd.Command.SilenceUsage = true
cmd.Command.Flags().SortFlags = false
return cmd
}
Loading
Loading