From a564422530b2c185ac8346a7a4046a2c433bf298 Mon Sep 17 00:00:00 2001 From: Nic Manoogian Date: Wed, 20 Nov 2024 11:08:47 -0500 Subject: [PATCH] Allow doppler secrets substitute to use environment vars --- pkg/cmd/secrets.go | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/pkg/cmd/secrets.go b/pkg/cmd/secrets.go index 5502f491..65d85b4f 100644 --- a/pkg/cmd/secrets.go +++ b/pkg/cmd/secrets.go @@ -575,7 +575,11 @@ func downloadSecrets(cmd *cobra.Command, args []string) { func substituteSecrets(cmd *cobra.Command, args []string) { localConfig := configuration.LocalConfig(cmd) - utils.RequireValue("token", localConfig.Token.Value) + useEnv := cmd.Flag("use-env").Value.String() + + if useEnv != "only" { + utils.RequireValue("token", localConfig.Token.Value) + } var outputFilePath string var err error @@ -586,24 +590,32 @@ func substituteSecrets(cmd *cobra.Command, args []string) { utils.HandleError(err, "Unable to parse output file path") } } + secretsMap := map[string]string{} - dynamicSecretsTTL := utils.GetDurationFlag(cmd, "dynamic-ttl") - response, responseErr := http.GetSecrets(localConfig.APIHost.Value, utils.GetBool(localConfig.VerifyTLS.Value, true), localConfig.Token.Value, localConfig.EnclaveProject.Value, localConfig.EnclaveConfig.Value, nil, true, dynamicSecretsTTL) - if !responseErr.IsNil() { - utils.HandleError(responseErr.Unwrap(), responseErr.Message) + if useEnv != "none" { + for k, v := range utils.ParseEnvStrings(os.Environ()) { + secretsMap[k] = v + } } - secrets, parseErr := models.ParseSecrets(response) - if parseErr != nil { - utils.HandleError(parseErr, "Unable to parse API response") - } + if useEnv != "only" { + dynamicSecretsTTL := utils.GetDurationFlag(cmd, "dynamic-ttl") + response, responseErr := http.GetSecrets(localConfig.APIHost.Value, utils.GetBool(localConfig.VerifyTLS.Value, true), localConfig.Token.Value, localConfig.EnclaveProject.Value, localConfig.EnclaveConfig.Value, nil, true, dynamicSecretsTTL) + if !responseErr.IsNil() { + utils.HandleError(responseErr.Unwrap(), responseErr.Message) + } - secretsMap := map[string]string{} - for _, secret := range secrets { - if secret.ComputedValue != nil { - // By not providing a default value when ComputedValue is nil (e.g. it's a restricted secret), we default - // to the same behavior the substituter provides if the template file contains a secret that doesn't exist. - secretsMap[secret.Name] = *secret.ComputedValue + secrets, parseErr := models.ParseSecrets(response) + if parseErr != nil { + utils.HandleError(parseErr, "Unable to parse API response") + } + + for _, secret := range secrets { + if secret.ComputedValue != nil { + // By not providing a default value when ComputedValue is nil (e.g. it's a restricted secret), we default + // to the same behavior the substituter provides if the template file contains a secret that doesn't exist. + secretsMap[secret.Name] = *secret.ComputedValue + } } } @@ -739,6 +751,7 @@ func init() { if err := secretsSubstituteCmd.RegisterFlagCompletionFunc("config", configNamesValidArgs); err != nil { utils.HandleError(err) } + secretsSubstituteCmd.Flags().String("use-env", "none", "setting for how to use environment variables passed to 'doppler secrets substitute'. 'none' (default) will not expose environment variables to templates. 'mix' will expose both templates and Doppler secrets to templates. 'only' will only expose environment variables to templates (and will not fetch Doppler secrets).") secretsSubstituteCmd.Flags().String("output", "", "path to the output file. by default the rendered text will be written to stdout.") secretsSubstituteCmd.Flags().Duration("dynamic-ttl", 0, "(BETA) dynamic secrets will expire after specified duration, (e.g. '3h', '15m')") secretsCmd.AddCommand(secretsSubstituteCmd)