daemon/daemon_unix_test.go
4785f1a7
 // +build !windows
ed39fbeb
 
e0af23dc
 package daemon
ed39fbeb
 
 import (
b6e5ea8e
 	"errors"
e0af23dc
 	"io/ioutil"
 	"os"
dc712b92
 	"path/filepath"
ed39fbeb
 	"testing"
 
91e197d6
 	containertypes "github.com/docker/docker/api/types/container"
42f2621b
 	"github.com/docker/docker/container"
db63f937
 	"github.com/docker/docker/daemon/config"
09cd96c5
 	"github.com/docker/docker/pkg/idtools"
dc712b92
 	"github.com/docker/docker/volume"
 	"github.com/docker/docker/volume/drivers"
 	"github.com/docker/docker/volume/local"
 	"github.com/docker/docker/volume/store"
09652bf8
 	"github.com/stretchr/testify/require"
ed39fbeb
 )
 
b6e5ea8e
 type fakeContainerGetter struct {
 	containers map[string]*container.Container
 }
 
 func (f *fakeContainerGetter) GetContainer(cid string) (*container.Container, error) {
 	container, ok := f.containers[cid]
 	if !ok {
 		return nil, errors.New("container not found")
 	}
 	return container, nil
 }
 
 // Unix test as uses settings which are not available on Windows
 func TestAdjustSharedNamespaceContainerName(t *testing.T) {
 	fakeID := "abcdef1234567890"
 	hostConfig := &containertypes.HostConfig{
 		IpcMode:     containertypes.IpcMode("container:base"),
 		PidMode:     containertypes.PidMode("container:base"),
 		NetworkMode: containertypes.NetworkMode("container:base"),
 	}
 	containerStore := &fakeContainerGetter{}
 	containerStore.containers = make(map[string]*container.Container)
 	containerStore.containers["base"] = &container.Container{
 		ID: fakeID,
 	}
 
 	adaptSharedNamespaceContainer(containerStore, hostConfig)
 	if hostConfig.IpcMode != containertypes.IpcMode("container:"+fakeID) {
 		t.Errorf("Expected IpcMode to be container:%s", fakeID)
 	}
 	if hostConfig.PidMode != containertypes.PidMode("container:"+fakeID) {
 		t.Errorf("Expected PidMode to be container:%s", fakeID)
 	}
 	if hostConfig.NetworkMode != containertypes.NetworkMode("container:"+fakeID) {
 		t.Errorf("Expected NetworkMode to be container:%s", fakeID)
 	}
 }
 
42f2621b
 // Unix test as uses settings which are not available on Windows
e0af23dc
 func TestAdjustCPUShares(t *testing.T) {
 	tmp, err := ioutil.TempDir("", "docker-daemon-unix-test-")
 	if err != nil {
 		t.Fatal(err)
 	}
 	defer os.RemoveAll(tmp)
 	daemon := &Daemon{
 		repository: tmp,
 		root:       tmp,
 	}
 
42f2621b
 	hostConfig := &containertypes.HostConfig{
 		Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
ed39fbeb
 	}
e0af23dc
 	daemon.adaptContainerSettings(hostConfig, true)
351f6b8e
 	if hostConfig.CPUShares != linuxMinCPUShares {
 		t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares)
ed39fbeb
 	}
 
351f6b8e
 	hostConfig.CPUShares = linuxMaxCPUShares + 1
e0af23dc
 	daemon.adaptContainerSettings(hostConfig, true)
351f6b8e
 	if hostConfig.CPUShares != linuxMaxCPUShares {
 		t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares)
ed39fbeb
 	}
 
5170a2c0
 	hostConfig.CPUShares = 0
e0af23dc
 	daemon.adaptContainerSettings(hostConfig, true)
5170a2c0
 	if hostConfig.CPUShares != 0 {
351f6b8e
 		t.Error("Expected CPUShares to be unchanged")
ed39fbeb
 	}
 
5170a2c0
 	hostConfig.CPUShares = 1024
e0af23dc
 	daemon.adaptContainerSettings(hostConfig, true)
5170a2c0
 	if hostConfig.CPUShares != 1024 {
351f6b8e
 		t.Error("Expected CPUShares to be unchanged")
ed39fbeb
 	}
 }
 
42f2621b
 // Unix test as uses settings which are not available on Windows
351f6b8e
 func TestAdjustCPUSharesNoAdjustment(t *testing.T) {
e0af23dc
 	tmp, err := ioutil.TempDir("", "docker-daemon-unix-test-")
 	if err != nil {
 		t.Fatal(err)
 	}
 	defer os.RemoveAll(tmp)
 	daemon := &Daemon{
 		repository: tmp,
 		root:       tmp,
 	}
 
42f2621b
 	hostConfig := &containertypes.HostConfig{
 		Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
ed39fbeb
 	}
e0af23dc
 	daemon.adaptContainerSettings(hostConfig, false)
351f6b8e
 	if hostConfig.CPUShares != linuxMinCPUShares-1 {
 		t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares-1)
ed39fbeb
 	}
 
