Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
9 changes: 9 additions & 0 deletions pkg/api/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,15 @@ type Interface interface {
GetObjectStoreKey(i *fastly.GetObjectStoreKeyInput) (string, error)
InsertObjectStoreKey(i *fastly.InsertObjectStoreKeyInput) error

CreateSecretStore(i *fastly.CreateSecretStoreInput) (*fastly.SecretStore, error)
GetSecretStore(i *fastly.GetSecretStoreInput) (*fastly.SecretStore, error)
DeleteSecretStore(i *fastly.DeleteSecretStoreInput) error
ListSecretStores(i *fastly.ListSecretStoresInput) (*fastly.SecretStores, error)
CreateSecret(i *fastly.CreateSecretInput) (*fastly.Secret, error)
GetSecret(i *fastly.GetSecretInput) (*fastly.Secret, error)
DeleteSecret(i *fastly.DeleteSecretInput) error
ListSecrets(i *fastly.ListSecretsInput) (*fastly.Secrets, error)

CreateResource(i *fastly.CreateResourceInput) (*fastly.Resource, error)
}

Expand Down
19 changes: 19 additions & 0 deletions pkg/app/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import (
"github.com/fastly/cli/pkg/commands/pop"
"github.com/fastly/cli/pkg/commands/profile"
"github.com/fastly/cli/pkg/commands/purge"
"github.com/fastly/cli/pkg/commands/secretstore"
"github.com/fastly/cli/pkg/commands/service"
"github.com/fastly/cli/pkg/commands/serviceauth"
"github.com/fastly/cli/pkg/commands/serviceversion"
Expand Down Expand Up @@ -312,6 +313,16 @@ func defineCommands(
profileToken := profile.NewTokenCommand(profileCmdRoot.CmdClause, globals)
profileUpdate := profile.NewUpdateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), globals)
purgeCmdRoot := purge.NewRootCommand(app, globals, data)
secretstoreCmdRoot := secretstore.NewStoreRootCommand(app, globals)
secretstoreCreateStore := secretstore.NewCreateStoreCommand(secretstoreCmdRoot.CmdClause, globals, data)
secretstoreGetStore := secretstore.NewDescribeStoreCommand(secretstoreCmdRoot.CmdClause, globals, data)
secretstoreDeleteStore := secretstore.NewDeleteStoreCommand(secretstoreCmdRoot.CmdClause, globals, data)
secretstoreListStores := secretstore.NewListStoresCommand(secretstoreCmdRoot.CmdClause, globals, data)
secretstoreSecretCmdRoot := secretstore.NewSecretRootCommand(app, globals)
secretstoreCreateSecret := secretstore.NewCreateSecretCommand(secretstoreSecretCmdRoot.CmdClause, globals, data)
secretstoreGetSecret := secretstore.NewDescribeSecretCommand(secretstoreSecretCmdRoot.CmdClause, globals, data)
secretstoreDeleteSecret := secretstore.NewDeleteSecretCommand(secretstoreSecretCmdRoot.CmdClause, globals, data)
secretstoreListSecrets := secretstore.NewListSecretsCommand(secretstoreSecretCmdRoot.CmdClause, globals, data)
serviceCmdRoot := service.NewRootCommand(app, globals)
serviceCreate := service.NewCreateCommand(serviceCmdRoot.CmdClause, globals)
serviceDelete := service.NewDeleteCommand(serviceCmdRoot.CmdClause, globals, data)
Expand Down Expand Up @@ -630,6 +641,14 @@ func defineCommands(
profileToken,
profileUpdate,
purgeCmdRoot,
secretstoreCreateStore,
secretstoreGetStore,
secretstoreDeleteStore,
secretstoreListStores,
secretstoreCreateSecret,
secretstoreGetSecret,
secretstoreDeleteSecret,
secretstoreListSecrets,
serviceCmdRoot,
serviceCreate,
serviceDelete,
Expand Down
2 changes: 1 addition & 1 deletion pkg/app/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func Run(opts RunOpts) error {
return err
}

// If we are using the token from config file, check the files permissions
// If we are using the token from config file, check the file's permissions
// to assert if they are not too open or have been altered outside of the
// application and warn if so.
segs := strings.Split(name, " ")
Expand Down
2 changes: 2 additions & 0 deletions pkg/app/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ objectstore
pops
profile
purge
secret-store
secret-store-entry
service
service-auth
service-version
Expand Down
31 changes: 30 additions & 1 deletion pkg/cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type BoolFlagOpts struct {

// RegisterFlagBool defines a boolean flag.
//
// TODO: Use generics support in go 1.18 to remove the need for two functions.
// TODO: Use generics support in go 1.18 to remove the need for multiple functions.
func (b Base) RegisterFlagBool(opts BoolFlagOpts) {
clause := b.CmdClause.Flag(opts.Name, opts.Description)
if opts.Short > 0 {
Expand All @@ -76,6 +76,35 @@ func (b Base) RegisterFlagBool(opts BoolFlagOpts) {
clause.BoolVar(opts.Dst)
}

// IntFlagOpts enables easy configuration of a flag.
type IntFlagOpts struct {
Action kingpin.Action
Default int
Description string
Dst *int
Name string
Required bool
Short rune
}

// RegisterFlagInt defines an integer flag.
func (b Base) RegisterFlagInt(opts IntFlagOpts) {
clause := b.CmdClause.Flag(opts.Name, opts.Description)
if opts.Short > 0 {
clause = clause.Short(opts.Short)
}
if opts.Required {
clause = clause.Required()
}
if opts.Action != nil {
clause = clause.Action(opts.Action)
}
if opts.Default != 0 {
clause = clause.Default(strconv.Itoa(opts.Default))
}
clause.IntVar(opts.Dst)
}

// OptionalServiceVersion represents a Fastly service version.
type OptionalServiceVersion struct {
OptionalString
Expand Down
127 changes: 127 additions & 0 deletions pkg/commands/secretstore/createsecret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package secretstore

import (
"bytes"
"encoding/hex"
"fmt"
"io"
"os"

"github.com/fastly/cli/pkg/cmd"
"github.com/fastly/cli/pkg/config"
fsterr "github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/manifest"
"github.com/fastly/cli/pkg/text"
"github.com/fastly/go-fastly/v7/fastly"
)

const (
// Maximum secret length, as defined at https://developer.fastly.com/reference/api/secret-store.
maxSecretKiB = 64
maxSecretLen = maxSecretKiB * 1024
)

// NewCreateSecretCommand returns a usable command registered under the parent.
func NewCreateSecretCommand(parent cmd.Registerer, globals *config.Data, data manifest.Data) *CreateSecretCommand {
c := CreateSecretCommand{
Base: cmd.Base{
Globals: globals,
},
manifest: data,
}

c.CmdClause = parent.Command("create", "Create a new secret within specified store")

// Required.
c.RegisterFlag(secretNameFlag(&c.Input.Name)) // --name
c.RegisterFlag(storeIDFlag(&c.Input.ID)) // --store-id

// Optional.
c.RegisterFlag(secretFileFlag(&c.secretFile)) // --file
c.RegisterFlagBool(c.jsonFlag()) // --json
c.RegisterFlagBool(secretStdinFlag(&c.secretSTDIN)) // --stdin

return &c
}

// CreateSecretCommand calls the Fastly API to create an appropriate resource.
type CreateSecretCommand struct {
cmd.Base
jsonOutput

Input fastly.CreateSecretInput
manifest manifest.Data
secretFile string
secretSTDIN bool
}

var errMultipleSecretValue = fsterr.RemediationError{
Inner: fmt.Errorf("invalid flag combination, --file and --stdin"),
Remediation: "Use one of --file or --stdin flag",
}

var errNoSTDINData = fsterr.RemediationError{
Inner: fmt.Errorf("unable to read from STDIN"),
Remediation: "Provide data to STDIN, or use --file to read from a file",
}

var errMaxSecretLength = fsterr.RemediationError{
Inner: fmt.Errorf("max secret size exceeded"),
Remediation: fmt.Sprintf("Maximum secret size is %dKiB", maxSecretKiB),
}

// Exec invokes the application logic for the command.
func (cmd *CreateSecretCommand) Exec(in io.Reader, out io.Writer) error {
if cmd.Globals.Verbose() && cmd.jsonOutput.enabled {
return fsterr.ErrInvalidVerboseJSONCombo
}
if cmd.secretFile != "" && cmd.secretSTDIN {
return errMultipleSecretValue
}

// Read secret's value: either from STDIN, a file, or prompt.
switch {
case cmd.secretSTDIN:
// Determine if 'in' has data available.
if in == nil || text.IsTTY(in) {
return errNoSTDINData
}
var buf bytes.Buffer
if _, err := buf.ReadFrom(in); err != nil {
return err
}
cmd.Input.Secret = buf.Bytes()

case cmd.secretFile != "":
var err error
if cmd.Input.Secret, err = os.ReadFile(cmd.secretFile); err != nil {
return err
}

default:
secret, err := text.InputSecure(out, "Secret: ", in)
if err != nil {
return err
}
cmd.Input.Secret = []byte(secret)
}

if len(cmd.Input.Secret) > maxSecretLen {
return errMaxSecretLength
}

o, err := cmd.Globals.APIClient.CreateSecret(&cmd.Input)
if err != nil {
cmd.Globals.ErrLog.Add(err)
return err
}

// TODO: Use this approach across the code base.
if ok, err := cmd.WriteJSON(out, o); ok {
return err
}

text.Success(out, "Created secret %s in store %s (digest %s)", o.Name, cmd.Input.ID, hex.EncodeToString(o.Digest))

return nil
}
62 changes: 62 additions & 0 deletions pkg/commands/secretstore/createstore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package secretstore

import (
"io"

"github.com/fastly/cli/pkg/cmd"
"github.com/fastly/cli/pkg/config"
fsterr "github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/manifest"
"github.com/fastly/cli/pkg/text"
"github.com/fastly/go-fastly/v7/fastly"
)

// NewCreateStoreCommand returns a usable command registered under the parent.
func NewCreateStoreCommand(parent cmd.Registerer, globals *config.Data, data manifest.Data) *CreateStoreCommand {
c := CreateStoreCommand{
Base: cmd.Base{
Globals: globals,
},
manifest: data,
}

c.CmdClause = parent.Command("create", "Create a new secret store")

// Required.
c.RegisterFlag(storeNameFlag(&c.Input.Name)) // --name

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

return &c
}

// CreateStoreCommand calls the Fastly API to create an appropriate resource.
type CreateStoreCommand struct {
cmd.Base
jsonOutput

Input fastly.CreateSecretStoreInput
manifest manifest.Data
}

// Exec invokes the application logic for the command.
func (cmd *CreateStoreCommand) Exec(_ io.Reader, out io.Writer) error {
if cmd.Globals.Verbose() && cmd.jsonOutput.enabled {
return fsterr.ErrInvalidVerboseJSONCombo
}

o, err := cmd.Globals.APIClient.CreateSecretStore(&cmd.Input)
if err != nil {
cmd.Globals.ErrLog.Add(err)
return err
}

if ok, err := cmd.WriteJSON(out, o); ok {
return err
}

text.Success(out, "Created secret store %s (name %s)", o.ID, o.Name)

return nil
}
73 changes: 73 additions & 0 deletions pkg/commands/secretstore/deletesecret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package secretstore

import (
"io"

"github.com/fastly/cli/pkg/cmd"
"github.com/fastly/cli/pkg/config"
fsterr "github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/manifest"
"github.com/fastly/cli/pkg/text"
"github.com/fastly/go-fastly/v7/fastly"
)

// NewDeleteSecretCommand returns a usable command registered under the parent.
func NewDeleteSecretCommand(parent cmd.Registerer, globals *config.Data, data manifest.Data) *DeleteSecretCommand {
c := DeleteSecretCommand{
Base: cmd.Base{
Globals: globals,
},
manifest: data,
}

c.CmdClause = parent.Command("delete", "Delete a secret")

// Required.
c.RegisterFlag(secretNameFlag(&c.Input.Name)) // --name
c.RegisterFlag(storeIDFlag(&c.Input.ID)) // --store-id

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

return &c
}

// DeleteSecretCommand calls the Fastly API to delete an appropriate resource.
type DeleteSecretCommand struct {
cmd.Base
jsonOutput

Input fastly.DeleteSecretInput
manifest manifest.Data
}

// Exec invokes the application logic for the command.
func (cmd *DeleteSecretCommand) Exec(_ io.Reader, out io.Writer) error {
if cmd.Globals.Verbose() && cmd.jsonOutput.enabled {
return fsterr.ErrInvalidVerboseJSONCombo
}

err := cmd.Globals.APIClient.DeleteSecret(&cmd.Input)
if err != nil {
cmd.Globals.ErrLog.Add(err)
return err
}

if cmd.jsonOutput.enabled {
o := struct {
Name string `json:"name"`
ID string `json:"store_id"`
Deleted bool `json:"deleted"`
}{
cmd.Input.Name,
cmd.Input.ID,
true,
}
_, err := cmd.WriteJSON(out, o)
return err
}

text.Success(out, "Deleted secret %s from store %s", cmd.Input.Name, cmd.Input.ID)

return nil
}
Loading