Skip to content

Commit

Permalink
feat: dnssec, dns quota (#446)
Browse files Browse the repository at this point in the history
* dep: update dns sdk

* feat: add quota

* feat: add dnsec

* feat: dnssec

* fix: breaking change in test

* fix: cols for creation

* doc: regen docs

* doc: changelog
  • Loading branch information
avirtopeanu-ionos committed Jun 27, 2024
1 parent 19f550f commit f5d987f
Show file tree
Hide file tree
Showing 281 changed files with 12,897 additions and 52,720 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## [v6.7.7] (June 2024)

### Added
- Added support for DNS resources:
- `ionosctl dns keys` commands which allows you to enable/disable DNSSEC and manage DNSKEY records
- `ionosctl dns quota` commands which allows you to get the DNS quota for your account

### Fixed
- Fixed the column path mapping for 'server' resource to display the actual server's type ('CUBE'/'ENTERPRISE'),
not the CloudAPI resource type ('server').
Expand Down
6 changes: 5 additions & 1 deletion commands/dns/dns.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dns

import (
"github.com/ionos-cloud/ionosctl/v6/commands/dns/dnssec"
"github.com/ionos-cloud/ionosctl/v6/commands/dns/quota"
"github.com/ionos-cloud/ionosctl/v6/commands/dns/record"
"github.com/ionos-cloud/ionosctl/v6/commands/dns/zone"
"github.com/ionos-cloud/ionosctl/v6/internal/core"
Expand All @@ -11,12 +13,14 @@ func DNSCommand() *core.Command {
cmd := &core.Command{
Command: &cobra.Command{
Use: "dns",
Short: "The sub-commands of `ionosctl dns` allow you to manage your DNS Zones (domains) and Records (where the traffic should be redirected)",
Short: "The sub-commands of the 'dns' resource help automate DNS Zone and Record management",
TraverseChildren: true,
},
}
cmd.AddCommand(zone.ZoneCommand())
cmd.AddCommand(record.RecordCommand())
cmd.AddCommand(quota.Root())
cmd.AddCommand(dnssec.Root())

return cmd
}
2 changes: 1 addition & 1 deletion commands/dns/dns_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ func Cleanup() {

err = functional.ApplyAndAggregateErrors(*ls.Items,
func(z dns.ZoneRead) error {
_, err2 := cl.DnsClient.ZonesApi.ZonesDelete(context.Background(), *z.Id).Execute()
_, _, err2 := cl.DnsClient.ZonesApi.ZonesDelete(context.Background(), *z.Id).Execute()
return err2
},
)
Expand Down
113 changes: 113 additions & 0 deletions commands/dns/dnssec/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package dnssec

import (
"context"
"fmt"

"github.com/ionos-cloud/ionosctl/v6/commands/dns/zone"
"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/ionos-cloud/ionosctl/v6/pkg/pointer"
dns "github.com/ionos-cloud/sdk-go-dns"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func Create() *core.Command {
const (
FlagAlgorithm = "algorithm"
FlagKskBits = "ksk-bits"
FlagZskBits = "zsk-bits"
FlagNsecMode = "nsec-mode"
FlagNsec3Iterations = "nsec3-iterations"
FlagNsec3SaltBits = "nsec3-salt-bits"
FlagValidity = "validity"
)

cmd := core.NewCommand(context.Background(), nil, core.CommandBuilder{
Namespace: "dns",
Resource: "dnssec",
Verb: "create",
Aliases: []string{"c", "post"},
ShortDesc: "Enable DNSSEC keys and create associated DNSKEY records for your DNS zone",
Example: "ionosctl dns keys create --zone ZONE",
PreCmdRun: func(c *core.PreCommandConfig) error {
if err := core.CheckRequiredFlags(c.Command, c.NS, constants.FlagZone); err != nil {
return err
}

return nil
},
CmdRun: func(c *core.CommandConfig) error {
zoneId, err := zone.Resolve(viper.GetString(core.GetFlagName(c.NS, constants.FlagZone)))
if err != nil {
return err
}

key, _, err := client.Must().DnsClient.DNSSECApi.ZonesKeysPost(context.Background(), zoneId).
DnssecKeyCreate(
dns.DnssecKeyCreate{
Properties: &dns.DnssecKeyParameters{
Validity: pointer.From(viper.GetInt32(core.GetFlagName(c.NS, FlagValidity))),
KeyParameters: &dns.KeyParameters{
Algorithm: pointer.From(dns.Algorithm(viper.GetString(core.GetFlagName(c.NS, FlagAlgorithm)))),
KskBits: pointer.From(dns.KskBits(viper.GetInt32(core.GetFlagName(c.NS, FlagKskBits)))),
ZskBits: pointer.From(dns.ZskBits(viper.GetInt32(core.GetFlagName(c.NS, FlagZskBits)))),
},
NsecParameters: &dns.NsecParameters{
NsecMode: pointer.From(dns.NsecMode(viper.GetString(core.GetFlagName(c.NS, FlagNsecMode)))),
Nsec3Iterations: pointer.From(viper.GetInt32(core.GetFlagName(c.NS, FlagNsec3Iterations))),
Nsec3SaltBits: pointer.From(viper.GetInt32(core.GetFlagName(c.NS, FlagNsec3SaltBits))),
},
},
}).Execute()
if err != nil {
return err
}

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

fmt.Fprintf(c.Command.Command.OutOrStdout(), out)
return nil
},
InitClient: true,
})

cmd.AddStringFlag(constants.FlagZone, constants.FlagZoneShort, "", constants.DescZone)
_ = cmd.Command.RegisterFlagCompletionFunc(constants.FlagZone, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return zone.ZonesProperty(func(t dns.ZoneRead) string {
return *t.Properties.ZoneName
}), cobra.ShellCompDirectiveNoFileComp
})

cmd.AddStringFlag(FlagAlgorithm, "", "RSASHA256", "Algorithm used to generate signing keys (both Key Signing Keys and Zone Signing Keys)")
cmd.AddIntFlag(FlagKskBits, "", 1024, "Key signing key length in bits. kskBits >= zskBits: [1024/2048/4096]")
_ = cmd.Command.RegisterFlagCompletionFunc(FlagKskBits, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"1024", "2048", "4096"}, cobra.ShellCompDirectiveNoFileComp
})
cmd.AddIntFlag(FlagZskBits, "", 1024, "Zone signing key length in bits. zskBits <= kskBits: [1024/2048/4096]")
_ = cmd.Command.RegisterFlagCompletionFunc(FlagZskBits, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"1024", "2048", "4096"}, cobra.ShellCompDirectiveNoFileComp
})
cmd.AddSetFlag(FlagNsecMode, "", "NSEC", []string{"NSEC", "NSEC3"}, "NSEC mode.")
cmd.AddIntFlag(FlagNsec3Iterations, "", 0, "Number of iterations for NSEC3. [0..50]")
cmd.AddIntFlag(FlagNsec3SaltBits, "", 64, "Salt length in bits for NSEC3. [64..128], multiples of 8")
_ = cmd.Command.RegisterFlagCompletionFunc(FlagNsec3SaltBits, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"64", "72", "80", "88", "96", "104", "112", "120", "128"}, cobra.ShellCompDirectiveNoFileComp
})
cmd.AddIntFlag(FlagValidity, "", 90, "Signature validity in days [90..365]")

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

