Browse code

Merge pull request #2829 from dotcloud/refactor_opts

Refactor opts

Michael Crosby authored on 2013/12/03 03:41:30
Showing 6 changed files
... ...
@@ -246,7 +246,7 @@ func (b *buildFile) CmdVolume(args string) error {
246 246
 		volume = []string{args}
247 247
 	}
248 248
 	if b.config.Volumes == nil {
249
-		b.config.Volumes = PathOpts{}
249
+		b.config.Volumes = map[string]struct{}{}
250 250
 	}
251 251
 	for _, v := range volume {
252 252
 		b.config.Volumes[v] = struct{}{}
... ...
@@ -23,7 +23,6 @@ import (
23 23
 	"os"
24 24
 	"os/signal"
25 25
 	"path"
26
-	"path/filepath"
27 26
 	"reflect"
28 27
 	"regexp"
29 28
 	"runtime"
... ...
@@ -1622,58 +1621,6 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
1622 1622
 // Ports type - Used to parse multiple -p flags
1623 1623
 type ports []int
1624 1624
 
1625
-// AttachOpts stores arguments to 'docker run -a', eg. which streams to attach to
1626
-type AttachOpts map[string]bool
1627
-
1628
-func (opts AttachOpts) String() string { return fmt.Sprintf("%v", map[string]bool(opts)) }
1629
-func (opts AttachOpts) Set(val string) error {
1630
-	if val != "stdin" && val != "stdout" && val != "stderr" {
1631
-		return fmt.Errorf("Unsupported stream name: %s", val)
1632
-	}
1633
-	opts[val] = true
1634
-	return nil
1635
-}
1636
-
1637
-// LinkOpts stores arguments to `docker run -link`
1638
-type LinkOpts []string
1639
-
1640
-func (link *LinkOpts) String() string { return fmt.Sprintf("%v", []string(*link)) }
1641
-func (link *LinkOpts) Set(val string) error {
1642
-	if _, err := parseLink(val); err != nil {
1643
-		return err
1644
-	}
1645
-	*link = append(*link, val)
1646
-	return nil
1647
-}
1648
-
1649
-// PathOpts stores a unique set of absolute paths
1650
-type PathOpts map[string]struct{}
1651
-
1652
-func (opts PathOpts) String() string { return fmt.Sprintf("%v", map[string]struct{}(opts)) }
1653
-func (opts PathOpts) Set(val string) error {
1654
-	var containerPath string
1655
-
1656
-	if strings.Count(val, ":") > 2 {
1657
-		return fmt.Errorf("bad format for volumes: %s", val)
1658
-	}
1659
-
1660
-	if splited := strings.SplitN(val, ":", 2); len(splited) == 1 {
1661
-		containerPath = splited[0]
1662
-		val = filepath.Clean(splited[0])
1663
-	} else {
1664
-		containerPath = splited[1]
1665
-		val = fmt.Sprintf("%s:%s", splited[0], filepath.Clean(splited[1]))
1666
-	}
1667
-
1668
-	if !filepath.IsAbs(containerPath) {
1669
-		utils.Debugf("%s is not an absolute path", containerPath)
1670
-		return fmt.Errorf("%s is not an absolute path", containerPath)
1671
-	}
1672
-	opts[val] = struct{}{}
1673
-
1674
-	return nil
1675
-}
1676
-
1677 1625
 func (cli *DockerCli) CmdTag(args ...string) error {
1678 1626
 	cmd := cli.Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY[:TAG]", "Tag an image into a repository")
1679 1627
 	force := cmd.Bool("f", false, "Force")
... ...
@@ -1719,16 +1666,16 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
1719 1719
 func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
1720 1720
 	var (
1721 1721
 		// FIXME: use utils.ListOpts for attach and volumes?
1722
-		flAttach  = AttachOpts{}
1723
-		flVolumes = PathOpts{}
1724
-		flLinks   = LinkOpts{}
1722
+		flAttach  = NewListOpts(ValidateAttach)
1723
+		flVolumes = NewListOpts(ValidatePath)
1724
+		flLinks   = NewListOpts(ValidateLink)
1725
+		flEnv     = NewListOpts(ValidateEnv)
1725 1726
 
1726
-		flPublish     utils.ListOpts
1727
-		flExpose      utils.ListOpts
1728
-		flEnv         utils.ListOpts
1729
-		flDns         utils.ListOpts
1730
-		flVolumesFrom utils.ListOpts
1731
-		flLxcOpts     utils.ListOpts
1727
+		flPublish     ListOpts
1728
+		flExpose      ListOpts
1729
+		flDns         ListOpts
1730
+		flVolumesFrom ListOpts
1731
+		flLxcOpts     ListOpts
1732 1732
 
1733 1733
 		flAutoRemove      = cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)")
1734 1734
 		flDetach          = cmd.Bool("d", false, "Detached mode: Run container in the background, print new container id")
... ...
@@ -1750,13 +1697,13 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co
1750 1750
 		_ = cmd.String("name", "", "Assign a name to the container")
1751 1751
 	)
1752 1752
 
1753
-	cmd.Var(flAttach, "a", "Attach to stdin, stdout or stderr.")
1754
-	cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
1753
+	cmd.Var(&flAttach, "a", "Attach to stdin, stdout or stderr.")
1754
+	cmd.Var(&flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
1755 1755
 	cmd.Var(&flLinks, "link", "Add link to another container (name:alias)")
1756
+	cmd.Var(&flEnv, "e", "Set environment variables")
1756 1757
 
1757 1758
 	cmd.Var(&flPublish, "p", fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", PortSpecTemplateFormat))
1758 1759
 	cmd.Var(&flExpose, "expose", "Expose a port from the container without publishing it to your host")
1759
-	cmd.Var(&flEnv, "e", "Set environment variables")
1760 1760
 	cmd.Var(&flDns, "dns", "Set custom dns servers")
1761 1761
 	cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container(s)")
1762 1762
 	cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
... ...
@@ -1771,7 +1718,7 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co
1771 1771
 	}
1772 1772
 
1773 1773
 	// Validate input params
1774
-	if *flDetach && len(flAttach) > 0 {
1774
+	if *flDetach && flAttach.Len() > 0 {
1775 1775
 		return nil, nil, cmd, ErrConflictAttachDetach
1776 1776
 	}
1777 1777
 	if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
... ...
@@ -1782,7 +1729,7 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co
1782 1782
 	}
1783 1783
 
1784 1784
 	// If neither -d or -a are set, attach to everything by default
1785
-	if len(flAttach) == 0 && !*flDetach {
1785
+	if flAttach.Len() == 0 && !*flDetach {
1786 1786
 		if !*flDetach {
1787 1787
 			flAttach.Set("stdout")
1788 1788
 			flAttach.Set("stderr")
... ...
@@ -1792,17 +1739,6 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co
1792 1792
 		}
1793 1793
 	}
1794 1794
 
1795
-	var envs []string
1796
-	for _, env := range flEnv {
1797
-		arr := strings.Split(env, "=")
1798
-		if len(arr) > 1 {
1799
-			envs = append(envs, env)
1800
-		} else {
1801
-			v := os.Getenv(env)
1802
-			envs = append(envs, env+"="+v)
1803
-		}
1804
-	}
1805
-
1806 1795
 	var flMemory int64
1807 1796
 	if *flMemoryString != "" {
1808 1797
 		parsedMemory, err := utils.RAMInBytes(*flMemoryString)
... ...
@@ -1814,16 +1750,15 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co
1814 1814
 
1815 1815
 	var binds []string
1816 1816
 	// add any bind targets to the list of container volumes
1817
-	for bind := range flVolumes {
1818
-		arr := strings.Split(bind, ":")
1819
-		if len(arr) > 1 {
1817
+	for bind := range flVolumes.GetMap() {
1818
+		if arr := strings.Split(bind, ":"); len(arr) > 1 {
1820 1819
 			if arr[0] == "/" {
1821 1820
 				return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
1822 1821
 			}
1823 1822
 			dstDir := arr[1]
1824
-			flVolumes[dstDir] = struct{}{}
1823
+			flVolumes.Set(dstDir)
1825 1824
 			binds = append(binds, bind)
1826
-			delete(flVolumes, bind)
1825
+			flVolumes.Delete(bind)
1827 1826
 		}
1828 1827
 	}
1829 1828
 
... ...
@@ -1858,13 +1793,13 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co
1858 1858
 		domainname = parts[1]
1859 1859
 	}
1860 1860
 
1861
-	ports, portBindings, err := parsePortSpecs(flPublish)
1861
+	ports, portBindings, err := parsePortSpecs(flPublish.GetAll())
1862 1862
 	if err != nil {
1863 1863
 		return nil, nil, cmd, err
1864 1864
 	}
1865 1865
 
1866 1866
 	// Merge in exposed ports to the map of published ports
1867
-	for _, e := range flExpose {
1867
+	for _, e := range flExpose.GetAll() {
1868 1868
 		if strings.Contains(e, ":") {
1869 1869
 			return nil, nil, cmd, fmt.Errorf("Invalid port format for -expose: %s", e)
1870 1870
 		}
... ...
@@ -1885,15 +1820,15 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co
1885 1885
 		OpenStdin:       *flStdin,
1886 1886
 		Memory:          flMemory,
1887 1887
 		CpuShares:       *flCpuShares,
1888
-		AttachStdin:     flAttach["stdin"],
1889
-		AttachStdout:    flAttach["stdout"],
1890
-		AttachStderr:    flAttach["stderr"],
1891
-		Env:             envs,
1888
+		AttachStdin:     flAttach.Get("stdin"),
1889
+		AttachStdout:    flAttach.Get("stdout"),
1890
+		AttachStderr:    flAttach.Get("stderr"),
1891
+		Env:             flEnv.GetAll(),
1892 1892
 		Cmd:             runCmd,
1893
-		Dns:             flDns,
1893
+		Dns:             flDns.GetAll(),
1894 1894
 		Image:           image,
1895
-		Volumes:         flVolumes,
1896
-		VolumesFrom:     strings.Join(flVolumesFrom, ","),
1895
+		Volumes:         flVolumes.GetMap(),
1896
+		VolumesFrom:     strings.Join(flVolumesFrom.GetAll(), ","),
1897 1897
 		Entrypoint:      entrypoint,
1898 1898
 		WorkingDir:      *flWorkingDir,
1899 1899
 	}
... ...
@@ -1904,7 +1839,7 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co
1904 1904
 		LxcConf:         lxcConf,
1905 1905
 		Privileged:      *flPrivileged,
1906 1906
 		PortBindings:    portBindings,
1907
-		Links:           flLinks,
1907
+		Links:           flLinks.GetAll(),
1908 1908
 		PublishAllPorts: *flPublishAll,
1909 1909
 	}
1910 1910
 
... ...
@@ -23,22 +23,24 @@ func main() {
23 23
 		sysinit.SysInit()
24 24
 		return
25 25
 	}
26
-	// FIXME: Switch d and D ? (to be more sshd like)
27
-	flVersion := flag.Bool("v", false, "Print version information and quit")
28
-	flDaemon := flag.Bool("d", false, "Enable daemon mode")
29
-	flDebug := flag.Bool("D", false, "Enable debug mode")
30
-	flAutoRestart := flag.Bool("r", true, "Restart previously running containers")
31
-	bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge; use 'none' to disable container networking")
32
-	pidfile := flag.String("p", "/var/run/docker.pid", "Path to use for daemon PID file")
33
-	flRoot := flag.String("g", "/var/lib/docker", "Path to use as the root of the docker runtime")
34
-	flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS headers in the remote API")
35
-	flDns := flag.String("dns", "", "Force docker to use specific DNS servers")
36
-	flHosts := utils.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)}
26
+
27
+	var (
28
+		flVersion            = flag.Bool("v", false, "Print version information and quit")
29
+		flDaemon             = flag.Bool("d", false, "Enable daemon mode")
30
+		flDebug              = flag.Bool("D", false, "Enable debug mode")
31
+		flAutoRestart        = flag.Bool("r", true, "Restart previously running containers")
32
+		bridgeName           = flag.String("b", "", "Attach containers to a pre-existing network bridge; use 'none' to disable container networking")
33
+		pidfile              = flag.String("p", "/var/run/docker.pid", "Path to use for daemon PID file")
34
+		flRoot               = flag.String("g", "/var/lib/docker", "Path to use as the root of the docker runtime")
35
+		flEnableCors         = flag.Bool("api-enable-cors", false, "Enable CORS headers in the remote API")
36
+		flDns                = flag.String("dns", "", "Force docker to use specific DNS servers")
37
+		flEnableIptables     = flag.Bool("iptables", true, "Disable docker's addition of iptables rules")
38
+		flDefaultIp          = flag.String("ip", "0.0.0.0", "Default IP address to use when binding container ports")
39
+		flInterContainerComm = flag.Bool("icc", true, "Enable inter-container communication")
40
+		flGraphDriver        = flag.String("s", "", "Force the docker runtime to use a specific storage driver")
41
+		flHosts              = docker.NewListOpts(docker.ValidateHost)
42
+	)
37 43
 	flag.Var(&flHosts, "H", "Multiple tcp://host:port or unix://path/to/socket to bind in daemon mode, single connection otherwise")
38
-	flEnableIptables := flag.Bool("iptables", true, "Disable docker's addition of iptables rules")
39
-	flDefaultIp := flag.String("ip", "0.0.0.0", "Default IP address to use when binding container ports")
40
-	flInterContainerComm := flag.Bool("icc", true, "Enable inter-container communication")
41
-	flGraphDriver := flag.String("s", "", "Force the docker runtime to use a specific storage driver")
42 44
 
43 45
 	flag.Parse()
44 46
 
... ...
@@ -46,16 +48,9 @@ func main() {
46 46
 		showVersion()
47 47
 		return
48 48
 	}
49
-	if len(flHosts) > 1 {
50
-		flHosts = flHosts[1:] //trick to display a nice default value in the usage
51
-	}
52
-	for i, flHost := range flHosts {
53
-		host, err := utils.ParseHost(docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT, flHost)
54
-		if err == nil {
55
-			flHosts[i] = host
56
-		} else {
57
-			log.Fatal(err)
58
-		}
49
+	if flHosts.Len() == 0 {
50
+		// If we do not have a host, default to unix socket
51
+		flHosts.Set(fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET))
59 52
 	}
60 53
 
61 54
 	if *flDebug {
... ...
@@ -88,16 +83,16 @@ func main() {
88 88
 			log.Fatal(err)
89 89
 		}
90 90
 		// Serve api
91
-		job = eng.Job("serveapi", flHosts...)
91
+		job = eng.Job("serveapi", flHosts.GetAll()...)
92 92
 		job.SetenvBool("Logging", true)
93 93
 		if err := job.Run(); err != nil {
94 94
 			log.Fatal(err)
95 95
 		}
96 96
 	} else {
97
-		if len(flHosts) > 1 {
97
+		if flHosts.Len() > 1 {
98 98
 			log.Fatal("Please specify only one -H")
99 99
 		}
100
-		protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
100
+		protoAddrParts := strings.SplitN(flHosts.GetAll()[0], "://", 2)
101 101
 		if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
102 102
 			if sterr, ok := err.(*utils.StatusError); ok {
103 103
 				if sterr.Status != "" {
104 104
new file mode 100644
... ...
@@ -0,0 +1,136 @@
0
+package docker
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/utils"
5
+	"os"
6
+	"path/filepath"
7
+	"strings"
8
+)
9
+
10
+// ListOpts type
11
+type ListOpts struct {
12
+	values    []string
13
+	validator ValidatorFctType
14
+}
15
+
16
+func NewListOpts(validator ValidatorFctType) ListOpts {
17
+	return ListOpts{
18
+		validator: validator,
19
+	}
20
+}
21
+
22
+func (opts *ListOpts) String() string {
23
+	return fmt.Sprintf("%v", []string(opts.values))
24
+}
25
+
26
+// Set validates if needed the input value and add it to the
27
+// internal slice.
28
+func (opts *ListOpts) Set(value string) error {
29
+	if opts.validator != nil {
30
+		v, err := opts.validator(value)
31
+		if err != nil {
32
+			return err
33
+		}
34
+		value = v
35
+	}
36
+	opts.values = append(opts.values, value)
37
+	return nil
38
+}
39
+
40
+// Delete remove the given element from the slice.
41
+func (opts *ListOpts) Delete(key string) {
42
+	for i, k := range opts.values {
43
+		if k == key {
44
+			opts.values = append(opts.values[:i], opts.values[i+1:]...)
45
+			return
46
+		}
47
+	}
48
+}
49
+
50
+// GetMap returns the content of values in a map in order to avoid
51
+// duplicates.
52
+// FIXME: can we remove this?
53
+func (opts *ListOpts) GetMap() map[string]struct{} {
54
+	ret := make(map[string]struct{})
55
+	for _, k := range opts.values {
56
+		ret[k] = struct{}{}
57
+	}
58
+	return ret
59
+}
60
+
61
+// GetAll returns the values' slice.
62
+// FIXME: Can we remove this?
63
+func (opts *ListOpts) GetAll() []string {
64
+	return opts.values
65
+}
66
+
67
+// Get checks the existence of the given key.
68
+func (opts *ListOpts) Get(key string) bool {
69
+	for _, k := range opts.values {
70
+		if k == key {
71
+			return true
72
+		}
73
+	}
74
+	return false
75
+}
76
+
77
+// Len returns the amount of element in the slice.
78
+func (opts *ListOpts) Len() int {
79
+	return len(opts.values)
80
+}
81
+
82
+// Validators
83
+type ValidatorFctType func(val string) (string, error)
84
+
85
+func ValidateAttach(val string) (string, error) {
86
+	if val != "stdin" && val != "stdout" && val != "stderr" {
87
+		return val, fmt.Errorf("Unsupported stream name: %s", val)
88
+	}
89
+	return val, nil
90
+}
91
+
92
+func ValidateLink(val string) (string, error) {
93
+	if _, err := parseLink(val); err != nil {
94
+		return val, err
95
+	}
96
+	return val, nil
97
+}
98
+
99
+func ValidatePath(val string) (string, error) {
100
+	var containerPath string
101
+
102
+	if strings.Count(val, ":") > 2 {
103
+		return val, fmt.Errorf("bad format for volumes: %s", val)
104
+	}
105
+
106
+	splited := strings.SplitN(val, ":", 2)
107
+	if len(splited) == 1 {
108
+		containerPath = splited[0]
109
+		val = filepath.Clean(splited[0])
110
+	} else {
111
+		containerPath = splited[1]
112
+		val = fmt.Sprintf("%s:%s", splited[0], filepath.Clean(splited[1]))
113
+	}
114
+
115
+	if !filepath.IsAbs(containerPath) {
116
+		return val, fmt.Errorf("%s is not an absolute path", containerPath)
117
+	}
118
+	return val, nil
119
+}
120
+
121
+func ValidateEnv(val string) (string, error) {
122
+	arr := strings.Split(val, "=")
123
+	if len(arr) > 1 {
124
+		return val, nil
125
+	}
126
+	return fmt.Sprintf("%s=%s", val, os.Getenv(val)), nil
127
+}
128
+
129
+func ValidateHost(val string) (string, error) {
130
+	host, err := utils.ParseHost(DEFAULTHTTPHOST, DEFAULTHTTPPORT, val)
131
+	if err != nil {
132
+		return val, err
133
+	}
134
+	return host, nil
135
+}
... ...
@@ -215,9 +215,9 @@ func MergeConfig(userConf, imageConf *Config) error {
215 215
 	return nil
216 216
 }
217 217
 
218
-func parseLxcConfOpts(opts utils.ListOpts) ([]KeyValuePair, error) {
219
-	out := make([]KeyValuePair, len(opts))
220
-	for i, o := range opts {
218
+func parseLxcConfOpts(opts ListOpts) ([]KeyValuePair, error) {
219
+	out := make([]KeyValuePair, opts.Len())
220
+	for i, o := range opts.GetAll() {
221 221
 		k, v, err := parseLxcOpt(o)
222 222
 		if err != nil {
223 223
 			return nil, err
... ...
@@ -34,18 +34,6 @@ type Fataler interface {
34 34
 	Fatal(args ...interface{})
35 35
 }
36 36
 
37
-// ListOpts type
38
-type ListOpts []string
39
-
40
-func (opts *ListOpts) String() string {
41
-	return fmt.Sprint(*opts)
42
-}
43
-
44
-func (opts *ListOpts) Set(value string) error {
45
-	*opts = append(*opts, value)
46
-	return nil
47
-}
48
-
49 37
 // Go is a basic promise implementation: it wraps calls a function in a goroutine,
50 38
 // and returns a channel which will later return the function's return value.
51 39
 func Go(f func() error) chan error {