351f6b8e
 	hostConfig.CPUShares = linuxMaxCPUShares + 1
e0af23dc
 	daemon.adaptContainerSettings(hostConfig, false)
351f6b8e
 	if hostConfig.CPUShares != linuxMaxCPUShares+1 {
 		t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares+1)
ed39fbeb
 	}
 
5170a2c0
 	hostConfig.CPUShares = 0
e0af23dc
 	daemon.adaptContainerSettings(hostConfig, false)
5170a2c0
 	if hostConfig.CPUShares != 0 {
351f6b8e
 		t.Error("Expected CPUShares to be unchanged")
ed39fbeb
 	}
 
5170a2c0
 	hostConfig.CPUShares = 1024
e0af23dc
 	daemon.adaptContainerSettings(hostConfig, false)
5170a2c0
 	if hostConfig.CPUShares != 1024 {
351f6b8e
 		t.Error("Expected CPUShares to be unchanged")
ed39fbeb
 	}
 }
42f2621b
 
 // Unix test as uses settings which are not available on Windows
cb9aeb04
 func TestParseSecurityOptWithDeprecatedColon(t *testing.T) {
42f2621b
 	container := &container.Container{}
 	config := &containertypes.HostConfig{}
 
 	// test apparmor
cb9aeb04
 	config.SecurityOpt = []string{"apparmor=test_profile"}
42f2621b
 	if err := parseSecurityOpt(container, config); err != nil {
 		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
 	}
 	if container.AppArmorProfile != "test_profile" {
 		t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", container.AppArmorProfile)
 	}
 
 	// test seccomp
 	sp := "/path/to/seccomp_test.json"
cb9aeb04
 	config.SecurityOpt = []string{"seccomp=" + sp}
42f2621b
 	if err := parseSecurityOpt(container, config); err != nil {
 		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
 	}
 	if container.SeccompProfile != sp {
 		t.Fatalf("Unexpected AppArmorProfile, expected: %q, got %q", sp, container.SeccompProfile)
 	}
 
 	// test valid label
cb9aeb04
 	config.SecurityOpt = []string{"label=user:USER"}
 	if err := parseSecurityOpt(container, config); err != nil {
 		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
 	}
 
 	// test invalid label
 	config.SecurityOpt = []string{"label"}
 	if err := parseSecurityOpt(container, config); err == nil {
 		t.Fatal("Expected parseSecurityOpt error, got nil")
 	}
 
 	// test invalid opt
 	config.SecurityOpt = []string{"test"}
 	if err := parseSecurityOpt(container, config); err == nil {
 		t.Fatal("Expected parseSecurityOpt error, got nil")
 	}
 }
 
 func TestParseSecurityOpt(t *testing.T) {
 	container := &container.Container{}
 	config := &containertypes.HostConfig{}
 
 	// test apparmor
 	config.SecurityOpt = []string{"apparmor=test_profile"}
 	if err := parseSecurityOpt(container, config); err != nil {
 		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
 	}
 	if container.AppArmorProfile != "test_profile" {
 		t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", container.AppArmorProfile)
 	}
 
 	// test seccomp
 	sp := "/path/to/seccomp_test.json"
 	config.SecurityOpt = []string{"seccomp=" + sp}
 	if err := parseSecurityOpt(container, config); err != nil {
 		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
 	}
 	if container.SeccompProfile != sp {
 		t.Fatalf("Unexpected SeccompProfile, expected: %q, got %q", sp, container.SeccompProfile)
 	}
 
 	// test valid label
 	config.SecurityOpt = []string{"label=user:USER"}
42f2621b
 	if err := parseSecurityOpt(container, config); err != nil {
 		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
 	}
 
 	// test invalid label
 	config.SecurityOpt = []string{"label"}
 	if err := parseSecurityOpt(container, config); err == nil {
 		t.Fatal("Expected parseSecurityOpt error, got nil")
 	}
 
 	// test invalid opt
 	config.SecurityOpt = []string{"test"}
 	if err := parseSecurityOpt(container, config); err == nil {
 		t.Fatal("Expected parseSecurityOpt error, got nil")
 	}
 }
 