return cmd
}
60 changes: 60 additions & 0 deletions commands/dns/dnssec/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package dnssec

import (
"context"
"fmt"

"github.com/ionos-cloud/ionosctl/v6/commands/dns/zone"
"github.com/ionos-cloud/ionosctl/v6/internal/client"
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
"github.com/ionos-cloud/ionosctl/v6/internal/core"
dns "github.com/ionos-cloud/sdk-go-dns"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func Delete() *core.Command {
cmd := core.NewCommand(context.Background(), nil, core.CommandBuilder{
Namespace: "dns",
Resource: "dnssec",
Verb: "delete",
Aliases: []string{"del", "rm", "remove"},
ShortDesc: "Removes ALL associated DNSKEY records for your DNS zone and disables DNSSEC keys.",
Example: `ionosctl dns keys delete --zone ZONE`,
PreCmdRun: func(c *core.PreCommandConfig) error {
if err := core.CheckRequiredFlags(c.Command, c.NS, constants.FlagZone); err != nil {
return err
}

return nil
},
CmdRun: func(c *core.CommandConfig) error {
zoneName := viper.GetString(core.GetFlagName(c.NS, constants.FlagZone))
zoneId, err := zone.Resolve(zoneName)
if err != nil {
return err
}

_, _, err = client.Must().DnsClient.DNSSECApi.ZonesKeysDelete(context.Background(), zoneId).Execute()
if err != nil {
return err
}

fmt.Fprintf(c.Command.Command.OutOrStdout(), "DNSKEY records deleted and DNSSEC keys disabled for zone %s\n", zoneName)
return nil
},
InitClient: true,
})

cmd.AddStringFlag(constants.FlagZone, constants.FlagZoneShort, "", constants.DescZone)
_ = cmd.Command.RegisterFlagCompletionFunc(constants.FlagZone, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return zone.ZonesProperty(func(t dns.ZoneRead) string {
return *t.Properties.ZoneName
}), cobra.ShellCompDirectiveNoFileComp
})

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

return cmd
}
37 changes: 37 additions & 0 deletions commands/dns/dnssec/dnssec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package dnssec

