From f2ab7099db6d8386299796e6eef8e30f65b21bcc Mon Sep 17 00:00:00 2001 From: Evan Van Dam Date: Wed, 6 Sep 2023 19:19:24 -0700 Subject: [PATCH] cmd: Prevent overwriting existing env vars with `--envfile` (#5803) Co-authored-by: Francis Lavoie --- cmd/commandfuncs.go | 42 +++++++++++++++++++++++++++++++----------- cmd/commands.go | 40 ++++++++++++++++++++-------------------- cmd/main.go | 8 ++++++-- 3 files changed, 57 insertions(+), 33 deletions(-) diff --git a/cmd/commandfuncs.go b/cmd/commandfuncs.go index f284b89b..89639886 100644 --- a/cmd/commandfuncs.go +++ b/cmd/commandfuncs.go @@ -46,7 +46,14 @@ func cmdStart(fl Flags) (int, error) { startCmdConfigAdapterFlag := fl.String("adapter") startCmdPidfileFlag := fl.String("pidfile") startCmdWatchFlag := fl.Bool("watch") - startCmdEnvfileFlag := fl.String("envfile") + + var err error + var startCmdEnvfileFlag []string + startCmdEnvfileFlag, err = fl.GetStringSlice("envfile") + if err != nil { + return caddy.ExitCodeFailedStartup, + fmt.Errorf("reading envfile flag: %v", err) + } // open a listener to which the child process will connect when // it is ready to confirm that it has successfully started @@ -70,8 +77,9 @@ func cmdStart(fl Flags) (int, error) { if startCmdConfigFlag != "" { cmd.Args = append(cmd.Args, "--config", startCmdConfigFlag) } - if startCmdEnvfileFlag != "" { - cmd.Args = append(cmd.Args, "--envfile", startCmdEnvfileFlag) + + for _, envFile := range startCmdEnvfileFlag { + cmd.Args = append(cmd.Args, "--envfile", envFile) } if startCmdConfigAdapterFlag != "" { cmd.Args = append(cmd.Args, "--adapter", startCmdConfigAdapterFlag) @@ -160,15 +168,22 @@ func cmdRun(fl Flags) (int, error) { runCmdConfigFlag := fl.String("config") runCmdConfigAdapterFlag := fl.String("adapter") runCmdResumeFlag := fl.Bool("resume") - runCmdLoadEnvfileFlag := fl.String("envfile") runCmdPrintEnvFlag := fl.Bool("environ") runCmdWatchFlag := fl.Bool("watch") runCmdPidfileFlag := fl.String("pidfile") runCmdPingbackFlag := fl.String("pingback") + var err error + var runCmdLoadEnvfileFlag []string + runCmdLoadEnvfileFlag, err = fl.GetStringSlice("envfile") + if err != nil { + return caddy.ExitCodeFailedStartup, + fmt.Errorf("reading envfile flag: %v", err) + } + // load all additional envs as soon as possible - if runCmdLoadEnvfileFlag != "" { - if err := loadEnvFromFile(runCmdLoadEnvfileFlag); err != nil { + for _, envFile := range runCmdLoadEnvfileFlag { + if err := loadEnvFromFile(envFile); err != nil { return caddy.ExitCodeFailedStartup, fmt.Errorf("loading additional environment variables: %v", err) } @@ -181,7 +196,6 @@ func cmdRun(fl Flags) (int, error) { // load the config, depending on flags var config []byte - var err error if runCmdResumeFlag { config, err = os.ReadFile(caddy.ConfigAutosavePath) if os.IsNotExist(err) { @@ -497,18 +511,24 @@ func cmdAdaptConfig(fl Flags) (int, error) { func cmdValidateConfig(fl Flags) (int, error) { validateCmdConfigFlag := fl.String("config") validateCmdAdapterFlag := fl.String("adapter") - runCmdLoadEnvfileFlag := fl.String("envfile") + + var err error + var runCmdLoadEnvfileFlag []string + runCmdLoadEnvfileFlag, err = fl.GetStringSlice("envfile") + if err != nil { + return caddy.ExitCodeFailedStartup, + fmt.Errorf("reading envfile flag: %v", err) + } // load all additional envs as soon as possible - if runCmdLoadEnvfileFlag != "" { - if err := loadEnvFromFile(runCmdLoadEnvfileFlag); err != nil { + for _, envFile := range runCmdLoadEnvfileFlag { + if err := loadEnvFromFile(envFile); err != nil { return caddy.ExitCodeFailedStartup, fmt.Errorf("loading additional environment variables: %v", err) } } // use default config and ensure a config file is specified - var err error validateCmdConfigFlag, err = configFileWithRespectToDefault(caddy.Log(), validateCmdConfigFlag) if err != nil { return caddy.ExitCodeFailedStartup, err diff --git a/cmd/commands.go b/cmd/commands.go index 0885577e..c64ab713 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -104,7 +104,7 @@ using 'caddy run' instead to keep it in the foreground. CobraFunc: func(cmd *cobra.Command) { cmd.Flags().StringP("config", "c", "", "Configuration file") cmd.Flags().StringP("adapter", "a", "", "Name of config adapter to apply") - cmd.Flags().StringP("envfile", "", "", "Environment file to load") + cmd.Flags().StringSliceP("envfile", "", []string{}, "Environment file(s) to load") cmd.Flags().BoolP("watch", "w", false, "Reload changed config file automatically") cmd.Flags().StringP("pidfile", "", "", "Path of file to which to write process ID") cmd.RunE = WrapCommandFuncForCobra(cmdStart) @@ -150,7 +150,7 @@ option in a local development environment. CobraFunc: func(cmd *cobra.Command) { cmd.Flags().StringP("config", "c", "", "Configuration file") cmd.Flags().StringP("adapter", "a", "", "Name of config adapter to apply") - cmd.Flags().StringP("envfile", "", "", "Environment file to load") + cmd.Flags().StringSliceP("envfile", "", []string{}, "Environment file(s) to load") cmd.Flags().BoolP("environ", "e", false, "Print environment") cmd.Flags().BoolP("resume", "r", false, "Use saved config, if any (and prefer over --config file)") cmd.Flags().BoolP("watch", "w", false, "Watch config file for changes and reload it automatically") @@ -301,7 +301,7 @@ the KEY=VALUE format will be loaded into the Caddy process. CobraFunc: func(cmd *cobra.Command) { cmd.Flags().StringP("config", "c", "", "Input configuration file") cmd.Flags().StringP("adapter", "a", "", "Name of config adapter") - cmd.Flags().StringP("envfile", "", "", "Environment file to load") + cmd.Flags().StringSliceP("envfile", "", []string{}, "Environment file(s) to load") cmd.RunE = WrapCommandFuncForCobra(cmdValidateConfig) }, }) @@ -402,7 +402,7 @@ latest versions. EXPERIMENTAL: May be changed or removed. Short: "Adds Caddy packages (EXPERIMENTAL)", Long: ` Downloads an updated Caddy binary with the specified packages (module/plugin) -added. Retains existing packages. Returns an error if the any of packages are +added. Retains existing packages. Returns an error if the any of packages are already included. EXPERIMENTAL: May be changed or removed. `, CobraFunc: func(cmd *cobra.Command) { @@ -417,8 +417,8 @@ already included. EXPERIMENTAL: May be changed or removed. Usage: "", Short: "Removes Caddy packages (EXPERIMENTAL)", Long: ` -Downloads an updated Caddy binaries without the specified packages (module/plugin). -Returns an error if any of the packages are not included. +Downloads an updated Caddy binaries without the specified packages (module/plugin). +Returns an error if any of the packages are not included. EXPERIMENTAL: May be changed or removed. `, CobraFunc: func(cmd *cobra.Command) { @@ -464,40 +464,40 @@ argument of --directory. If the directory does not exist, it will be created. Use: "completion [bash|zsh|fish|powershell]", Short: "Generate completion script", Long: fmt.Sprintf(`To load completions: - + Bash: - + $ source <(%[1]s completion bash) - + # To load completions for each session, execute once: # Linux: $ %[1]s completion bash > /etc/bash_completion.d/%[1]s # macOS: $ %[1]s completion bash > $(brew --prefix)/etc/bash_completion.d/%[1]s - + Zsh: - + # If shell completion is not already enabled in your environment, # you will need to enable it. You can execute the following once: - + $ echo "autoload -U compinit; compinit" >> ~/.zshrc - + # To load completions for each session, execute once: $ %[1]s completion zsh > "${fpath[1]}/_%[1]s" - + # You will need to start a new shell for this setup to take effect. - + fish: - + $ %[1]s completion fish | source - + # To load completions for each session, execute once: $ %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish - + PowerShell: - + PS> %[1]s completion powershell | Out-String | Invoke-Expression - + # To load completions for every new session, run: PS> %[1]s completion powershell > %[1]s.ps1 # and source this file from your PowerShell profile. diff --git a/cmd/main.go b/cmd/main.go index 1d6478a4..b4e3fdc8 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -300,8 +300,12 @@ func loadEnvFromFile(envFile string) error { } for k, v := range envMap { - if err := os.Setenv(k, v); err != nil { - return fmt.Errorf("setting environment variables: %v", err) + // do not overwrite existing environment variables + _, exists := os.LookupEnv(k) + if !exists { + if err := os.Setenv(k, v); err != nil { + return fmt.Errorf("setting environment variables: %v", err) + } } }