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,
 | |
| 	},
 | |
| }
 |