import (
"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", "KeyTag", "DigestAlgorithmMnemonic", "Digest", "Validity",
"Flags", "PubKey", "ComposedKeyData", "Algorithm", "KskBits", "ZskBits", "NsecMode", "Nsec3Iterations", "Nsec3SaltBits"}

defaultCols = []string{"Id", "KeyTag", "DigestAlgorithmMnemonic", "Digest", "Validity"}
)

func Root() *core.Command {
cmd := &core.Command{
Command: &cobra.Command{
Use: "dnssec",
Aliases: []string{"sec", "dnskey", "key", "keys"},
Short: "The sub-commands of 'ionosctl dns dnssec' allow you to manage your DNSSEC Keys",
TraverseChildren: true,
},
}

cmd.Command.PersistentFlags().StringSlice(constants.ArgCols, defaultCols, 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(Get())
cmd.AddCommand(Create())
cmd.AddCommand(Delete())

return cmd
}
75 changes: 75 additions & 0 deletions commands/dns/dnssec/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package dnssec

import (
"context"
"fmt"

"github.com/ionos-cloud/ionosctl/v6/commands/dns/zone"
"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/resource2table"
"github.com/ionos-cloud/ionosctl/v6/internal/printer/jsontabwriter"
"github.com/ionos-cloud/ionosctl/v6/internal/printer/tabheaders"
dns "github.com/ionos-cloud/sdk-go-dns"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func Get() *core.Command {
cmd := core.NewCommand(context.Background(), nil, core.CommandBuilder{
Namespace: "dns",
Resource: "dnssec",
Verb: "list",
Aliases: []string{"l", "ls", "get", "g"},
ShortDesc: "Retrieve your zone's DNSSEC keys",
Example: `ionosctl dns keys list --zone ZONE
ionosctl dns keys list --zone ZONE --cols ComposedKeyData --no-headers
ionosctl dns keys list --zone ZONE --cols PubKey --no-headers`,
PreCmdRun: func(c *core.PreCommandConfig) error {
if err := core.CheckRequiredFlags(c.Command, c.NS, constants.FlagZone); err != nil {
return err
}

return nil
},
CmdRun: func(c *core.CommandConfig) error {
zoneId, err := zone.Resolve(viper.GetString(core.GetFlagName(c.NS, constants.FlagZone)))
if err != nil {
return err
}

key, _, err := client.Must().DnsClient.DNSSECApi.ZonesKeysGet(context.Background(), zoneId).Execute()
if err != nil {
return err
}

table, err := resource2table.ConvertDNSSECToTable(key)
if err != nil {
return fmt.Errorf("could not convert from JSON to Table format: %w", err)
}
cols, _ := c.Command.Command.Flags().GetStringSlice(constants.ArgCols)
out, err := jsontabwriter.GenerateOutputPreconverted(key, table,
tabheaders.GetHeadersAllDefault(allCols, cols))
if err != nil {
return err
}

fmt.Fprintf(c.Command.Command.OutOrStdout(), out)
return nil
},
InitClient: true,
})

cmd.AddStringFlag(constants.FlagZone, constants.FlagZoneShort, "", constants.DescZone)
_ = cmd.Command.RegisterFlagCompletionFunc(constants.FlagZone, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return zone.ZonesProperty(func(t dns.ZoneRead) string {
return *t.Properties.ZoneName
}), cobra.ShellCompDirectiveNoFileComp
})

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

return cmd
}
45 changes: 45 additions & 0 deletions commands/dns/quota/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package quota

import (
"context"
"fmt"

"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"
)

func Get() *core.Command {
cmd := core.NewCommand(context.Background(), nil, core.CommandBuilder{
Namespace: "dns",
Resource: "quota",
Verb: "get",
Aliases: []string{"g"},
ShortDesc: "Retrieve your quotas",
Example: "ionosctl dns quota get",
CmdRun: func(c *core.CommandConfig) error {
q, _, err := client.Must().DnsClient.QuotaApi.QuotaGet(context.Background()).Execute()
if err != nil {
return err
}

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

fmt.Fprintf(c.Command.Command.OutOrStdout(), out)
return nil
},
InitClient: true,
})

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

return cmd
}
Loading

0 comments on commit f5d987f

Please sign in to comment.