forked from toolshed/abra
		
	Closes coop-cloud/organising#389. Closes coop-cloud/organising#341. Closes coop-cloud/organising#326. Closes coop-cloud/organising#380. Closes coop-cloud/organising#360.
		
			
				
	
	
		
			359 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			359 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package app
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"strconv"
 | 
						|
 | 
						|
	"coopcloud.tech/abra/cli/internal"
 | 
						|
	"coopcloud.tech/abra/pkg/autocomplete"
 | 
						|
	"coopcloud.tech/abra/pkg/client"
 | 
						|
	"coopcloud.tech/abra/pkg/config"
 | 
						|
	"coopcloud.tech/abra/pkg/formatter"
 | 
						|
	"coopcloud.tech/abra/pkg/secret"
 | 
						|
	"github.com/docker/docker/api/types"
 | 
						|
	dockerClient "github.com/docker/docker/client"
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
	"github.com/urfave/cli"
 | 
						|
)
 | 
						|
 | 
						|
var allSecrets bool
 | 
						|
var allSecretsFlag = &cli.BoolFlag{
 | 
						|
	Name:        "all, a",
 | 
						|
	Destination: &allSecrets,
 | 
						|
	Usage:       "Generate all secrets",
 | 
						|
}
 | 
						|
 | 
						|
var rmAllSecrets bool
 | 
						|
var rmAllSecretsFlag = &cli.BoolFlag{
 | 
						|
	Name:        "all, a",
 | 
						|
	Destination: &rmAllSecrets,
 | 
						|
	Usage:       "Remove all secrets",
 | 
						|
}
 | 
						|
 | 
						|
