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