Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- fix(docs): corrected stale and missing API reference links in usage.json metadata([#1803](https://github.com/fastly/cli/pull/1803))

### Enhancements:
- feat(dns): add support for DNS Zones and TSIG Keys ([#1809](https://github.com/fastly/cli/pull/1809))
- fix(compute/init): Add starter kits for C++ language [#1807](https://github.com/fastly/cli/pull/1807)

### Dependencies:
Expand Down
1 change: 1 addition & 0 deletions pkg/app/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ config
config-store
config-store-entry
dashboard
dns
domain
install
ip-list
Expand Down
29 changes: 29 additions & 0 deletions pkg/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ import (
"github.com/fastly/cli/pkg/commands/configstoreentry"
"github.com/fastly/cli/pkg/commands/dashboard"
dashboardItem "github.com/fastly/cli/pkg/commands/dashboard/item"
"github.com/fastly/cli/pkg/commands/dns"
dnstsigkey "github.com/fastly/cli/pkg/commands/dns/tsigkey"
dnszone "github.com/fastly/cli/pkg/commands/dns/zone"
"github.com/fastly/cli/pkg/commands/domain"
"github.com/fastly/cli/pkg/commands/install"
"github.com/fastly/cli/pkg/commands/ip"
Expand Down Expand Up @@ -282,6 +285,19 @@ func Define( // nolint:revive // function-length
dashboardItemDescribe := dashboardItem.NewDescribeCommand(dashboardItemCmdRoot.CmdClause, data)
dashboardItemUpdate := dashboardItem.NewUpdateCommand(dashboardItemCmdRoot.CmdClause, data)
dashboardItemDelete := dashboardItem.NewDeleteCommand(dashboardItemCmdRoot.CmdClause, data)
dnsCmdRoot := dns.NewRootCommand(app, data)
dnsTSIGKeyCmdRoot := dnstsigkey.NewRootCommand(dnsCmdRoot.CmdClause, data)
dnsTSIGKeyCreate := dnstsigkey.NewCreateCommand(dnsTSIGKeyCmdRoot.CmdClause, data)
dnsTSIGKeyDelete := dnstsigkey.NewDeleteCommand(dnsTSIGKeyCmdRoot.CmdClause, data)
dnsTSIGKeyDescribe := dnstsigkey.NewDescribeCommand(dnsTSIGKeyCmdRoot.CmdClause, data)
dnsTSIGKeyList := dnstsigkey.NewListCommand(dnsTSIGKeyCmdRoot.CmdClause, data)
dnsTSIGKeyUpdate := dnstsigkey.NewUpdateCommand(dnsTSIGKeyCmdRoot.CmdClause, data)
dnsZoneCmdRoot := dnszone.NewRootCommand(dnsCmdRoot.CmdClause, data)
dnsZoneCreate := dnszone.NewCreateCommand(dnsZoneCmdRoot.CmdClause, data)
dnsZoneDelete := dnszone.NewDeleteCommand(dnsZoneCmdRoot.CmdClause, data)
dnsZoneDescribe := dnszone.NewDescribeCommand(dnsZoneCmdRoot.CmdClause, data)
dnsZoneList := dnszone.NewListCommand(dnsZoneCmdRoot.CmdClause, data)
dnsZoneUpdate := dnszone.NewUpdateCommand(dnsZoneCmdRoot.CmdClause, data)
domainCmdRoot := domain.NewRootCommand(app, data)
domainCreate := domain.NewCreateCommand(domainCmdRoot.CmdClause, data)
domainDelete := domain.NewDeleteCommand(domainCmdRoot.CmdClause, data)
Expand Down Expand Up @@ -1156,6 +1172,19 @@ func Define( // nolint:revive // function-length
dashboardItemDescribe,
dashboardItemUpdate,
dashboardItemDelete,
dnsCmdRoot,
dnsTSIGKeyCmdRoot,
dnsTSIGKeyCreate,
dnsTSIGKeyDelete,
dnsTSIGKeyDescribe,
dnsTSIGKeyList,
dnsTSIGKeyUpdate,
dnsZoneCmdRoot,
dnsZoneCreate,
dnsZoneDelete,
dnsZoneDescribe,
dnsZoneList,
dnsZoneUpdate,
domainCmdRoot,
domainCreate,
domainDelete,
Expand Down
2 changes: 2 additions & 0 deletions pkg/commands/dns/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package dns contains commands to inspect and manipulate Fastly DNS Zones and TSIG Keys.
package dns
31 changes: 31 additions & 0 deletions pkg/commands/dns/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package dns

import (
"io"

"github.com/fastly/cli/pkg/argparser"
"github.com/fastly/cli/pkg/global"
)

// RootCommand is the parent command for all subcommands in this package.
// It should be installed under the primary root command.
type RootCommand struct {
argparser.Base
// no flags
}

// CommandName is the string to be used to invoke this command.
const CommandName = "dns"

// NewRootCommand returns a new command registered in the parent.
func NewRootCommand(parent argparser.Registerer, g *global.Data) *RootCommand {
var c RootCommand
c.Globals = g
c.CmdClause = parent.Command(CommandName, "Manipulate Fastly DNS Zones and TSIG Keys")
return &c
}

// Exec implements the command interface.
func (c *RootCommand) Exec(_ io.Reader, _ io.Writer) error {
panic("unreachable")
}
17 changes: 17 additions & 0 deletions pkg/commands/dns/tsigkey/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package tsigkey

// SortOptions are the valid values for the --sort flag.
// Since Kingpin interprets any '-' as a flag, we'll need to remap the API enums
// to more friendly values and then send back to the API accordingly.
var SortOptions = []string{"name_asc", "name_desc", "created_at_asc", "created_at_desc"}

// sortAPIValue maps CLI sort values to the API sort parameter.
var sortAPIValue = map[string]string{
"name_asc": "name",
"name_desc": "-name",
"created_at_asc": "created_at",
"created_at_desc": "-created_at",
}

// AlgorithmOptions are the valid values for the --algorithm flag.
var AlgorithmOptions = []string{"hmac-sha224", "hmac-sha256", "hmac-sha384", "hmac-sha512"}
95 changes: 95 additions & 0 deletions pkg/commands/dns/tsigkey/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package tsigkey

import (
"context"
"errors"
"fmt"
"io"
"strings"

"github.com/fastly/go-fastly/v15/fastly"
"github.com/fastly/go-fastly/v15/fastly/dns/v1/tsigkeys"

"github.com/fastly/cli/pkg/argparser"
fsterr "github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/global"
"github.com/fastly/cli/pkg/text"
)

// CreateCommand calls the Fastly API to create a TSIG key.
type CreateCommand struct {
argparser.Base
argparser.JSONOutput

// Required.
name string
algorithm string
secret string

// Optional.
description argparser.OptionalString
}

// NewCreateCommand returns a usable command registered under the parent.
func NewCreateCommand(parent argparser.Registerer, g *global.Data) *CreateCommand {
c := CreateCommand{
Base: argparser.Base{
Globals: g,
},
}
c.CmdClause = parent.Command("create", "Create a TSIG key").Alias("add")

// Required.
c.CmdClause.Flag("name", "The name of the TSIG key.").Required().StringVar(&c.name)
c.CmdClause.Flag("algorithm", "The algorithm of the TSIG key. Valid values are: hmac-sha224, hmac-sha256, hmac-sha384, hmac-sha512.").Required().HintOptions(AlgorithmOptions...).EnumVar(&c.algorithm, AlgorithmOptions...)
c.CmdClause.Flag("secret", "The Base64 encoded secret key.").Required().StringVar(&c.secret)

// Optional.
c.CmdClause.Flag("description", "A freeform descriptive note.").Action(c.description.Set).StringVar(&c.description.Value)
c.RegisterFlagBool(c.JSONFlag()) // --json

return &c
}

// Exec invokes the application logic for the command.
func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error {
if c.Globals.Verbose() && c.JSONOutput.Enabled {
return fsterr.ErrInvalidVerboseJSONCombo
}

if strings.Contains(c.name, " ") {
return fmt.Errorf("invalid --name value %q: TSIG key names cannot contain spaces", c.name)
}
if len(c.name) > 255 {
return fmt.Errorf("invalid --name value %q: TSIG key names cannot exceed 255 characters", c.name)
}

fc, ok := c.Globals.APIClient.(*fastly.Client)
if !ok {
return errors.New("failed to convert interface to a fastly client")
}

input := &tsigkeys.CreateInput{
Name: &c.name,
Algorithm: &c.algorithm,
Secret: &c.secret,
}
if c.description.WasSet {
input.Description = &c.description.Value
}

k, err := tsigkeys.Create(context.TODO(), fc, input)
if err != nil {
c.Globals.ErrLog.AddWithContext(err, map[string]any{
"Name": c.name,
})
return err
}

if ok, err := c.WriteJSON(out, k); ok {
return err
}

text.Success(out, "Created TSIG key '%s' (tsig-key-id: %s)", *k.Name, *k.ID)
return nil
}
75 changes: 75 additions & 0 deletions pkg/commands/dns/tsigkey/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package tsigkey

import (
"context"
"errors"
"io"

"github.com/fastly/go-fastly/v15/fastly"
"github.com/fastly/go-fastly/v15/fastly/dns/v1/tsigkeys"

"github.com/fastly/cli/pkg/argparser"
fsterr "github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/global"
"github.com/fastly/cli/pkg/text"
)

// DeleteCommand calls the Fastly API to delete a TSIG key.
type DeleteCommand struct {
argparser.Base
argparser.JSONOutput

tsigKeyID string
}

// NewDeleteCommand returns a usable command registered under the parent.
func NewDeleteCommand(parent argparser.Registerer, g *global.Data) *DeleteCommand {
c := DeleteCommand{
Base: argparser.Base{
Globals: g,
},
}
c.CmdClause = parent.Command("delete", "Delete a TSIG key").Alias("remove")

// Required.
c.CmdClause.Flag("tsig-key-id", "The TSIG key ID to delete.").Required().StringVar(&c.tsigKeyID)

// Optional.
c.RegisterFlagBool(c.JSONFlag()) // --json

return &c
}

// Exec invokes the application logic for the command.
func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error {
if c.Globals.Verbose() && c.JSONOutput.Enabled {
return fsterr.ErrInvalidVerboseJSONCombo
}

fc, ok := c.Globals.APIClient.(*fastly.Client)
if !ok {
return errors.New("failed to convert interface to a fastly client")
}

err := tsigkeys.Delete(context.TODO(), fc, &tsigkeys.DeleteInput{
TSIGKeyID: &c.tsigKeyID,
})
if err != nil {
c.Globals.ErrLog.AddWithContext(err, map[string]any{
"TSIG Key ID": c.tsigKeyID,
})
return err
}

if c.JSONOutput.Enabled {
o := struct {
ID string `json:"id"`
Deleted bool `json:"deleted"`
}{c.tsigKeyID, true}
_, err := c.WriteJSON(out, o)
return err
}

text.Success(out, "Deleted TSIG key (tsig-key-id: %s)", c.tsigKeyID)
return nil
}
70 changes: 70 additions & 0 deletions pkg/commands/dns/tsigkey/describe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package tsigkey

import (
"context"
"errors"
"io"

"github.com/fastly/go-fastly/v15/fastly"
"github.com/fastly/go-fastly/v15/fastly/dns/v1/tsigkeys"

"github.com/fastly/cli/pkg/argparser"
fsterr "github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/global"
"github.com/fastly/cli/pkg/text"
)

// DescribeCommand calls the Fastly API to describe a TSIG key.
type DescribeCommand struct {
argparser.Base
argparser.JSONOutput

tsigKeyID string
}

// NewDescribeCommand returns a usable command registered under the parent.
func NewDescribeCommand(parent argparser.Registerer, g *global.Data) *DescribeCommand {
c := DescribeCommand{
Base: argparser.Base{
Globals: g,
},
}
c.CmdClause = parent.Command("describe", "Describe a TSIG key").Alias("get")

// Required.
c.CmdClause.Flag("tsig-key-id", "The TSIG key ID to describe.").Required().StringVar(&c.tsigKeyID)

// Optional.
c.RegisterFlagBool(c.JSONFlag()) // --json

return &c
}

// Exec invokes the application logic for the command.
func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error {
if c.Globals.Verbose() && c.JSONOutput.Enabled {
return fsterr.ErrInvalidVerboseJSONCombo
}

fc, ok := c.Globals.APIClient.(*fastly.Client)
if !ok {
return errors.New("failed to convert interface to a fastly client")
}

k, err := tsigkeys.Get(context.TODO(), fc, &tsigkeys.GetInput{
TSIGKeyID: &c.tsigKeyID,
})
if err != nil {
c.Globals.ErrLog.AddWithContext(err, map[string]any{
"TSIG Key ID": c.tsigKeyID,
})
return err
}

if ok, err := c.WriteJSON(out, k); ok {
return err
}

text.PrintTSIGKey(out, "", k)
return nil
}
Loading