d7fda019
 func TestParseNNPSecurityOptions(t *testing.T) {
 	daemon := &Daemon{
 		configStore: &config.Config{NoNewPrivileges: true},
 	}
 	container := &container.Container{}
 	config := &containertypes.HostConfig{}
 
 	// test NNP when "daemon:true" and "no-new-privileges=false""
 	config.SecurityOpt = []string{"no-new-privileges=false"}
 
 	if err := daemon.parseSecurityOpt(container, config); err != nil {
 		t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
 	}
 	if container.NoNewPrivileges {
 		t.Fatalf("container.NoNewPrivileges should be FALSE: %v", container.NoNewPrivileges)
 	}
 
 	// test NNP when "daemon:false" and "no-new-privileges=true""
 	daemon.configStore.NoNewPrivileges = false
 	config.SecurityOpt = []string{"no-new-privileges=true"}
 
 	if err := daemon.parseSecurityOpt(container, config); err != nil {
 		t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
 	}
 	if !container.NoNewPrivileges {
 		t.Fatalf("container.NoNewPrivileges should be TRUE: %v", container.NoNewPrivileges)
 	}
 }
 
42f2621b
 func TestNetworkOptions(t *testing.T) {
 	daemon := &Daemon{}
db63f937
 	dconfigCorrect := &config.Config{
 		CommonConfig: config.CommonConfig{
42f2621b
 			ClusterStore:     "consul://localhost:8500",
 			ClusterAdvertise: "192.168.0.1:8000",
 		},
 	}
 
a00940f0
 	if _, err := daemon.networkOptions(dconfigCorrect, nil, nil); err != nil {
2eee6133
 		t.Fatalf("Expect networkOptions success, got error: %v", err)
42f2621b
 	}
 
db63f937
 	dconfigWrong := &config.Config{
 		CommonConfig: config.CommonConfig{
42f2621b
 			ClusterStore: "consul://localhost:8500://test://bbb",
 		},
 	}
 
a00940f0
 	if _, err := daemon.networkOptions(dconfigWrong, nil, nil); err == nil {
2a8d6368
 		t.Fatal("Expected networkOptions error, got nil")
42f2621b
 	}
 }
dc712b92
 
 func TestMigratePre17Volumes(t *testing.T) {
 	rootDir, err := ioutil.TempDir("", "test-daemon-volumes")
 	if err != nil {
 		t.Fatal(err)
 	}
 	defer os.RemoveAll(rootDir)
 
 	volumeRoot := filepath.Join(rootDir, "volumes")
 	err = os.MkdirAll(volumeRoot, 0755)
 	if err != nil {
 		t.Fatal(err)
 	}
 
 	containerRoot := filepath.Join(rootDir, "containers")
 	cid := "1234"
 	err = os.MkdirAll(filepath.Join(containerRoot, cid), 0755)
09652bf8
 	require.NoError(t, err)
dc712b92
 
 	vid := "5678"
 	vfsPath := filepath.Join(rootDir, "vfs", "dir", vid)
 	err = os.MkdirAll(vfsPath, 0755)
09652bf8
 	require.NoError(t, err)
dc712b92
 
 	config := []byte(`
 		{
 			"ID": "` + cid + `",
 			"Volumes": {
 				"/foo": "` + vfsPath + `",
 				"/bar": "/foo",
 				"/quux": "/quux"
 			},
 			"VolumesRW": {
 				"/foo": true,
 				"/bar": true,
 				"/quux": false
 			}
 		}
 	`)
 
 	volStore, err := store.New(volumeRoot)
 	if err != nil {
 		t.Fatal(err)
 	}
09cd96c5
 	drv, err := local.New(volumeRoot, idtools.IDPair{UID: 0, GID: 0})
dc712b92
 	if err != nil {
 		t.Fatal(err)
 	}
 	volumedrivers.Register(drv, volume.DefaultDriverName)
 
edad5270
 	daemon := &Daemon{
76d96418
 		root:       rootDir,
 		repository: containerRoot,
 		volumes:    volStore,
edad5270
 	}
dc712b92
 	err = ioutil.WriteFile(filepath.Join(containerRoot, cid, "config.v2.json"), config, 600)
 	if err != nil {
 		t.Fatal(err)
 	}
 	c, err := daemon.load(cid)
 	if err != nil {
 		t.Fatal(err)
 	}
 	if err := daemon.verifyVolumesInfo(c); err != nil {
 		t.Fatal(err)
 	}
 
 	expected := map[string]volume.MountPoint{
 		"/foo":  {Destination: "/foo", RW: true, Name: vid},
 		"/bar":  {Source: "/foo", Destination: "/bar", RW: true},
 		"/quux": {Source: "/quux", Destination: "/quux", RW: false},
 	}
 	for id, mp := range c.MountPoints {
 		x, exists := expected[id]
 		if !exists {
 			t.Fatal("volume not migrated")
 		}
 		if mp.Source != x.Source || mp.Destination != x.Destination || mp.RW != x.RW || mp.Name != x.Name {
 			t.Fatalf("got unexpected mountpoint, expected: %+v, got: %+v", x, mp)
 		}
 	}
 }