daemon/reload_test.go
4f0d95fa
 package daemon // import "github.com/docker/docker/daemon"
75f5d63e
 
 import (
16670ed4
 	"os"
75f5d63e
 	"reflect"
67fdf574
 	"sort"
75f5d63e
 	"testing"
 	"time"
 
 	"github.com/docker/docker/daemon/config"
2b1a2b10
 	"github.com/docker/docker/daemon/images"
75f5d63e
 	"github.com/docker/docker/pkg/discovery"
 	_ "github.com/docker/docker/pkg/discovery/memory"
 	"github.com/docker/docker/registry"
a97e4579
 	"github.com/docker/libnetwork"
1741d669
 	"github.com/sirupsen/logrus"
9f0b3f56
 	"gotest.tools/v3/assert"
 	is "gotest.tools/v3/assert/cmp"
75f5d63e
 )
 
1741d669
 // muteLogs suppresses logs that are generated during the test
 func muteLogs() {
 	logrus.SetLevel(logrus.ErrorLevel)
 }
 
75f5d63e
 func TestDaemonReloadLabels(t *testing.T) {
0dab53ff
 	daemon := &Daemon{
 		configStore: &config.Config{
 			CommonConfig: config.CommonConfig{
 				Labels: []string{"foo:bar"},
 			},
75f5d63e
 		},
2b1a2b10
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
75f5d63e
 	}
1741d669
 	muteLogs()
75f5d63e
 
 	valuesSets := make(map[string]interface{})
 	valuesSets["labels"] = "foo:baz"
 	newConfig := &config.Config{
 		CommonConfig: config.CommonConfig{
 			Labels:    []string{"foo:baz"},
 			ValuesSet: valuesSets,
 		},
 	}
 
 	if err := daemon.Reload(newConfig); err != nil {
 		t.Fatal(err)
 	}
 
 	label := daemon.configStore.Labels[0]
 	if label != "foo:baz" {
 		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
 	}
 }
 
67fdf574
 func TestDaemonReloadAllowNondistributableArtifacts(t *testing.T) {
 	daemon := &Daemon{
0dab53ff
 		configStore:  &config.Config{},
2b1a2b10
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
67fdf574
 	}
1741d669
 	muteLogs()
67fdf574
 
5258297d
 	var err error
67fdf574
 	// Initialize daemon with some registries.
5258297d
 	daemon.RegistryService, err = registry.NewService(registry.ServiceOptions{
67fdf574
 		AllowNondistributableArtifacts: []string{
 			"127.0.0.0/8",
 			"10.10.1.11:5000",
 			"10.10.1.22:5000", // This will be removed during reload.
 			"docker1.com",
 			"docker2.com", // This will be removed during reload.
 		},
 	})
5258297d
 	if err != nil {
 		t.Fatal(err)
 	}
67fdf574
 
 	registries := []string{
 		"127.0.0.0/8",
 		"10.10.1.11:5000",
 		"10.10.1.33:5000", // This will be added during reload.
 		"docker1.com",
 		"docker3.com", // This will be added during reload.
 	}
 
 	newConfig := &config.Config{
 		CommonConfig: config.CommonConfig{
 			ServiceOptions: registry.ServiceOptions{
 				AllowNondistributableArtifacts: registries,
 			},
 			ValuesSet: map[string]interface{}{
 				"allow-nondistributable-artifacts": registries,
 			},
 		},
 	}
 
 	if err := daemon.Reload(newConfig); err != nil {
 		t.Fatal(err)
 	}
 
f23c00d8
 	var actual []string
67fdf574
 	serviceConfig := daemon.RegistryService.ServiceConfig()
 	for _, value := range serviceConfig.AllowNondistributableArtifactsCIDRs {
 		actual = append(actual, value.String())
 	}
f7f101d5
 	actual = append(actual, serviceConfig.AllowNondistributableArtifactsHostnames...)
67fdf574
 
 	sort.Strings(registries)
 	sort.Strings(actual)
6be0f709
 	assert.Check(t, is.DeepEqual(registries, actual))
67fdf574
 }
 
