github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/daemon/daemon_unix_test.go (about)

     1  // +build !windows
     2  
     3  package daemon
     4  
     5  import (
     6  	"errors"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	containertypes "github.com/docker/docker/api/types/container"
    13  	"github.com/docker/docker/container"
    14  	"github.com/docker/docker/daemon/config"
    15  	"github.com/docker/docker/pkg/idtools"
    16  	"github.com/docker/docker/volume"
    17  	"github.com/docker/docker/volume/drivers"
    18  	"github.com/docker/docker/volume/local"
    19  	"github.com/docker/docker/volume/store"
    20  	"github.com/stretchr/testify/require"
    21  )
    22  
    23  type fakeContainerGetter struct {
    24  	containers map[string]*container.Container
    25  }
    26  
    27  func (f *fakeContainerGetter) GetContainer(cid string) (*container.Container, error) {
    28  	container, ok := f.containers[cid]
    29  	if !ok {
    30  		return nil, errors.New("container not found")
    31  	}
    32  	return container, nil
    33  }
    34  
    35  // Unix test as uses settings which are not available on Windows
    36  func TestAdjustSharedNamespaceContainerName(t *testing.T) {
    37  	fakeID := "abcdef1234567890"
    38  	hostConfig := &containertypes.HostConfig{
    39  		IpcMode:     containertypes.IpcMode("container:base"),
    40  		PidMode:     containertypes.PidMode("container:base"),
    41  		NetworkMode: containertypes.NetworkMode("container:base"),
    42  	}
    43  	containerStore := &fakeContainerGetter{}
    44  	containerStore.containers = make(map[string]*container.Container)
    45  	containerStore.containers["base"] = &container.Container{
    46  		ID: fakeID,
    47  	}
    48  
    49  	adaptSharedNamespaceContainer(containerStore, hostConfig)
    50  	if hostConfig.IpcMode != containertypes.IpcMode("container:"+fakeID) {
    51  		t.Errorf("Expected IpcMode to be container:%s", fakeID)
    52  	}
    53  	if hostConfig.PidMode != containertypes.PidMode("container:"+fakeID) {
    54  		t.Errorf("Expected PidMode to be container:%s", fakeID)
    55  	}
    56  	if hostConfig.NetworkMode != containertypes.NetworkMode("container:"+fakeID) {
    57  		t.Errorf("Expected NetworkMode to be container:%s", fakeID)
    58  	}
    59  }
    60  
    61  // Unix test as uses settings which are not available on Windows
    62  func TestAdjustCPUShares(t *testing.T) {
    63  	tmp, err := ioutil.TempDir("", "docker-daemon-unix-test-")
    64  	if err != nil {
    65  		t.Fatal(err)
    66  	}
    67  	defer os.RemoveAll(tmp)
    68  	daemon := &Daemon{
    69  		repository: tmp,
    70  		root:       tmp,
    71  	}
    72  
    73  	hostConfig := &containertypes.HostConfig{
    74  		Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
    75  	}
    76  	daemon.adaptContainerSettings(hostConfig, true)
    77  	if hostConfig.CPUShares != linuxMinCPUShares {
    78  		t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares)
    79  	}
    80  
    81  	hostConfig.CPUShares = linuxMaxCPUShares + 1
    82  	daemon.adaptContainerSettings(hostConfig, true)
    83  	if hostConfig.CPUShares != linuxMaxCPUShares {
    84  		t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares)
    85  	}
    86  
    87  	hostConfig.CPUShares = 0
    88  	daemon.adaptContainerSettings(hostConfig, true)
    89  	if hostConfig.CPUShares != 0 {
    90  		t.Error("Expected CPUShares to be unchanged")
    91  	}
    92  
    93  	hostConfig.CPUShares = 1024
    94  	daemon.adaptContainerSettings(hostConfig, true)
    95  	if hostConfig.CPUShares != 1024 {
    96  		t.Error("Expected CPUShares to be unchanged")
    97  	}
    98  }
    99  
   100  // Unix test as uses settings which are not available on Windows
   101  func TestAdjustCPUSharesNoAdjustment(t *testing.T) {
   102  	tmp, err := ioutil.TempDir("", "docker-daemon-unix-test-")
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  	defer os.RemoveAll(tmp)
   107  	daemon := &Daemon{
   108  		repository: tmp,
   109  		root:       tmp,
   110  	}
   111  
   112  	hostConfig := &containertypes.HostConfig{
   113  		Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
   114  	}
   115  	daemon.adaptContainerSettings(hostConfig, false)
   116  	if hostConfig.CPUShares != linuxMinCPUShares-1 {
   117  		t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares-1)
   118  	}
   119  
   120  	hostConfig.CPUShares = linuxMaxCPUShares + 1
   121  	daemon.adaptContainerSettings(hostConfig, false)
   122  	if hostConfig.CPUShares != linuxMaxCPUShares+1 {
   123  		t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares+1)
   124  	}
   125  
   126  	hostConfig.CPUShares = 0
   127  	daemon.adaptContainerSettings(hostConfig, false)
   128  	if hostConfig.CPUShares != 0 {
   129  		t.Error("Expected CPUShares to be unchanged")
   130  	}
   131  
   132  	hostConfig.CPUShares = 1024
   133  	daemon.adaptContainerSettings(hostConfig, false)
   134  	if hostConfig.CPUShares != 1024 {
   135  		t.Error("Expected CPUShares to be unchanged")
   136  	}
   137  }
   138  
   139  // Unix test as uses settings which are not available on Windows
   140  func TestParseSecurityOptWithDeprecatedColon(t *testing.T) {
   141  	container := &container.Container{}
   142  	config := &containertypes.HostConfig{}
   143  
   144  	// test apparmor
   145  	config.SecurityOpt = []string{"apparmor=test_profile"}
   146  	if err := parseSecurityOpt(container, config); err != nil {
   147  		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
   148  	}
   149  	if container.AppArmorProfile != "test_profile" {
   150  		t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", container.AppArmorProfile)
   151  	}
   152  
   153  	// test seccomp
   154  	sp := "/path/to/seccomp_test.json"
   155  	config.SecurityOpt = []string{"seccomp=" + sp}
   156  	if err := parseSecurityOpt(container, config); err != nil {
   157  		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
   158  	}
   159  	if container.SeccompProfile != sp {
   160  		t.Fatalf("Unexpected AppArmorProfile, expected: %q, got %q", sp, container.SeccompProfile)
   161  	}
   162  
   163  	// test valid label
   164  	config.SecurityOpt = []string{"label=user:USER"}
   165  	if err := parseSecurityOpt(container, config); err != nil {
   166  		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
   167  	}
   168  
   169  	// test invalid label
   170  	config.SecurityOpt = []string{"label"}
   171  	if err := parseSecurityOpt(container, config); err == nil {
   172  		t.Fatal("Expected parseSecurityOpt error, got nil")
   173  	}
   174  
   175  	// test invalid opt
   176  	config.SecurityOpt = []string{"test"}
   177  	if err := parseSecurityOpt(container, config); err == nil {
   178  		t.Fatal("Expected parseSecurityOpt error, got nil")
   179  	}
   180  }
   181  
   182  func TestParseSecurityOpt(t *testing.T) {
   183  	container := &container.Container{}
   184  	config := &containertypes.HostConfig{}
   185  
   186  	// test apparmor
   187  	config.SecurityOpt = []string{"apparmor=test_profile"}
   188  	if err := parseSecurityOpt(container, config); err != nil {
   189  		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
   190  	}
   191  	if container.AppArmorProfile != "test_profile" {
   192  		t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", container.AppArmorProfile)
   193  	}
   194  
   195  	// test seccomp
   196  	sp := "/path/to/seccomp_test.json"
   197  	config.SecurityOpt = []string{"seccomp=" + sp}
   198  	if err := parseSecurityOpt(container, config); err != nil {
   199  		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
   200  	}
   201  	if container.SeccompProfile != sp {
   202  		t.Fatalf("Unexpected SeccompProfile, expected: %q, got %q", sp, container.SeccompProfile)
   203  	}
   204  
   205  	// test valid label
   206  	config.SecurityOpt = []string{"label=user:USER"}
   207  	if err := parseSecurityOpt(container, config); err != nil {
   208  		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
   209  	}
   210  
   211  	// test invalid label
   212  	config.SecurityOpt = []string{"label"}
   213  	if err := parseSecurityOpt(container, config); err == nil {
   214  		t.Fatal("Expected parseSecurityOpt error, got nil")
   215  	}
   216  
   217  	// test invalid opt
   218  	config.SecurityOpt = []string{"test"}
   219  	if err := parseSecurityOpt(container, config); err == nil {
   220  		t.Fatal("Expected parseSecurityOpt error, got nil")
   221  	}
   222  }
   223  
   224  func TestParseNNPSecurityOptions(t *testing.T) {
   225  	daemon := &Daemon{
   226  		configStore: &config.Config{NoNewPrivileges: true},
   227  	}
   228  	container := &container.Container{}
   229  	config := &containertypes.HostConfig{}
   230  
   231  	// test NNP when "daemon:true" and "no-new-privileges=false""
   232  	config.SecurityOpt = []string{"no-new-privileges=false"}
   233  
   234  	if err := daemon.parseSecurityOpt(container, config); err != nil {
   235  		t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
   236  	}
   237  	if container.NoNewPrivileges {
   238  		t.Fatalf("container.NoNewPrivileges should be FALSE: %v", container.NoNewPrivileges)
   239  	}
   240  
   241  	// test NNP when "daemon:false" and "no-new-privileges=true""
   242  	daemon.configStore.NoNewPrivileges = false
   243  	config.SecurityOpt = []string{"no-new-privileges=true"}
   244  
   245  	if err := daemon.parseSecurityOpt(container, config); err != nil {
   246  		t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
   247  	}
   248  	if !container.NoNewPrivileges {
   249  		t.Fatalf("container.NoNewPrivileges should be TRUE: %v", container.NoNewPrivileges)
   250  	}
   251  }
   252  
   253  func TestNetworkOptions(t *testing.T) {
   254  	daemon := &Daemon{}
   255  	dconfigCorrect := &config.Config{
   256  		CommonConfig: config.CommonConfig{
   257  			ClusterStore:     "consul://localhost:8500",
   258  			ClusterAdvertise: "192.168.0.1:8000",
   259  		},
   260  	}
   261  
   262  	if _, err := daemon.networkOptions(dconfigCorrect, nil, nil); err != nil {
   263  		t.Fatalf("Expect networkOptions success, got error: %v", err)
   264  	}
   265  
   266  	dconfigWrong := &config.Config{
   267  		CommonConfig: config.CommonConfig{
   268  			ClusterStore: "consul://localhost:8500://test://bbb",
   269  		},
   270  	}
   271  
   272  	if _, err := daemon.networkOptions(dconfigWrong, nil, nil); err == nil {
   273  		t.Fatal("Expected networkOptions error, got nil")
   274  	}
   275  }
   276  
   277  func TestMigratePre17Volumes(t *testing.T) {
   278  	rootDir, err := ioutil.TempDir("", "test-daemon-volumes")
   279  	if err != nil {
   280  		t.Fatal(err)
   281  	}
   282  	defer os.RemoveAll(rootDir)
   283  
   284  	volumeRoot := filepath.Join(rootDir, "volumes")
   285  	err = os.MkdirAll(volumeRoot, 0755)
   286  	if err != nil {
   287  		t.Fatal(err)
   288  	}
   289  
   290  	containerRoot := filepath.Join(rootDir, "containers")
   291  	cid := "1234"
   292  	err = os.MkdirAll(filepath.Join(containerRoot, cid), 0755)
   293  	require.NoError(t, err)
   294  
   295  	vid := "5678"
   296  	vfsPath := filepath.Join(rootDir, "vfs", "dir", vid)
   297  	err = os.MkdirAll(vfsPath, 0755)
   298  	require.NoError(t, err)
   299  
   300  	config := []byte(`
   301  		{
   302  			"ID": "` + cid + `",
   303  			"Volumes": {
   304  				"/foo": "` + vfsPath + `",
   305  				"/bar": "/foo",
   306  				"/quux": "/quux"
   307  			},
   308  			"VolumesRW": {
   309  				"/foo": true,
   310  				"/bar": true,
   311  				"/quux": false
   312  			}
   313  		}
   314  	`)
   315  
   316  	volStore, err := store.New(volumeRoot)
   317  	if err != nil {
   318  		t.Fatal(err)
   319  	}
   320  	drv, err := local.New(volumeRoot, idtools.IDPair{UID: 0, GID: 0})
   321  	if err != nil {
   322  		t.Fatal(err)
   323  	}
   324  	volumedrivers.Register(drv, volume.DefaultDriverName)
   325  
   326  	daemon := &Daemon{
   327  		root:       rootDir,
   328  		repository: containerRoot,
   329  		volumes:    volStore,
   330  	}
   331  	err = ioutil.WriteFile(filepath.Join(containerRoot, cid, "config.v2.json"), config, 600)
   332  	if err != nil {
   333  		t.Fatal(err)
   334  	}
   335  	c, err := daemon.load(cid)
   336  	if err != nil {
   337  		t.Fatal(err)
   338  	}
   339  	if err := daemon.verifyVolumesInfo(c); err != nil {
   340  		t.Fatal(err)
   341  	}
   342  
   343  	expected := map[string]volume.MountPoint{
   344  		"/foo":  {Destination: "/foo", RW: true, Name: vid},
   345  		"/bar":  {Source: "/foo", Destination: "/bar", RW: true},
   346  		"/quux": {Source: "/quux", Destination: "/quux", RW: false},
   347  	}
   348  	for id, mp := range c.MountPoints {
   349  		x, exists := expected[id]
   350  		if !exists {
   351  			t.Fatal("volume not migrated")
   352  		}
   353  		if mp.Source != x.Source || mp.Destination != x.Destination || mp.RW != x.RW || mp.Name != x.Name {
   354  			t.Fatalf("got unexpected mountpoint, expected: %+v, got: %+v", x, mp)
   355  		}
   356  	}
   357  }