Browse Source

abstract the string slice struct to stringutils package

Signed-off-by: Shijiang Wei <mountkin@gmail.com>
Shijiang Wei 10 năm trước cách đây
mục cha
commit
ea4a06740b

+ 6 - 5
builder/dispatchers.go

@@ -20,6 +20,7 @@ import (
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/nat"
+	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/runconfig"
 )
 )
 
 
@@ -352,7 +353,7 @@ func run(b *builder, args []string, attributes map[string]bool, original string)
 	b.Config.Cmd = config.Cmd
 	b.Config.Cmd = config.Cmd
 	runconfig.Merge(b.Config, config)
 	runconfig.Merge(b.Config, config)
 
 
-	defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
+	defer func(cmd *stringutils.StrSlice) { b.Config.Cmd = cmd }(cmd)
 
 
 	logrus.Debugf("[BUILDER] Command to be executed: %v", b.Config.Cmd)
 	logrus.Debugf("[BUILDER] Command to be executed: %v", b.Config.Cmd)
 
 
@@ -405,7 +406,7 @@ func cmd(b *builder, args []string, attributes map[string]bool, original string)
 		}
 		}
 	}
 	}
 
 
-	b.Config.Cmd = runconfig.NewCommand(cmdSlice...)
+	b.Config.Cmd = stringutils.NewStrSlice(cmdSlice...)
 
 
 	if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %q", cmdSlice)); err != nil {
 	if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %q", cmdSlice)); err != nil {
 		return err
 		return err
