Переглянути джерело

Fix regression in parsing capabilities list when a single string is given

Signed-off-by: Antonio Murdaca <runcom@linux.com>
Antonio Murdaca 10 роки тому
батько
коміт
10a3061c5f

+ 2 - 2
daemon/container_linux.go

@@ -297,8 +297,8 @@ func populateCommand(c *Container, env []string) error {
 		Resources:          resources,
 		AllowedDevices:     allowedDevices,
 		AutoCreatedDevices: autoCreatedDevices,
-		CapAdd:             c.hostConfig.CapAdd,
-		CapDrop:            c.hostConfig.CapDrop,
+		CapAdd:             c.hostConfig.CapAdd.Slice(),
+		CapDrop:            c.hostConfig.CapDrop.Slice(),
 		ProcessConfig:      processConfig,
 		ProcessLabel:       c.GetProcessLabel(),
 		MountLabel:         c.GetMountLabel(),

+ 2 - 2
daemon/container_windows.go

@@ -111,8 +111,8 @@ func populateCommand(c *Container, env []string) error {
 		Network:        en,
 		Pid:            pid,
 		Resources:      resources,
-		CapAdd:         c.hostConfig.CapAdd,
-		CapDrop:        c.hostConfig.CapDrop,
+		CapAdd:         c.hostConfig.CapAdd.Slice(),
+		CapDrop:        c.hostConfig.CapDrop.Slice(),
 		ProcessConfig:  processConfig,
 		ProcessLabel:   c.GetProcessLabel(),
 		MountLabel:     c.GetMountLabel(),

+ 21 - 0
integration-cli/docker_api_containers_test.go

@@ -1705,3 +1705,24 @@ func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) {
 	out, _ = dockerCmd(c, "start", "-a", "echotest2")
 	c.Assert(strings.TrimSpace(out), check.Equals, "hello world")
 }
+
+// regression #14318
+func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *check.C) {
+	config := struct {
+		Image   string
+		CapAdd  string
+		CapDrop string
+	}{"busybox", "NET_ADMIN", "SYS_ADMIN"}
+	status, _, err := sockRequest("POST", "/containers/create?name=capaddtest0", config)
+	c.Assert(err, check.IsNil)
+	c.Assert(status, check.Equals, http.StatusCreated)
+
+	config2 := struct {
+		Image   string
+		CapAdd  []string
+		CapDrop []string
+	}{"busybox", []string{"NET_ADMIN", "SYS_ADMIN"}, []string{"SETGID"}}
+	status, _, err = sockRequest("POST", "/containers/create?name=capaddtest1", config2)
+	c.Assert(err, check.IsNil)
+	c.Assert(status, check.Equals, http.StatusCreated)
+}

+ 49 - 2
runconfig/hostconfig.go

@@ -173,6 +173,53 @@ func NewLxcConfig(values []KeyValuePair) *LxcConfig {
 	return &LxcConfig{values}
 }
 
+type CapList struct {
+	caps []string
+}
+
+func (c *CapList) MarshalJSON() ([]byte, error) {
+	if c == nil {
+		return []byte{}, nil
+	}
+	return json.Marshal(c.Slice())
+}
+
+func (c *CapList) UnmarshalJSON(b []byte) error {
+	if len(b) == 0 {
+		return nil
+	}
+
+	var caps []string
+	if err := json.Unmarshal(b, &caps); err != nil {
+		var s string
+		if err := json.Unmarshal(b, &s); err != nil {
+			return err
+		}
+		caps = append(caps, s)
+	}
+	c.caps = caps
+
+	return nil
+}
+
+func (c *CapList) Len() int {
+	if c == nil {
+		return 0
+	}
+	return len(c.caps)
+}
+
+func (c *CapList) Slice() []string {
+	if c == nil {
+		return nil
+	}
+	return c.caps
+}
+
+func NewCapList(caps []string) *CapList {
+	return &CapList{caps}
+}
+
 type HostConfig struct {
 	Binds           []string
 	ContainerIDFile string
@@ -199,8 +246,8 @@ type HostConfig struct {
 	IpcMode         IpcMode
 	PidMode         PidMode
 	UTSMode         UTSMode
-	CapAdd          []string
-	CapDrop         []string
+	CapAdd          *CapList
+	CapDrop         *CapList
 	RestartPolicy   RestartPolicy
 	SecurityOpt     []string
 	ReadonlyRootfs  bool

+ 40 - 2
runconfig/hostconfig_test.go

@@ -2,6 +2,7 @@ package runconfig
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
 	"io/ioutil"
 	"testing"
@@ -254,12 +255,49 @@ func TestDecodeHostConfig(t *testing.T) {
 			t.Fatalf("Expected 1 bind, found %v\n", c.Binds)
 		}
 
-		if len(c.CapAdd) != 1 && c.CapAdd[0] != "NET_ADMIN" {
+		if c.CapAdd.Len() != 1 && c.CapAdd.Slice()[0] != "NET_ADMIN" {
 			t.Fatalf("Expected CapAdd NET_ADMIN, got %v", c.CapAdd)
 		}
 
-		if len(c.CapDrop) != 1 && c.CapDrop[0] != "NET_ADMIN" {
+		if c.CapDrop.Len() != 1 && c.CapDrop.Slice()[0] != "NET_ADMIN" {
 			t.Fatalf("Expected CapDrop MKNOD, got %v", c.CapDrop)
 		}
 	}
 }
+
+func TestCapListUnmarshalSliceAndString(t *testing.T) {
+	var cl *CapList
+	cap0, err := json.Marshal([]string{"CAP_SOMETHING"})
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err := json.Unmarshal(cap0, &cl); err != nil {
+		t.Fatal(err)
+	}
+
+	slice := cl.Slice()
+	if len(slice) != 1 {
+		t.Fatalf("expected 1 element after unmarshal: %q", slice)
+	}
+
+	if slice[0] != "CAP_SOMETHING" {
+		t.Fatalf("expected `CAP_SOMETHING`, got: %q", slice[0])
+	}
+
+	cap1, err := json.Marshal("CAP_SOMETHING")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err := json.Unmarshal(cap1, &cl); err != nil {
+		t.Fatal(err)
+	}
+
+	slice = cl.Slice()
+	if len(slice) != 1 {
+		t.Fatalf("expected 1 element after unmarshal: %q", slice)
+	}
+
+	if slice[0] != "CAP_SOMETHING" {
+		t.Fatalf("expected `CAP_SOMETHING`, got: %q", slice[0])
+	}
+}

+ 2 - 2
runconfig/parse.go

@@ -353,8 +353,8 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 		PidMode:         pidMode,
 		UTSMode:         utsMode,
 		Devices:         deviceMappings,
-		CapAdd:          flCapAdd.GetAll(),
-		CapDrop:         flCapDrop.GetAll(),
+		CapAdd:          NewCapList(flCapAdd.GetAll()),
+		CapDrop:         NewCapList(flCapDrop.GetAll()),
 		RestartPolicy:   restartPolicy,
 		SecurityOpt:     flSecurityOpt.GetAll(),
 		ReadonlyRootfs:  *flReadonlyRootfs,