75f5d63e
 func TestDaemonReloadMirrors(t *testing.T) {
0dab53ff
 	daemon := &Daemon{
2b1a2b10
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
0dab53ff
 	}
1741d669
 	muteLogs()
 
5258297d
 	var err error
 	daemon.RegistryService, err = registry.NewService(registry.ServiceOptions{
75f5d63e
 		InsecureRegistries: []string{},
 		Mirrors: []string{
 			"https://mirror.test1.com",
 			"https://mirror.test2.com", // this will be removed when reloading
 			"https://mirror.test3.com", // this will be removed when reloading
 		},
 	})
5258297d
 	if err != nil {
 		t.Fatal(err)
 	}
75f5d63e
 
 	daemon.configStore = &config.Config{}
 
 	type pair struct {
 		valid   bool
 		mirrors []string
 		after   []string
 	}
 
 	loadMirrors := []pair{
 		{
 			valid:   false,
 			mirrors: []string{"10.10.1.11:5000"}, // this mirror is invalid
 			after:   []string{},
 		},
 		{
 			valid:   false,
 			mirrors: []string{"mirror.test1.com"}, // this mirror is invalid
 			after:   []string{},
 		},
 		{
 			valid:   false,
 			mirrors: []string{"10.10.1.11:5000", "mirror.test1.com"}, // mirrors are invalid
 			after:   []string{},
 		},
 		{
 			valid:   true,
 			mirrors: []string{"https://mirror.test1.com", "https://mirror.test4.com"},
 			after:   []string{"https://mirror.test1.com/", "https://mirror.test4.com/"},
 		},
 	}
 
 	for _, value := range loadMirrors {
 		valuesSets := make(map[string]interface{})
 		valuesSets["registry-mirrors"] = value.mirrors
 
 		newConfig := &config.Config{
 			CommonConfig: config.CommonConfig{
 				ServiceOptions: registry.ServiceOptions{
 					Mirrors: value.mirrors,
 				},
 				ValuesSet: valuesSets,
 			},
 		}
 
 		err := daemon.Reload(newConfig)
 		if !value.valid && err == nil {
 			// mirrors should be invalid, should be a non-nil error
 			t.Fatalf("Expected daemon reload error with invalid mirrors: %s, while get nil", value.mirrors)
 		}
 
 		if value.valid {
 			if err != nil {
 				// mirrors should be valid, should be no error
 				t.Fatal(err)
 			}
 			registryService := daemon.RegistryService.ServiceConfig()
 
 			if len(registryService.Mirrors) != len(value.after) {
 				t.Fatalf("Expected %d daemon mirrors %s while get %d with %s",
 					len(value.after),
 					value.after,
 					len(registryService.Mirrors),
 					registryService.Mirrors)
 			}
 
 			dataMap := map[string]struct{}{}
 
 			for _, mirror := range registryService.Mirrors {
 				if _, exist := dataMap[mirror]; !exist {
 					dataMap[mirror] = struct{}{}
 				}
 			}
 
 			for _, address := range value.after {
 				if _, exist := dataMap[address]; !exist {
 					t.Fatalf("Expected %s in daemon mirrors, while get none", address)
 				}
 			}
 		}
 	}
 }
 
 func TestDaemonReloadInsecureRegistries(t *testing.T) {
0dab53ff
 	daemon := &Daemon{
2b1a2b10
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
0dab53ff
 	}
1741d669
 	muteLogs()
 
5258297d
 	var err error
75f5d63e
 	// initialize daemon with existing insecure registries: "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000"
5258297d
 	daemon.RegistryService, err = registry.NewService(registry.ServiceOptions{
75f5d63e
 		InsecureRegistries: []string{
 			"127.0.0.0/8",
 			"10.10.1.11:5000",
 			"10.10.1.22:5000", // this will be removed when reloading
 			"docker1.com",
 			"docker2.com", // this will be removed when reloading
 		},
 	})
5258297d
 	if err != nil {
 		t.Fatal(err)
 	}
75f5d63e
 
 	daemon.configStore = &config.Config{}
 
 	insecureRegistries := []string{
 		"127.0.0.0/8",     // this will be kept
 		"10.10.1.11:5000", // this will be kept
 		"10.10.1.33:5000", // this will be newly added
 		"docker1.com",     // this will be kept
 		"docker3.com",     // this will be newly added
 	}
 
 	valuesSets := make(map[string]interface{})
 	valuesSets["insecure-registries"] = insecureRegistries
 
 	newConfig := &config.Config{
 		CommonConfig: config.CommonConfig{
 			ServiceOptions: registry.ServiceOptions{
 				InsecureRegistries: insecureRegistries,
 			},
 			ValuesSet: valuesSets,
 		},
 	}
 
 	if err := daemon.Reload(newConfig); err != nil {
 		t.Fatal(err)
 	}
 
 	// After Reload, daemon.RegistryService will be changed which is useful
 	// for registry communication in daemon.
 	registries := daemon.RegistryService.ServiceConfig()
 
 	// After Reload(), newConfig has come to registries.InsecureRegistryCIDRs and registries.IndexConfigs in daemon.
 	// Then collect registries.InsecureRegistryCIDRs in dataMap.
 	// When collecting, we need to convert CIDRS into string as a key,
 	// while the times of key appears as value.
 	dataMap := map[string]int{}
 	for _, value := range registries.InsecureRegistryCIDRs {
 		if _, ok := dataMap[value.String()]; !ok {
 			dataMap[value.String()] = 1
 		} else {
 			dataMap[value.String()]++
 		}
 	}
 
 	for _, value := range registries.IndexConfigs {
 		if _, ok := dataMap[value.Name]; !ok {
 			dataMap[value.Name] = 1
 		} else {
 			dataMap[value.Name]++
 		}
 	}
 
 	// Finally compare dataMap with the original insecureRegistries.
 	// Each value in insecureRegistries should appear in daemon's insecure registries,
 	// and each can only appear exactly ONCE.
 	for _, r := range insecureRegistries {
 		if value, ok := dataMap[r]; !ok {
 			t.Fatalf("Expected daemon insecure registry %s, got none", r)
 		} else if value != 1 {
 			t.Fatalf("Expected only 1 daemon insecure registry %s, got %d", r, value)
 		}
 	}
 
 	// assert if "10.10.1.22:5000" is removed when reloading
 	if value, ok := dataMap["10.10.1.22:5000"]; ok {
 		t.Fatalf("Expected no insecure registry of 10.10.1.22:5000, got %d", value)
 	}
 
 	// assert if "docker2.com" is removed when reloading
 	if value, ok := dataMap["docker2.com"]; ok {
 		t.Fatalf("Expected no insecure registry of docker2.com, got %d", value)
 	}
 }
 
 func TestDaemonReloadNotAffectOthers(t *testing.T) {
0dab53ff
 	daemon := &Daemon{
2b1a2b10
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
0dab53ff
 	}
1741d669
 	muteLogs()
 
75f5d63e
 	daemon.configStore = &config.Config{
 		CommonConfig: config.CommonConfig{
 			Labels: []string{"foo:bar"},
 			Debug:  true,
 		},
 	}
 
 	valuesSets := make(map[string]interface{})
 	valuesSets["labels"] = "foo:baz"
 	newConfig := &config.Config{
 		CommonConfig: config.CommonConfig{
 			Labels:    []string{"foo:baz"},
 			ValuesSet: valuesSets,
 		},
 	}
 
 	if err := daemon.Reload(newConfig); err != nil {
 		t.Fatal(err)
 	}
 
 	label := daemon.configStore.Labels[0]
 	if label != "foo:baz" {
 		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
 	}
 	debug := daemon.configStore.Debug
 	if !debug {
2a8d6368
 		t.Fatal("Expected debug 'enabled', got 'disabled'")
75f5d63e
 	}
 }
 
 func TestDaemonDiscoveryReload(t *testing.T) {
0dab53ff
 	daemon := &Daemon{
2b1a2b10
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
0dab53ff
 	}
1741d669
 	muteLogs()
75f5d63e
 	daemon.configStore = &config.Config{
 		CommonConfig: config.CommonConfig{
 			ClusterStore:     "memory://127.0.0.1",
 			ClusterAdvertise: "127.0.0.1:3333",
 		},
 	}
 
 	if err := daemon.initDiscovery(daemon.configStore); err != nil {
 		t.Fatal(err)
 	}
 
 	expected := discovery.Entries{
 		&discovery.Entry{Host: "127.0.0.1", Port: "3333"},
 	}
 
 	select {
 	case <-time.After(10 * time.Second):
 		t.Fatal("timeout waiting for discovery")
 	case <-daemon.discoveryWatcher.ReadyCh():
 	}
 
 	stopCh := make(chan struct{})
 	defer close(stopCh)
 	ch, errCh := daemon.discoveryWatcher.Watch(stopCh)
 
 	select {
 	case <-time.After(1 * time.Second):
 		t.Fatal("failed to get discovery advertisements in time")
 	case e := <-ch:
 		if !reflect.DeepEqual(e, expected) {
 			t.Fatalf("expected %v, got %v\n", expected, e)
 		}
 	case e := <-errCh:
 		t.Fatal(e)
 	}
 
 	valuesSets := make(map[string]interface{})
 	valuesSets["cluster-store"] = "memory://127.0.0.1:2222"
 	valuesSets["cluster-advertise"] = "127.0.0.1:5555"
 	newConfig := &config.Config{
 		CommonConfig: config.CommonConfig{
 			ClusterStore:     "memory://127.0.0.1:2222",
 			ClusterAdvertise: "127.0.0.1:5555",
 			ValuesSet:        valuesSets,
 		},
 	}
 
 	expected = discovery.Entries{
 		&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
 	}
 
 	if err := daemon.Reload(newConfig); err != nil {
 		t.Fatal(err)
 	}
 
 	select {
 	case <-time.After(10 * time.Second):
 		t.Fatal("timeout waiting for discovery")
 	case <-daemon.discoveryWatcher.ReadyCh():
 	}
 
 	ch, errCh = daemon.discoveryWatcher.Watch(stopCh)
 
 	select {
 	case <-time.After(1 * time.Second):
 		t.Fatal("failed to get discovery advertisements in time")
 	case e := <-ch:
 		if !reflect.DeepEqual(e, expected) {
 			t.Fatalf("expected %v, got %v\n", expected, e)
 		}
 	case e := <-errCh:
 		t.Fatal(e)
 	}
 }
 
 func TestDaemonDiscoveryReloadFromEmptyDiscovery(t *testing.T) {
0dab53ff
 	daemon := &Daemon{
2b1a2b10
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
0dab53ff
 	}
75f5d63e
 	daemon.configStore = &config.Config{}
1741d669
 	muteLogs()
75f5d63e
 
 	valuesSet := make(map[string]interface{})
 	valuesSet["cluster-store"] = "memory://127.0.0.1:2222"
 	valuesSet["cluster-advertise"] = "127.0.0.1:5555"
 	newConfig := &config.Config{
 		CommonConfig: config.CommonConfig{
 			ClusterStore:     "memory://127.0.0.1:2222",
 			ClusterAdvertise: "127.0.0.1:5555",
 			ValuesSet:        valuesSet,
 		},
 	}
 
 	expected := discovery.Entries{
 		&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
 	}
 
 	if err := daemon.Reload(newConfig); err != nil {
 		t.Fatal(err)
 	}
 
 	select {
 	case <-time.After(10 * time.Second):
 		t.Fatal("timeout waiting for discovery")
 	case <-daemon.discoveryWatcher.ReadyCh():
 	}
 
 	stopCh := make(chan struct{})
 	defer close(stopCh)
 	ch, errCh := daemon.discoveryWatcher.Watch(stopCh)
 
 	select {
 	case <-time.After(1 * time.Second):
 		t.Fatal("failed to get discovery advertisements in time")
 	case e := <-ch:
 		if !reflect.DeepEqual(e, expected) {
 			t.Fatalf("expected %v, got %v\n", expected, e)
 		}
 	case e := <-errCh:
 		t.Fatal(e)
 	}
 }
 
 func TestDaemonDiscoveryReloadOnlyClusterAdvertise(t *testing.T) {
0dab53ff
 	daemon := &Daemon{
2b1a2b10
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
0dab53ff
 	}
75f5d63e
 	daemon.configStore = &config.Config{
 		CommonConfig: config.CommonConfig{
 			ClusterStore: "memory://127.0.0.1",
 		},
 	}
 	valuesSets := make(map[string]interface{})
 	valuesSets["cluster-advertise"] = "127.0.0.1:5555"
 	newConfig := &config.Config{
 		CommonConfig: config.CommonConfig{
 			ClusterAdvertise: "127.0.0.1:5555",
 			ValuesSet:        valuesSets,
 		},
 	}
 	expected := discovery.Entries{
 		&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
 	}
 
 	if err := daemon.Reload(newConfig); err != nil {
 		t.Fatal(err)
 	}
 
 	select {
 	case <-daemon.discoveryWatcher.ReadyCh():
 	case <-time.After(10 * time.Second):
 		t.Fatal("Timeout waiting for discovery")
 	}
 	stopCh := make(chan struct{})
 	defer close(stopCh)
 	ch, errCh := daemon.discoveryWatcher.Watch(stopCh)
 
 	select {
 	case <-time.After(1 * time.Second):
 		t.Fatal("failed to get discovery advertisements in time")
 	case e := <-ch:
 		if !reflect.DeepEqual(e, expected) {
 			t.Fatalf("expected %v, got %v\n", expected, e)
 		}
 	case e := <-errCh:
 		t.Fatal(e)
 	}
 }