@@ -436,16 +437,16 @@ func entrypoint(b *builder, args []string, attributes map[string]bool, original
 	switch {
 	switch {
 	case attributes["json"]:
 	case attributes["json"]:
 		// ENTRYPOINT ["echo", "hi"]
 		// ENTRYPOINT ["echo", "hi"]
-		b.Config.Entrypoint = runconfig.NewEntrypoint(parsed...)
+		b.Config.Entrypoint = stringutils.NewStrSlice(parsed...)
 	case len(parsed) == 0:
 	case len(parsed) == 0:
 		// ENTRYPOINT []
 		// ENTRYPOINT []
 		b.Config.Entrypoint = nil
 		b.Config.Entrypoint = nil
 	default:
 	default:
 		// ENTRYPOINT echo hi
 		// ENTRYPOINT echo hi
 		if runtime.GOOS != "windows" {
 		if runtime.GOOS != "windows" {
-			b.Config.Entrypoint = runconfig.NewEntrypoint("/bin/sh", "-c", parsed[0])
+			b.Config.Entrypoint = stringutils.NewStrSlice("/bin/sh", "-c", parsed[0])
 		} else {
 		} else {
-			b.Config.Entrypoint = runconfig.NewEntrypoint("cmd", "/S /C", parsed[0])
+			b.Config.Entrypoint = stringutils.NewStrSlice("cmd", "/S /C", parsed[0])
 		}
 		}
 	}
 	}
 
 

+ 9 - 8
builder/internals.go

@@ -33,6 +33,7 @@ import (
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/progressreader"
 	"github.com/docker/docker/pkg/progressreader"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/tarsum"
 	"github.com/docker/docker/pkg/tarsum"
 	"github.com/docker/docker/pkg/urlutil"
 	"github.com/docker/docker/pkg/urlutil"
@@ -73,7 +74,7 @@ func (b *builder) readContext(context io.Reader) (err error) {
 	return
 	return
 }
 }
 
 
-func (b *builder) commit(id string, autoCmd *runconfig.Command, comment string) error {
+func (b *builder) commit(id string, autoCmd *stringutils.StrSlice, comment string) error {
 	if b.disableCommit {
 	if b.disableCommit {
 		return nil
 		return nil
 	}
 	}
@@ -84,11 +85,11 @@ func (b *builder) commit(id string, autoCmd *runconfig.Command, comment string)
 	if id == "" {
 	if id == "" {
 		cmd := b.Config.Cmd
 		cmd := b.Config.Cmd
 		if runtime.GOOS != "windows" {
 		if runtime.GOOS != "windows" {
-			b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", "#(nop) "+comment)
+			b.Config.Cmd = stringutils.NewStrSlice("/bin/sh", "-c", "#(nop) "+comment)
 		} else {
 		} else {
-			b.Config.Cmd = runconfig.NewCommand("cmd", "/S /C", "REM (nop) "+comment)
+			b.Config.Cmd = stringutils.NewStrSlice("cmd", "/S /C", "REM (nop) "+comment)
 		}
 		}
-		defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
+		defer func(cmd *stringutils.StrSlice) { b.Config.Cmd = cmd }(cmd)
 
 
 		hit, err := b.probeCache()
 		hit, err := b.probeCache()
 		if err != nil {
 		if err != nil {
@@ -215,11 +216,11 @@ func (b *builder) runContextCommand(args []string, allowRemote bool, allowDecomp
 
 
 	cmd := b.Config.Cmd
 	cmd := b.Config.Cmd
 	if runtime.GOOS != "windows" {
 	if runtime.GOOS != "windows" {
-		b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest))
+		b.Config.Cmd = stringutils.NewStrSlice("/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest))
 	} else {
 	} else {
-		b.Config.Cmd = runconfig.NewCommand("cmd", "/S /C", fmt.Sprintf("REM (nop) %s %s in %s", cmdName, srcHash, dest))
+		b.Config.Cmd = stringutils.NewStrSlice("cmd", "/S /C", fmt.Sprintf("REM (nop) %s %s in %s", cmdName, srcHash, dest))
 	}
 	}
-	defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
+	defer func(cmd *stringutils.StrSlice) { b.Config.Cmd = cmd }(cmd)
 
 
 	hit, err := b.probeCache()
 	hit, err := b.probeCache()
 	if err != nil {
 	if err != nil {
@@ -630,7 +631,7 @@ func (b *builder) create() (*daemon.Container, error) {
 		c.Path = s[0]
 		c.Path = s[0]
 		c.Args = s[1:]
 		c.Args = s[1:]
 	} else {
 	} else {
-		config.Cmd = runconfig.NewCommand()
+		config.Cmd = stringutils.NewStrSlice()
 	}
 	}
 
 
 	return c, nil
 	return c, nil

+ 2 - 1
daemon/daemon.go

@@ -33,6 +33,7 @@ import (
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/truncindex"
 	"github.com/docker/docker/pkg/truncindex"
@@ -437,7 +438,7 @@ func (daemon *Daemon) generateHostname(id string, config *runconfig.Config) {
 	}
 	}
 }
 }
 
 
-func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint *runconfig.Entrypoint, configCmd *runconfig.Command) (string, []string) {
+func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint *stringutils.StrSlice, configCmd *stringutils.StrSlice) (string, []string) {
 	var (
 	var (
 		entrypoint string
 		entrypoint string
 		args       []string
 		args       []string

+ 3 - 2
daemon/exec.go

@@ -14,6 +14,7 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/pools"
 	"github.com/docker/docker/pkg/pools"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/runconfig"
 )
 )
 
 
@@ -139,8 +140,8 @@ func (d *Daemon) ContainerExecCreate(config *runconfig.ExecConfig) (string, erro
 		return "", err
 		return "", err
 	}
 	}
 
 
-	cmd := runconfig.NewCommand(config.Cmd...)
-	entrypoint, args := d.getEntrypointAndArgs(runconfig.NewEntrypoint(), cmd)
+	cmd := stringutils.NewStrSlice(config.Cmd...)
+	entrypoint, args := d.getEntrypointAndArgs(stringutils.NewStrSlice(), cmd)
 
 
 	user := config.User
 	user := config.User
 	if len(user) == 0 {
 	if len(user) == 0 {

+ 71 - 0
pkg/stringutils/strslice.go

@@ -0,0 +1,71 @@
+package stringutils
+
+import (
+	"encoding/json"
+	"strings"
+)
+
+// StrSlice representes a string or an array of strings.
+// We need to override the json decoder to accept both options.
+type StrSlice struct {
+	parts []string
+}
+
+// MarshalJSON Marshals (or serializes) the StrSlice into the json format.
+// This method is needed to implement json.Marshaller.
+func (e *StrSlice) MarshalJSON() ([]byte, error) {
+	if e == nil {
+		return []byte{}, nil
+	}
+	return json.Marshal(e.Slice())
+}
+
+// UnmarshalJSON decodes the byte slice whether it's a string or an array of strings.
+// This method is needed to implement json.Unmarshaler.
+func (e *StrSlice) UnmarshalJSON(b []byte) error {
+	if len(b) == 0 {
+		return nil
+	}
+
+	p := make([]string, 0, 1)
+	if err := json.Unmarshal(b, &p); err != nil {
+		var s string
+		if err := json.Unmarshal(b, &s); err != nil {
+			return err
+		}
+		p = append(p, s)
+	}
+
+	e.parts = p
+	return nil
+}
+
+// Len returns the number of parts of the StrSlice.
+func (e *StrSlice) Len() int {
+	if e == nil {
+		return 0
+	}
+	return len(e.parts)
+}
+
+// Slice gets the parts of the StrSlice as a Slice of string.
+func (e *StrSlice) Slice() []string {
+	if e == nil {
+		return nil
+	}
+	return e.parts
+}
+
+// ToString gets space separated string of all the parts.
+func (e *StrSlice) ToString() string {
+	s := e.Slice()
+	if s == nil {
+		return ""
+	}
+	return strings.Join(s, " ")
+}
+
+// NewStrSlice creates an StrSlice based on the specified parts (as strings).
+func NewStrSlice(parts ...string) *StrSlice {
+	return &StrSlice{parts}
+}

+ 105 - 0
pkg/stringutils/strslice_test.go

@@ -0,0 +1,105 @@
+package stringutils
+
+import (
+	"encoding/json"
+	"testing"
+)
+
+func TestStrSliceMarshalJSON(t *testing.T) {
+	strss := map[*StrSlice]string{
+		nil:         "",
+		&StrSlice{}: "null",
+		&StrSlice{[]string{"/bin/sh", "-c", "echo"}}: `["/bin/sh","-c","echo"]`,
+	}
+
+	for strs, expected := range strss {
+		data, err := strs.MarshalJSON()
+		if err != nil {
+			t.Fatal(err)
+		}
+		if string(data) != expected {
+			t.Fatalf("Expected %v, got %v", expected, string(data))
+		}
+	}
+}
+
+func TestStrSliceUnmarshalJSON(t *testing.T) {
+	parts := map[string][]string{
+		"":   {"default", "values"},
+		"[]": {},
+		`["/bin/sh","-c","echo"]`: {"/bin/sh", "-c", "echo"},
+	}
+	for json, expectedParts := range parts {
+		strs := &StrSlice{
+			[]string{"default", "values"},
+		}
+		if err := strs.UnmarshalJSON([]byte(json)); err != nil {
+			t.Fatal(err)
+		}
+
+		actualParts := strs.Slice()
+		if len(actualParts) != len(expectedParts) {
+			t.Fatalf("Expected %v parts, got %v (%v)", len(expectedParts), len(actualParts), expectedParts)
+		}
+		for index, part := range actualParts {
+			if part != expectedParts[index] {
+				t.Fatalf("Expected %v, got %v", expectedParts, actualParts)
+				break
+			}
+		}
+	}
+}
+
+func TestStrSliceUnmarshalString(t *testing.T) {
+	var e *StrSlice
+	echo, err := json.Marshal("echo")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err := json.Unmarshal(echo, &e); err != nil {
+		t.Fatal(err)
+	}
+
+	slice := e.Slice()
+	if len(slice) != 1 {
+		t.Fatalf("expected 1 element after unmarshal: %q", slice)
+	}
+
+	if slice[0] != "echo" {
+		t.Fatalf("expected `echo`, got: %q", slice[0])
+	}
+}
+
+func TestStrSliceUnmarshalSlice(t *testing.T) {
+	var e *StrSlice
+	echo, err := json.Marshal([]string{"echo"})
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err := json.Unmarshal(echo, &e); err != nil {
+		t.Fatal(err)
+	}
+
+	slice := e.Slice()
+	if len(slice) != 1 {
+		t.Fatalf("expected 1 element after unmarshal: %q", slice)
+	}
+
+	if slice[0] != "echo" {
+		t.Fatalf("expected `echo`, got: %q", slice[0])
+	}
+}
+
+func TestStrSliceToString(t *testing.T) {
+	slices := map[*StrSlice]string{
+		NewStrSlice(""):           "",
+		NewStrSlice("one"):        "one",
+		NewStrSlice("one", "two"): "one two",
+	}
+	for s, expected := range slices {
+		toString := s.ToString()
+		if toString != expected {
+			t.Fatalf("Expected %v, got %v", expected, toString)
+		}
+	}
+}

+ 7 - 6
runconfig/compare_test.go

@@ -4,6 +4,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/nat"
+	"github.com/docker/docker/pkg/stringutils"
 )
 )
 
 
 // Just to make life easier
 // Just to make life easier
@@ -32,12 +33,12 @@ func TestCompare(t *testing.T) {
 	volumes3["/test3"] = struct{}{}
 	volumes3["/test3"] = struct{}{}
 	envs1 := []string{"ENV1=value1", "ENV2=value2"}
 	envs1 := []string{"ENV1=value1", "ENV2=value2"}
 	envs2 := []string{"ENV1=value1", "ENV3=value3"}
 	envs2 := []string{"ENV1=value1", "ENV3=value3"}
-	entrypoint1 := &Entrypoint{parts: []string{"/bin/sh", "-c"}}
-	entrypoint2 := &Entrypoint{parts: []string{"/bin/sh", "-d"}}
-	entrypoint3 := &Entrypoint{parts: []string{"/bin/sh", "-c", "echo"}}
-	cmd1 := &Command{parts: []string{"/bin/sh", "-c"}}
-	cmd2 := &Command{parts: []string{"/bin/sh", "-d"}}
-	cmd3 := &Command{parts: []string{"/bin/sh", "-c", "echo"}}
+	entrypoint1 := stringutils.NewStrSlice("/bin/sh", "-c")
+	entrypoint2 := stringutils.NewStrSlice("/bin/sh", "-d")
+	entrypoint3 := stringutils.NewStrSlice("/bin/sh", "-c", "echo")
+	cmd1 := stringutils.NewStrSlice("/bin/sh", "-c")
+	cmd2 := stringutils.NewStrSlice("/bin/sh", "-d")
+	cmd3 := stringutils.NewStrSlice("/bin/sh", "-c", "echo")
 	labels1 := map[string]string{"LABEL1": "value1", "LABEL2": "value2"}
 	labels1 := map[string]string{"LABEL1": "value1", "LABEL2": "value2"}
 	labels2 := map[string]string{"LABEL1": "value1", "LABEL2": "value3"}
 	labels2 := map[string]string{"LABEL1": "value1", "LABEL2": "value3"}
 	labels3 := map[string]string{"LABEL1": "value1", "LABEL2": "value2", "LABEL3": "value3"}
 	labels3 := map[string]string{"LABEL1": "value1", "LABEL2": "value2", "LABEL3": "value3"}

+ 3 - 124
runconfig/config.go

@@ -3,132 +3,11 @@ package runconfig
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"io"
 	"io"
-	"strings"
 
 
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/nat"
+	"github.com/docker/docker/pkg/stringutils"
 )
 )
 
 
-// Entrypoint encapsulates the container entrypoint.
-// It might be represented as a string or an array of strings.
-// We need to override the json decoder to accept both options.
-// The JSON decoder will fail if the api sends an string and
-//  we try to decode it into an array of string.
-type Entrypoint struct {
-	parts []string
-}
-
-// MarshalJSON Marshals (or serializes) the Entrypoint into the json format.
-// This method is needed to implement json.Marshaller.
-func (e *Entrypoint) MarshalJSON() ([]byte, error) {
-	if e == nil {
-		return []byte{}, nil
-	}
-	return json.Marshal(e.Slice())
-}
-
-// UnmarshalJSON decodes the entrypoint whether it's a string or an array of strings.
-// This method is needed to implement json.Unmarshaler.
-func (e *Entrypoint) UnmarshalJSON(b []byte) error {
-	if len(b) == 0 {
-		return nil
-	}
-
-	p := make([]string, 0, 1)
-	if err := json.Unmarshal(b, &p); err != nil {
-		var s string
-		if err := json.Unmarshal(b, &s); err != nil {
-			return err
-		}
-		p = append(p, s)
-	}
-	e.parts = p
-	return nil
-}
-
-// Len returns the number of parts of the Entrypoint.
-func (e *Entrypoint) Len() int {
-	if e == nil {
-		return 0
-	}
-	return len(e.parts)
-}
-
-// Slice gets the parts of the Entrypoint as a Slice of string.
-func (e *Entrypoint) Slice() []string {
-	if e == nil {
-		return nil
-	}
-	return e.parts
-}
-
-// NewEntrypoint creates an Entrypoint based on the specified parts (as strings).
-func NewEntrypoint(parts ...string) *Entrypoint {
-	return &Entrypoint{parts}
-}
-
-// Command encapsulates the container command.
-// It might be represented as a string or an array of strings.
-// We need to override the json decoder to accept both options.
-// The JSON decoder will fail if the api sends an string and
-//  we try to decode it into an array of string.
-type Command struct {
-	parts []string
-}
-
-// ToString gets a string representing a Command.
-func (e *Command) ToString() string {
-	return strings.Join(e.parts, " ")
-}
-
-// MarshalJSON Marshals (or serializes) the Command into the json format.
-// This method is needed to implement json.Marshaller.
-func (e *Command) MarshalJSON() ([]byte, error) {
-	if e == nil {
-		return []byte{}, nil
-	}
-	return json.Marshal(e.Slice())
-}
-
-// UnmarshalJSON decodes the entrypoint whether it's a string or an array of strings.
-// This method is needed to implement json.Unmarshaler.
-func (e *Command) UnmarshalJSON(b []byte) error {
-	if len(b) == 0 {
-		return nil
-	}
-
-	p := make([]string, 0, 1)
-	if err := json.Unmarshal(b, &p); err != nil {
-		var s string
-		if err := json.Unmarshal(b, &s); err != nil {
-			return err
-		}
-		p = append(p, s)
-	}
-	e.parts = p
-	return nil
-}
-
-// Len returns the number of parts of the Entrypoint.
-func (e *Command) Len() int {
-	if e == nil {
-		return 0
-	}
-	return len(e.parts)
-}
-
-// Slice gets the parts of the Entrypoint as a Slice of string.
-func (e *Command) Slice() []string {
-	if e == nil {
-		return nil
-	}
-	return e.parts
-}
-
-// NewCommand creates a Command based on the specified parts (as strings).
-func NewCommand(parts ...string) *Command {
-	return &Command{parts}
-}
-
 // Config contains the configuration data about a container.
 // Config contains the configuration data about a container.
 // It should hold only portable information about the container.
 // It should hold only portable information about the container.
 // Here, "portable" means "independent from the host we are running on".
 // Here, "portable" means "independent from the host we are running on".
@@ -146,12 +25,12 @@ type Config struct {
 	OpenStdin       bool                  // Open stdin
 	OpenStdin       bool                  // Open stdin
 	StdinOnce       bool                  // If true, close stdin after the 1 attached client disconnects.
 	StdinOnce       bool                  // If true, close stdin after the 1 attached client disconnects.
 	Env             []string              // List of environment variable to set in the container
 	Env             []string              // List of environment variable to set in the container
-	Cmd             *Command              // Command to run when starting the container
+	Cmd             *stringutils.StrSlice // Command to run when starting the container
 	Image           string                // Name of the image as it was passed by the operator (eg. could be symbolic)
 	Image           string                // Name of the image as it was passed by the operator (eg. could be symbolic)
 	Volumes         map[string]struct{}   // List of volumes (mounts) used for the container
 	Volumes         map[string]struct{}   // List of volumes (mounts) used for the container
 	VolumeDriver    string                // Name of the volume driver used to mount volumes
 	VolumeDriver    string                // Name of the volume driver used to mount volumes
 	WorkingDir      string                // Current directory (PWD) in the command will be launched
 	WorkingDir      string                // Current directory (PWD) in the command will be launched
-	Entrypoint      *Entrypoint           // Entrypoint to run when starting the container
+	Entrypoint      *stringutils.StrSlice // Entrypoint to run when starting the container
 	NetworkDisabled bool                  // Is network disabled
 	NetworkDisabled bool                  // Is network disabled
 	MacAddress      string                // Mac Address of the container
 	MacAddress      string                // Mac Address of the container
 	OnBuild         []string              // ONBUILD metadata that were defined on the image Dockerfile
 	OnBuild         []string              // ONBUILD metadata that were defined on the image Dockerfile

+ 6 - 189
runconfig/config_test.go

@@ -2,124 +2,21 @@ package runconfig
 
 
 import (
 import (
 	"bytes"
 	"bytes"
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"io/ioutil"
 	"io/ioutil"
 	"testing"
 	"testing"
-)
-
-func TestEntrypointMarshalJSON(t *testing.T) {
-	entrypoints := map[*Entrypoint]string{
-		nil:                                            "",
-		&Entrypoint{}:                                  "null",
-		&Entrypoint{[]string{"/bin/sh", "-c", "echo"}}: `["/bin/sh","-c","echo"]`,
-	}
-
-	for entrypoint, expected := range entrypoints {
-		data, err := entrypoint.MarshalJSON()
-		if err != nil {
-			t.Fatal(err)
-		}
-		if string(data) != expected {
-			t.Fatalf("Expected %v, got %v", expected, string(data))
-		}
-	}
-}
-
-func TestEntrypointUnmarshalJSON(t *testing.T) {
-	parts := map[string][]string{
-		"":   {"default", "values"},
-		"[]": {},
-		`["/bin/sh","-c","echo"]`: {"/bin/sh", "-c", "echo"},
-	}
-	for json, expectedParts := range parts {
-		entrypoint := &Entrypoint{
-			[]string{"default", "values"},
-		}
-		if err := entrypoint.UnmarshalJSON([]byte(json)); err != nil {
-			t.Fatal(err)
-		}
-
-		actualParts := entrypoint.Slice()
-		if len(actualParts) != len(expectedParts) {
-			t.Fatalf("Expected %v parts, got %v (%v)", len(expectedParts), len(actualParts), expectedParts)
-		}
-		for index, part := range actualParts {
-			if part != expectedParts[index] {
-				t.Fatalf("Expected %v, got %v", expectedParts, actualParts)
-				break
-			}
-		}
-	}
-}
-
-func TestCommandToString(t *testing.T) {
-	commands := map[*Command]string{
-		&Command{[]string{""}}:           "",
-		&Command{[]string{"one"}}:        "one",
-		&Command{[]string{"one", "two"}}: "one two",
-	}
-	for command, expected := range commands {
-		toString := command.ToString()
-		if toString != expected {
-			t.Fatalf("Expected %v, got %v", expected, toString)
-		}
-	}
-}
-
-func TestCommandMarshalJSON(t *testing.T) {
-	commands := map[*Command]string{
-		nil:        "",
-		&Command{}: "null",
-		&Command{[]string{"/bin/sh", "-c", "echo"}}: `["/bin/sh","-c","echo"]`,
-	}
-
-	for command, expected := range commands {
-		data, err := command.MarshalJSON()
-		if err != nil {
-			t.Fatal(err)
-		}
-		if string(data) != expected {
-			t.Fatalf("Expected %v, got %v", expected, string(data))
-		}
-	}
-}
-
-func TestCommandUnmarshalJSON(t *testing.T) {
-	parts := map[string][]string{
-		"":   {"default", "values"},
-		"[]": {},
-		`["/bin/sh","-c","echo"]`: {"/bin/sh", "-c", "echo"},
-	}
-	for json, expectedParts := range parts {
-		command := &Command{
-			[]string{"default", "values"},
-		}
-		if err := command.UnmarshalJSON([]byte(json)); err != nil {
-			t.Fatal(err)
-		}
 
 
-		actualParts := command.Slice()
-		if len(actualParts) != len(expectedParts) {
-			t.Fatalf("Expected %v parts, got %v (%v)", len(expectedParts), len(actualParts), expectedParts)
-		}
-		for index, part := range actualParts {
-			if part != expectedParts[index] {
-				t.Fatalf("Expected %v, got %v", expectedParts, actualParts)
-				break
-			}
-		}
-	}
-}
+	"github.com/docker/docker/pkg/stringutils"
+)
 
 
 func TestDecodeContainerConfig(t *testing.T) {
 func TestDecodeContainerConfig(t *testing.T) {
 	fixtures := []struct {
 	fixtures := []struct {
 		file       string
 		file       string
-		entrypoint *Entrypoint
+		entrypoint *stringutils.StrSlice
 	}{
 	}{
-		{"fixtures/container_config_1_14.json", NewEntrypoint()},
-		{"fixtures/container_config_1_17.json", NewEntrypoint("bash")},
-		{"fixtures/container_config_1_19.json", NewEntrypoint("bash")},
+		{"fixtures/container_config_1_14.json", stringutils.NewStrSlice()},
+		{"fixtures/container_config_1_17.json", stringutils.NewStrSlice("bash")},
+		{"fixtures/container_config_1_19.json", stringutils.NewStrSlice("bash")},
 	}
 	}
 
 
 	for _, f := range fixtures {
 	for _, f := range fixtures {
@@ -146,83 +43,3 @@ func TestDecodeContainerConfig(t *testing.T) {
 		}
 		}
 	}
 	}
 }
 }
-
-func TestEntrypointUnmarshalString(t *testing.T) {
-	var e *Entrypoint
-	echo, err := json.Marshal("echo")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if err := json.Unmarshal(echo, &e); err != nil {
-		t.Fatal(err)
-	}
-
-	slice := e.Slice()
-	if len(slice) != 1 {
-		t.Fatalf("expected 1 element after unmarshal: %q", slice)
-	}
-
-	if slice[0] != "echo" {
-		t.Fatalf("expected `echo`, got: %q", slice[0])
-	}
-}
-
-func TestEntrypointUnmarshalSlice(t *testing.T) {
-	var e *Entrypoint
-	echo, err := json.Marshal([]string{"echo"})
-	if err != nil {
-		t.Fatal(err)
-	}
-	if err := json.Unmarshal(echo, &e); err != nil {
-		t.Fatal(err)
-	}
-
-	slice := e.Slice()
-	if len(slice) != 1 {
-		t.Fatalf("expected 1 element after unmarshal: %q", slice)
-	}
-
-	if slice[0] != "echo" {
-		t.Fatalf("expected `echo`, got: %q", slice[0])
-	}
-}
-
-func TestCommandUnmarshalSlice(t *testing.T) {
-	var e *Command
-	echo, err := json.Marshal([]string{"echo"})
-	if err != nil {
-		t.Fatal(err)
-	}
-	if err := json.Unmarshal(echo, &e); err != nil {
-		t.Fatal(err)
-	}
-
-	slice := e.Slice()
-	if len(slice) != 1 {
-		t.Fatalf("expected 1 element after unmarshal: %q", slice)
-	}
-
-	if slice[0] != "echo" {
-		t.Fatalf("expected `echo`, got: %q", slice[0])
-	}
-}
-
-func TestCommandUnmarshalString(t *testing.T) {
-	var e *Command
-	echo, err := json.Marshal("echo")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if err := json.Unmarshal(echo, &e); err != nil {
-		t.Fatal(err)
-	}
-
-	slice := e.Slice()
-	if len(slice) != 1 {
-		t.Fatalf("expected 1 element after unmarshal: %q", slice)
-	}
-
-	if slice[0] != "echo" {
-		t.Fatalf("expected `echo`, got: %q", slice[0])
-	}
-}

+ 5 - 4
runconfig/parse.go

@@ -9,6 +9,7 @@ import (
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
+	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/pkg/units"
 	"github.com/docker/docker/pkg/units"
 )
 )
 
 
@@ -199,15 +200,15 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 
 
 	var (
 	var (
 		parsedArgs = cmd.Args()
 		parsedArgs = cmd.Args()
-		runCmd     *Command
-		entrypoint *Entrypoint
+		runCmd     *stringutils.StrSlice
+		entrypoint *stringutils.StrSlice
 		image      = cmd.Arg(0)
 		image      = cmd.Arg(0)
 	)
 	)
 	if len(parsedArgs) > 1 {
 	if len(parsedArgs) > 1 {
-		runCmd = NewCommand(parsedArgs[1:]...)
+		runCmd = stringutils.NewStrSlice(parsedArgs[1:]...)
 	}
 	}
 	if *flEntrypoint != "" {
 	if *flEntrypoint != "" {
-		entrypoint = NewEntrypoint(*flEntrypoint)
+		entrypoint = stringutils.NewStrSlice(*flEntrypoint)
 	}
 	}
 
 
 	lc, err := parseKeyValueOpts(flLxcOpts)
 	lc, err := parseKeyValueOpts(flLxcOpts)

+ 1 - 1
runconfig/parse_test.go

@@ -486,7 +486,7 @@ func TestParseEntryPoint(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if config.Entrypoint.Len() != 1 && config.Entrypoint.parts[0] != "anything" {
+	if config.Entrypoint.Len() != 1 && config.Entrypoint.Slice()[0] != "anything" {
 		t.Fatalf("Expected entrypoint 'anything', got %v", config.Entrypoint)
 		t.Fatalf("Expected entrypoint 'anything', got %v", config.Entrypoint)
 	}
 	}
 }
 }