mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #18941 from dnephin/runconfig_in_cli
Move runconfig/parse.go into the runconfig/opts package
This commit is contained in:
commit
dc4ca0e897
14 changed files with 203 additions and 203 deletions
|
@ -12,7 +12,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/jsonmessage"
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
"github.com/docker/docker/reference"
|
"github.com/docker/docker/reference"
|
||||||
"github.com/docker/docker/registry"
|
"github.com/docker/docker/registry"
|
||||||
"github.com/docker/docker/runconfig"
|
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (cli *DockerCli) pullImage(image string) error {
|
func (cli *DockerCli) pullImage(image string) error {
|
||||||
|
@ -156,7 +156,7 @@ func (cli *DockerCli) CmdCreate(args ...string) error {
|
||||||
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
||||||
)
|
)
|
||||||
|
|
||||||
config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
|
config, hostConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.ReportError(err.Error(), true)
|
cmd.ReportError(err.Error(), true)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
Cli "github.com/docker/docker/cli"
|
Cli "github.com/docker/docker/cli"
|
||||||
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
"github.com/docker/docker/pkg/promise"
|
"github.com/docker/docker/pkg/promise"
|
||||||
"github.com/docker/docker/runconfig"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdExec runs a command in a running container.
|
// CmdExec runs a command in a running container.
|
||||||
|
@ -18,7 +18,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
||||||
cmd := Cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, Cli.DockerCommands["exec"].Description, true)
|
cmd := Cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, Cli.DockerCommands["exec"].Description, true)
|
||||||
detachKeys := cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
detachKeys := cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
||||||
|
|
||||||
execConfig, err := runconfig.ParseExec(cmd, args)
|
execConfig, err := ParseExec(cmd, args)
|
||||||
// just in case the ParseExec does not exit
|
// just in case the ParseExec does not exit
|
||||||
if execConfig.Container == "" || err != nil {
|
if execConfig.Container == "" || err != nil {
|
||||||
return Cli.StatusError{StatusCode: 1}
|
return Cli.StatusError{StatusCode: 1}
|
||||||
|
@ -113,3 +113,46 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseExec parses the specified args for the specified command and generates
|
||||||
|
// an ExecConfig from it.
|
||||||
|
// If the minimal number of specified args is not right or if specified args are
|
||||||
|
// not valid, it will return an error.
|
||||||
|
func ParseExec(cmd *flag.FlagSet, args []string) (*types.ExecConfig, error) {
|
||||||
|
var (
|
||||||
|
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")
|
||||||
|
flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run command in the background")
|
||||||
|
flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID (format: <name|uid>[:<group|gid>])")
|
||||||
|
flPrivileged = cmd.Bool([]string{"-privileged"}, false, "Give extended privileges to the command")
|
||||||
|
execCmd []string
|
||||||
|
container string
|
||||||
|
)
|
||||||
|
cmd.Require(flag.Min, 2)
|
||||||
|
if err := cmd.ParseFlags(args, true); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
container = cmd.Arg(0)
|
||||||
|
parsedArgs := cmd.Args()
|
||||||
|
execCmd = parsedArgs[1:]
|
||||||
|
|
||||||
|
execConfig := &types.ExecConfig{
|
||||||
|
User: *flUser,
|
||||||
|
Privileged: *flPrivileged,
|
||||||
|
Tty: *flTty,
|
||||||
|
Cmd: execCmd,
|
||||||
|
Container: container,
|
||||||
|
Detach: *flDetach,
|
||||||
|
}
|
||||||
|
|
||||||
|
// If -d is not set, attach to everything by default
|
||||||
|
if !*flDetach {
|
||||||
|
execConfig.AttachStdout = true
|
||||||
|
execConfig.AttachStderr = true
|
||||||
|
if *flStdin {
|
||||||
|
execConfig.AttachStdin = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return execConfig, nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package runconfig
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"github.com/docker/docker/opts"
|
"github.com/docker/docker/opts"
|
||||||
"github.com/docker/docker/pkg/promise"
|
"github.com/docker/docker/pkg/promise"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
"github.com/docker/docker/runconfig"
|
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||||
"github.com/docker/libnetwork/resolvconf/dns"
|
"github.com/docker/libnetwork/resolvconf/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
||||||
)
|
)
|
||||||
|
|
||||||
config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
|
config, hostConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
||||||
// just in case the Parse does not exit
|
// just in case the Parse does not exit
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.ReportError(err.Error(), true)
|
cmd.ReportError(err.Error(), true)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
|
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -337,7 +338,7 @@ func run(b *Builder, args []string, attributes map[string]bool, original string)
|
||||||
// of RUN, without leaking it to the final image. It also aids cache
|
// of RUN, without leaking it to the final image. It also aids cache
|
||||||
// lookup for same image built with same build time environment.
|
// lookup for same image built with same build time environment.
|
||||||
cmdBuildEnv := []string{}
|
cmdBuildEnv := []string{}
|
||||||
configEnv := runconfig.ConvertKVStringsToMap(b.runConfig.Env)
|
configEnv := runconfigopts.ConvertKVStringsToMap(b.runConfig.Env)
|
||||||
for key, val := range b.BuildArgs {
|
for key, val := range b.BuildArgs {
|
||||||
if !b.isBuildArgAllowed(key) {
|
if !b.isBuildArgAllowed(key) {
|
||||||
// skip build-args that are not in allowed list, meaning they have
|
// skip build-args that are not in allowed list, meaning they have
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/sysinfo"
|
"github.com/docker/docker/pkg/sysinfo"
|
||||||
"github.com/docker/docker/reference"
|
"github.com/docker/docker/reference"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
|
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||||
"github.com/docker/libnetwork"
|
"github.com/docker/libnetwork"
|
||||||
nwconfig "github.com/docker/libnetwork/config"
|
nwconfig "github.com/docker/libnetwork/config"
|
||||||
"github.com/docker/libnetwork/drivers/bridge"
|
"github.com/docker/libnetwork/drivers/bridge"
|
||||||
|
@ -681,7 +682,7 @@ func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, l := range hostConfig.Links {
|
for _, l := range hostConfig.Links {
|
||||||
name, alias, err := runconfig.ParseLink(l)
|
name, alias, err := runconfigopts.ParseLink(l)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
32
runconfig/errors.go
Normal file
32
runconfig/errors.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package runconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrConflictContainerNetworkAndLinks conflict between --net=container and links
|
||||||
|
ErrConflictContainerNetworkAndLinks = fmt.Errorf("Conflicting options: container type network can't be used with links. This would result in undefined behavior")
|
||||||
|
// ErrConflictUserDefinedNetworkAndLinks conflict between --net=<NETWORK> and links
|
||||||
|
ErrConflictUserDefinedNetworkAndLinks = fmt.Errorf("Conflicting options: networking can't be used with links. This would result in undefined behavior")
|
||||||
|
// ErrConflictSharedNetwork conflict between private and other networks
|
||||||
|
ErrConflictSharedNetwork = fmt.Errorf("Container sharing network namespace with another container or host cannot be connected to any other network")
|
||||||
|
// ErrConflictHostNetwork conflict from being disconnected from host network or connected to host network.
|
||||||
|
ErrConflictHostNetwork = fmt.Errorf("Container cannot be disconnected from host network or connected to host network")
|
||||||
|
// ErrConflictNoNetwork conflict between private and other networks
|
||||||
|
ErrConflictNoNetwork = fmt.Errorf("Container cannot be connected to multiple networks with one of the networks in private (none) mode")
|
||||||
|
// ErrConflictNetworkAndDNS conflict between --dns and the network mode
|
||||||
|
ErrConflictNetworkAndDNS = fmt.Errorf("Conflicting options: dns and the network mode")
|
||||||
|
// ErrConflictNetworkHostname conflict between the hostname and the network mode
|
||||||
|
ErrConflictNetworkHostname = fmt.Errorf("Conflicting options: hostname and the network mode")
|
||||||
|
// ErrConflictHostNetworkAndLinks conflict between --net=host and links
|
||||||
|
ErrConflictHostNetworkAndLinks = fmt.Errorf("Conflicting options: host type networking can't be used with links. This would result in undefined behavior")
|
||||||
|
// ErrConflictContainerNetworkAndMac conflict between the mac address and the network mode
|
||||||
|
ErrConflictContainerNetworkAndMac = fmt.Errorf("Conflicting options: mac-address and the network mode")
|
||||||
|
// ErrConflictNetworkHosts conflict between add-host and the network mode
|
||||||
|
ErrConflictNetworkHosts = fmt.Errorf("Conflicting options: custom host-to-IP mapping and the network mode")
|
||||||
|
// ErrConflictNetworkPublishPorts conflict between the publish options and the network mode
|
||||||
|
ErrConflictNetworkPublishPorts = fmt.Errorf("Conflicting options: port publishing and the container type network mode")
|
||||||
|
// ErrConflictNetworkExposePorts conflict between the expose option and the network mode
|
||||||
|
ErrConflictNetworkExposePorts = fmt.Errorf("Conflicting options: port exposing and the container type network mode")
|
||||||
|
)
|
|
@ -1,49 +0,0 @@
|
||||||
package runconfig
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseExec parses the specified args for the specified command and generates
|
|
||||||
// an ExecConfig from it.
|
|
||||||
// If the minimal number of specified args is not right or if specified args are
|
|
||||||
// not valid, it will return an error.
|
|
||||||
func ParseExec(cmd *flag.FlagSet, args []string) (*types.ExecConfig, error) {
|
|
||||||
var (
|
|
||||||
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")
|
|
||||||
flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run command in the background")
|
|
||||||
flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID (format: <name|uid>[:<group|gid>])")
|
|
||||||
flPrivileged = cmd.Bool([]string{"-privileged"}, false, "Give extended privileges to the command")
|
|
||||||
execCmd []string
|
|
||||||
container string
|
|
||||||
)
|
|
||||||
cmd.Require(flag.Min, 2)
|
|
||||||
if err := cmd.ParseFlags(args, true); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
container = cmd.Arg(0)
|
|
||||||
parsedArgs := cmd.Args()
|
|
||||||
execCmd = parsedArgs[1:]
|
|
||||||
|
|
||||||
execConfig := &types.ExecConfig{
|
|
||||||
User: *flUser,
|
|
||||||
Privileged: *flPrivileged,
|
|
||||||
Tty: *flTty,
|
|
||||||
Cmd: execCmd,
|
|
||||||
Container: container,
|
|
||||||
Detach: *flDetach,
|
|
||||||
}
|
|
||||||
|
|
||||||
// If -d is not set, attach to everything by default
|
|
||||||
if !*flDetach {
|
|
||||||
execConfig.AttachStdout = true
|
|
||||||
execConfig.AttachStderr = true
|
|
||||||
if *flStdin {
|
|
||||||
execConfig.AttachStdin = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return execConfig, nil
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package runconfig
|
package opts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -12,39 +12,10 @@ import (
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
"github.com/docker/docker/pkg/mount"
|
"github.com/docker/docker/pkg/mount"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
||||||
"github.com/docker/docker/volume"
|
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrConflictContainerNetworkAndLinks conflict between --net=container and links
|
|
||||||
ErrConflictContainerNetworkAndLinks = fmt.Errorf("Conflicting options: container type network can't be used with links. This would result in undefined behavior")
|
|
||||||
// ErrConflictUserDefinedNetworkAndLinks conflict between --net=<NETWORK> and links
|
|
||||||
ErrConflictUserDefinedNetworkAndLinks = fmt.Errorf("Conflicting options: networking can't be used with links. This would result in undefined behavior")
|
|
||||||
// ErrConflictSharedNetwork conflict between private and other networks
|
|
||||||
ErrConflictSharedNetwork = fmt.Errorf("Container sharing network namespace with another container or host cannot be connected to any other network")
|
|
||||||
// ErrConflictHostNetwork conflict from being disconnected from host network or connected to host network.
|
|
||||||
ErrConflictHostNetwork = fmt.Errorf("Container cannot be disconnected from host network or connected to host network")
|
|
||||||
// ErrConflictNoNetwork conflict between private and other networks
|
|
||||||
ErrConflictNoNetwork = fmt.Errorf("Container cannot be connected to multiple networks with one of the networks in private (none) mode")
|
|
||||||
// ErrConflictNetworkAndDNS conflict between --dns and the network mode
|
|
||||||
ErrConflictNetworkAndDNS = fmt.Errorf("Conflicting options: dns and the network mode")
|
|
||||||
// ErrConflictNetworkHostname conflict between the hostname and the network mode
|
|
||||||
ErrConflictNetworkHostname = fmt.Errorf("Conflicting options: hostname and the network mode")
|
|
||||||
// ErrConflictHostNetworkAndLinks conflict between --net=host and links
|
|
||||||
ErrConflictHostNetworkAndLinks = fmt.Errorf("Conflicting options: host type networking can't be used with links. This would result in undefined behavior")
|
|
||||||
// ErrConflictContainerNetworkAndMac conflict between the mac address and the network mode
|
|
||||||
ErrConflictContainerNetworkAndMac = fmt.Errorf("Conflicting options: mac-address and the network mode")
|
|
||||||
// ErrConflictNetworkHosts conflict between add-host and the network mode
|
|
||||||
ErrConflictNetworkHosts = fmt.Errorf("Conflicting options: custom host-to-IP mapping and the network mode")
|
|
||||||
// ErrConflictNetworkPublishPorts conflict between the publish options and the network mode
|
|
||||||
ErrConflictNetworkPublishPorts = fmt.Errorf("Conflicting options: port publishing and the container type network mode")
|
|
||||||
// ErrConflictNetworkExposePorts conflict between the expose option and the network mode
|
|
||||||
ErrConflictNetworkExposePorts = fmt.Errorf("Conflicting options: port exposing and the container type network mode")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Parse parses the specified args for the specified command and generates a Config,
|
// Parse parses the specified args for the specified command and generates a Config,
|
||||||
// a HostConfig and returns them with the specified command.
|
// a HostConfig and returns them with the specified command.
|
||||||
// If the specified args are not valid, it will return an error.
|
// If the specified args are not valid, it will return an error.
|
||||||
|
@ -54,17 +25,17 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
flAttach = opts.NewListOpts(opts.ValidateAttach)
|
flAttach = opts.NewListOpts(opts.ValidateAttach)
|
||||||
flVolumes = opts.NewListOpts(nil)
|
flVolumes = opts.NewListOpts(nil)
|
||||||
flTmpfs = opts.NewListOpts(nil)
|
flTmpfs = opts.NewListOpts(nil)
|
||||||
flBlkioWeightDevice = runconfigopts.NewWeightdeviceOpt(runconfigopts.ValidateWeightDevice)
|
flBlkioWeightDevice = NewWeightdeviceOpt(ValidateWeightDevice)
|
||||||
flDeviceReadBps = runconfigopts.NewThrottledeviceOpt(runconfigopts.ValidateThrottleBpsDevice)
|
flDeviceReadBps = NewThrottledeviceOpt(ValidateThrottleBpsDevice)
|
||||||
flDeviceWriteBps = runconfigopts.NewThrottledeviceOpt(runconfigopts.ValidateThrottleBpsDevice)
|
flDeviceWriteBps = NewThrottledeviceOpt(ValidateThrottleBpsDevice)
|
||||||
flLinks = opts.NewListOpts(ValidateLink)
|
flLinks = opts.NewListOpts(ValidateLink)
|
||||||
flDeviceReadIOps = runconfigopts.NewThrottledeviceOpt(runconfigopts.ValidateThrottleIOpsDevice)
|
flDeviceReadIOps = NewThrottledeviceOpt(ValidateThrottleIOpsDevice)
|
||||||
flDeviceWriteIOps = runconfigopts.NewThrottledeviceOpt(runconfigopts.ValidateThrottleIOpsDevice)
|
flDeviceWriteIOps = NewThrottledeviceOpt(ValidateThrottleIOpsDevice)
|
||||||
flEnv = opts.NewListOpts(opts.ValidateEnv)
|
flEnv = opts.NewListOpts(opts.ValidateEnv)
|
||||||
flLabels = opts.NewListOpts(opts.ValidateEnv)
|
flLabels = opts.NewListOpts(opts.ValidateEnv)
|
||||||
flDevices = opts.NewListOpts(ValidateDevice)
|
flDevices = opts.NewListOpts(ValidateDevice)
|
||||||
|
|
||||||
flUlimits = runconfigopts.NewUlimitOpt(nil)
|
flUlimits = NewUlimitOpt(nil)
|
||||||
|
|
||||||
flPublish = opts.NewListOpts(nil)
|
flPublish = opts.NewListOpts(nil)
|
||||||
flExpose = opts.NewListOpts(nil)
|
flExpose = opts.NewListOpts(nil)
|
||||||
|
@ -227,7 +198,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
var binds []string
|
var binds []string
|
||||||
// add any bind targets to the list of container volumes
|
// add any bind targets to the list of container volumes
|
||||||
for bind := range flVolumes.GetMap() {
|
for bind := range flVolumes.GetMap() {
|
||||||
if arr := volume.SplitN(bind, 2); len(arr) > 1 {
|
if arr := volumeSplitN(bind, 2); len(arr) > 1 {
|
||||||
// after creating the bind mount we want to delete it from the flVolumes values because
|
// 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
|
// we do not want bind mounts being committed to image configs
|
||||||
binds = append(binds, bind)
|
binds = append(binds, bind)
|
||||||
|
@ -649,3 +620,59 @@ func validatePath(val string, validator func(string) bool) (string, error) {
|
||||||
}
|
}
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SplitN splits raw into a maximum of n parts, separated by a separator colon.
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package runconfig
|
package opts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -288,7 +289,7 @@ func callDecodeContainerConfig(volumes []string, binds []string) (*container.Con
|
||||||
c *container.Config
|
c *container.Config
|
||||||
h *container.HostConfig
|
h *container.HostConfig
|
||||||
)
|
)
|
||||||
w := ContainerConfigWrapper{
|
w := runconfig.ContainerConfigWrapper{
|
||||||
Config: &container.Config{
|
Config: &container.Config{
|
||||||
Volumes: map[string]struct{}{},
|
Volumes: map[string]struct{}{},
|
||||||
},
|
},
|
||||||
|
@ -303,7 +304,7 @@ func callDecodeContainerConfig(volumes []string, binds []string) (*container.Con
|
||||||
if b, err = json.Marshal(w); err != nil {
|
if b, err = json.Marshal(w); err != nil {
|
||||||
return nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
|
return nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
|
||||||
}
|
}
|
||||||
c, h, err = DecodeContainerConfig(bytes.NewReader(b))
|
c, h, err = runconfig.DecodeContainerConfig(bytes.NewReader(b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("Error parsing %s: %v", string(b), err)
|
return nil, nil, fmt.Errorf("Error parsing %s: %v", string(b), err)
|
||||||
}
|
}
|
||||||
|
@ -762,3 +763,52 @@ func TestValidateDevice(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVolumeSplitN(t *testing.T) {
|
||||||
|
for _, x := range []struct {
|
||||||
|
input string
|
||||||
|
n int
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{`C:\foo:d:`, -1, []string{`C:\foo`, `d:`}},
|
||||||
|
{`:C:\foo:d:`, -1, nil},
|
||||||
|
{`/foo:/bar:ro`, 3, []string{`/foo`, `/bar`, `ro`}},
|
||||||
|
{`/foo:/bar:ro`, 2, []string{`/foo`, `/bar:ro`}},
|
||||||
|
{`C:\foo\:/foo`, -1, []string{`C:\foo\`, `/foo`}},
|
||||||
|
|
||||||
|
{`d:\`, -1, []string{`d:\`}},
|
||||||
|
{`d:`, -1, []string{`d:`}},
|
||||||
|
{`d:\path`, -1, []string{`d:\path`}},
|
||||||
|
{`d:\path with space`, -1, []string{`d:\path with space`}},
|
||||||
|
{`d:\pathandmode:rw`, -1, []string{`d:\pathandmode`, `rw`}},
|
||||||
|
{`c:\:d:\`, -1, []string{`c:\`, `d:\`}},
|
||||||
|
{`c:\windows\:d:`, -1, []string{`c:\windows\`, `d:`}},
|
||||||
|
{`c:\windows:d:\s p a c e`, -1, []string{`c:\windows`, `d:\s p a c e`}},
|
||||||
|
{`c:\windows:d:\s p a c e:RW`, -1, []string{`c:\windows`, `d:\s p a c e`, `RW`}},
|
||||||
|
{`c:\program files:d:\s p a c e i n h o s t d i r`, -1, []string{`c:\program files`, `d:\s p a c e i n h o s t d i r`}},
|
||||||
|
{`0123456789name:d:`, -1, []string{`0123456789name`, `d:`}},
|
||||||
|
{`MiXeDcAsEnAmE:d:`, -1, []string{`MiXeDcAsEnAmE`, `d:`}},
|
||||||
|
{`name:D:`, -1, []string{`name`, `D:`}},
|
||||||
|
{`name:D::rW`, -1, []string{`name`, `D:`, `rW`}},
|
||||||
|
{`name:D::RW`, -1, []string{`name`, `D:`, `RW`}},
|
||||||
|
{`c:/:d:/forward/slashes/are/good/too`, -1, []string{`c:/`, `d:/forward/slashes/are/good/too`}},
|
||||||
|
{`c:\Windows`, -1, []string{`c:\Windows`}},
|
||||||
|
{`c:\Program Files (x86)`, -1, []string{`c:\Program Files (x86)`}},
|
||||||
|
|
||||||
|
{``, -1, nil},
|
||||||
|
{`.`, -1, []string{`.`}},
|
||||||
|
{`..\`, -1, []string{`..\`}},
|
||||||
|
{`c:\:..\`, -1, []string{`c:\`, `..\`}},
|
||||||
|
{`c:\:d:\:xyzzy`, -1, []string{`c:\`, `d:\`, `xyzzy`}},
|
||||||
|
} {
|
||||||
|
res := volumeSplitN(x.input, x.n)
|
||||||
|
if len(res) < len(x.expected) {
|
||||||
|
t.Fatalf("input: %v, expected: %v, got: %v", x.input, x.expected, res)
|
||||||
|
}
|
||||||
|
for i, e := range res {
|
||||||
|
if e != x.expected[i] {
|
||||||
|
t.Fatalf("input: %v, expected: %v, got: %v", x.input, x.expected, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -113,59 +113,3 @@ func ParseVolumesFrom(spec string) (string, string, error) {
|
||||||
}
|
}
|
||||||
return id, mode, nil
|
return id, mode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SplitN splits raw into a maximum of n parts, separated by a separator colon.
|
|
||||||
// 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 SplitN(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
|
|
||||||
}
|
|
||||||
|
|
|
@ -133,55 +133,6 @@ func TestParseMountSpec(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSplitN(t *testing.T) {
|
|
||||||
for _, x := range []struct {
|
|
||||||
input string
|
|
||||||
n int
|
|
||||||
expected []string
|
|
||||||
}{
|
|
||||||
{`C:\foo:d:`, -1, []string{`C:\foo`, `d:`}},
|
|
||||||
{`:C:\foo:d:`, -1, nil},
|
|
||||||
{`/foo:/bar:ro`, 3, []string{`/foo`, `/bar`, `ro`}},
|
|
||||||
{`/foo:/bar:ro`, 2, []string{`/foo`, `/bar:ro`}},
|
|
||||||
{`C:\foo\:/foo`, -1, []string{`C:\foo\`, `/foo`}},
|
|
||||||
|
|
||||||
{`d:\`, -1, []string{`d:\`}},
|
|
||||||
{`d:`, -1, []string{`d:`}},
|
|
||||||
{`d:\path`, -1, []string{`d:\path`}},
|
|
||||||
{`d:\path with space`, -1, []string{`d:\path with space`}},
|
|
||||||
{`d:\pathandmode:rw`, -1, []string{`d:\pathandmode`, `rw`}},
|
|
||||||
{`c:\:d:\`, -1, []string{`c:\`, `d:\`}},
|
|
||||||
{`c:\windows\:d:`, -1, []string{`c:\windows\`, `d:`}},
|
|
||||||
{`c:\windows:d:\s p a c e`, -1, []string{`c:\windows`, `d:\s p a c e`}},
|
|
||||||
{`c:\windows:d:\s p a c e:RW`, -1, []string{`c:\windows`, `d:\s p a c e`, `RW`}},
|
|
||||||
{`c:\program files:d:\s p a c e i n h o s t d i r`, -1, []string{`c:\program files`, `d:\s p a c e i n h o s t d i r`}},
|
|
||||||
{`0123456789name:d:`, -1, []string{`0123456789name`, `d:`}},
|
|
||||||
{`MiXeDcAsEnAmE:d:`, -1, []string{`MiXeDcAsEnAmE`, `d:`}},
|
|
||||||
{`name:D:`, -1, []string{`name`, `D:`}},
|
|
||||||
{`name:D::rW`, -1, []string{`name`, `D:`, `rW`}},
|
|
||||||
{`name:D::RW`, -1, []string{`name`, `D:`, `RW`}},
|
|
||||||
{`c:/:d:/forward/slashes/are/good/too`, -1, []string{`c:/`, `d:/forward/slashes/are/good/too`}},
|
|
||||||
{`c:\Windows`, -1, []string{`c:\Windows`}},
|
|
||||||
{`c:\Program Files (x86)`, -1, []string{`c:\Program Files (x86)`}},
|
|
||||||
|
|
||||||
{``, -1, nil},
|
|
||||||
{`.`, -1, []string{`.`}},
|
|
||||||
{`..\`, -1, []string{`..\`}},
|
|
||||||
{`c:\:..\`, -1, []string{`c:\`, `..\`}},
|
|
||||||
{`c:\:d:\:xyzzy`, -1, []string{`c:\`, `d:\`, `xyzzy`}},
|
|
||||||
} {
|
|
||||||
res := SplitN(x.input, x.n)
|
|
||||||
if len(res) < len(x.expected) {
|
|
||||||
t.Fatalf("input: %v, expected: %v, got: %v", x.input, x.expected, res)
|
|
||||||
}
|
|
||||||
for i, e := range res {
|
|
||||||
if e != x.expected[i] {
|
|
||||||
t.Fatalf("input: %v, expected: %v, got: %v", x.input, x.expected, res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// testParseMountSpec is a structure used by TestParseMountSpecSplit for
|
// testParseMountSpec is a structure used by TestParseMountSpecSplit for
|
||||||
// specifying test cases for the ParseMountSpec() function.
|
// specifying test cases for the ParseMountSpec() function.
|
||||||
type testParseMountSpec struct {
|
type testParseMountSpec struct {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue