forked from toolshed/abra
		
	feat(secrets): Reading from stdin and reproducible secret list(#614)
Reviewed-on: toolshed/abra#614 Reviewed-by: decentral1se <decentral1se@noreply.git.coopcloud.tech> Co-authored-by: p4u1 <p4u1_f4u1@riseup.net> Co-committed-by: p4u1 <p4u1_f4u1@riseup.net>
This commit is contained in:
		| @ -2,8 +2,11 @@ package app | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| @ -157,7 +160,10 @@ environment. Typically, you can let Abra generate them for you on app creation | ||||
|   abra app secret insert 1312.net my_secret v1 mySuperSecret | ||||
|  | ||||
|   # insert secret as file | ||||
|   abra app secret insert 1312.net my_secret v1 secret.txt -f`), | ||||
|   abra app secret insert 1312.net my_secret v1 secret.txt -f | ||||
|  | ||||
|   # insert secret from stdin | ||||
|   echo "mmySuperSecret" | abra app secret insert 1312.net my_secret v1`), | ||||
| 	Args: cobra.MinimumNArgs(3), | ||||
| 	ValidArgsFunction: func( | ||||
| 		cmd *cobra.Command, | ||||
| @ -191,12 +197,9 @@ environment. Typically, you can let Abra generate them for you on app creation | ||||
|  | ||||
| 		name := args[1] | ||||
| 		version := args[2] | ||||
| 		data := "" | ||||
|  | ||||
| 		if len(args) > 3 { | ||||
| 			data = args[3] | ||||
| 		} else if internal.NoInput { | ||||
| 			log.Fatal(i18n.G("must provide <data> argument if --no-input is passed")) | ||||
| 		data, err := readSecretData(args) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		composeFiles, err := app.Recipe.GetComposeFiles(app.Env) | ||||
| @ -219,23 +222,6 @@ environment. Typically, you can let Abra generate them for you on app creation | ||||
| 			log.Fatal(i18n.G("no secret %s available for recipe %s?", name, app.Recipe.Name)) | ||||
| 		} | ||||
|  | ||||
| 		if data == "" && !internal.NoInput { | ||||
| 			log.Debug(i18n.G("secret data not provided on command-line, prompting")) | ||||
| 			var prompt survey.Prompt | ||||
| 			if !insertFromFile { | ||||
| 				prompt = &survey.Password{ | ||||
| 					Message: i18n.G("specify secret value"), | ||||
| 				} | ||||
| 			} else { | ||||
| 				prompt = &survey.Input{ | ||||
| 					Message: i18n.G("specify secret file"), | ||||
| 				} | ||||
| 			} | ||||
| 			if err := survey.AskOne(prompt, &data); err != nil { | ||||
| 				log.Fatal(err) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if insertFromFile { | ||||
| 			raw, err := os.ReadFile(data) | ||||
| 			if err != nil { | ||||
| @ -263,6 +249,55 @@ environment. Typically, you can let Abra generate them for you on app creation | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| func readSecretData(args []string) (string, error) { | ||||
| 	if len(args) == 4 { | ||||
| 		return args[3], nil | ||||
| 	} | ||||
|  | ||||
| 	if len(args) != 3 { | ||||
| 		return "", errors.New(i18n.G("need 3 or 4 arguments")) | ||||
| 	} | ||||
| 	// First check if data is provided by stdin | ||||
| 	fi, err := os.Stdin.Stat() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if fi.Mode()&os.ModeNamedPipe != 0 { | ||||
| 		// Can't insert from stdin and read from file | ||||
| 		if insertFromFile { | ||||
| 			return "", errors.New(i18n.G("can not insert from file and read from stdin")) | ||||
| 		} | ||||
|  | ||||
| 		log.Debug(i18n.G("reading secret data from stdin")) | ||||
| 		bytes, err := io.ReadAll(os.Stdin) | ||||
| 		if err != nil { | ||||
| 			return "", errors.New(i18n.G("reading data from stdin: %s", err)) | ||||
| 		} | ||||
|  | ||||
| 		return string(bytes), nil | ||||
| 	} | ||||
| 	if internal.NoInput { | ||||
| 		return "", errors.New(i18n.G("must provide <data> argument if --no-input is passed")) | ||||
| 	} | ||||
|  | ||||
| 	log.Debug(i18n.G("secret data not provided on command-line or stdin, prompting")) | ||||
| 	var prompt survey.Prompt | ||||
| 	if !insertFromFile { | ||||
| 		prompt = &survey.Password{ | ||||
| 			Message: i18n.G("specify secret value"), | ||||
| 		} | ||||
| 	} else { | ||||
| 		prompt = &survey.Input{ | ||||
| 			Message: i18n.G("specify secret file"), | ||||
| 		} | ||||
| 	} | ||||
| 	var data string | ||||
| 	if err := survey.AskOne(prompt, &data); err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return data, nil | ||||
| } | ||||
|  | ||||
| // secretRm removes a secret. | ||||
| func secretRm(cl *dockerClient.Client, app appPkg.App, secretName, parsed string) error { | ||||
| 	if err := cl.SecretRemove(context.Background(), secretName); err != nil { | ||||
| @ -432,6 +467,10 @@ var AppSecretLsCommand = &cobra.Command{ | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		// Sort secrets to ensure reproducible output | ||||
| 		sort.Slice(secStats, func(i, j int) bool { | ||||
| 			return secStats[i].LocalName < secStats[j].LocalName | ||||
| 		}) | ||||
| 		var rows [][]string | ||||
| 		for _, secStat := range secStats { | ||||
| 			row := []string{ | ||||
|  | ||||
| @ -206,10 +206,10 @@ teardown(){ | ||||
|   run $ABRA app secret insert "$TEST_APP_DOMAIN" bar | ||||
|   assert_failure | ||||
|  | ||||
|   run $ABRA app secret insert "$TEST_APP_DOMAIN" bar baz | ||||
|   run $ABRA app secret insert "$TEST_APP_DOMAIN" bar baz --no-input | ||||
|   assert_failure | ||||
|  | ||||
|   run $ABRA app secret insert "$TEST_APP_DOMAIN" test_pass_one v1 -n | ||||
|   run bash -c "echo foo | $ABRA app secret insert $TEST_APP_DOMAIN bar baz -f" | ||||
|   assert_failure | ||||
| } | ||||
|  | ||||
| @ -251,6 +251,20 @@ teardown(){ | ||||
|   assert_output --partial 'true' | ||||
| } | ||||
|  | ||||
| @test "insert: create secret from stdin" { | ||||
|   run $ABRA app secret ls "$TEST_APP_DOMAIN" | ||||
|   assert_success | ||||
|   assert_output --partial 'false' | ||||
|  | ||||
|   run bash -c "echo foo | $ABRA app secret insert $TEST_APP_DOMAIN test_pass_one v1" | ||||
|   assert_success | ||||
|   assert_output --partial 'successfully stored on server' | ||||
|  | ||||
|   run $ABRA app secret ls "$TEST_APP_DOMAIN" | ||||
|   assert_success | ||||
|   assert_output --partial 'true' | ||||
| } | ||||
|  | ||||
| @test "rm: validate arguments" { | ||||
|   run $ABRA app secret rm | ||||
|   assert_failure | ||||
| @ -343,6 +357,12 @@ teardown(){ | ||||
|     | jq -r ".[] | select(.name==\"test_pass_two\") | .version"' | ||||
|   assert_success | ||||
|   assert_output --partial 'v1' | ||||
|  | ||||
|   # Can always expect the secret at this position | ||||
|   run bash -c '$ABRA app secret ls "$TEST_APP_DOMAIN" --machine \ | ||||
|     | jq -r ".[1] | .name"' | ||||
|   assert_success | ||||
|   assert_output --partial 'test_pass_two' | ||||
| } | ||||
|  | ||||
| @test "ls: bail if unstaged changes and no --chaos" { | ||||
|  | ||||
		Reference in New Issue
	
	Block a user