Skip to content

Commit 2f76e20

Browse files
committed
Add docker-style --env-file flag support.
Allow generating configs with environment variables sourced from a file using the same syntax as docker. Signed-off-by: Will Rouesnel <w.rouesnel@gmail.com>
1 parent 476f1fb commit 2f76e20

3 files changed

Lines changed: 115 additions & 2 deletions

File tree

cmd/oci-runtime-tool/generate.go

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package main
22

33
import (
4+
"bufio"
5+
"bytes"
46
"fmt"
57
"os"
68
"runtime"
79
"strconv"
810
"strings"
11+
"unicode"
12+
"unicode/utf8"
913

1014
rspec "github.com/opencontainers/runtime-spec/specs-go"
1115
"github.com/opencontainers/runtime-tools/generate"
@@ -25,6 +29,7 @@ var generateFlags = []cli.Flag{
2529
cli.StringFlag{Name: "cwd", Value: "/", Usage: "current working directory for the process"},
2630
cli.BoolFlag{Name: "disable-oom-kill", Usage: "disable OOM Killer"},
2731
cli.StringSliceFlag{Name: "env", Usage: "add environment variable e.g. key=value"},
32+
cli.StringSliceFlag{Name: "env-file", Usage: "read in a file of environment variables"},
2833
cli.IntFlag{Name: "gid", Usage: "gid for the process"},
2934
cli.StringSliceFlag{Name: "gidmappings", Usage: "add GIDMappings e.g HostID:ContainerID:Size"},
3035
cli.StringSliceFlag{Name: "groups", Usage: "supplementary groups for the process"},
@@ -193,8 +198,12 @@ func setupSpec(g *generate.Generator, context *cli.Context) error {
193198
g.SetProcessArgs(context.StringSlice("args"))
194199
}
195200

196-
if context.IsSet("env") {
197-
envs := context.StringSlice("env")
201+
{
202+
envs, err := readKVStrings(context.StringSlice("env-file"), context.StringSlice("env"))
203+
if err != nil {
204+
return err
205+
}
206+
198207
for _, env := range envs {
199208
g.AddProcessEnv(env)
200209
}
@@ -664,3 +673,94 @@ func seccompSet(context *cli.Context, seccompFlag string, g *generate.Generator)
664673
}
665674
return nil
666675
}
676+
677+
// readKVStrings reads a file of line terminated key=value pairs, and overrides any keys
678+
// present in the file with additional pairs specified in the override parameter
679+
//
680+
// This function is copied from github.com/docker/docker/runconfig/opts/parse.go
681+
func readKVStrings(files []string, override []string) ([]string, error) {
682+
envVariables := []string{}
683+
for _, ef := range files {
684+
parsedVars, err := parseEnvFile(ef)
685+
if err != nil {
686+
return nil, err
687+
}
688+
envVariables = append(envVariables, parsedVars...)
689+
}
690+
// parse the '-e' and '--env' after, to allow override
691+
envVariables = append(envVariables, override...)
692+
693+
return envVariables, nil
694+
}
695+
696+
// parseEnvFile reads a file with environment variables enumerated by lines
697+
//
698+
// ``Environment variable names used by the utilities in the Shell and
699+
// Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase
700+
// letters, digits, and the '_' (underscore) from the characters defined in
701+
// Portable Character Set and do not begin with a digit. *But*, other
702+
// characters may be permitted by an implementation; applications shall
703+
// tolerate the presence of such names.''
704+
// -- http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
705+
//
706+
// As of #16585, it's up to application inside docker to validate or not
707+
// environment variables, that's why we just strip leading whitespace and
708+
// nothing more.
709+
//
710+
// This function is copied from github.com/docker/docker/runconfig/opts/envfile.go
711+
func parseEnvFile(filename string) ([]string, error) {
712+
fh, err := os.Open(filename)
713+
if err != nil {
714+
return []string{}, err
715+
}
716+
defer fh.Close()
717+
718+
lines := []string{}
719+
scanner := bufio.NewScanner(fh)
720+
currentLine := 0
721+
utf8bom := []byte{0xEF, 0xBB, 0xBF}
722+
for scanner.Scan() {
723+
scannedBytes := scanner.Bytes()
724+
if !utf8.Valid(scannedBytes) {
725+
return []string{}, fmt.Errorf("env file %s contains invalid utf8 bytes at line %d: %v", filename, currentLine+1, scannedBytes)
726+
}
727+
// We trim UTF8 BOM
728+
if currentLine == 0 {
729+
scannedBytes = bytes.TrimPrefix(scannedBytes, utf8bom)
730+
}
731+
// trim the line from all leading whitespace first
732+
line := strings.TrimLeftFunc(string(scannedBytes), unicode.IsSpace)
733+
currentLine++
734+
// line is not empty, and not starting with '#'
735+
if len(line) > 0 && !strings.HasPrefix(line, "#") {
736+
data := strings.SplitN(line, "=", 2)
737+
738+
// trim the front of a variable, but nothing else
739+
variable := strings.TrimLeft(data[0], whiteSpaces)
740+
if strings.ContainsAny(variable, whiteSpaces) {
741+
return []string{}, ErrBadEnvVariable{fmt.Sprintf("variable '%s' has white spaces", variable)}
742+
}
743+
744+
if len(data) > 1 {
745+
746+
// pass the value through, no trimming
747+
lines = append(lines, fmt.Sprintf("%s=%s", variable, data[1]))
748+
} else {
749+
// if only a pass-through variable is given, clean it up.
750+
lines = append(lines, fmt.Sprintf("%s=%s", strings.TrimSpace(line), os.Getenv(line)))
751+
}
752+
}
753+
}
754+
return lines, scanner.Err()
755+
}
756+
757+
var whiteSpaces = " \t"
758+
759+
// ErrBadEnvVariable typed error for bad environment variable
760+
type ErrBadEnvVariable struct {
761+
msg string
762+
}
763+
764+
func (e ErrBadEnvVariable) Error() string {
765+
return fmt.Sprintf("poorly formatted environment: %s", e.msg)
766+
}

completions/bash/oci-runtime-tool

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ _oci-runtime-tool_generate() {
279279
--cwd
280280
--disable-oom-kill
281281
--env
282+
--env-file
282283
--gid
283284
--gidmappings
284285
--groups
@@ -403,6 +404,11 @@ _oci-runtime-tool_generate() {
403404
__oci-runtime-tool_nospace
404405
return
405406
;;
407+
--env-file)
408+
_filedir
409+
__oci-runtime-tool_nospace
410+
return
411+
;;
406412
$(__oci-runtime-tool_to_extglob "$options_with_args") )
407413
return
408414
;;

man/oci-runtime-tool-generate.1.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ read the configuration from `config.json`.
6565
This option allows you to specify arbitrary environment variables
6666
that are available for the process that will be launched inside of
6767
the container.
68+
69+
**--env-file**=[]
70+
Set environment variables from a file.
71+
This option sets environment variables in the container from the
72+
contents of a file formatted with key=value pairs, one per line.
73+
When specified multiple times, files are loaded in order with duplicate
74+
keys overwriting previous ones.
6875

6976
**--gid**=GID
7077
Gid for the process inside of container

0 commit comments

Comments
 (0)