var appSecretGenerateCommand = cli.Command{
 | 
						|
	Name:      "generate",
 | 
						|
	Aliases:   []string{"g"},
 | 
						|
	Usage:     "Generate secrets",
 | 
						|
	ArgsUsage: "<domain> <secret> <version>",
 | 
						|
	Flags: []cli.Flag{
 | 
						|
		internal.DebugFlag,
 | 
						|
		allSecretsFlag,
 | 
						|
		internal.PassFlag,
 | 
						|
	},
 | 
						|
	Before:       internal.SubCommandBefore,
 | 
						|
	BashComplete: autocomplete.AppNameComplete,
 | 
						|
	Action: func(c *cli.Context) error {
 | 
						|
		app := internal.ValidateApp(c)
 | 
						|
 | 
						|
		cl, err := client.New(app.Server)
 | 
						|
		if err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		if len(c.Args()) == 1 && !allSecrets {
 | 
						|
			err := errors.New("missing arguments <secret>/<version> or '--all'")
 | 
						|
			internal.ShowSubcommandHelpAndError(c, err)
 | 
						|
		}
 | 
						|
 | 
						|
		if c.Args().Get(1) != "" && allSecrets {
 | 
						|
			err := errors.New("cannot use '<secret> <version>' and '--all' together")
 | 
						|
			internal.ShowSubcommandHelpAndError(c, err)
 | 
						|
		}
 | 
						|
 | 
						|
		secretsToCreate := make(map[string]string)
 | 
						|
		secretEnvVars := secret.ReadSecretEnvVars(app.Env)
 | 
						|
		if allSecrets {
 | 
						|
			secretsToCreate = secretEnvVars
 | 
						|
		} else {
 | 
						|
			secretName := c.Args().Get(1)
 | 
						|
			secretVersion := c.Args().Get(2)
 | 
						|
			matches := false
 | 
						|
			for sec := range secretEnvVars {
 | 
						|
				parsed := secret.ParseSecretEnvVarName(sec)
 | 
						|
				if secretName == parsed {
 | 
						|
					secretsToCreate[sec] = secretVersion
 | 
						|
					matches = true
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if !matches {
 | 
						|
				logrus.Fatalf("%s doesn't exist in the env config?", secretName)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		secretVals, err := secret.GenerateSecrets(cl, secretsToCreate, app.StackName(), app.Server)
 | 
						|
		if err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		if internal.Pass {
 | 
						|
			for name, data := range secretVals {
 | 
						|
				if err := secret.PassInsertSecret(data, name, app.Name, app.Server); err != nil {
 | 
						|
					logrus.Fatal(err)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if len(secretVals) == 0 {
 | 
						|
			logrus.Warn("no secrets generated")
 | 
						|
			os.Exit(1)
 | 
						|
		}
 | 
						|
 | 
						|
		tableCol := []string{"name", "value"}
 | 
						|
		table := formatter.CreateTable(tableCol)
 | 
						|
		for name, val := range secretVals {
 | 
						|
			table.Append([]string{name, val})
 | 
						|
		}
 | 
						|
		table.Render()
 | 
						|
		logrus.Warn("generated secrets are not shown again, please take note of them *now*")
 | 
						|
 | 
						|
		return nil
 | 
						|
	},
 | 
						|
}
 | 
						|
 | 
						|
var appSecretInsertCommand = cli.Command{
 | 
						|
	Name:    "insert",
 | 
						|
	Aliases: []string{"i"},
 | 
						|
	Usage:   "Insert secret",
 | 
						|
	Flags: []cli.Flag{
 | 
						|
		internal.DebugFlag,
 | 
						|
		internal.PassFlag,
 | 
						|
	},
 | 
						|
	Before:       internal.SubCommandBefore,
 | 
						|
	ArgsUsage:    "<domain> <secret-name> <version> <data>",
 | 
						|
	BashComplete: autocomplete.AppNameComplete,
 | 
						|
	Description: `
 | 
						|
This command inserts a secret into an app environment.
 | 
						|
 | 
						|
This can be useful when you want to manually generate secrets for an app
 | 
						|
environment. Typically, you can let Abra generate them for you on app creation
 | 
						|
(see "abra app new --secrets" for more).
 | 
						|
 | 
						|
Example:
 | 
						|
 | 
						|
    abra app secret insert myapp db_pass v1 mySecretPassword
 | 
						|
 | 
						|
`,
 | 
						|
	Action: func(c *cli.Context) error {
 | 
						|
		app := internal.ValidateApp(c)
 | 
						|
 | 
						|
		cl, err := client.New(app.Server)
 | 
						|
		if err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		if len(c.Args()) != 4 {
 | 
						|
			internal.ShowSubcommandHelpAndError(c, errors.New("missing arguments?"))
 | 
						|
		}
 | 
						|
 | 
						|
		name := c.Args().Get(1)
 | 
						|
		version := c.Args().Get(2)
 | 
						|
		data := c.Args().Get(3)
 | 
						|
 | 
						|
		secretName := fmt.Sprintf("%s_%s_%s", app.StackName(), name, version)
 | 
						|
		if err := client.StoreSecret(cl, secretName, data, app.Server); err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		logrus.Infof("%s successfully stored on server", secretName)
 | 
						|
 | 
						|
		if internal.Pass {
 | 
						|
			if err := secret.PassInsertSecret(data, name, app.Name, app.Server); err != nil {
 | 
						|
				logrus.Fatal(err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return nil
 | 
						|
	},
 | 
						|
}
 | 
						|
 | 
						|
// secretRm removes a secret.
 | 
						|
func secretRm(cl *dockerClient.Client, app config.App, secretName, parsed string) error {
 | 
						|
	if err := cl.SecretRemove(context.Background(), secretName); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	logrus.Infof("deleted %s successfully from server", secretName)
 | 
						|
 | 
						|
	if internal.PassRemove {
 | 
						|
		if err := secret.PassRmSecret(parsed, app.StackName(), app.Server); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		logrus.Infof("deleted %s successfully from local pass store", secretName)
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
var appSecretRmCommand = cli.Command{
 | 
						|
	Name:    "remove",
 | 
						|
	Aliases: []string{"rm"},
 | 
						|
	Usage:   "Remove a secret",
 | 
						|
	Flags: []cli.Flag{
 | 
						|
		internal.DebugFlag,
 | 
						|
		internal.NoInputFlag,
 | 
						|
		rmAllSecretsFlag,
 | 
						|
		internal.PassRemoveFlag,
 | 
						|
	},
 | 
						|
	Before:       internal.SubCommandBefore,
 | 
						|
	ArgsUsage:    "<domain> [<secret-name>]",
 | 
						|
	BashComplete: autocomplete.AppNameComplete,
 | 
						|
	Description: `
 | 
						|
This command removes app secrets.
 | 
						|
 | 
						|
Example:
 | 
						|
 | 
						|
    abra app secret remove myapp db_pass
 | 
						|
`,
 | 
						|
	Action: func(c *cli.Context) error {
 | 
						|
		app := internal.ValidateApp(c)
 | 
						|
		secrets := secret.ReadSecretEnvVars(app.Env)
 | 
						|
 | 
						|
		if c.Args().Get(1) != "" && rmAllSecrets {
 | 
						|
			internal.ShowSubcommandHelpAndError(c, errors.New("cannot use '<secret-name>' and '--all' together"))
 | 
						|
		}
 | 
						|
 | 
						|
		if c.Args().Get(1) == "" && !rmAllSecrets {
 | 
						|
			internal.ShowSubcommandHelpAndError(c, errors.New("no secret(s) specified?"))
 | 
						|
		}
 | 
						|
 | 
						|
		cl, err := client.New(app.Server)
 | 
						|
		if err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		filters, err := app.Filters(false, false)
 | 
						|
		if err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		secretList, err := cl.SecretList(context.Background(), types.SecretListOptions{Filters: filters})
 | 
						|
		if err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		remoteSecretNames := make(map[string]bool)
 | 
						|
		for _, cont := range secretList {
 | 
						|
			remoteSecretNames[cont.Spec.Annotations.Name] = true
 | 
						|
		}
 | 
						|
 | 
						|
		match := false
 | 
						|
		secretToRm := c.Args().Get(1)
 | 
						|
		for sec := range secrets {
 | 
						|
			secretName := secret.ParseSecretEnvVarName(sec)
 | 
						|
 | 
						|
			secVal, err := secret.ParseSecretEnvVarValue(secrets[sec])
 | 
						|
			if err != nil {
 | 
						|
				logrus.Fatal(err)
 | 
						|
			}
 | 
						|
 | 
						|
			secretRemoteName := fmt.Sprintf("%s_%s_%s", app.StackName(), secretName, secVal.Version)
 | 
						|
			if _, ok := remoteSecretNames[secretRemoteName]; ok {
 | 
						|
				if secretToRm != "" {
 | 
						|
					if secretName == secretToRm {
 | 
						|
						if err := secretRm(cl, app, secretRemoteName, secretName); err != nil {
 | 
						|
							logrus.Fatal(err)
 | 
						|
						}
 | 
						|
 | 
						|
						return nil
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					match = true
 | 
						|
 | 
						|
					if err := secretRm(cl, app, secretRemoteName, secretName); err != nil {
 | 
						|
						logrus.Fatal(err)
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if !match && secretToRm != "" {
 | 
						|
			logrus.Fatalf("%s doesn't exist on server?", secretToRm)
 | 
						|
		}
 | 
						|
 | 
						|
		if !match {
 | 
						|
			logrus.Fatal("no secrets to remove?")
 | 
						|
		}
 | 
						|
 | 
						|
		return nil
 | 
						|
	},
 | 
						|
}
 | 
						|
 | 
						|
var appSecretLsCommand = cli.Command{
 | 
						|
	Name:    "list",
 | 
						|
	Aliases: []string{"ls"},
 | 
						|
	Flags: []cli.Flag{
 | 
						|
		internal.DebugFlag,
 | 
						|
	},
 | 
						|
	Before: internal.SubCommandBefore,
 | 
						|
	Usage:  "List all secrets",
 | 
						|
	Action: func(c *cli.Context) error {
 | 
						|
		app := internal.ValidateApp(c)
 | 
						|
		secrets := secret.ReadSecretEnvVars(app.Env)
 | 
						|
 | 
						|
		tableCol := []string{"Name", "Version", "Generated Name", "Created On Server"}
 | 
						|
		table := formatter.CreateTable(tableCol)
 | 
						|
 | 
						|
		cl, err := client.New(app.Server)
 | 
						|
		if err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		filters, err := app.Filters(false, false)
 | 
						|
		if err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		secretList, err := cl.SecretList(context.Background(), types.SecretListOptions{Filters: filters})
 | 
						|
		if err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		remoteSecretNames := make(map[string]bool)
 | 
						|
		for _, cont := range secretList {
 | 
						|
			remoteSecretNames[cont.Spec.Annotations.Name] = true
 | 
						|
		}
 | 
						|
 | 
						|
		for sec := range secrets {
 | 
						|
			createdRemote := false
 | 
						|
			secretName := secret.ParseSecretEnvVarName(sec)
 | 
						|
			secVal, err := secret.ParseSecretEnvVarValue(secrets[sec])
 | 
						|
			if err != nil {
 | 
						|
				logrus.Fatal(err)
 | 
						|
			}
 | 
						|
			secretRemoteName := fmt.Sprintf("%s_%s_%s", app.StackName(), secretName, secVal.Version)
 | 
						|
			if _, ok := remoteSecretNames[secretRemoteName]; ok {
 | 
						|
				createdRemote = true
 | 
						|
			}
 | 
						|
			tableRow := []string{secretName, secVal.Version, secretRemoteName, strconv.FormatBool(createdRemote)}
 | 
						|
			table.Append(tableRow)
 | 
						|
		}
 | 
						|
 | 
						|
		if table.NumLines() > 0 {
 | 
						|
			table.Render()
 | 
						|
		} else {
 | 
						|
			logrus.Warnf("no secrets stored for %s", app.Name)
 | 
						|
		}
 | 
						|
 | 
						|
		return nil
 | 
						|
	},
 | 
						|
	BashComplete: autocomplete.AppNameComplete,
 | 
						|
}
 | 
						|
 | 
						|
var appSecretCommand = cli.Command{
 | 
						|
	Name:      "secret",
 | 
						|
	Aliases:   []string{"s"},
 | 
						|
	Usage:     "Manage app secrets",
 | 
						|
	ArgsUsage: "<domain>",
 | 
						|
	Subcommands: []cli.Command{
 | 
						|
		appSecretGenerateCommand,
 | 
						|
		appSecretInsertCommand,
 | 
						|
		appSecretRmCommand,
 | 
						|
		appSecretLsCommand,
 | 
						|
	},
 | 
						|
}
 |