4f0d95fa6e
Signed-off-by: Daniel Nephin <dnephin@docker.com>
357 lines
10 KiB
Go
357 lines
10 KiB
Go
// +build !windows
|
|
|
|
package daemon // import "github.com/docker/docker/daemon"
|
|
|
|
import (
|
|
"errors"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
containertypes "github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/container"
|
|
"github.com/docker/docker/daemon/config"
|
|
"github.com/docker/docker/pkg/idtools"
|
|
"github.com/docker/docker/volume"
|
|
"github.com/docker/docker/volume/drivers"
|
|
"github.com/docker/docker/volume/local"
|
|
"github.com/docker/docker/volume/store"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
// Unix test as uses settings which are not available on Windows
|
|
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,
|
|
}
|
|
|
|
hostConfig := &containertypes.HostConfig{
|
|
Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
|
|
}
|
|
daemon.adaptContainerSettings(hostConfig, true)
|
|
if hostConfig.CPUShares != linuxMinCPUShares {
|
|
t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares)
|
|
}
|
|
|
|
hostConfig.CPUShares = linuxMaxCPUShares + 1
|
|
daemon.adaptContainerSettings(hostConfig, true)
|
|
if hostConfig.CPUShares != linuxMaxCPUShares {
|
|
t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares)
|
|
}
|
|
|
|
hostConfig.CPUShares = 0
|
|
daemon.adaptContainerSettings(hostConfig, true)
|
|
if hostConfig.CPUShares != 0 {
|
|
t.Error("Expected CPUShares to be unchanged")
|
|
}
|
|
|
|
hostConfig.CPUShares = 1024
|
|
daemon.adaptContainerSettings(hostConfig, true)
|
|
if hostConfig.CPUShares != 1024 {
|
|
t.Error("Expected CPUShares to be unchanged")
|
|
}
|
|
}
|
|
|
|
// Unix test as uses settings which are not available on Windows
|
|
func TestAdjustCPUSharesNoAdjustment(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,
|
|
}
|
|
|
|
hostConfig := &containertypes.HostConfig{
|
|
Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
|
|
}
|
|
daemon.adaptContainerSettings(hostConfig, false)
|
|
if hostConfig.CPUShares != linuxMinCPUShares-1 {
|
|
t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares-1)
|
|
}
|
|
|
|
hostConfig.CPUShares = linuxMaxCPUShares + 1
|
|
daemon.adaptContainerSettings(hostConfig, false)
|
|
if hostConfig.CPUShares != linuxMaxCPUShares+1 {
|
|
t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares+1)
|
|
}
|
|
|
|
hostConfig.CPUShares = 0
|
|
daemon.adaptContainerSettings(hostConfig, false)
|
|
if hostConfig.CPUShares != 0 {
|
|
t.Error("Expected CPUShares to be unchanged")
|
|
}
|
|
|
|
hostConfig.CPUShares = 1024
|
|
daemon.adaptContainerSettings(hostConfig, false)
|
|
if hostConfig.CPUShares != 1024 {
|
|
t.Error("Expected CPUShares to be unchanged")
|
|
}
|
|
}
|
|
|
|
// Unix test as uses settings which are not available on Windows
|
|
func TestParseSecurityOptWithDeprecatedColon(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 AppArmorProfile, expected: %q, got %q", sp, container.SeccompProfile)
|
|
}
|
|
|
|
// test valid label
|
|
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"}
|
|
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 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)
|
|
}
|
|
}
|
|
|
|
func TestNetworkOptions(t *testing.T) {
|
|
daemon := &Daemon{}
|
|
dconfigCorrect := &config.Config{
|
|
CommonConfig: config.CommonConfig{
|
|
ClusterStore: "consul://localhost:8500",
|
|
ClusterAdvertise: "192.168.0.1:8000",
|
|
},
|
|
}
|
|
|
|
if _, err := daemon.networkOptions(dconfigCorrect, nil, nil); err != nil {
|
|
t.Fatalf("Expect networkOptions success, got error: %v", err)
|
|
}
|
|
|
|
dconfigWrong := &config.Config{
|
|
CommonConfig: config.CommonConfig{
|
|
ClusterStore: "consul://localhost:8500://test://bbb",
|
|
},
|
|
}
|
|
|
|
if _, err := daemon.networkOptions(dconfigWrong, nil, nil); err == nil {
|
|
t.Fatal("Expected networkOptions error, got nil")
|
|
}
|
|
}
|
|
|
|
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)
|
|
require.NoError(t, err)
|
|
|
|
vid := "5678"
|
|
vfsPath := filepath.Join(rootDir, "vfs", "dir", vid)
|
|
err = os.MkdirAll(vfsPath, 0755)
|
|
require.NoError(t, err)
|
|
|
|
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)
|
|
}
|
|
drv, err := local.New(volumeRoot, idtools.IDPair{UID: 0, GID: 0})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
volumedrivers.Register(drv, volume.DefaultDriverName)
|
|
|
|
daemon := &Daemon{
|
|
root: rootDir,
|
|
repository: containerRoot,
|
|
volumes: volStore,
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
}
|