Browse Source

Merge pull request #18941 from dnephin/runconfig_in_cli

Move runconfig/parse.go into the runconfig/opts package
David Calavera 9 years ago
parent
commit
dc4ca0e897

+ 2 - 2
api/client/create.go

@@ -12,7 +12,7 @@ import (
 	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
-	"github.com/docker/docker/runconfig"
+	runconfigopts "github.com/docker/docker/runconfig/opts"
 )
 
 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")
 	)
 
-	config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
+	config, hostConfig, cmd, err := runconfigopts.Parse(cmd, args)
 	if err != nil {
 		cmd.ReportError(err.Error(), true)
 		os.Exit(1)

+ 45 - 2
api/client/exec.go

@@ -7,8 +7,8 @@ import (
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
 	Cli "github.com/docker/docker/cli"
+	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/promise"
-	"github.com/docker/docker/runconfig"
 )
 
 // 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)
 	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
 	if execConfig.Container == "" || err != nil {
 		return Cli.StatusError{StatusCode: 1}
@@ -113,3 +113,46 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 
 	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 - 1
runconfig/exec_test.go → api/client/exec_test.go

@@ -1,4 +1,4 @@
-package runconfig
+package client
 
 import (
 	"fmt"

+ 2 - 2
api/client/run.go

@@ -14,7 +14,7 @@ import (
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/promise"
 	"github.com/docker/docker/pkg/signal"
-	"github.com/docker/docker/runconfig"
+	runconfigopts "github.com/docker/docker/runconfig/opts"
 	"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")
 	)
 
-	config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
+	config, hostConfig, cmd, err := runconfigopts.Parse(cmd, args)
 	// just in case the Parse does not exit
 	if err != nil {
 		cmd.ReportError(err.Error(), true)

+ 2 - 1
builder/dockerfile/dispatchers.go

@@ -25,6 +25,7 @@ import (
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/runconfig"
+	runconfigopts "github.com/docker/docker/runconfig/opts"
 	"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
 	// lookup for same image built with same build time environment.
 	cmdBuildEnv := []string{}
-	configEnv := runconfig.ConvertKVStringsToMap(b.runConfig.Env)
+	configEnv := runconfigopts.ConvertKVStringsToMap(b.runConfig.Env)
 	for key, val := range b.BuildArgs {
 		if !b.isBuildArgAllowed(key) {
 			// skip build-args that are not in allowed list, meaning they have

+ 2 - 1
daemon/daemon_unix.go

@@ -23,6 +23,7 @@ import (
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/runconfig"
+	runconfigopts "github.com/docker/docker/runconfig/opts"
 	"github.com/docker/libnetwork"
 	nwconfig "github.com/docker/libnetwork/config"
 	"github.com/docker/libnetwork/drivers/bridge"
@@ -681,7 +682,7 @@ func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *
 	}
 
 	for _, l := range hostConfig.Links {
-		name, alias, err := runconfig.ParseLink(l)
+		name, alias, err := runconfigopts.ParseLink(l)
 		if err != nil {
 			return err
 		}

+ 32 - 0
runconfig/errors.go

@@ -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")
+)

+ 0 - 49
runconfig/exec.go

@@ -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
-}

+ 0 - 0
runconfig/fixtures/valid.env → runconfig/opts/fixtures/valid.env


+ 0 - 0
runconfig/fixtures/valid.label → runconfig/opts/fixtures/valid.label


+ 64 - 37
runconfig/parse.go → runconfig/opts/parse.go

@@ -1,4 +1,4 @@
-package runconfig
+package opts
 
 import (
 	"fmt"
@@ -12,39 +12,10 @@ import (
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/mount"
 	"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-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,
 // a HostConfig and returns them with the specified command.
 // 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)
 		flVolumes           = opts.NewListOpts(nil)
 		flTmpfs             = opts.NewListOpts(nil)
-		flBlkioWeightDevice = runconfigopts.NewWeightdeviceOpt(runconfigopts.ValidateWeightDevice)
-		flDeviceReadBps     = runconfigopts.NewThrottledeviceOpt(runconfigopts.ValidateThrottleBpsDevice)
-		flDeviceWriteBps    = runconfigopts.NewThrottledeviceOpt(runconfigopts.ValidateThrottleBpsDevice)
+		flBlkioWeightDevice = NewWeightdeviceOpt(ValidateWeightDevice)
+		flDeviceReadBps     = NewThrottledeviceOpt(ValidateThrottleBpsDevice)
+		flDeviceWriteBps    = NewThrottledeviceOpt(ValidateThrottleBpsDevice)
 		flLinks             = opts.NewListOpts(ValidateLink)
-		flDeviceReadIOps    = runconfigopts.NewThrottledeviceOpt(runconfigopts.ValidateThrottleIOpsDevice)
-		flDeviceWriteIOps   = runconfigopts.NewThrottledeviceOpt(runconfigopts.ValidateThrottleIOpsDevice)
+		flDeviceReadIOps    = NewThrottledeviceOpt(ValidateThrottleIOpsDevice)
+		flDeviceWriteIOps   = NewThrottledeviceOpt(ValidateThrottleIOpsDevice)
 		flEnv               = opts.NewListOpts(opts.ValidateEnv)
 		flLabels            = opts.NewListOpts(opts.ValidateEnv)
 		flDevices           = opts.NewListOpts(ValidateDevice)
 
-		flUlimits = runconfigopts.NewUlimitOpt(nil)
+		flUlimits = NewUlimitOpt(nil)
 
 		flPublish           = 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
 	// add any bind targets to the list of container volumes
 	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
 			// we do not want bind mounts being committed to image configs
 			binds = append(binds, bind)
@@ -649,3 +620,59 @@ func validatePath(val string, validator func(string) bool) (string, error) {
 	}
 	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
+}

+ 53 - 3
runconfig/parse_test.go → runconfig/opts/parse_test.go

@@ -1,4 +1,4 @@
-package runconfig
+package opts
 
 import (
 	"bytes"
@@ -12,6 +12,7 @@ import (
 
 	"github.com/docker/docker/api/types/container"
 	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/docker/docker/runconfig"
 	"github.com/docker/go-connections/nat"
 )
 
@@ -288,7 +289,7 @@ func callDecodeContainerConfig(volumes []string, binds []string) (*container.Con
 		c   *container.Config
 		h   *container.HostConfig
 	)
-	w := ContainerConfigWrapper{
+	w := runconfig.ContainerConfigWrapper{
 		Config: &container.Config{
 			Volumes: map[string]struct{}{},
 		},
@@ -303,7 +304,7 @@ func callDecodeContainerConfig(volumes []string, binds []string) (*container.Con
 	if b, err = json.Marshal(w); err != nil {
 		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 {
 		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)
+			}
+		}
+	}
+}

+ 0 - 56
volume/volume.go

@@ -113,59 +113,3 @@ func ParseVolumesFrom(spec string) (string, string, error) {
 	}
 	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
-}

+ 0 - 49
volume/volume_test.go

@@ -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
 // specifying test cases for the ParseMountSpec() function.
 type testParseMountSpec struct {