mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
abstract the string slice struct to stringutils package
Signed-off-by: Shijiang Wei <mountkin@gmail.com>
This commit is contained in:
parent
fdc73cc3fc
commit
ea4a06740b
11 changed files with 218 additions and 340 deletions
|
@ -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])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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...)
|
cmd := stringutils.NewStrSlice(config.Cmd...)
|
||||||
entrypoint, args := d.getEntrypointAndArgs(runconfig.NewEntrypoint(), cmd)
|
entrypoint, args := d.getEntrypointAndArgs(stringutils.NewStrSlice(), cmd)
|
||||||
|
|
||||||
user := config.User
|
user := config.User
|
||||||
if len(user) == 0 {
|
if len(user) == 0 {
|
||||||
|
|
71
pkg/stringutils/strslice.go
Normal file
71
pkg/stringutils/strslice.go
Normal file
|
@ -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
pkg/stringutils/strslice_test.go
Normal file
105
pkg/stringutils/strslice_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"}}
|
entrypoint1 := stringutils.NewStrSlice("/bin/sh", "-c")
|
||||||
entrypoint2 := &Entrypoint{parts: []string{"/bin/sh", "-d"}}
|
entrypoint2 := stringutils.NewStrSlice("/bin/sh", "-d")
|
||||||
entrypoint3 := &Entrypoint{parts: []string{"/bin/sh", "-c", "echo"}}
|
entrypoint3 := stringutils.NewStrSlice("/bin/sh", "-c", "echo")
|
||||||
cmd1 := &Command{parts: []string{"/bin/sh", "-c"}}
|
cmd1 := stringutils.NewStrSlice("/bin/sh", "-c")
|
||||||
cmd2 := &Command{parts: []string{"/bin/sh", "-d"}}
|
cmd2 := stringutils.NewStrSlice("/bin/sh", "-d")
|
||||||
cmd3 := &Command{parts: []string{"/bin/sh", "-c", "echo"}}
|
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,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
|
||||||
|
|
|
@ -2,124 +2,21 @@ package runconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/stringutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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_14.json", stringutils.NewStrSlice()},
|
||||||
{"fixtures/container_config_1_17.json", NewEntrypoint("bash")},
|
{"fixtures/container_config_1_17.json", stringutils.NewStrSlice("bash")},
|
||||||
{"fixtures/container_config_1_19.json", NewEntrypoint("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])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
runCmd *stringutils.StrSlice
|
||||||
entrypoint *Entrypoint
|
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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue