Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add env file #2176

Merged
merged 1 commit into from
May 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions caddy/caddymain/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
Expand Down Expand Up @@ -50,6 +51,7 @@ func init() {
flag.StringVar(&disabledMetrics, "disabled-metrics", "", "Comma-separated list of telemetry metrics to disable")
flag.StringVar(&conf, "conf", "", "Caddyfile to load (default \""+caddy.DefaultConfigFile+"\")")
flag.StringVar(&cpu, "cpu", "100%", "CPU cap")
flag.StringVar(&envFile, "env", "", "Path to file with environment variables to load in KEY=VALUE format")
flag.BoolVar(&plugins, "plugins", false, "List installed plugins")
flag.StringVar(&caddytls.DefaultEmail, "email", "", "Default ACME CA account email address")
flag.DurationVar(&acme.HTTPClient.Timeout, "catimeout", acme.HTTPClient.Timeout, "Default ACME CA HTTP timeout")
Expand Down Expand Up @@ -90,6 +92,11 @@ func Run() {
})
}

//Load all additional envs as soon as possible
if err := LoadEnvFromFile(envFile); err != nil {
mustLogFatalf("%v", err)
}

// initialize telemetry client
if enableTelemetry {
err := initTelemetry()
Expand Down Expand Up @@ -409,13 +416,88 @@ func initTelemetry() error {
return nil
}

// LoadEnvFromFile loads additional envs if file provided and exists
// Envs in file should be in KEY=VALUE format
func LoadEnvFromFile(envFile string) error {
if envFile == "" {
return nil
}

file, err := os.Open(envFile)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a defer file.Close() here to close the file after reading.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, add it after the error check below instead.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, sorry, i missed it:)

if err != nil {
return err
}
defer file.Close()

envMap, err := ParseEnvFile(file)
if err != nil {
return err
}

for k, v := range envMap {
if err := os.Setenv(k, v); err != nil {
return err
}
}

return nil
}

// ParseEnvFile implements parse logic for environment files
func ParseEnvFile(envInput io.Reader) (map[string]string, error) {
envMap := make(map[string]string)

scanner := bufio.NewScanner(envInput)
var line string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to declare this outside the loop; can use line := ... on line 455.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, i will look up for this kind of things in future:) thanks

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 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing that would be handled by trimming the line of whitespaces. With this, a line containing only whitespace characters will (wrongfully) not be skipped.

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
}

const appName = "Caddy"

// Flags that control program flow or startup
var (
serverType string
conf string
cpu string
envFile string
logfile string
revoke string
version bool
Expand Down
33 changes: 33 additions & 0 deletions caddy/caddymain/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
package caddymain

import (
"reflect"
"runtime"
"strings"
"testing"
)

Expand Down Expand Up @@ -57,3 +59,34 @@ func TestSetCPU(t *testing.T) {
runtime.GOMAXPROCS(currentCPU)
}
}

func TestParseEnvFile(t *testing.T) {
tests := []struct {
name string
input string
want map[string]string
wantErr bool
}{
{"parsing KEY=VALUE", "PORT=4096", map[string]string{"PORT": "4096"}, false},
Copy link
Collaborator

@tobya tobya May 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like your test with a comment explaining what it is testing.

{"empty KEY", "=4096", nil, true},
{"one value", "test", nil, true},
{"comments skipped", "#TEST=1\nPORT=8888", map[string]string{"PORT": "8888"}, false},
{"empty line", "\nPORT=7777", map[string]string{"PORT": "7777"}, false},
{"comments with space skipped", " #TEST=1", map[string]string{}, false},
{"KEY with space", "PORT =8888", nil, true},
{"only spaces", " ", map[string]string{}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reader := strings.NewReader(tt.input)
got, err := ParseEnvFile(reader)
if (err != nil) != tt.wantErr {
t.Errorf("ParseEnvFile() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ParseEnvFile() = %v, want %v", got, tt.want)
}
})
}
}