2015-12-21 20:05:55 -05:00
package opts
2014-02-11 23:04:39 -05:00
import (
2016-01-04 12:59:26 -05:00
"bytes"
"encoding/json"
2014-02-11 23:04:39 -05:00
"fmt"
2016-01-04 12:59:26 -05:00
"io/ioutil"
2015-12-15 21:36:35 -05:00
"path"
2014-08-04 19:14:43 -04:00
"strconv"
2014-04-30 18:46:56 -04:00
"strings"
2014-07-24 18:19:50 -04:00
"github.com/docker/docker/opts"
flag "github.com/docker/docker/pkg/mflag"
2015-12-01 13:39:34 -05:00
"github.com/docker/docker/pkg/mount"
2015-08-04 16:51:48 -04:00
"github.com/docker/docker/pkg/signal"
2016-01-04 19:05:26 -05:00
"github.com/docker/engine-api/types/container"
2016-01-07 19:18:34 -05:00
networktypes "github.com/docker/engine-api/types/network"
2016-01-04 19:05:26 -05:00
"github.com/docker/engine-api/types/strslice"
2015-12-18 12:58:48 -05:00
"github.com/docker/go-connections/nat"
2015-12-16 12:26:49 -05:00
"github.com/docker/go-units"
2014-02-11 23:04:39 -05:00
)
2015-07-25 05:11:45 -04:00
// Parse parses the specified args for the specified command and generates a Config,
// a HostConfig and returns them with the specified command.
// If the specified args are not valid, it will return an error.
2016-01-07 19:18:34 -05:00
func Parse ( cmd * flag . FlagSet , args [ ] string ) ( * container . Config , * container . HostConfig , * networktypes . NetworkingConfig , * flag . FlagSet , error ) {
2014-02-11 23:04:39 -05:00
var (
// FIXME: use utils.ListOpts for attach and volumes?
2015-12-29 11:28:21 -05:00
flAttach = opts . NewListOpts ( ValidateAttach )
2015-06-11 20:34:20 -04:00
flVolumes = opts . NewListOpts ( nil )
2015-12-01 13:39:34 -05:00
flTmpfs = opts . NewListOpts ( nil )
2015-12-21 20:05:55 -05:00
flBlkioWeightDevice = NewWeightdeviceOpt ( ValidateWeightDevice )
flDeviceReadBps = NewThrottledeviceOpt ( ValidateThrottleBpsDevice )
flDeviceWriteBps = NewThrottledeviceOpt ( ValidateThrottleBpsDevice )
2015-12-15 21:36:35 -05:00
flLinks = opts . NewListOpts ( ValidateLink )
2016-01-08 08:45:56 -05:00
flAliases = opts . NewListOpts ( nil )
2015-12-21 20:05:55 -05:00
flDeviceReadIOps = NewThrottledeviceOpt ( ValidateThrottleIOpsDevice )
flDeviceWriteIOps = NewThrottledeviceOpt ( ValidateThrottleIOpsDevice )
2015-12-29 11:28:21 -05:00
flEnv = opts . NewListOpts ( ValidateEnv )
flLabels = opts . NewListOpts ( ValidateEnv )
2015-12-15 21:36:35 -05:00
flDevices = opts . NewListOpts ( ValidateDevice )
2014-02-11 23:04:39 -05:00
2015-12-21 20:05:55 -05:00
flUlimits = NewUlimitOpt ( nil )
2015-02-11 14:21:38 -05:00
2015-09-18 21:21:57 -04:00
flPublish = opts . NewListOpts ( nil )
flExpose = opts . NewListOpts ( nil )
flDNS = opts . NewListOpts ( opts . ValidateIPAddress )
flDNSSearch = opts . NewListOpts ( opts . ValidateDNSSearch )
flDNSOptions = opts . NewListOpts ( nil )
2015-12-29 11:28:21 -05:00
flExtraHosts = opts . NewListOpts ( ValidateExtraHost )
2015-09-18 21:21:57 -04:00
flVolumesFrom = opts . NewListOpts ( nil )
flEnvFile = opts . NewListOpts ( nil )
flCapAdd = opts . NewListOpts ( nil )
flCapDrop = opts . NewListOpts ( nil )
flGroupAdd = opts . NewListOpts ( nil )
flSecurityOpt = opts . NewListOpts ( nil )
flLabelsFile = opts . NewListOpts ( nil )
flLoggingOpts = opts . NewListOpts ( nil )
2015-11-09 09:37:24 -05:00
flPrivileged = cmd . Bool ( [ ] string { "-privileged" } , false , "Give extended privileges to this container" )
2015-09-23 02:02:45 -04:00
flPidMode = cmd . String ( [ ] string { "-pid" } , "" , "PID namespace to use" )
flUTSMode = cmd . String ( [ ] string { "-uts" } , "" , "UTS namespace to use" )
flPublishAll = cmd . Bool ( [ ] string { "P" , "-publish-all" } , false , "Publish all exposed ports to random ports" )
flStdin = cmd . Bool ( [ ] string { "i" , "-interactive" } , false , "Keep STDIN open even if not attached" )
flTty = cmd . Bool ( [ ] string { "t" , "-tty" } , false , "Allocate a pseudo-TTY" )
flOomKillDisable = cmd . Bool ( [ ] string { "-oom-kill-disable" } , false , "Disable OOM Killer" )
2015-10-13 05:26:27 -04:00
flOomScoreAdj = cmd . Int ( [ ] string { "-oom-score-adj" } , 0 , "Tune host's OOM preferences (-1000 to 1000)" )
2015-11-09 09:37:24 -05:00
flContainerIDFile = cmd . String ( [ ] string { "-cidfile" } , "" , "Write the container ID to the file" )
flEntrypoint = cmd . String ( [ ] string { "-entrypoint" } , "" , "Overwrite the default ENTRYPOINT of the image" )
2015-09-23 02:02:45 -04:00
flHostname = cmd . String ( [ ] string { "h" , "-hostname" } , "" , "Container host name" )
flMemoryString = cmd . String ( [ ] string { "m" , "-memory" } , "" , "Memory limit" )
flMemoryReservation = cmd . String ( [ ] string { "-memory-reservation" } , "" , "Memory soft limit" )
2015-12-29 20:23:35 -05:00
flMemorySwap = cmd . String ( [ ] string { "-memory-swap" } , "" , "Swap limit equal to memory plus swap: '-1' to enable unlimited swap" )
2015-09-23 02:02:45 -04:00
flKernelMemory = cmd . String ( [ ] string { "-kernel-memory" } , "" , "Kernel memory limit" )
flUser = cmd . String ( [ ] string { "u" , "-user" } , "" , "Username or UID (format: <name|uid>[:<group|gid>])" )
flWorkingDir = cmd . String ( [ ] string { "w" , "-workdir" } , "" , "Working directory inside the container" )
flCPUShares = cmd . Int64 ( [ ] string { "#c" , "-cpu-shares" } , 0 , "CPU shares (relative weight)" )
flCPUPeriod = cmd . Int64 ( [ ] string { "-cpu-period" } , 0 , "Limit CPU CFS (Completely Fair Scheduler) period" )
flCPUQuota = cmd . Int64 ( [ ] string { "-cpu-quota" } , 0 , "Limit CPU CFS (Completely Fair Scheduler) quota" )
2015-11-09 09:37:24 -05:00
flCpusetCpus = cmd . String ( [ ] string { "-cpuset-cpus" } , "" , "CPUs in which to allow execution (0-3, 0,1)" )
2015-09-23 02:02:45 -04:00
flCpusetMems = cmd . String ( [ ] string { "-cpuset-mems" } , "" , "MEMs in which to allow execution (0-3, 0,1)" )
2015-10-06 11:21:43 -04:00
flBlkioWeight = cmd . Uint16 ( [ ] string { "-blkio-weight" } , 0 , "Block IO (relative weight), between 10 and 1000" )
2015-10-13 05:26:27 -04:00
flSwappiness = cmd . Int64 ( [ ] string { "-memory-swappiness" } , - 1 , "Tune container memory swappiness (0 to 100)" )
2015-12-03 02:16:53 -05:00
flNetMode = cmd . String ( [ ] string { "-net" } , "default" , "Connect a container to a network" )
2015-09-23 02:02:45 -04:00
flMacAddress = cmd . String ( [ ] string { "-mac-address" } , "" , "Container MAC address (e.g. 92:d0:c6:0a:29:33)" )
2016-01-07 19:18:34 -05:00
flIPv4Address = cmd . String ( [ ] string { "-ip" } , "" , "Container IPv4 address (e.g. 172.30.100.104)" )
flIPv6Address = cmd . String ( [ ] string { "-ip6" } , "" , "Container IPv6 address (e.g. 2001:db8::33)" )
2015-09-23 02:02:45 -04:00
flIpcMode = cmd . String ( [ ] string { "-ipc" } , "" , "IPC namespace to use" )
flRestartPolicy = cmd . String ( [ ] string { "-restart" } , "no" , "Restart policy to apply when a container exits" )
flReadonlyRootfs = cmd . Bool ( [ ] string { "-read-only" } , false , "Mount the container's root filesystem as read only" )
flLoggingDriver = cmd . String ( [ ] string { "-log-driver" } , "" , "Logging driver for container" )
flCgroupParent = cmd . String ( [ ] string { "-cgroup-parent" } , "" , "Optional parent cgroup for the container" )
flVolumeDriver = cmd . String ( [ ] string { "-volume-driver" } , "" , "Optional volume driver for the container" )
flStopSignal = cmd . String ( [ ] string { "-stop-signal" } , signal . DefaultStopSignal , fmt . Sprintf ( "Signal to stop a container, %v by default" , signal . DefaultStopSignal ) )
2016-02-03 15:07:00 -05:00
flIsolation = cmd . String ( [ ] string { "-isolation" } , "" , "Container isolation technology" )
2015-09-09 02:30:56 -04:00
flShmSize = cmd . String ( [ ] string { "-shm-size" } , "" , "Size of /dev/shm, default value is 64MB" )
2014-02-11 23:04:39 -05:00
)
2015-03-03 12:04:06 -05:00
cmd . Var ( & flAttach , [ ] string { "a" , "-attach" } , "Attach to STDIN, STDOUT or STDERR" )
2015-06-11 20:34:20 -04:00
cmd . Var ( & flBlkioWeightDevice , [ ] string { "-blkio-weight-device" } , "Block IO weight (relative device weight)" )
2015-07-08 07:06:48 -04:00
cmd . Var ( & flDeviceReadBps , [ ] string { "-device-read-bps" } , "Limit read rate (bytes per second) from a device" )
cmd . Var ( & flDeviceWriteBps , [ ] string { "-device-write-bps" } , "Limit write rate (bytes per second) to a device" )
2015-07-08 07:06:48 -04:00
cmd . Var ( & flDeviceReadIOps , [ ] string { "-device-read-iops" } , "Limit read rate (IO per second) from a device" )
cmd . Var ( & flDeviceWriteIOps , [ ] string { "-device-write-iops" } , "Limit write rate (IO per second) to a device" )
2015-02-03 22:51:35 -05:00
cmd . Var ( & flVolumes , [ ] string { "v" , "-volume" } , "Bind mount a volume" )
2015-12-01 13:39:34 -05:00
cmd . Var ( & flTmpfs , [ ] string { "-tmpfs" } , "Mount a tmpfs directory" )
2015-11-09 09:37:24 -05:00
cmd . Var ( & flLinks , [ ] string { "-link" } , "Add link to another container" )
2016-01-08 08:45:56 -05:00
cmd . Var ( & flAliases , [ ] string { "-net-alias" } , "Add network-scoped alias for the container" )
2015-02-03 22:51:35 -05:00
cmd . Var ( & flDevices , [ ] string { "-device" } , "Add a host device to the container" )
2015-02-16 19:36:03 -05:00
cmd . Var ( & flLabels , [ ] string { "l" , "-label" } , "Set meta data on a container" )
2015-01-06 19:04:10 -05:00
cmd . Var ( & flLabelsFile , [ ] string { "-label-file" } , "Read in a line delimited file of labels" )
2014-02-11 23:04:39 -05:00
cmd . Var ( & flEnv , [ ] string { "e" , "-env" } , "Set environment variables" )
2015-02-03 22:51:35 -05:00
cmd . Var ( & flEnvFile , [ ] string { "-env-file" } , "Read in a file of environment variables" )
cmd . Var ( & flPublish , [ ] string { "p" , "-publish" } , "Publish a container's port(s) to the host" )
2015-11-09 09:37:24 -05:00
cmd . Var ( & flExpose , [ ] string { "-expose" } , "Expose a port or a range of ports" )
cmd . Var ( & flDNS , [ ] string { "-dns" } , "Set custom DNS servers" )
2015-07-25 05:11:45 -04:00
cmd . Var ( & flDNSSearch , [ ] string { "-dns-search" } , "Set custom DNS search domains" )
2015-08-31 14:47:25 -04:00
cmd . Var ( & flDNSOptions , [ ] string { "-dns-opt" } , "Set DNS options" )
2014-09-13 00:35:59 -04:00
cmd . Var ( & flExtraHosts , [ ] string { "-add-host" } , "Add a custom host-to-IP mapping (host:ip)" )
2015-11-09 09:37:24 -05:00
cmd . Var ( & flVolumesFrom , [ ] string { "-volumes-from" } , "Mount volumes from the specified container(s)" )
2014-07-10 19:50:45 -04:00
cmd . Var ( & flCapAdd , [ ] string { "-cap-add" } , "Add Linux capabilities" )
cmd . Var ( & flCapDrop , [ ] string { "-cap-drop" } , "Drop Linux capabilities" )
2015-06-17 14:39:17 -04:00
cmd . Var ( & flGroupAdd , [ ] string { "-group-add" } , "Add additional groups to join" )
2014-09-29 06:44:32 -04:00
cmd . Var ( & flSecurityOpt , [ ] string { "-security-opt" } , "Security Options" )
2015-02-11 14:21:38 -05:00
cmd . Var ( flUlimits , [ ] string { "-ulimit" } , "Ulimit options" )
2015-05-04 17:39:48 -04:00
cmd . Var ( & flLoggingOpts , [ ] string { "-log-opt" } , "Log driver options" )
2014-07-10 14:41:11 -04:00
2014-11-05 11:57:51 -05:00
cmd . Require ( flag . Min , 1 )
2015-03-28 21:22:46 -04:00
if err := cmd . ParseFlags ( args , true ) ; err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2014-02-11 23:04:39 -05:00
}
2014-03-10 09:11:23 -04:00
var (
attachStdin = flAttach . Get ( "stdin" )
attachStdout = flAttach . Get ( "stdout" )
attachStderr = flAttach . Get ( "stderr" )
)
2014-02-11 23:04:39 -05:00
2015-05-05 07:27:07 -04:00
// Validate the input mac address
if * flMacAddress != "" {
2015-12-29 11:28:21 -05:00
if _ , err := ValidateMACAddress ( * flMacAddress ) ; err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , fmt . Errorf ( "%s is not a valid mac address" , * flMacAddress )
2015-05-05 07:27:07 -04:00
}
2014-07-30 10:51:28 -04:00
}
2015-07-06 18:24:23 -04:00
if * flStdin {
attachStdin = true
}
// If -a is not set attach to the output stdio
2014-03-10 09:11:23 -04:00
if flAttach . Len ( ) == 0 {
attachStdout = true
attachStderr = true
2014-02-11 23:04:39 -05:00
}
2015-09-14 08:30:11 -04:00
var err error
2014-02-11 23:04:39 -05:00
var flMemory int64
if * flMemoryString != "" {
2015-09-14 08:30:11 -04:00
flMemory , err = units . RAMInBytes ( * flMemoryString )
2014-02-11 23:04:39 -05:00
if err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2014-02-11 23:04:39 -05:00
}
}
2015-09-23 02:02:45 -04:00
var MemoryReservation int64
if * flMemoryReservation != "" {
MemoryReservation , err = units . RAMInBytes ( * flMemoryReservation )
if err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2015-09-23 02:02:45 -04:00
}
}
2015-08-06 07:55:56 -04:00
var memorySwap int64
2014-12-10 19:53:43 -05:00
if * flMemorySwap != "" {
2015-02-08 21:36:49 -05:00
if * flMemorySwap == "-1" {
2015-08-06 07:55:56 -04:00
memorySwap = - 1
2015-02-08 21:36:49 -05:00
} else {
2015-09-14 08:30:11 -04:00
memorySwap , err = units . RAMInBytes ( * flMemorySwap )
2015-02-08 21:36:49 -05:00
if err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2015-02-08 21:36:49 -05:00
}
2014-12-10 19:53:43 -05:00
}
}
2015-08-19 11:56:55 -04:00
var KernelMemory int64
if * flKernelMemory != "" {
2015-09-14 08:30:11 -04:00
KernelMemory , err = units . RAMInBytes ( * flKernelMemory )
2015-08-19 11:56:55 -04:00
if err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2015-08-19 11:56:55 -04:00
}
}
2015-07-20 04:10:10 -04:00
swappiness := * flSwappiness
if swappiness != - 1 && ( swappiness < 0 || swappiness > 100 ) {
2016-02-18 03:26:47 -05:00
return nil , nil , nil , cmd , fmt . Errorf ( "invalid value: %d. Valid memory swappiness range is 0-100" , swappiness )
2015-07-12 03:46:33 -04:00
}
2015-12-29 15:49:17 -05:00
var shmSize int64
2015-09-09 02:30:56 -04:00
if * flShmSize != "" {
2015-12-29 15:49:17 -05:00
shmSize , err = units . RAMInBytes ( * flShmSize )
2015-09-09 02:30:56 -04:00
if err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2015-09-09 02:30:56 -04:00
}
}
2014-02-11 23:04:39 -05:00
var binds [ ] string
// add any bind targets to the list of container volumes
for bind := range flVolumes . GetMap ( ) {
2015-12-22 19:52:27 -05:00
if arr := volumeSplitN ( bind , 2 ) ; len ( arr ) > 1 {
2014-05-19 18:18:37 -04:00
// after creating the bind mount we want to delete it from the flVolumes values because
// we do not want bind mounts being committed to image configs
2014-02-11 23:04:39 -05:00
binds = append ( binds , bind )
flVolumes . Delete ( bind )
}
}
2015-12-13 11:00:39 -05:00
// Can't evaluate options passed into --tmpfs until we actually mount
2015-12-01 13:39:34 -05:00
tmpfs := make ( map [ string ] string )
for _ , t := range flTmpfs . GetAll ( ) {
if arr := strings . SplitN ( t , ":" , 2 ) ; len ( arr ) > 1 {
if _ , _ , err := mount . ParseTmpfsOptions ( arr [ 1 ] ) ; err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2015-12-01 13:39:34 -05:00
}
tmpfs [ arr [ 0 ] ] = arr [ 1 ]
} else {
tmpfs [ arr [ 0 ] ] = ""
}
}
2014-02-11 23:04:39 -05:00
var (
parsedArgs = cmd . Args ( )
2016-02-29 06:28:37 -05:00
runCmd strslice . StrSlice
entrypoint strslice . StrSlice
2014-10-15 17:14:12 -04:00
image = cmd . Arg ( 0 )
2014-02-11 23:04:39 -05:00
)
if len ( parsedArgs ) > 1 {
2016-02-29 06:28:37 -05:00
runCmd = strslice . StrSlice ( parsedArgs [ 1 : ] )
2014-02-11 23:04:39 -05:00
}
if * flEntrypoint != "" {
2016-02-29 06:28:37 -05:00
entrypoint = strslice . StrSlice { * flEntrypoint }
2014-02-11 23:04:39 -05:00
}
var (
domainname string
hostname = * flHostname
parts = strings . SplitN ( hostname , "." , 2 )
)
if len ( parts ) > 1 {
hostname = parts [ 0 ]
domainname = parts [ 1 ]
}
ports , portBindings , err := nat . ParsePortSpecs ( flPublish . GetAll ( ) )
if err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2014-02-11 23:04:39 -05:00
}
// Merge in exposed ports to the map of published ports
for _ , e := range flExpose . GetAll ( ) {
if strings . Contains ( e , ":" ) {
2016-02-18 03:26:47 -05:00
return nil , nil , nil , cmd , fmt . Errorf ( "invalid port format for --expose: %s" , e )
2014-02-11 23:04:39 -05:00
}
2014-09-16 21:08:30 -04:00
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
2015-02-17 10:12:02 -05:00
proto , port := nat . SplitProtoPort ( e )
//parse the start and end port and create a sequence of ports to expose
//if expose a port, the start and end port are the same
2015-12-22 19:02:47 -05:00
start , end , err := nat . ParsePortRange ( port )
2015-02-17 10:12:02 -05:00
if err != nil {
2016-02-18 03:26:47 -05:00
return nil , nil , nil , cmd , fmt . Errorf ( "invalid range format for --expose: %s, error: %s" , e , err )
2015-02-17 10:12:02 -05:00
}
for i := start ; i <= end ; i ++ {
2015-07-15 23:45:48 -04:00
p , err := nat . NewPort ( proto , strconv . FormatUint ( i , 10 ) )
if err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2015-07-15 23:45:48 -04:00
}
2014-09-16 21:08:30 -04:00
if _ , exists := ports [ p ] ; ! exists {
ports [ p ] = struct { } { }
}
2014-02-11 23:04:39 -05:00
}
}
2014-05-31 00:00:47 -04:00
// parse device mappings
2015-12-18 13:36:17 -05:00
deviceMappings := [ ] container . DeviceMapping { }
2014-05-31 00:00:47 -04:00
for _ , device := range flDevices . GetAll ( ) {
2015-07-29 05:08:30 -04:00
deviceMapping , err := ParseDevice ( device )
2014-05-31 00:00:47 -04:00
if err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2014-05-31 00:00:47 -04:00
}
deviceMappings = append ( deviceMappings , deviceMapping )
}
2014-02-16 19:24:22 -05:00
// collect all the environment variables for the container
2015-01-06 19:04:10 -05:00
envVariables , err := readKVStrings ( flEnvFile . GetAll ( ) , flEnv . GetAll ( ) )
if err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2015-01-06 19:04:10 -05:00
}
// collect all the labels for the container
labels , err := readKVStrings ( flLabelsFile . GetAll ( ) , flLabels . GetAll ( ) )
if err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2014-02-16 19:24:22 -05:00
}
2015-12-18 13:36:17 -05:00
ipcMode := container . IpcMode ( * flIpcMode )
2014-11-10 16:14:17 -05:00
if ! ipcMode . Valid ( ) {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , fmt . Errorf ( "--ipc: invalid IPC mode" )
2014-11-25 15:10:53 -05:00
}
2015-12-18 13:36:17 -05:00
pidMode := container . PidMode ( * flPidMode )
2014-11-25 15:10:53 -05:00
if ! pidMode . Valid ( ) {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , fmt . Errorf ( "--pid: invalid PID mode" )
2014-11-10 16:14:17 -05:00
}
2015-12-18 13:36:17 -05:00
utsMode := container . UTSMode ( * flUTSMode )
2015-05-05 18:32:36 -04:00
if ! utsMode . Valid ( ) {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , fmt . Errorf ( "--uts: invalid UTS mode" )
2015-05-05 18:32:36 -04:00
}
2015-04-24 15:23:54 -04:00
restartPolicy , err := ParseRestartPolicy ( * flRestartPolicy )
2014-08-04 19:14:43 -04:00
if err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2014-08-04 19:14:43 -04:00
}
2015-05-04 17:39:48 -04:00
loggingOpts , err := parseLoggingOpts ( * flLoggingDriver , flLoggingOpts . GetAll ( ) )
if err != nil {
2016-01-07 19:18:34 -05:00
return nil , nil , nil , cmd , err
2015-05-04 17:39:48 -04:00
}
2016-01-04 12:59:26 -05:00
securityOpts , err := parseSecurityOpts ( flSecurityOpt . GetAll ( ) )
if err != nil {
return nil , nil , nil , cmd , err
}
2015-12-18 13:36:17 -05:00
resources := container . Resources {
2015-07-08 07:06:48 -04:00
CgroupParent : * flCgroupParent ,
Memory : flMemory ,
MemoryReservation : MemoryReservation ,
MemorySwap : memorySwap ,
MemorySwappiness : flSwappiness ,
KernelMemory : KernelMemory ,
2015-12-31 01:17:18 -05:00
OomKillDisable : flOomKillDisable ,
2015-07-08 07:06:48 -04:00
CPUShares : * flCPUShares ,
CPUPeriod : * flCPUPeriod ,
CpusetCpus : * flCpusetCpus ,
CpusetMems : * flCpusetMems ,
CPUQuota : * flCPUQuota ,
BlkioWeight : * flBlkioWeight ,
BlkioWeightDevice : flBlkioWeightDevice . GetList ( ) ,
BlkioDeviceReadBps : flDeviceReadBps . GetList ( ) ,
BlkioDeviceWriteBps : flDeviceWriteBps . GetList ( ) ,
BlkioDeviceReadIOps : flDeviceReadIOps . GetList ( ) ,
BlkioDeviceWriteIOps : flDeviceWriteIOps . GetList ( ) ,
Ulimits : flUlimits . GetList ( ) ,
Devices : deviceMappings ,
2015-11-18 14:03:08 -05:00
}
2015-12-18 13:36:17 -05:00
config := & container . Config {
2015-11-09 09:37:24 -05:00
Hostname : hostname ,
Domainname : domainname ,
ExposedPorts : ports ,
User : * flUser ,
Tty : * flTty ,
// TODO: deprecated, it comes from -n, --networking
// it's still needed internally to set the network to disabled
// if e.g. bridge is none in daemon opts, and in inspect
NetworkDisabled : false ,
2014-02-11 23:04:39 -05:00
OpenStdin : * flStdin ,
2014-03-10 09:11:23 -04:00
AttachStdin : attachStdin ,
AttachStdout : attachStdout ,
AttachStderr : attachStderr ,
2014-02-16 19:24:22 -05:00
Env : envVariables ,
2014-02-11 23:04:39 -05:00
Cmd : runCmd ,
Image : image ,
Volumes : flVolumes . GetMap ( ) ,
2014-10-03 17:02:17 -04:00
MacAddress : * flMacAddress ,
2014-02-11 23:04:39 -05:00
Entrypoint : entrypoint ,
WorkingDir : * flWorkingDir ,
2014-11-14 13:59:14 -05:00
Labels : ConvertKVStringsToMap ( labels ) ,
2016-02-12 17:56:40 -05:00
}
if cmd . IsSet ( "-stop-signal" ) {
config . StopSignal = * flStopSignal
2014-02-11 23:04:39 -05:00
}
2015-12-18 13:36:17 -05:00
hostConfig := & container . HostConfig {
2015-11-18 14:03:08 -05:00
Binds : binds ,
ContainerIDFile : * flContainerIDFile ,
2015-10-13 05:26:27 -04:00
OomScoreAdj : * flOomScoreAdj ,
2015-11-18 14:03:08 -05:00
Privileged : * flPrivileged ,
PortBindings : portBindings ,
Links : flLinks . GetAll ( ) ,
PublishAllPorts : * flPublishAll ,
2015-11-06 17:22:48 -05:00
// Make sure the dns fields are never nil.
// New containers don't ever have those fields nil,
// but pre created containers can still have those nil values.
// See https://github.com/docker/docker/pull/17779
// for a more detailed explanation on why we don't want that.
DNS : flDNS . GetAllOrEmpty ( ) ,
DNSSearch : flDNSSearch . GetAllOrEmpty ( ) ,
DNSOptions : flDNSOptions . GetAllOrEmpty ( ) ,
ExtraHosts : flExtraHosts . GetAll ( ) ,
VolumesFrom : flVolumesFrom . GetAll ( ) ,
2015-12-18 13:36:17 -05:00
NetworkMode : container . NetworkMode ( * flNetMode ) ,
2015-11-06 17:22:48 -05:00
IpcMode : ipcMode ,
PidMode : pidMode ,
UTSMode : utsMode ,
2016-02-29 06:28:37 -05:00
CapAdd : strslice . StrSlice ( flCapAdd . GetAll ( ) ) ,
CapDrop : strslice . StrSlice ( flCapDrop . GetAll ( ) ) ,
2015-11-06 17:22:48 -05:00
GroupAdd : flGroupAdd . GetAll ( ) ,
RestartPolicy : restartPolicy ,
2016-01-04 12:59:26 -05:00
SecurityOpt : securityOpts ,
2015-11-06 17:22:48 -05:00
ReadonlyRootfs : * flReadonlyRootfs ,
2015-12-18 13:36:17 -05:00
LogConfig : container . LogConfig { Type : * flLoggingDriver , Config : loggingOpts } ,
2015-11-06 17:22:48 -05:00
VolumeDriver : * flVolumeDriver ,
2016-02-03 15:07:00 -05:00
Isolation : container . Isolation ( * flIsolation ) ,
2015-12-29 15:49:17 -05:00
ShmSize : shmSize ,
2015-11-18 14:03:08 -05:00
Resources : resources ,
2015-12-01 13:39:34 -05:00
Tmpfs : tmpfs ,
2014-02-11 23:04:39 -05:00
}
// When allocating stdin in attached mode, close stdin at client disconnect
if config . OpenStdin && config . AttachStdin {
config . StdinOnce = true
}
2016-01-07 19:18:34 -05:00
2016-01-05 14:20:47 -05:00
networkingConfig := & networktypes . NetworkingConfig {
EndpointsConfig : make ( map [ string ] * networktypes . EndpointSettings ) ,
}
2016-01-07 19:18:34 -05:00
if * flIPv4Address != "" || * flIPv6Address != "" {
networkingConfig . EndpointsConfig [ string ( hostConfig . NetworkMode ) ] = & networktypes . EndpointSettings {
IPAMConfig : & networktypes . EndpointIPAMConfig {
IPv4Address : * flIPv4Address ,
IPv6Address : * flIPv6Address ,
} ,
}
}
2016-01-05 14:20:47 -05:00
if hostConfig . NetworkMode . IsUserDefined ( ) && len ( hostConfig . Links ) > 0 {
epConfig := networkingConfig . EndpointsConfig [ string ( hostConfig . NetworkMode ) ]
if epConfig == nil {
epConfig = & networktypes . EndpointSettings { }
}
epConfig . Links = make ( [ ] string , len ( hostConfig . Links ) )
copy ( epConfig . Links , hostConfig . Links )
networkingConfig . EndpointsConfig [ string ( hostConfig . NetworkMode ) ] = epConfig
}
2016-01-20 20:51:41 -05:00
if flAliases . Len ( ) > 0 {
2016-01-08 08:45:56 -05:00
epConfig := networkingConfig . EndpointsConfig [ string ( hostConfig . NetworkMode ) ]
if epConfig == nil {
epConfig = & networktypes . EndpointSettings { }
}
epConfig . Aliases = make ( [ ] string , flAliases . Len ( ) )
copy ( epConfig . Aliases , flAliases . GetAll ( ) )
networkingConfig . EndpointsConfig [ string ( hostConfig . NetworkMode ) ] = epConfig
}
2016-01-07 19:18:34 -05:00
return config , hostConfig , networkingConfig , cmd , nil
2014-02-11 23:04:39 -05:00
}
2015-01-06 19:04:10 -05:00
// reads a file of line terminated key=value pairs and override that with override parameter
func readKVStrings ( files [ ] string , override [ ] string ) ( [ ] string , error ) {
envVariables := [ ] string { }
for _ , ef := range files {
2015-12-29 11:28:21 -05:00
parsedVars , err := ParseEnvFile ( ef )
2015-01-06 19:04:10 -05:00
if err != nil {
return nil , err
}
envVariables = append ( envVariables , parsedVars ... )
}
// parse the '-e' and '--env' after, to allow override
envVariables = append ( envVariables , override ... )
return envVariables , nil
}
2014-11-14 13:59:14 -05:00
// ConvertKVStringsToMap converts ["key=value"] to {"key":"value"}
func ConvertKVStringsToMap ( values [ ] string ) map [ string ] string {
2015-01-06 19:04:10 -05:00
result := make ( map [ string ] string , len ( values ) )
for _ , value := range values {
kv := strings . SplitN ( value , "=" , 2 )
if len ( kv ) == 1 {
result [ kv [ 0 ] ] = ""
} else {
result [ kv [ 0 ] ] = kv [ 1 ]
}
}
return result
}
2015-05-04 17:39:48 -04:00
func parseLoggingOpts ( loggingDriver string , loggingOpts [ ] string ) ( map [ string ] string , error ) {
2014-11-14 13:59:14 -05:00
loggingOptsMap := ConvertKVStringsToMap ( loggingOpts )
2015-05-04 17:39:48 -04:00
if loggingDriver == "none" && len ( loggingOpts ) > 0 {
2016-02-18 03:26:47 -05:00
return map [ string ] string { } , fmt . Errorf ( "invalid logging opts for driver %s" , loggingDriver )
2015-05-04 17:39:48 -04:00
}
return loggingOptsMap , nil
}
2016-01-04 12:59:26 -05:00
// takes a local seccomp daemon, reads the file contents for sending to the daemon
func parseSecurityOpts ( securityOpts [ ] string ) ( [ ] string , error ) {
for key , opt := range securityOpts {
con := strings . SplitN ( opt , ":" , 2 )
if len ( con ) == 1 {
2016-02-18 03:26:47 -05:00
return securityOpts , fmt . Errorf ( "invalid --security-opt: %q" , opt )
2016-01-04 12:59:26 -05:00
}
if con [ 0 ] == "seccomp" && con [ 1 ] != "unconfined" {
f , err := ioutil . ReadFile ( con [ 1 ] )
if err != nil {
2016-02-18 03:26:47 -05:00
return securityOpts , fmt . Errorf ( "opening seccomp profile (%s) failed: %v" , con [ 1 ] , err )
2016-01-04 12:59:26 -05:00
}
b := bytes . NewBuffer ( nil )
if err := json . Compact ( b , f ) ; err != nil {
2016-02-18 03:26:47 -05:00
return securityOpts , fmt . Errorf ( "compacting json for seccomp profile (%s) failed: %v" , con [ 1 ] , err )
2016-01-04 12:59:26 -05:00
}
securityOpts [ key ] = fmt . Sprintf ( "seccomp:%s" , b . Bytes ( ) )
}
}
return securityOpts , nil
}
2015-04-24 15:23:54 -04:00
// ParseRestartPolicy returns the parsed policy or an error indicating what is incorrect
2015-12-18 13:36:17 -05:00
func ParseRestartPolicy ( policy string ) ( container . RestartPolicy , error ) {
p := container . RestartPolicy { }
2014-08-04 19:14:43 -04:00
if policy == "" {
return p , nil
}
var (
parts = strings . Split ( policy , ":" )
name = parts [ 0 ]
)
2015-01-08 04:15:55 -05:00
p . Name = name
2014-08-04 19:14:43 -04:00
switch name {
2015-08-05 17:09:08 -04:00
case "always" , "unless-stopped" :
2015-07-03 05:33:33 -04:00
if len ( parts ) > 1 {
2015-08-05 17:09:08 -04:00
return p , fmt . Errorf ( "maximum restart count not valid with restart policy of \"%s\"" , name )
2014-08-04 21:20:53 -04:00
}
case "no" :
// do nothing
case "on-failure" :
2015-07-03 05:33:33 -04:00
if len ( parts ) > 2 {
return p , fmt . Errorf ( "restart count format is not valid, usage: 'on-failure:N' or 'on-failure'" )
}
2014-08-04 19:14:43 -04:00
if len ( parts ) == 2 {
count , err := strconv . Atoi ( parts [ 1 ] )
if err != nil {
return p , err
}
p . MaximumRetryCount = count
}
default :
return p , fmt . Errorf ( "invalid restart policy %s" , name )
}
return p , nil
}
2015-12-18 13:36:17 -05:00
// ParseDevice parses a device mapping string to a container.DeviceMapping struct
func ParseDevice ( device string ) ( container . DeviceMapping , error ) {
2014-05-31 00:00:47 -04:00
src := ""
dst := ""
permissions := "rwm"
arr := strings . Split ( device , ":" )
switch len ( arr ) {
case 3 :
permissions = arr [ 2 ]
fallthrough
case 2 :
2015-12-15 21:36:35 -05:00
if ValidDeviceMode ( arr [ 1 ] ) {
2015-08-24 05:57:12 -04:00
permissions = arr [ 1 ]
} else {
dst = arr [ 1 ]
}
2014-05-31 00:00:47 -04:00
fallthrough
case 1 :
src = arr [ 0 ]
default :
2016-02-18 03:26:47 -05:00
return container . DeviceMapping { } , fmt . Errorf ( "invalid device specification: %s" , device )
2014-05-31 00:00:47 -04:00
}
if dst == "" {
dst = src
}
2015-12-18 13:36:17 -05:00
deviceMapping := container . DeviceMapping {
2014-05-31 00:00:47 -04:00
PathOnHost : src ,
PathInContainer : dst ,
CgroupPermissions : permissions ,
}
return deviceMapping , nil
}
2015-12-15 21:36:35 -05:00
// ParseLink parses and validates the specified string as a link format (name:alias)
func ParseLink ( val string ) ( string , string , error ) {
if val == "" {
return "" , "" , fmt . Errorf ( "empty string specified for links" )
}
arr := strings . Split ( val , ":" )
if len ( arr ) > 2 {
return "" , "" , fmt . Errorf ( "bad format for links: %s" , val )
}
if len ( arr ) == 1 {
return val , val , nil
}
// This is kept because we can actually get an HostConfig with links
// from an already created container and the format is not `foo:bar`
// but `/foo:/c1/bar`
if strings . HasPrefix ( arr [ 0 ] , "/" ) {
_ , alias := path . Split ( arr [ 1 ] )
return arr [ 0 ] [ 1 : ] , alias , nil
}
return arr [ 0 ] , arr [ 1 ] , nil
}
// ValidateLink validates that the specified string has a valid link format (containerName:alias).
func ValidateLink ( val string ) ( string , error ) {
if _ , _ , err := ParseLink ( val ) ; err != nil {
return val , err
}
return val , nil
}
// ValidDeviceMode checks if the mode for device is valid or not.
// Valid mode is a composition of r (read), w (write), and m (mknod).
func ValidDeviceMode ( mode string ) bool {
var legalDeviceMode = map [ rune ] bool {
'r' : true ,
'w' : true ,
'm' : true ,
}
if mode == "" {
return false
}
for _ , c := range mode {
if ! legalDeviceMode [ c ] {
return false
}
legalDeviceMode [ c ] = false
}
return true
}
// ValidateDevice validates a path for devices
// It will make sure 'val' is in the form:
// [host-dir:]container-path[:mode]
// It also validates the device mode.
func ValidateDevice ( val string ) ( string , error ) {
return validatePath ( val , ValidDeviceMode )
}
func validatePath ( val string , validator func ( string ) bool ) ( string , error ) {
var containerPath string
var mode string
if strings . Count ( val , ":" ) > 2 {
return val , fmt . Errorf ( "bad format for path: %s" , val )
}
split := strings . SplitN ( val , ":" , 3 )
if split [ 0 ] == "" {
return val , fmt . Errorf ( "bad format for path: %s" , val )
}
switch len ( split ) {
case 1 :
containerPath = split [ 0 ]
val = path . Clean ( containerPath )
case 2 :
if isValid := validator ( split [ 1 ] ) ; isValid {
containerPath = split [ 0 ]
mode = split [ 1 ]
val = fmt . Sprintf ( "%s:%s" , path . Clean ( containerPath ) , mode )
} else {
containerPath = split [ 1 ]
val = fmt . Sprintf ( "%s:%s" , split [ 0 ] , path . Clean ( containerPath ) )
}
case 3 :
containerPath = split [ 1 ]
mode = split [ 2 ]
if isValid := validator ( split [ 2 ] ) ; ! isValid {
return val , fmt . Errorf ( "bad mode specified: %s" , mode )
}
val = fmt . Sprintf ( "%s:%s:%s" , split [ 0 ] , containerPath , mode )
}
if ! path . IsAbs ( containerPath ) {
return val , fmt . Errorf ( "%s is not an absolute path" , containerPath )
}
return val , nil
}
2015-12-22 19:52:27 -05:00
2016-01-24 12:02:21 -05:00
// volumeSplitN splits raw into a maximum of n parts, separated by a separator colon.
2015-12-22 19:52:27 -05:00
// A separator colon is the last `:` character in the regex `[/:\\]?[a-zA-Z]:` (note `\\` is `\` escaped).
// This allows to correctly split strings such as `C:\foo:D:\:rw`.
func volumeSplitN ( raw string , n int ) [ ] string {
var array [ ] string
if len ( raw ) == 0 || raw [ 0 ] == ':' {
// invalid
return nil
}
// numberOfParts counts the number of parts separated by a separator colon
numberOfParts := 0
// left represents the left-most cursor in raw, updated at every `:` character considered as a separator.
left := 0
// right represents the right-most cursor in raw incremented with the loop. Note this
// starts at index 1 as index 0 is already handle above as a special case.
for right := 1 ; right < len ( raw ) ; right ++ {
// stop parsing if reached maximum number of parts
if n >= 0 && numberOfParts >= n {
break
}
if raw [ right ] != ':' {
continue
}
potentialDriveLetter := raw [ right - 1 ]
if ( potentialDriveLetter >= 'A' && potentialDriveLetter <= 'Z' ) || ( potentialDriveLetter >= 'a' && potentialDriveLetter <= 'z' ) {
if right > 1 {
beforePotentialDriveLetter := raw [ right - 2 ]
if beforePotentialDriveLetter != ':' && beforePotentialDriveLetter != '/' && beforePotentialDriveLetter != '\\' {
// e.g. `C:` is not preceded by any delimiter, therefore it was not a drive letter but a path ending with `C:`.
array = append ( array , raw [ left : right ] )
left = right + 1
numberOfParts ++
}
// else, `C:` is considered as a drive letter and not as a delimiter, so we continue parsing.
}
// if right == 1, then `C:` is the beginning of the raw string, therefore `:` is again not considered a delimiter and we continue parsing.
} else {
// if `:` is not preceded by a potential drive letter, then consider it as a delimiter.
array = append ( array , raw [ left : right ] )
left = right + 1
numberOfParts ++
}
}
// need to take care of the last part
if left < len ( raw ) {
if n >= 0 && numberOfParts >= n {
// if the maximum number of parts is reached, just append the rest to the last part
// left-1 is at the last `:` that needs to be included since not considered a separator.
array [ n - 1 ] += raw [ left - 1 : ]
} else {
array = append ( array , raw [ left : ] )
}
}
return array
}