a97e4579
 
 func TestDaemonReloadNetworkDiagnosticPort(t *testing.T) {
16670ed4
 	if os.Getuid() != 0 {
 		t.Skip("root required")
 	}
0dab53ff
 	daemon := &Daemon{
2b1a2b10
 		imageService: images.NewImageService(images.ImageServiceConfig{}),
0dab53ff
 	}
a97e4579
 	daemon.configStore = &config.Config{}
 
 	valuesSet := make(map[string]interface{})
 	valuesSet["network-diagnostic-port"] = 2000
 	enableConfig := &config.Config{
 		CommonConfig: config.CommonConfig{
 			NetworkDiagnosticPort: 2000,
 			ValuesSet:             valuesSet,
 		},
 	}
 	disableConfig := &config.Config{
 		CommonConfig: config.CommonConfig{},
 	}
 
 	netOptions, err := daemon.networkOptions(enableConfig, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
 	controller, err := libnetwork.New(netOptions...)
 	if err != nil {
 		t.Fatal(err)
 	}
 	daemon.netController = controller
 
 	// Enable/Disable the server for some iterations
 	for i := 0; i < 10; i++ {
 		enableConfig.CommonConfig.NetworkDiagnosticPort++
 		if err := daemon.Reload(enableConfig); err != nil {
 			t.Fatal(err)
 		}
ec865472
 		// Check that the diagnostic is enabled
 		if !daemon.netController.IsDiagnosticEnabled() {
 			t.Fatalf("diagnostic should be enable")
a97e4579
 		}
 
 		// Reload
 		if err := daemon.Reload(disableConfig); err != nil {
 			t.Fatal(err)
 		}
ec865472
 		// Check that the diagnostic is disabled
 		if daemon.netController.IsDiagnosticEnabled() {
 			t.Fatalf("diagnostic should be disable")
a97e4579
 		}
 	}
 
 	enableConfig.CommonConfig.NetworkDiagnosticPort++
 	// 2 times the enable should not create problems
 	if err := daemon.Reload(enableConfig); err != nil {
 		t.Fatal(err)
 	}
ec865472
 	// Check that the diagnostic is enabled
 	if !daemon.netController.IsDiagnosticEnabled() {
 		t.Fatalf("diagnostic should be enable")
a97e4579
 	}
 
 	// Check that another reload does not cause issues
 	if err := daemon.Reload(enableConfig); err != nil {
 		t.Fatal(err)
 	}
ec865472
 	// Check that the diagnostic is enable
 	if !daemon.netController.IsDiagnosticEnabled() {
 		t.Fatalf("diagnostic should be enable")
a97e4579
 	}
 
 }