diff --git a/components/engine/docs/sources/reference/commandline/cli.rst b/components/engine/docs/sources/reference/commandline/cli.rst index 3d2aac5233..0c9db138c2 100644 --- a/components/engine/docs/sources/reference/commandline/cli.rst +++ b/components/engine/docs/sources/reference/commandline/cli.rst @@ -1152,6 +1152,7 @@ image is removed. --cidfile="": Write the container ID to the file -d, --detach=false: Detached mode: Run container in the background, print new container id -e, --env=[]: Set environment variables + --env-file="": Read in a line delimited file of ENV variables -h, --hostname="": Container host name -i, --interactive=false: Keep stdin open even if not attached --privileged=false: Give extended privileges to this container @@ -1284,6 +1285,54 @@ This exposes port ``80`` of the container for use within a link without publishing the port to the host system's interfaces. :ref:`port_redirection` explains in detail how to manipulate ports in Docker. +.. code-block:: bash + + $ sudo docker run -e MYVAR1 --env MYVAR2=foo --env-file ./env.list ubuntu bash + +This sets environmental variables in the container. For illustration all three +flags are shown here. Where ``-e``, ``--env`` take an environment variable and +value, or if no "=" is provided, then that variable's current value is passed +through (i.e. $MYVAR1 from the host is set to $MYVAR1 in the container). All +three flags, ``-e``, ``--env`` and ``--env-file`` can be repeated. + +Regardless of the order of these three flags, the ``--env-file`` are processed +first, and then ``-e``/``--env`` flags. This way, the ``-e`` or ``--env`` will +override variables as needed. + +.. code-block:: bash + + $ cat ./env.list + TEST_FOO=BAR + $ sudo docker run --env TEST_FOO="This is a test" --env-file ./env.list busybox env | grep TEST_FOO + TEST_FOO=This is a test + +The ``--env-file`` flag takes a filename as an argument and expects each line +to be in the VAR=VAL format, mimicking the argument passed to ``--env``. +Comment lines need only be prefixed with ``#`` + +An example of a file passed with ``--env-file`` + +.. code-block:: bash + + $ cat ./env.list + TEST_FOO=BAR + + # this is a comment + TEST_APP_DEST_HOST=10.10.0.127 + TEST_APP_DEST_PORT=8888 + + # pass through this variable from the caller + TEST_PASSTHROUGH + $ sudo TEST_PASSTHROUGH=howdy docker run --env-file ./env.list busybox env + HOME=/ + PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + HOSTNAME=5198e0745561 + TEST_FOO=BAR + TEST_APP_DEST_HOST=10.10.0.127 + TEST_APP_DEST_PORT=8888 + TEST_PASSTHROUGH=howdy + + .. code-block:: bash $ sudo docker run --name console -t -i ubuntu bash diff --git a/components/engine/opts/envfile.go b/components/engine/opts/envfile.go new file mode 100644 index 0000000000..19ee8955f9 --- /dev/null +++ b/components/engine/opts/envfile.go @@ -0,0 +1,54 @@ +package opts + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +/* +Read in a line delimited file with environment variables enumerated +*/ +func ParseEnvFile(filename string) ([]string, error) { + fh, err := os.Open(filename) + if err != nil { + return []string{}, err + } + defer fh.Close() + + lines := []string{} + scanner := bufio.NewScanner(fh) + for scanner.Scan() { + line := scanner.Text() + // line is not empty, and not starting with '#' + if len(line) > 0 && !strings.HasPrefix(line, "#") { + if strings.Contains(line, "=") { + data := strings.SplitN(line, "=", 2) + + // trim the front of a variable, but nothing else + variable := strings.TrimLeft(data[0], whiteSpaces) + if strings.ContainsAny(variable, whiteSpaces) { + return []string{}, ErrBadEnvVariable{fmt.Sprintf("variable '%s' has white spaces", variable)} + } + + // pass the value through, no trimming + lines = append(lines, fmt.Sprintf("%s=%s", variable, data[1])) + } else { + // if only a pass-through variable is given, clean it up. + lines = append(lines, fmt.Sprintf("%s=%s", strings.TrimSpace(line), os.Getenv(line))) + } + } + } + return lines, nil +} + +var whiteSpaces = " \t" + +type ErrBadEnvVariable struct { + msg string +} + +func (e ErrBadEnvVariable) Error() string { + return fmt.Sprintf("poorly formatted environment: %s", e.msg) +} diff --git a/components/engine/runconfig/parse.go b/components/engine/runconfig/parse.go index 43aecdb753..db4ac351a9 100644 --- a/components/engine/runconfig/parse.go +++ b/components/engine/runconfig/parse.go @@ -52,6 +52,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf flVolumesFrom opts.ListOpts flLxcOpts opts.ListOpts flDriverOpts opts.ListOpts + flEnvFile opts.ListOpts flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)") flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id") @@ -78,6 +79,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)") cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables") + cmd.Var(&flEnvFile, []string{"-env-file"}, "Read in a line delimited file of ENV variables") cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", nat.PortSpecTemplateFormat)) cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host") @@ -199,6 +201,20 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf } } + // collect all the environment variables for the container + envVariables := []string{} + for _, ef := range flEnvFile.GetAll() { + parsedVars, err := opts.ParseEnvFile(ef) + if err != nil { + return nil, nil, cmd, err + } + envVariables = append(envVariables, parsedVars...) + } + // parse the '-e' and '--env' after, to allow override + envVariables = append(envVariables, flEnv.GetAll()...) + // boo, there's no debug output for docker run + //utils.Debugf("Environment variables for the container: %#v", envVariables) + config := &Config{ Hostname: hostname, Domainname: domainname, @@ -213,7 +229,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf AttachStdin: flAttach.Get("stdin"), AttachStdout: flAttach.Get("stdout"), AttachStderr: flAttach.Get("stderr"), - Env: flEnv.GetAll(), + Env: envVariables, Cmd: runCmd, Dns: flDns.GetAll(), DnsSearch: flDnsSearch.GetAll(),