package app import ( "context" "errors" "fmt" "strconv" abraFormatter "coopcloud.tech/abra/cli/formatter" "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/secret" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) var allSecrets bool var allSecretsFlag = &cli.BoolFlag{ Name: "all", Value: false, Destination: &allSecrets, Usage: "Generate all secrets", } var appSecretGenerateCommand = &cli.Command{ Name: "generate", Aliases: []string{"g"}, Usage: "Generate secrets", ArgsUsage: " ", Flags: []cli.Flag{allSecretsFlag, internal.PassFlag}, Action: func(c *cli.Context) error { appName := internal.ValidateAppNameArg(c) if c.Args().Get(1) != "" && allSecrets { internal.ShowSubcommandHelpAndError(c, errors.New("cannot use ' ' and '--all' together")) } appFiles, err := config.LoadAppFiles("") if err != nil { logrus.Fatal(err) } appEnv, err := config.GetApp(appFiles, appName) if err != nil { logrus.Fatal(err) } secretsToCreate := make(map[string]string) server := appFiles[appName].Server secretEnvVars := secret.ReadSecretEnvVars(appEnv.Env) if allSecrets { secretsToCreate = secretEnvVars } else { secretName := c.Args().Get(1) secretVersion := c.Args().Get(2) for sec := range secretEnvVars { parsed := secret.ParseSecretEnvVarName(sec) if secretName == parsed { secretsToCreate[sec] = secretVersion } } } secretVals, err := secret.GenerateSecrets(secretsToCreate, appEnv.StackName(), server) if err != nil { logrus.Fatal(err) } if internal.Pass { for name, data := range secretVals { if err := secret.PassInsertSecret(data, name, appEnv.StackName(), server); err != nil { logrus.Fatal(err) } } } return nil }, } var appSecretInsertCommand = &cli.Command{ Name: "insert", Aliases: []string{"i"}, Usage: "Insert secret", Flags: []cli.Flag{internal.PassFlag}, ArgsUsage: " ", Action: func(c *cli.Context) error { appName := internal.ValidateAppNameArg(c) if c.Args().Len() != 4 { internal.ShowSubcommandHelpAndError(c, errors.New("missing arguments?")) } name := c.Args().Get(1) version := c.Args().Get(2) data := c.Args().Get(3) appFiles, err := config.LoadAppFiles("") if err != nil { logrus.Fatal(err) } appEnv, err := config.GetApp(appFiles, appName) if err != nil { logrus.Fatal(err) } server := appFiles[appName].Server secretName := fmt.Sprintf("%s_%s_%s", appEnv.StackName(), name, version) if err := client.StoreSecret(secretName, data, server); err != nil { logrus.Fatal(err) } if internal.Pass { if err := secret.PassInsertSecret(data, name, appEnv.StackName(), server); err != nil { logrus.Fatal(err) } } return nil }, } var appSecretRmCommand = &cli.Command{ Name: "remove", Usage: "Remove a secret", Aliases: []string{"rm"}, Flags: []cli.Flag{allSecretsFlag, internal.PassFlag}, ArgsUsage: "", Action: func(c *cli.Context) error { appName := internal.ValidateAppNameArg(c) if c.Args().Get(1) != "" && allSecrets { internal.ShowSubcommandHelpAndError(c, errors.New("cannot use '' and '--all' together")) } if c.Args().Get(1) == "" && !allSecrets { internal.ShowSubcommandHelpAndError(c, errors.New("no secret(s) specified?")) } appFiles, err := config.LoadAppFiles("") if err != nil { logrus.Fatal(err) } appEnv, err := config.GetApp(appFiles, appName) if err != nil { logrus.Fatal(err) } server := appFiles[appName].Server ctx := context.Background() cl, err := client.New(server) if err != nil { logrus.Fatal(err) } filters := filters.NewArgs() filters.Add("name", appEnv.StackName()) secretList, err := cl.SecretList(ctx, types.SecretListOptions{Filters: filters}) if err != nil { logrus.Fatal(err) } secretToRm := c.Args().Get(1) for _, cont := range secretList { secretName := cont.Spec.Annotations.Name parsed := secret.ParseGeneratedSecretName(secretName, appEnv) if allSecrets { if err := cl.SecretRemove(ctx, secretName); err != nil { logrus.Fatal(err) } if internal.Pass { if err := secret.PassRmSecret(parsed, appEnv.StackName(), server); err != nil { logrus.Fatal(err) } } } else { if parsed == secretToRm { if err := cl.SecretRemove(ctx, secretName); err != nil { logrus.Fatal(err) } if internal.Pass { if err := secret.PassRmSecret(parsed, appEnv.StackName(), server); err != nil { logrus.Fatal(err) } } } } } return nil }, } var appSecretLsCommand = &cli.Command{ Name: "list", Usage: "List all secrets", Aliases: []string{"ls"}, Action: func(c *cli.Context) error { appName := internal.ValidateAppNameArg(c) appFiles, err := config.LoadAppFiles("") if err != nil { logrus.Fatal(err) } appEnv, err := config.GetApp(appFiles, appName) if err != nil { logrus.Fatal(err) } secrets := secret.ReadSecretEnvVars(appEnv.Env) tableCol := []string{"Name", "Version", "Generated Name", "Created On Server"} table := abraFormatter.CreateTable(tableCol) server := appFiles[appName].Server ctx := context.Background() cl, err := client.New(server) if err != nil { logrus.Fatal(err) } filters := filters.NewArgs() filters.Add("name", appEnv.StackName()) secretList, err := cl.SecretList(ctx, 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", appEnv.StackName(), secretName, secVal.Version) if _, ok := remoteSecretNames[secretRemoteName]; ok { createdRemote = true } tableRow := []string{secretName, secVal.Version, secretRemoteName, strconv.FormatBool(createdRemote)} table.Append(tableRow) } table.Render() return nil }, } var appSecretCommand = &cli.Command{ Name: "secret", Aliases: []string{"s"}, Usage: "Manage app secrets", ArgsUsage: "", Subcommands: []*cli.Command{ appSecretGenerateCommand, appSecretInsertCommand, appSecretRmCommand, appSecretLsCommand, }, }