From 936b363053c4314dff2164c196048ba7753ad5e9 Mon Sep 17 00:00:00 2001 From: Eldin Hadzic Date: Sun, 19 Apr 2020 01:19:38 +0200 Subject: [PATCH 1/4] run: Add the possibility to load an env file --- cmd/commandfuncs.go | 9 ++++++ cmd/commands.go | 6 +++- cmd/main.go | 68 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/cmd/commandfuncs.go b/cmd/commandfuncs.go index efdcfdca546..40b5e65e0aa 100644 --- a/cmd/commandfuncs.go +++ b/cmd/commandfuncs.go @@ -147,10 +147,19 @@ func cmdRun(fl Flags) (int, error) { runCmdConfigFlag := fl.String("config") runCmdConfigAdapterFlag := fl.String("adapter") runCmdResumeFlag := fl.Bool("resume") + runCmdLoadEnvfile := fl.String("envfile") runCmdPrintEnvFlag := fl.Bool("environ") runCmdWatchFlag := fl.Bool("watch") runCmdPingbackFlag := fl.String("pingback") + // load all additional envs as soon as possible + if runCmdLoadEnvfile != "" { + if err := loadEnvFromFile(runCmdLoadEnvfile); err != nil { + return caddy.ExitCodeFailedStartup, + fmt.Errorf("loading additional environment variables: %v", err) + } + } + // if we are supposed to print the environment, do that first if runCmdPrintEnvFlag { printEnvironment() diff --git a/cmd/commands.go b/cmd/commands.go index 43aba01bfd4..ecc27b540a6 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -95,7 +95,7 @@ using 'caddy run' instead to keep it in the foreground.`, RegisterCommand(Command{ Name: "run", Func: cmdRun, - Usage: "[--config [--adapter ]] [--environ] [--watch]", + Usage: "[--config [--adapter ]] [--envfile ] [--environ] [--watch]", Short: `Starts the Caddy process and blocks indefinitely`, Long: ` Starts the Caddy process, optionally bootstrapped with an initial config file, @@ -115,6 +115,9 @@ As a special case, if the current working directory has a file called that file will be loaded and used to configure Caddy, even without any command line flags. +If --envfile is specified, an environment file with environment variables in +the KEY=VALUE format will be loaded into the Caddy process. + If --environ is specified, the environment as seen by the Caddy process will be printed before starting. This is the same as the environ command but does not quit after printing, and can be useful for troubleshooting. @@ -129,6 +132,7 @@ development environment.`, fs := flag.NewFlagSet("run", flag.ExitOnError) fs.String("config", "", "Configuration file") fs.String("adapter", "", "Name of config adapter to apply") + fs.String("envfile", "", "Environment file to load") fs.Bool("environ", false, "Print environment") fs.Bool("resume", false, "Use saved config, if any (and prefer over --config file)") fs.Bool("watch", false, "Watch config file for changes and reload it automatically") diff --git a/cmd/main.go b/cmd/main.go index bdc95a45d33..2bdd381a9cc 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -15,6 +15,7 @@ package caddycmd import ( + "bufio" "bytes" "flag" "fmt" @@ -331,6 +332,73 @@ func flagHelp(fs *flag.FlagSet) string { return buf.String() } +func loadEnvFromFile(envFile string) error { + file, err := os.Open(envFile) + if err != nil { + return fmt.Errorf("reading environment file: %v", err) + } + defer file.Close() + + envMap, err := parseEnvFile(file) + if err != nil { + return fmt.Errorf("parsing environment file: %v", err) + } + + for k, v := range envMap { + if err := os.Setenv(k, v); err != nil { + return fmt.Errorf("setting environment variables") + } + } + + return nil +} + +func parseEnvFile(envInput io.Reader) (map[string]string, error) { + envMap := make(map[string]string) + + scanner := bufio.NewScanner(envInput) + var line string + lineNumber := 0 + + for scanner.Scan() { + line = strings.TrimSpace(scanner.Text()) + lineNumber++ + + // skip lines starting with comment + if strings.HasPrefix(line, "#") { + continue + } + + // skip empty line + if len(line) == 0 { + continue + } + + fields := strings.SplitN(line, "=", 2) + if len(fields) != 2 { + return nil, fmt.Errorf("Can't parse line %d; line should be in KEY=VALUE format", lineNumber) + } + + if strings.Contains(fields[0], " ") { + return nil, fmt.Errorf("Can't parse line %d; KEY contains whitespace", lineNumber) + } + + key := fields[0] + val := fields[1] + + if key == "" { + return nil, fmt.Errorf("Can't parse line %d; KEY can't be empty string", lineNumber) + } + envMap[key] = val + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return envMap, nil +} + func printEnvironment() { fmt.Printf("caddy.HomeDir=%s\n", caddy.HomeDir()) fmt.Printf("caddy.AppDataDir=%s\n", caddy.AppDataDir()) From e584ec0bb0ccd80d6980f5923f738a6a1a206dd2 Mon Sep 17 00:00:00 2001 From: Eldin Hadzic Date: Sun, 19 Apr 2020 01:33:12 +0200 Subject: [PATCH 2/4] run: change envfile flag var --- cmd/commandfuncs.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/commandfuncs.go b/cmd/commandfuncs.go index 40b5e65e0aa..85e83369d4e 100644 --- a/cmd/commandfuncs.go +++ b/cmd/commandfuncs.go @@ -147,14 +147,14 @@ func cmdRun(fl Flags) (int, error) { runCmdConfigFlag := fl.String("config") runCmdConfigAdapterFlag := fl.String("adapter") runCmdResumeFlag := fl.Bool("resume") - runCmdLoadEnvfile := fl.String("envfile") + runCmdLoadEnvfileFlag := fl.String("envfile") runCmdPrintEnvFlag := fl.Bool("environ") runCmdWatchFlag := fl.Bool("watch") runCmdPingbackFlag := fl.String("pingback") // load all additional envs as soon as possible - if runCmdLoadEnvfile != "" { - if err := loadEnvFromFile(runCmdLoadEnvfile); err != nil { + if runCmdLoadEnvfileFlag != "" { + if err := loadEnvFromFile(runCmdLoadEnvfileFlag); err != nil { return caddy.ExitCodeFailedStartup, fmt.Errorf("loading additional environment variables: %v", err) } From 7f05d27e6ab0e118549f8299ff2e30a2928c8cc9 Mon Sep 17 00:00:00 2001 From: Eldin Hadzic Date: Sun, 19 Apr 2020 01:54:23 +0200 Subject: [PATCH 3/4] run: do not ignore err values --- cmd/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/main.go b/cmd/main.go index 2bdd381a9cc..1903290a330 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -346,7 +346,7 @@ func loadEnvFromFile(envFile string) error { for k, v := range envMap { if err := os.Setenv(k, v); err != nil { - return fmt.Errorf("setting environment variables") + return fmt.Errorf("setting environment variables: %v", err) } } From 5378e55d2cc09c280fa88a0683e0ac1dcb7c9fdd Mon Sep 17 00:00:00 2001 From: elcore Date: Fri, 15 May 2020 19:53:04 +0200 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Matt Holt --- cmd/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 1f2797f2eed..4fd8d681555 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -384,18 +384,18 @@ func parseEnvFile(envInput io.Reader) (map[string]string, error) { fields := strings.SplitN(line, "=", 2) if len(fields) != 2 { - return nil, fmt.Errorf("Can't parse line %d; line should be in KEY=VALUE format", lineNumber) + return nil, fmt.Errorf("can't parse line %d; line should be in KEY=VALUE format", lineNumber) } if strings.Contains(fields[0], " ") { - return nil, fmt.Errorf("Can't parse line %d; KEY contains whitespace", lineNumber) + return nil, fmt.Errorf("bad key on line %d: contains whitespace", lineNumber) } key := fields[0] val := fields[1] if key == "" { - return nil, fmt.Errorf("Can't parse line %d; KEY can't be empty string", lineNumber) + return nil, fmt.Errorf("missing or empty key on line %d", lineNumber) } envMap[key] = val }