Skip to content

Commit

Permalink
feat(compute/deploy): add Secret Store to manifest [setup] (#769)
Browse files Browse the repository at this point in the history
Adds ability to define Secret Stores in the `[setup]` section of a `fastly.toml` config file.
The user will be prompted to create the defined stores and secrets on creation of a service when using `fastly compute deploy`.

Related: #717, #764
  • Loading branch information
awilliams-fastly authored Apr 13, 2023
1 parent 92952d8 commit e977ad3
Show file tree
Hide file tree
Showing 3 changed files with 297 additions and 73 deletions.
153 changes: 84 additions & 69 deletions pkg/commands/compute/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,15 @@ func (c *DeployCommand) Exec(in io.Reader, out io.Writer) (err error) {
return nil
}

domains, backends, dictionaries, loggers, kvStores, err := constructSetupKVs(
so, err := constructSetupObjects(
newService, serviceID, serviceVersion.Number, c, in, out,
)
if err != nil {
return err
}

if err := processSetupConfig(
newService, domains, backends, dictionaries, loggers, kvStores,
serviceID, serviceVersion.Number, c,
if err = processSetupConfig(
newService, so, serviceID, serviceVersion.Number, c,
); err != nil {
return err
}
Expand All @@ -147,9 +146,8 @@ func (c *DeployCommand) Exec(in io.Reader, out io.Writer) (err error) {
undoStack.RunIfError(out, err)
}(c.Globals.ErrLog)

if err := processSetupCreation(
newService, domains, backends, dictionaries, kvStores, spinner, c,
serviceID, serviceVersion.Number,
if err = processSetupCreation(
newService, so, spinner, c, serviceID, serviceVersion.Number,
); err != nil {
return err
}
Expand All @@ -164,7 +162,7 @@ func (c *DeployCommand) Exec(in io.Reader, out io.Writer) (err error) {
return nil
}

if err := processService(c, serviceID, serviceVersion.Number, spinner); err != nil {
if err = processService(c, serviceID, serviceVersion.Number, spinner); err != nil {
return err
}

Expand Down Expand Up @@ -839,37 +837,43 @@ func pkgUpload(spinner text.Spinner, client api.Interface, serviceID string, ver
return spinner.Stop()
}

func constructSetupKVs(
// setupObjects is a collection of backend objects created during setup.
// Objects may be nil.
type setupObjects struct {
domains *setup.Domains
backends *setup.Backends
dictionaries *setup.Dictionaries
loggers *setup.Loggers
kvStores *setup.KVStores
secretStores *setup.SecretStores
}

func constructSetupObjects(
newService bool,
serviceID string,
serviceVersion int,
c *DeployCommand,
in io.Reader,
out io.Writer,
) (
*setup.Domains,
*setup.Backends,
*setup.Dictionaries,
*setup.Loggers,
*setup.KVStores,
error,
) {
var err error
) (setupObjects, error) {
var (
so setupObjects
err error
)

// We only check the Service ID is valid when handling an existing service.
if !newService {
err = checkServiceID(serviceID, c.Globals.APIClient)
if err != nil {
if err = checkServiceID(serviceID, c.Globals.APIClient); err != nil {
errLogService(c.Globals.ErrLog, err, serviceID, serviceVersion)
return nil, nil, nil, nil, nil, err
return setupObjects{}, err
}
}

// Because a service_id exists in the fastly.toml doesn't mean it's valid
// e.g. it could be missing required resources such as a domain or backend.
// We check and allow the user to configure these settings before continuing.

domains := &setup.Domains{
so.domains = &setup.Domains{
APIClient: c.Globals.APIClient,
AcceptDefaults: c.Globals.Flags.AcceptDefaults,
NonInteractive: c.Globals.Flags.NonInteractive,
Expand All @@ -882,21 +886,13 @@ func constructSetupKVs(
Verbose: c.Globals.Verbose(),
}

err = domains.Validate()
if err != nil {
if err = so.domains.Validate(); err != nil {
errLogService(c.Globals.ErrLog, err, serviceID, serviceVersion)
return nil, nil, nil, nil, nil, fmt.Errorf("error configuring service domains: %w", err)
return setupObjects{}, fmt.Errorf("error configuring service domains: %w", err)
}

var (
backends *setup.Backends
dictionaries *setup.Dictionaries
loggers *setup.Loggers
kvStores *setup.KVStores
)

if newService {
backends = &setup.Backends{
so.backends = &setup.Backends{
APIClient: c.Globals.APIClient,
AcceptDefaults: c.Globals.Flags.AcceptDefaults,
NonInteractive: c.Globals.Flags.NonInteractive,
Expand All @@ -907,7 +903,7 @@ func constructSetupKVs(
Stdout: out,
}

dictionaries = &setup.Dictionaries{
so.dictionaries = &setup.Dictionaries{
APIClient: c.Globals.APIClient,
AcceptDefaults: c.Globals.Flags.AcceptDefaults,
NonInteractive: c.Globals.Flags.NonInteractive,
Expand All @@ -918,12 +914,12 @@ func constructSetupKVs(
Stdout: out,
}

loggers = &setup.Loggers{
so.loggers = &setup.Loggers{
Setup: c.Manifest.File.Setup.Loggers,
Stdout: out,
}

kvStores = &setup.KVStores{
so.kvStores = &setup.KVStores{
APIClient: c.Globals.APIClient,
AcceptDefaults: c.Globals.Flags.AcceptDefaults,
NonInteractive: c.Globals.Flags.NonInteractive,
Expand All @@ -933,25 +929,31 @@ func constructSetupKVs(
Stdin: in,
Stdout: out,
}

so.secretStores = &setup.SecretStores{
APIClient: c.Globals.APIClient,
AcceptDefaults: c.Globals.Flags.AcceptDefaults,
NonInteractive: c.Globals.Flags.NonInteractive,
ServiceID: serviceID,
ServiceVersion: serviceVersion,
Setup: c.Manifest.File.Setup.SecretStores,
Stdin: in,
Stdout: out,
}
}

return domains, backends, dictionaries, loggers, kvStores, nil
return so, nil
}

func processSetupConfig(
newService bool,
domains *setup.Domains,
backends *setup.Backends,
dictionaries *setup.Dictionaries,
loggers *setup.Loggers,
kvStores *setup.KVStores,
so setupObjects,
serviceID string,
serviceVersion int,
c *DeployCommand,
) (err error) {
if domains.Missing() {
err = domains.Configure()
if err != nil {
) error {
if so.domains.Missing() {
if err := so.domains.Configure(); err != nil {
errLogService(c.Globals.ErrLog, err, serviceID, serviceVersion)
return fmt.Errorf("error configuring service domains: %w", err)
}
Expand All @@ -965,57 +967,58 @@ func processSetupConfig(
// the .Predefined() method, as the call to .Configure() will ensure the
// user is prompted regardless of whether there is a [setup.backends]
// defined in the fastly.toml configuration.
err = backends.Configure()
if err != nil {
if err := so.backends.Configure(); err != nil {
errLogService(c.Globals.ErrLog, err, serviceID, serviceVersion)
return fmt.Errorf("error configuring service backends: %w", err)
}

if dictionaries.Predefined() {
err = dictionaries.Configure()
if err != nil {
if so.dictionaries.Predefined() {
if err := so.dictionaries.Configure(); err != nil {
errLogService(c.Globals.ErrLog, err, serviceID, serviceVersion)
return fmt.Errorf("error configuring service dictionaries: %w", err)
}
}

if loggers.Predefined() {
if so.loggers.Predefined() {
// NOTE: We don't handle errors from the Configure() method because we
// don't actually do anything other than display a message to the user
// informing them that they need to create a log endpoint and which
// provider type they should be. The reason we don't implement logic for
// creating logging objects is because the API input fields vary
// significantly between providers.
_ = loggers.Configure()
_ = so.loggers.Configure()
}

if kvStores.Predefined() {
err = kvStores.Configure()
if err != nil {
if so.kvStores.Predefined() {
if err := so.kvStores.Configure(); err != nil {
errLogService(c.Globals.ErrLog, err, serviceID, serviceVersion)
return fmt.Errorf("error configuring service kv stores: %w", err)
}
}

if so.secretStores.Predefined() {
if err := so.secretStores.Configure(); err != nil {
errLogService(c.Globals.ErrLog, err, serviceID, serviceVersion)
return fmt.Errorf("error configuring service secret stores: %w", err)
}
}
}

return nil
}

func processSetupCreation(
newService bool,
domains *setup.Domains,
backends *setup.Backends,
dictionaries *setup.Dictionaries,
kvStores *setup.KVStores,
so setupObjects,
spinner text.Spinner,
c *DeployCommand,
serviceID string,
serviceVersion int,
) error {
if domains.Missing() {
domains.Spinner = spinner
if so.domains.Missing() {
so.domains.Spinner = spinner

if err := domains.Create(); err != nil {
if err := so.domains.Create(); err != nil {
c.Globals.ErrLog.AddWithContext(err, map[string]any{
"Accept defaults": c.Globals.Flags.AcceptDefaults,
"Auto-yes": c.Globals.Flags.AutoYes,
Expand All @@ -1030,11 +1033,23 @@ func processSetupCreation(
// IMPORTANT: The pointer refs in this block are not checked for nil.
// We presume if we're dealing with newService they have been set.
if newService {
backends.Spinner = spinner
dictionaries.Spinner = spinner
kvStores.Spinner = spinner
so.backends.Spinner = spinner
so.dictionaries.Spinner = spinner
so.kvStores.Spinner = spinner
so.secretStores.Spinner = spinner

if err := so.backends.Create(); err != nil {
c.Globals.ErrLog.AddWithContext(err, map[string]any{
"Accept defaults": c.Globals.Flags.AcceptDefaults,
"Auto-yes": c.Globals.Flags.AutoYes,
"Non-interactive": c.Globals.Flags.NonInteractive,
"Service ID": serviceID,
"Service Version": serviceVersion,
})
return err
}

if err := backends.Create(); err != nil {
if err := so.dictionaries.Create(); err != nil {
c.Globals.ErrLog.AddWithContext(err, map[string]any{
"Accept defaults": c.Globals.Flags.AcceptDefaults,
"Auto-yes": c.Globals.Flags.AutoYes,
Expand All @@ -1045,7 +1060,7 @@ func processSetupCreation(
return err
}

if err := dictionaries.Create(); err != nil {
if err := so.kvStores.Create(); err != nil {
c.Globals.ErrLog.AddWithContext(err, map[string]any{
"Accept defaults": c.Globals.Flags.AcceptDefaults,
"Auto-yes": c.Globals.Flags.AutoYes,
Expand All @@ -1056,7 +1071,7 @@ func processSetupCreation(
return err
}

if err := kvStores.Create(); err != nil {
if err := so.secretStores.Create(); err != nil {
c.Globals.ErrLog.AddWithContext(err, map[string]any{
"Accept defaults": c.Globals.Flags.AcceptDefaults,
"Auto-yes": c.Globals.Flags.AutoYes,
Expand Down
Loading

0 comments on commit e977ad3

Please sign in to comment.