Browse code

rewrite reload code

Signed-off-by: allencloud <allen.sun@daocloud.io>

allencloud authored on 2017/01/07 23:30:25
Showing 7 changed files
... ...
@@ -6,7 +6,6 @@
6 6
 package daemon
7 7
 
8 8
 import (
9
-	"encoding/json"
10 9
 	"fmt"
11 10
 	"io/ioutil"
12 11
 	"net"
... ...
@@ -996,213 +995,6 @@ func (daemon *Daemon) initDiscovery(conf *config.Config) error {
996 996
 	return nil
997 997
 }
998 998
 
999
-// Reload reads configuration changes and modifies the
1000
-// daemon according to those changes.
1001
-// These are the settings that Reload changes:
1002
-// - Daemon labels
1003
-// - Daemon debug log level
1004
-// - Insecure registries
1005
-// - Registry mirrors
1006
-// - Daemon max concurrent downloads
1007
-// - Daemon max concurrent uploads
1008
-// - Cluster discovery (reconfigure and restart)
1009
-// - Daemon live restore
1010
-// - Daemon shutdown timeout (in seconds).
1011
-func (daemon *Daemon) Reload(conf *config.Config) (err error) {
1012
-
1013
-	daemon.configStore.Lock()
1014
-
1015
-	attributes := daemon.platformReload(conf)
1016
-
1017
-	defer func() {
1018
-		// we're unlocking here, because
1019
-		// LogDaemonEventWithAttributes() -> SystemInfo() -> GetAllRuntimes()
1020
-		// holds that lock too.
1021
-		daemon.configStore.Unlock()
1022
-		if err == nil {
1023
-			daemon.LogDaemonEventWithAttributes("reload", attributes)
1024
-		}
1025
-	}()
1026
-
1027
-	if err := daemon.reloadClusterDiscovery(conf); err != nil {
1028
-		return err
1029
-	}
1030
-
1031
-	if conf.IsValueSet("labels") {
1032
-		daemon.configStore.Labels = conf.Labels
1033
-	}
1034
-	if conf.IsValueSet("debug") {
1035
-		daemon.configStore.Debug = conf.Debug
1036
-	}
1037
-	if conf.IsValueSet("insecure-registries") {
1038
-		daemon.configStore.InsecureRegistries = conf.InsecureRegistries
1039
-		if err := daemon.RegistryService.LoadInsecureRegistries(conf.InsecureRegistries); err != nil {
1040
-			return err
1041
-		}
1042
-	}
1043
-
1044
-	if conf.IsValueSet("registry-mirrors") {
1045
-		daemon.configStore.Mirrors = conf.Mirrors
1046
-		if err := daemon.RegistryService.LoadMirrors(conf.Mirrors); err != nil {
1047
-			return err
1048
-		}
1049
-	}
1050
-
1051
-	if conf.IsValueSet("live-restore") {
1052
-		daemon.configStore.LiveRestoreEnabled = conf.LiveRestoreEnabled
1053
-		if err := daemon.containerdRemote.UpdateOptions(libcontainerd.WithLiveRestore(conf.LiveRestoreEnabled)); err != nil {
1054
-			return err
1055
-		}
1056
-	}
1057
-
1058
-	// If no value is set for max-concurrent-downloads we assume it is the default value
1059
-	// We always "reset" as the cost is lightweight and easy to maintain.
1060
-	if conf.IsValueSet("max-concurrent-downloads") && conf.MaxConcurrentDownloads != nil {
1061
-		*daemon.configStore.MaxConcurrentDownloads = *conf.MaxConcurrentDownloads
1062
-	} else {
1063
-		maxConcurrentDownloads := config.DefaultMaxConcurrentDownloads
1064
-		daemon.configStore.MaxConcurrentDownloads = &maxConcurrentDownloads
1065
-	}
1066
-	logrus.Debugf("Reset Max Concurrent Downloads: %d", *daemon.configStore.MaxConcurrentDownloads)
1067
-	if daemon.downloadManager != nil {
1068
-		daemon.downloadManager.SetConcurrency(*daemon.configStore.MaxConcurrentDownloads)
1069
-	}
1070
-
1071
-	// If no value is set for max-concurrent-upload we assume it is the default value
1072
-	// We always "reset" as the cost is lightweight and easy to maintain.
1073
-	if conf.IsValueSet("max-concurrent-uploads") && conf.MaxConcurrentUploads != nil {
1074
-		*daemon.configStore.MaxConcurrentUploads = *conf.MaxConcurrentUploads
1075
-	} else {
1076
-		maxConcurrentUploads := config.DefaultMaxConcurrentUploads
1077
-		daemon.configStore.MaxConcurrentUploads = &maxConcurrentUploads
1078
-	}
1079
-	logrus.Debugf("Reset Max Concurrent Uploads: %d", *daemon.configStore.MaxConcurrentUploads)
1080
-	if daemon.uploadManager != nil {
1081
-		daemon.uploadManager.SetConcurrency(*daemon.configStore.MaxConcurrentUploads)
1082
-	}
1083
-
1084
-	if conf.IsValueSet("shutdown-timeout") {
1085
-		daemon.configStore.ShutdownTimeout = conf.ShutdownTimeout
1086
-		logrus.Debugf("Reset Shutdown Timeout: %d", daemon.configStore.ShutdownTimeout)
1087
-	}
1088
-
1089
-	// We emit daemon reload event here with updatable configurations
1090
-	attributes["debug"] = fmt.Sprintf("%t", daemon.configStore.Debug)
1091
-	attributes["live-restore"] = fmt.Sprintf("%t", daemon.configStore.LiveRestoreEnabled)
1092
-
1093
-	if daemon.configStore.InsecureRegistries != nil {
1094
-		insecureRegistries, err := json.Marshal(daemon.configStore.InsecureRegistries)
1095
-		if err != nil {
1096
-			return err
1097
-		}
1098
-		attributes["insecure-registries"] = string(insecureRegistries)
1099
-	} else {
1100
-		attributes["insecure-registries"] = "[]"
1101
-	}
1102
-
1103
-	if daemon.configStore.Mirrors != nil {
1104
-		mirrors, err := json.Marshal(daemon.configStore.Mirrors)
1105
-		if err != nil {
1106
-			return err
1107
-		}
1108
-		attributes["registry-mirrors"] = string(mirrors)
1109
-	} else {
1110
-		attributes["registry-mirrors"] = "[]"
1111
-	}
1112
-
1113
-	attributes["cluster-store"] = daemon.configStore.ClusterStore
1114
-	if daemon.configStore.ClusterOpts != nil {
1115
-		opts, err := json.Marshal(daemon.configStore.ClusterOpts)
1116
-		if err != nil {
1117
-			return err
1118
-		}
1119
-		attributes["cluster-store-opts"] = string(opts)
1120
-	} else {
1121
-		attributes["cluster-store-opts"] = "{}"
1122
-	}
1123
-	attributes["cluster-advertise"] = daemon.configStore.ClusterAdvertise
1124
-
1125
-	if daemon.configStore.Labels != nil {
1126
-		labels, err := json.Marshal(daemon.configStore.Labels)
1127
-		if err != nil {
1128
-			return err
1129
-		}
1130
-		attributes["labels"] = string(labels)
1131
-	} else {
1132
-		attributes["labels"] = "[]"
1133
-	}
1134
-
1135
-	attributes["max-concurrent-downloads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentDownloads)
1136
-	attributes["max-concurrent-uploads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentUploads)
1137
-	attributes["shutdown-timeout"] = fmt.Sprintf("%d", daemon.configStore.ShutdownTimeout)
1138
-
1139
-	return nil
1140
-}
1141
-
1142
-func (daemon *Daemon) reloadClusterDiscovery(conf *config.Config) error {
1143
-	var err error
1144
-	newAdvertise := daemon.configStore.ClusterAdvertise
1145
-	newClusterStore := daemon.configStore.ClusterStore
1146
-	if conf.IsValueSet("cluster-advertise") {
1147
-		if conf.IsValueSet("cluster-store") {
1148
-			newClusterStore = conf.ClusterStore
1149
-		}
1150
-		newAdvertise, err = config.ParseClusterAdvertiseSettings(newClusterStore, conf.ClusterAdvertise)
1151
-		if err != nil && err != discovery.ErrDiscoveryDisabled {
1152
-			return err
1153
-		}
1154
-	}
1155
-
1156
-	if daemon.clusterProvider != nil {
1157
-		if err := conf.IsSwarmCompatible(); err != nil {
1158
-			return err
1159
-		}
1160
-	}
1161
-
1162
-	// check discovery modifications
1163
-	if !config.ModifiedDiscoverySettings(daemon.configStore, newClusterStore, newAdvertise, conf.ClusterOpts) {
1164
-		return nil
1165
-	}
1166
-
1167
-	// enable discovery for the first time if it was not previously enabled
1168
-	if daemon.discoveryWatcher == nil {
1169
-		discoveryWatcher, err := discovery.Init(newClusterStore, newAdvertise, conf.ClusterOpts)
1170
-		if err != nil {
1171
-			return fmt.Errorf("discovery initialization failed (%v)", err)
1172
-		}
1173
-		daemon.discoveryWatcher = discoveryWatcher
1174
-	} else {
1175
-		if err == discovery.ErrDiscoveryDisabled {
1176
-			// disable discovery if it was previously enabled and it's disabled now
1177
-			daemon.discoveryWatcher.Stop()
1178
-		} else {
1179
-			// reload discovery
1180
-			if err = daemon.discoveryWatcher.Reload(conf.ClusterStore, newAdvertise, conf.ClusterOpts); err != nil {
1181
-				return err
1182
-			}
1183
-		}
1184
-	}
1185
-
1186
-	daemon.configStore.ClusterStore = newClusterStore
1187
-	daemon.configStore.ClusterOpts = conf.ClusterOpts
1188
-	daemon.configStore.ClusterAdvertise = newAdvertise
1189
-
1190
-	if daemon.netController == nil {
1191
-		return nil
1192
-	}
1193
-	netOptions, err := daemon.networkOptions(daemon.configStore, daemon.PluginStore, nil)
1194
-	if err != nil {
1195
-		logrus.WithError(err).Warnf("failed to get options with network controller")
1196
-		return nil
1197
-	}
1198
-	err = daemon.netController.ReloadConfiguration(netOptions...)
1199
-	if err != nil {
1200
-		logrus.Warnf("Failed to reload configuration with network controller: %v", err)
1201
-	}
1202
-
1203
-	return nil
1204
-}
1205
-
1206 999
 func isBridgeNetworkDisabled(conf *config.Config) bool {
1207 1000
 	return conf.BridgeConfig.Iface == config.DisableNetworkBridge
1208 1001
 }
... ...
@@ -301,9 +301,9 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
301 301
 	return warnings, nil
302 302
 }
303 303
 
304
-// platformReload updates configuration with platform specific options
305
-func (daemon *Daemon) platformReload(config *Config) map[string]string {
306
-	return map[string]string{}
304
+// reloadPlatform updates configuration with platform specific options
305
+// and updates the passed attributes
306
+func (daemon *Daemon) reloadPlatform(config *Config, attributes map[string]string) {
307 307
 }
308 308
 
309 309
 // verifyDaemonSettings performs validation of daemon config struct
... ...
@@ -6,18 +6,13 @@ import (
6 6
 	"io/ioutil"
7 7
 	"os"
8 8
 	"path/filepath"
9
-	"reflect"
10 9
 	"testing"
11
-	"time"
12 10
 
13 11
 	containertypes "github.com/docker/docker/api/types/container"
14 12
 	"github.com/docker/docker/container"
15
-	"github.com/docker/docker/daemon/config"
16
-	"github.com/docker/docker/pkg/discovery"
17 13
 	_ "github.com/docker/docker/pkg/discovery/memory"
18 14
 	"github.com/docker/docker/pkg/registrar"
19 15
 	"github.com/docker/docker/pkg/truncindex"
20
-	"github.com/docker/docker/registry"
21 16
 	"github.com/docker/docker/volume"
22 17
 	volumedrivers "github.com/docker/docker/volume/drivers"
23 18
 	"github.com/docker/docker/volume/local"
... ...
@@ -314,409 +309,3 @@ func TestMerge(t *testing.T) {
314 314
 		}
315 315
 	}
316 316
 }
317
-
318
-func TestDaemonReloadLabels(t *testing.T) {
319
-	daemon := &Daemon{}
320
-	daemon.configStore = &config.Config{
321
-		CommonConfig: config.CommonConfig{
322
-			Labels: []string{"foo:bar"},
323
-		},
324
-	}
325
-
326
-	valuesSets := make(map[string]interface{})
327
-	valuesSets["labels"] = "foo:baz"
328
-	newConfig := &config.Config{
329
-		CommonConfig: config.CommonConfig{
330
-			Labels:    []string{"foo:baz"},
331
-			ValuesSet: valuesSets,
332
-		},
333
-	}
334
-
335
-	if err := daemon.Reload(newConfig); err != nil {
336
-		t.Fatal(err)
337
-	}
338
-
339
-	label := daemon.configStore.Labels[0]
340
-	if label != "foo:baz" {
341
-		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
342
-	}
343
-}
344
-
345
-func TestDaemonReloadMirrors(t *testing.T) {
346
-	daemon := &Daemon{}
347
-
348
-	daemon.RegistryService = registry.NewService(registry.ServiceOptions{
349
-		InsecureRegistries: []string{},
350
-		Mirrors: []string{
351
-			"https://mirror.test1.com",
352
-			"https://mirror.test2.com", // this will be removed when reloading
353
-			"https://mirror.test3.com", // this will be removed when reloading
354
-		},
355
-	})
356
-
357
-	daemon.configStore = &config.Config{}
358
-
359
-	type pair struct {
360
-		valid   bool
361
-		mirrors []string
362
-		after   []string
363
-	}
364
-
365
-	loadMirrors := []pair{
366
-		{
367
-			valid:   false,
368
-			mirrors: []string{"10.10.1.11:5000"}, // this mirror is invalid
369
-			after:   []string{},
370
-		},
371
-		{
372
-			valid:   false,
373
-			mirrors: []string{"mirror.test1.com"}, // this mirror is invalid
374
-			after:   []string{},
375
-		},
376
-		{
377
-			valid:   false,
378
-			mirrors: []string{"10.10.1.11:5000", "mirror.test1.com"}, // mirrors are invalid
379
-			after:   []string{},
380
-		},
381
-		{
382
-			valid:   true,
383
-			mirrors: []string{"https://mirror.test1.com", "https://mirror.test4.com"},
384
-			after:   []string{"https://mirror.test1.com/", "https://mirror.test4.com/"},
385
-		},
386
-	}
387
-
388
-	for _, value := range loadMirrors {
389
-		valuesSets := make(map[string]interface{})
390
-		valuesSets["registry-mirrors"] = value.mirrors
391
-
392
-		newConfig := &config.Config{
393
-			CommonConfig: config.CommonConfig{
394
-				ServiceOptions: registry.ServiceOptions{
395
-					Mirrors: value.mirrors,
396
-				},
397
-				ValuesSet: valuesSets,
398
-			},
399
-		}
400
-
401
-		err := daemon.Reload(newConfig)
402
-		if !value.valid && err == nil {
403
-			// mirrors should be invalid, should be a non-nil error
404
-			t.Fatalf("Expected daemon reload error with invalid mirrors: %s, while get nil", value.mirrors)
405
-		}
406
-
407
-		if value.valid {
408
-			if err != nil {
409
-				// mirrors should be valid, should be no error
410
-				t.Fatal(err)
411
-			}
412
-			registryService := daemon.RegistryService.ServiceConfig()
413
-
414
-			if len(registryService.Mirrors) != len(value.after) {
415
-				t.Fatalf("Expected %d daemon mirrors %s while get %d with %s",
416
-					len(value.after),
417
-					value.after,
418
-					len(registryService.Mirrors),
419
-					registryService.Mirrors)
420
-			}
421
-
422
-			dataMap := map[string]struct{}{}
423
-
424
-			for _, mirror := range registryService.Mirrors {
425
-				if _, exist := dataMap[mirror]; !exist {
426
-					dataMap[mirror] = struct{}{}
427
-				}
428
-			}
429
-
430
-			for _, address := range value.after {
431
-				if _, exist := dataMap[address]; !exist {
432
-					t.Fatalf("Expected %s in daemon mirrors, while get none", address)
433
-				}
434
-			}
435
-		}
436
-	}
437
-}
438
-
439
-func TestDaemonReloadInsecureRegistries(t *testing.T) {
440
-	daemon := &Daemon{}
441
-	// initialize daemon with existing insecure registries: "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000"
442
-	daemon.RegistryService = registry.NewService(registry.ServiceOptions{
443
-		InsecureRegistries: []string{
444
-			"127.0.0.0/8",
445
-			"10.10.1.11:5000",
446
-			"10.10.1.22:5000", // this will be removed when reloading
447
-			"docker1.com",
448
-			"docker2.com", // this will be removed when reloading
449
-		},
450
-	})
451
-
452
-	daemon.configStore = &config.Config{}
453
-
454
-	insecureRegistries := []string{
455
-		"127.0.0.0/8",     // this will be kept
456
-		"10.10.1.11:5000", // this will be kept
457
-		"10.10.1.33:5000", // this will be newly added
458
-		"docker1.com",     // this will be kept
459
-		"docker3.com",     // this will be newly added
460
-	}
461
-
462
-	valuesSets := make(map[string]interface{})
463
-	valuesSets["insecure-registries"] = insecureRegistries
464
-
465
-	newConfig := &config.Config{
466
-		CommonConfig: config.CommonConfig{
467
-			ServiceOptions: registry.ServiceOptions{
468
-				InsecureRegistries: insecureRegistries,
469
-			},
470
-			ValuesSet: valuesSets,
471
-		},
472
-	}
473
-
474
-	if err := daemon.Reload(newConfig); err != nil {
475
-		t.Fatal(err)
476
-	}
477
-
478
-	// After Reload, daemon.RegistryService will be changed which is useful
479
-	// for registry communication in daemon.
480
-	registries := daemon.RegistryService.ServiceConfig()
481
-
482
-	// After Reload(), newConfig has come to registries.InsecureRegistryCIDRs and registries.IndexConfigs in daemon.
483
-	// Then collect registries.InsecureRegistryCIDRs in dataMap.
484
-	// When collecting, we need to convert CIDRS into string as a key,
485
-	// while the times of key appears as value.
486
-	dataMap := map[string]int{}
487
-	for _, value := range registries.InsecureRegistryCIDRs {
488
-		if _, ok := dataMap[value.String()]; !ok {
489
-			dataMap[value.String()] = 1
490
-		} else {
491
-			dataMap[value.String()]++
492
-		}
493
-	}
494
-
495
-	for _, value := range registries.IndexConfigs {
496
-		if _, ok := dataMap[value.Name]; !ok {
497
-			dataMap[value.Name] = 1
498
-		} else {
499
-			dataMap[value.Name]++
500
-		}
501
-	}
502
-
503
-	// Finally compare dataMap with the original insecureRegistries.
504
-	// Each value in insecureRegistries should appear in daemon's insecure registries,
505
-	// and each can only appear exactly ONCE.
506
-	for _, r := range insecureRegistries {
507
-		if value, ok := dataMap[r]; !ok {
508
-			t.Fatalf("Expected daemon insecure registry %s, got none", r)
509
-		} else if value != 1 {
510
-			t.Fatalf("Expected only 1 daemon insecure registry %s, got %d", r, value)
511
-		}
512
-	}
513
-
514
-	// assert if "10.10.1.22:5000" is removed when reloading
515
-	if value, ok := dataMap["10.10.1.22:5000"]; ok {
516
-		t.Fatalf("Expected no insecure registry of 10.10.1.22:5000, got %d", value)
517
-	}
518
-
519
-	// assert if "docker2.com" is removed when reloading
520
-	if value, ok := dataMap["docker2.com"]; ok {
521
-		t.Fatalf("Expected no insecure registry of docker2.com, got %d", value)
522
-	}
523
-}
524
-
525
-func TestDaemonReloadNotAffectOthers(t *testing.T) {
526
-	daemon := &Daemon{}
527
-	daemon.configStore = &config.Config{
528
-		CommonConfig: config.CommonConfig{
529
-			Labels: []string{"foo:bar"},
530
-			Debug:  true,
531
-		},
532
-	}
533
-
534
-	valuesSets := make(map[string]interface{})
535
-	valuesSets["labels"] = "foo:baz"
536
-	newConfig := &config.Config{
537
-		CommonConfig: config.CommonConfig{
538
-			Labels:    []string{"foo:baz"},
539
-			ValuesSet: valuesSets,
540
-		},
541
-	}
542
-
543
-	if err := daemon.Reload(newConfig); err != nil {
544
-		t.Fatal(err)
545
-	}
546
-
547
-	label := daemon.configStore.Labels[0]
548
-	if label != "foo:baz" {
549
-		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
550
-	}
551
-	debug := daemon.configStore.Debug
552
-	if !debug {
553
-		t.Fatalf("Expected debug 'enabled', got 'disabled'")
554
-	}
555
-}
556
-
557
-func TestDaemonDiscoveryReload(t *testing.T) {
558
-	daemon := &Daemon{}
559
-	daemon.configStore = &config.Config{
560
-		CommonConfig: config.CommonConfig{
561
-			ClusterStore:     "memory://127.0.0.1",
562
-			ClusterAdvertise: "127.0.0.1:3333",
563
-		},
564
-	}
565
-
566
-	if err := daemon.initDiscovery(daemon.configStore); err != nil {
567
-		t.Fatal(err)
568
-	}
569
-
570
-	expected := discovery.Entries{
571
-		&discovery.Entry{Host: "127.0.0.1", Port: "3333"},
572
-	}
573
-
574
-	select {
575
-	case <-time.After(10 * time.Second):
576
-		t.Fatal("timeout waiting for discovery")
577
-	case <-daemon.discoveryWatcher.ReadyCh():
578
-	}
579
-
580
-	stopCh := make(chan struct{})
581
-	defer close(stopCh)
582
-	ch, errCh := daemon.discoveryWatcher.Watch(stopCh)
583
-
584
-	select {
585
-	case <-time.After(1 * time.Second):
586
-		t.Fatal("failed to get discovery advertisements in time")
587
-	case e := <-ch:
588
-		if !reflect.DeepEqual(e, expected) {
589
-			t.Fatalf("expected %v, got %v\n", expected, e)
590
-		}
591
-	case e := <-errCh:
592
-		t.Fatal(e)
593
-	}
594
-
595
-	valuesSets := make(map[string]interface{})
596
-	valuesSets["cluster-store"] = "memory://127.0.0.1:2222"
597
-	valuesSets["cluster-advertise"] = "127.0.0.1:5555"
598
-	newConfig := &config.Config{
599
-		CommonConfig: config.CommonConfig{
600
-			ClusterStore:     "memory://127.0.0.1:2222",
601
-			ClusterAdvertise: "127.0.0.1:5555",
602
-			ValuesSet:        valuesSets,
603
-		},
604
-	}
605
-
606
-	expected = discovery.Entries{
607
-		&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
608
-	}
609
-
610
-	if err := daemon.Reload(newConfig); err != nil {
611
-		t.Fatal(err)
612
-	}
613
-
614
-	select {
615
-	case <-time.After(10 * time.Second):
616
-		t.Fatal("timeout waiting for discovery")
617
-	case <-daemon.discoveryWatcher.ReadyCh():
618
-	}
619
-
620
-	ch, errCh = daemon.discoveryWatcher.Watch(stopCh)
621
-
622
-	select {
623
-	case <-time.After(1 * time.Second):
624
-		t.Fatal("failed to get discovery advertisements in time")
625
-	case e := <-ch:
626
-		if !reflect.DeepEqual(e, expected) {
627
-			t.Fatalf("expected %v, got %v\n", expected, e)
628
-		}
629
-	case e := <-errCh:
630
-		t.Fatal(e)
631
-	}
632
-}
633
-
634
-func TestDaemonDiscoveryReloadFromEmptyDiscovery(t *testing.T) {
635
-	daemon := &Daemon{}
636
-	daemon.configStore = &config.Config{}
637
-
638
-	valuesSet := make(map[string]interface{})
639
-	valuesSet["cluster-store"] = "memory://127.0.0.1:2222"
640
-	valuesSet["cluster-advertise"] = "127.0.0.1:5555"
641
-	newConfig := &config.Config{
642
-		CommonConfig: config.CommonConfig{
643
-			ClusterStore:     "memory://127.0.0.1:2222",
644
-			ClusterAdvertise: "127.0.0.1:5555",
645
-			ValuesSet:        valuesSet,
646
-		},
647
-	}
648
-
649
-	expected := discovery.Entries{
650
-		&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
651
-	}
652
-
653
-	if err := daemon.Reload(newConfig); err != nil {
654
-		t.Fatal(err)
655
-	}
656
-
657
-	select {
658
-	case <-time.After(10 * time.Second):
659
-		t.Fatal("timeout waiting for discovery")
660
-	case <-daemon.discoveryWatcher.ReadyCh():
661
-	}
662
-
663
-	stopCh := make(chan struct{})
664
-	defer close(stopCh)
665
-	ch, errCh := daemon.discoveryWatcher.Watch(stopCh)
666
-
667
-	select {
668
-	case <-time.After(1 * time.Second):
669
-		t.Fatal("failed to get discovery advertisements in time")
670
-	case e := <-ch:
671
-		if !reflect.DeepEqual(e, expected) {
672
-			t.Fatalf("expected %v, got %v\n", expected, e)
673
-		}
674
-	case e := <-errCh:
675
-		t.Fatal(e)
676
-	}
677
-}
678
-
679
-func TestDaemonDiscoveryReloadOnlyClusterAdvertise(t *testing.T) {
680
-	daemon := &Daemon{}
681
-	daemon.configStore = &config.Config{
682
-		CommonConfig: config.CommonConfig{
683
-			ClusterStore: "memory://127.0.0.1",
684
-		},
685
-	}
686
-	valuesSets := make(map[string]interface{})
687
-	valuesSets["cluster-advertise"] = "127.0.0.1:5555"
688
-	newConfig := &config.Config{
689
-		CommonConfig: config.CommonConfig{
690
-			ClusterAdvertise: "127.0.0.1:5555",
691
-			ValuesSet:        valuesSets,
692
-		},
693
-	}
694
-	expected := discovery.Entries{
695
-		&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
696
-	}
697
-
698
-	if err := daemon.Reload(newConfig); err != nil {
699
-		t.Fatal(err)
700
-	}
701
-
702
-	select {
703
-	case <-daemon.discoveryWatcher.ReadyCh():
704
-	case <-time.After(10 * time.Second):
705
-		t.Fatal("Timeout waiting for discovery")
706
-	}
707
-	stopCh := make(chan struct{})
708
-	defer close(stopCh)
709
-	ch, errCh := daemon.discoveryWatcher.Watch(stopCh)
710
-
711
-	select {
712
-	case <-time.After(1 * time.Second):
713
-		t.Fatal("failed to get discovery advertisements in time")
714
-	case e := <-ch:
715
-		if !reflect.DeepEqual(e, expected) {
716
-			t.Fatalf("expected %v, got %v\n", expected, e)
717
-		}
718
-	case e := <-errCh:
719
-		t.Fatal(e)
720
-	}
721
-
722
-}
... ...
@@ -568,8 +568,9 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
568 568
 	return warnings, nil
569 569
 }
570 570
 
571
-// platformReload updates configuration with platform specific options
572
-func (daemon *Daemon) platformReload(conf *config.Config) map[string]string {
571
+// reloadPlatform updates configuration with platform specific options
572
+// and updates the passed attributes
573
+func (daemon *Daemon) reloadPlatform(conf *config.Config, attributes map[string]string) {
573 574
 	if conf.IsValueSet("runtimes") {
574 575
 		daemon.configStore.Runtimes = conf.Runtimes
575 576
 		// Always set the default one
... ...
@@ -593,11 +594,9 @@ func (daemon *Daemon) platformReload(conf *config.Config) map[string]string {
593 593
 		runtimeList.WriteString(fmt.Sprintf("%s:%s", name, rt))
594 594
 	}
595 595
 
596
-	return map[string]string{
597
-		"runtimes":         runtimeList.String(),
598
-		"default-runtime":  daemon.configStore.DefaultRuntime,
599
-		"default-shm-size": fmt.Sprintf("%d", daemon.configStore.ShmSize),
600
-	}
596
+	attributes["runtimes"] = runtimeList.String()
597
+	attributes["default-runtime"] = daemon.configStore.DefaultRuntime
598
+	attributes["default-shm-size"] = fmt.Sprintf("%d", daemon.configStore.ShmSize)
601 599
 }
602 600
 
603 601
 // verifyDaemonSettings performs validation of daemon config struct
... ...
@@ -210,9 +210,9 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
210 210
 	return warnings, err
211 211
 }
212 212
 
213
-// platformReload updates configuration with platform specific options
214
-func (daemon *Daemon) platformReload(config *config.Config) map[string]string {
215
-	return map[string]string{}
213
+// reloadPlatform updates configuration with platform specific options
214
+// and updates the passed attributes
215
+func (daemon *Daemon) reloadPlatform(config *config.Config, attributes map[string]string) {
216 216
 }
217 217
 
218 218
 // verifyDaemonSettings performs validation of daemon config struct
219 219
new file mode 100644
... ...
@@ -0,0 +1,284 @@
0
+package daemon
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+
6
+	"github.com/Sirupsen/logrus"
7
+	"github.com/docker/docker/daemon/config"
8
+	"github.com/docker/docker/daemon/discovery"
9
+	"github.com/docker/docker/libcontainerd"
10
+)
11
+
12
+// Reload reads configuration changes and modifies the
13
+// daemon according to those changes.
14
+// These are the settings that Reload changes:
15
+// - Platform runtime
16
+// - Daemon debug log level
17
+// - Daemon max concurrent downloads
18
+// - Daemon max concurrent uploads
19
+// - Daemon shutdown timeout (in seconds)
20
+// - Cluster discovery (reconfigure and restart)
21
+// - Daemon labels
22
+// - Insecure registries
23
+// - Registry mirrors
24
+// - Daemon live restore
25
+func (daemon *Daemon) Reload(conf *config.Config) (err error) {
26
+	daemon.configStore.Lock()
27
+	attributes := map[string]string{}
28
+
29
+	defer func() {
30
+		// we're unlocking here, because
31
+		// LogDaemonEventWithAttributes() -> SystemInfo() -> GetAllRuntimes()
32
+		// holds that lock too.
33
+		daemon.configStore.Unlock()
34
+		if err == nil {
35
+			daemon.LogDaemonEventWithAttributes("reload", attributes)
36
+		}
37
+	}()
38
+
39
+	daemon.reloadPlatform(conf, attributes)
40
+	daemon.reloadDebug(conf, attributes)
41
+	daemon.reloadMaxConcurrentDowloadsAndUploads(conf, attributes)
42
+	daemon.reloadShutdownTimeout(conf, attributes)
43
+
44
+	if err := daemon.reloadClusterDiscovery(conf, attributes); err != nil {
45
+		return err
46
+	}
47
+	if err := daemon.reloadLabels(conf, attributes); err != nil {
48
+		return err
49
+	}
50
+	if err := daemon.reloadInsecureRegistries(conf, attributes); err != nil {
51
+		return err
52
+	}
53
+	if err := daemon.reloadRegistryMirrors(conf, attributes); err != nil {
54
+		return err
55
+	}
56
+	if err := daemon.reloadLiveRestore(conf, attributes); err != nil {
57
+		return err
58
+	}
59
+	return nil
60
+}
61
+
62
+// reloadDebug updates configuration with Debug option
63
+// and updates the passed attributes
64
+func (daemon *Daemon) reloadDebug(conf *config.Config, attributes map[string]string) {
65
+	// update corresponding configuration
66
+	if conf.IsValueSet("debug") {
67
+		daemon.configStore.Debug = conf.Debug
68
+	}
69
+	// prepare reload event attributes with updatable configurations
70
+	attributes["debug"] = fmt.Sprintf("%t", daemon.configStore.Debug)
71
+}
72
+
73
+// reloadMaxConcurrentDowloadsAndUploads updates configuration with max concurrent
74
+// download and upload options and updates the passed attributes
75
+func (daemon *Daemon) reloadMaxConcurrentDowloadsAndUploads(conf *config.Config, attributes map[string]string) {
76
+	// If no value is set for max-concurrent-downloads we assume it is the default value
77
+	// We always "reset" as the cost is lightweight and easy to maintain.
78
+	if conf.IsValueSet("max-concurrent-downloads") && conf.MaxConcurrentDownloads != nil {
79
+		*daemon.configStore.MaxConcurrentDownloads = *conf.MaxConcurrentDownloads
80
+	} else {
81
+		maxConcurrentDownloads := config.DefaultMaxConcurrentDownloads
82
+		daemon.configStore.MaxConcurrentDownloads = &maxConcurrentDownloads
83
+	}
84
+	logrus.Debugf("Reset Max Concurrent Downloads: %d", *daemon.configStore.MaxConcurrentDownloads)
85
+	if daemon.downloadManager != nil {
86
+		daemon.downloadManager.SetConcurrency(*daemon.configStore.MaxConcurrentDownloads)
87
+	}
88
+
89
+	// prepare reload event attributes with updatable configurations
90
+	attributes["max-concurrent-downloads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentDownloads)
91
+
92
+	// If no value is set for max-concurrent-upload we assume it is the default value
93
+	// We always "reset" as the cost is lightweight and easy to maintain.
94
+	if conf.IsValueSet("max-concurrent-uploads") && conf.MaxConcurrentUploads != nil {
95
+		*daemon.configStore.MaxConcurrentUploads = *conf.MaxConcurrentUploads
96
+	} else {
97
+		maxConcurrentUploads := config.DefaultMaxConcurrentUploads
98
+		daemon.configStore.MaxConcurrentUploads = &maxConcurrentUploads
99
+	}
100
+	logrus.Debugf("Reset Max Concurrent Uploads: %d", *daemon.configStore.MaxConcurrentUploads)
101
+	if daemon.uploadManager != nil {
102
+		daemon.uploadManager.SetConcurrency(*daemon.configStore.MaxConcurrentUploads)
103
+	}
104
+
105
+	// prepare reload event attributes with updatable configurations
106
+	attributes["max-concurrent-uploads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentUploads)
107
+}
108
+
109
+// reloadShutdownTimeout updates configuration with daemon shutdown timeout option
110
+// and updates the passed attributes
111
+func (daemon *Daemon) reloadShutdownTimeout(conf *config.Config, attributes map[string]string) {
112
+	// update corresponding configuration
113
+	if conf.IsValueSet("shutdown-timeout") {
114
+		daemon.configStore.ShutdownTimeout = conf.ShutdownTimeout
115
+		logrus.Debugf("Reset Shutdown Timeout: %d", daemon.configStore.ShutdownTimeout)
116
+	}
117
+
118
+	// prepare reload event attributes with updatable configurations
119
+	attributes["shutdown-timeout"] = fmt.Sprintf("%d", daemon.configStore.ShutdownTimeout)
120
+}
121
+
122
+// reloadClusterDiscovery updates configuration with cluster discovery options
123
+// and updates the passed attributes
124
+func (daemon *Daemon) reloadClusterDiscovery(conf *config.Config, attributes map[string]string) (err error) {
125
+	defer func() {
126
+		// prepare reload event attributes with updatable configurations
127
+		attributes["cluster-store"] = conf.ClusterStore
128
+		attributes["cluster-advertise"] = conf.ClusterAdvertise
129
+
130
+		attributes["cluster-store-opts"] = "{}"
131
+		if daemon.configStore.ClusterOpts != nil {
132
+			opts, err2 := json.Marshal(conf.ClusterOpts)
133
+			if err != nil {
134
+				err = err2
135
+			}
136
+			attributes["cluster-store-opts"] = string(opts)
137
+		}
138
+	}()
139
+
140
+	newAdvertise := conf.ClusterAdvertise
141
+	newClusterStore := daemon.configStore.ClusterStore
142
+	if conf.IsValueSet("cluster-advertise") {
143
+		if conf.IsValueSet("cluster-store") {
144
+			newClusterStore = conf.ClusterStore
145
+		}
146
+		newAdvertise, err = config.ParseClusterAdvertiseSettings(newClusterStore, conf.ClusterAdvertise)
147
+		if err != nil && err != discovery.ErrDiscoveryDisabled {
148
+			return err
149
+		}
150
+	}
151
+
152
+	if daemon.clusterProvider != nil {
153
+		if err := conf.IsSwarmCompatible(); err != nil {
154
+			return err
155
+		}
156
+	}
157
+
158
+	// check discovery modifications
159
+	if !config.ModifiedDiscoverySettings(daemon.configStore, newClusterStore, newAdvertise, conf.ClusterOpts) {
160
+		return nil
161
+	}
162
+
163
+	// enable discovery for the first time if it was not previously enabled
164
+	if daemon.discoveryWatcher == nil {
165
+		discoveryWatcher, err := discovery.Init(newClusterStore, newAdvertise, conf.ClusterOpts)
166
+		if err != nil {
167
+			return fmt.Errorf("failed to initialize discovery: %v", err)
168
+		}
169
+		daemon.discoveryWatcher = discoveryWatcher
170
+	} else if err == discovery.ErrDiscoveryDisabled {
171
+		// disable discovery if it was previously enabled and it's disabled now
172
+		daemon.discoveryWatcher.Stop()
173
+	} else if err = daemon.discoveryWatcher.Reload(conf.ClusterStore, newAdvertise, conf.ClusterOpts); err != nil {
174
+		// reload discovery
175
+		return err
176
+	}
177
+
178
+	daemon.configStore.ClusterStore = newClusterStore
179
+	daemon.configStore.ClusterOpts = conf.ClusterOpts
180
+	daemon.configStore.ClusterAdvertise = newAdvertise
181
+
182
+	if daemon.netController == nil {
183
+		return nil
184
+	}
185
+	netOptions, err := daemon.networkOptions(daemon.configStore, daemon.PluginStore, nil)
186
+	if err != nil {
187
+		logrus.WithError(err).Warnf("failed to get options with network controller")
188
+		return nil
189
+	}
190
+	err = daemon.netController.ReloadConfiguration(netOptions...)
191
+	if err != nil {
192
+		logrus.Warnf("Failed to reload configuration with network controller: %v", err)
193
+	}
194
+	return nil
195
+}
196
+
197
+// reloadLabels updates configuration with engine labels
198
+// and updates the passed attributes
199
+func (daemon *Daemon) reloadLabels(conf *config.Config, attributes map[string]string) error {
200
+	// update corresponding configuration
201
+	if conf.IsValueSet("labels") {
202
+		daemon.configStore.Labels = conf.Labels
203
+	}
204
+
205
+	// prepare reload event attributes with updatable configurations
206
+	if daemon.configStore.Labels != nil {
207
+		labels, err := json.Marshal(daemon.configStore.Labels)
208
+		if err != nil {
209
+			return err
210
+		}
211
+		attributes["labels"] = string(labels)
212
+	} else {
213
+		attributes["labels"] = "[]"
214
+	}
215
+
216
+	return nil
217
+}
218
+
219
+// reloadInsecureRegistries updates configuration with insecure registry option
220
+// and updates the passed attributes
221
+func (daemon *Daemon) reloadInsecureRegistries(conf *config.Config, attributes map[string]string) error {
222
+	// update corresponding configuration
223
+	if conf.IsValueSet("insecure-registries") {
224
+		daemon.configStore.InsecureRegistries = conf.InsecureRegistries
225
+		if err := daemon.RegistryService.LoadInsecureRegistries(conf.InsecureRegistries); err != nil {
226
+			return err
227
+		}
228
+	}
229
+
230
+	// prepare reload event attributes with updatable configurations
231
+	if daemon.configStore.InsecureRegistries != nil {
232
+		insecureRegistries, err := json.Marshal(daemon.configStore.InsecureRegistries)
233
+		if err != nil {
234
+			return err
235
+		}
236
+		attributes["insecure-registries"] = string(insecureRegistries)
237
+	} else {
238
+		attributes["insecure-registries"] = "[]"
239
+	}
240
+
241
+	return nil
242
+}
243
+
244
+// reloadRegistryMirrors updates configuration with registry mirror options
245
+// and updates the passed attributes
246
+func (daemon *Daemon) reloadRegistryMirrors(conf *config.Config, attributes map[string]string) error {
247
+	// update corresponding configuration
248
+	if conf.IsValueSet("registry-mirrors") {
249
+		daemon.configStore.Mirrors = conf.Mirrors
250
+		if err := daemon.RegistryService.LoadMirrors(conf.Mirrors); err != nil {
251
+			return err
252
+		}
253
+	}
254
+
255
+	// prepare reload event attributes with updatable configurations
256
+	if daemon.configStore.Mirrors != nil {
257
+		mirrors, err := json.Marshal(daemon.configStore.Mirrors)
258
+		if err != nil {
259
+			return err
260
+		}
261
+		attributes["registry-mirrors"] = string(mirrors)
262
+	} else {
263
+		attributes["registry-mirrors"] = "[]"
264
+	}
265
+
266
+	return nil
267
+}
268
+
269
+// reloadLiveRestore updates configuration with live retore option
270
+// and updates the passed attributes
271
+func (daemon *Daemon) reloadLiveRestore(conf *config.Config, attributes map[string]string) error {
272
+	// update corresponding configuration
273
+	if conf.IsValueSet("live-restore") {
274
+		daemon.configStore.LiveRestoreEnabled = conf.LiveRestoreEnabled
275
+		if err := daemon.containerdRemote.UpdateOptions(libcontainerd.WithLiveRestore(conf.LiveRestoreEnabled)); err != nil {
276
+			return err
277
+		}
278
+	}
279
+
280
+	// prepare reload event attributes with updatable configurations
281
+	attributes["live-restore"] = fmt.Sprintf("%t", daemon.configStore.LiveRestoreEnabled)
282
+	return nil
283
+}
0 284
new file mode 100644
... ...
@@ -0,0 +1,418 @@
0
+// +build !solaris
1
+
2
+package daemon
3
+
4
+import (
5
+	"reflect"
6
+	"testing"
7
+	"time"
8
+
9
+	"github.com/docker/docker/daemon/config"
10
+	"github.com/docker/docker/pkg/discovery"
11
+	_ "github.com/docker/docker/pkg/discovery/memory"
12
+	"github.com/docker/docker/registry"
13
+)
14
+
15
+func TestDaemonReloadLabels(t *testing.T) {
16
+	daemon := &Daemon{}
17
+	daemon.configStore = &config.Config{
18
+		CommonConfig: config.CommonConfig{
19
+			Labels: []string{"foo:bar"},
20
+		},
21
+	}
22
+
23
+	valuesSets := make(map[string]interface{})
24
+	valuesSets["labels"] = "foo:baz"
25
+	newConfig := &config.Config{
26
+		CommonConfig: config.CommonConfig{
27
+			Labels:    []string{"foo:baz"},
28
+			ValuesSet: valuesSets,
29
+		},
30
+	}
31
+
32
+	if err := daemon.Reload(newConfig); err != nil {
33
+		t.Fatal(err)
34
+	}
35
+
36
+	label := daemon.configStore.Labels[0]
37
+	if label != "foo:baz" {
38
+		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
39
+	}
40
+}
41
+
42
+func TestDaemonReloadMirrors(t *testing.T) {
43
+	daemon := &Daemon{}
44
+	daemon.RegistryService = registry.NewService(registry.ServiceOptions{
45
+		InsecureRegistries: []string{},
46
+		Mirrors: []string{
47
+			"https://mirror.test1.com",
48
+			"https://mirror.test2.com", // this will be removed when reloading
49
+			"https://mirror.test3.com", // this will be removed when reloading
50
+		},
51
+	})
52
+
53
+	daemon.configStore = &config.Config{}
54
+
55
+	type pair struct {
56
+		valid   bool
57
+		mirrors []string
58
+		after   []string
59
+	}
60
+
61
+	loadMirrors := []pair{
62
+		{
63
+			valid:   false,
64
+			mirrors: []string{"10.10.1.11:5000"}, // this mirror is invalid
65
+			after:   []string{},
66
+		},
67
+		{
68
+			valid:   false,
69
+			mirrors: []string{"mirror.test1.com"}, // this mirror is invalid
70
+			after:   []string{},
71
+		},
72
+		{
73
+			valid:   false,
74
+			mirrors: []string{"10.10.1.11:5000", "mirror.test1.com"}, // mirrors are invalid
75
+			after:   []string{},
76
+		},
77
+		{
78
+			valid:   true,
79
+			mirrors: []string{"https://mirror.test1.com", "https://mirror.test4.com"},
80
+			after:   []string{"https://mirror.test1.com/", "https://mirror.test4.com/"},
81
+		},
82
+	}
83
+
84
+	for _, value := range loadMirrors {
85
+		valuesSets := make(map[string]interface{})
86
+		valuesSets["registry-mirrors"] = value.mirrors
87
+
88
+		newConfig := &config.Config{
89
+			CommonConfig: config.CommonConfig{
90
+				ServiceOptions: registry.ServiceOptions{
91
+					Mirrors: value.mirrors,
92
+				},
93
+				ValuesSet: valuesSets,
94
+			},
95
+		}
96
+
97
+		err := daemon.Reload(newConfig)
98
+		if !value.valid && err == nil {
99
+			// mirrors should be invalid, should be a non-nil error
100
+			t.Fatalf("Expected daemon reload error with invalid mirrors: %s, while get nil", value.mirrors)
101
+		}
102
+
103
+		if value.valid {
104
+			if err != nil {
105
+				// mirrors should be valid, should be no error
106
+				t.Fatal(err)
107
+			}
108
+			registryService := daemon.RegistryService.ServiceConfig()
109
+
110
+			if len(registryService.Mirrors) != len(value.after) {
111
+				t.Fatalf("Expected %d daemon mirrors %s while get %d with %s",
112
+					len(value.after),
113
+					value.after,
114
+					len(registryService.Mirrors),
115
+					registryService.Mirrors)
116
+			}
117
+
118
+			dataMap := map[string]struct{}{}
119
+
120
+			for _, mirror := range registryService.Mirrors {
121
+				if _, exist := dataMap[mirror]; !exist {
122
+					dataMap[mirror] = struct{}{}
123
+				}
124
+			}
125
+
126
+			for _, address := range value.after {
127
+				if _, exist := dataMap[address]; !exist {
128
+					t.Fatalf("Expected %s in daemon mirrors, while get none", address)
129
+				}
130
+			}
131
+		}
132
+	}
133
+}
134
+
135
+func TestDaemonReloadInsecureRegistries(t *testing.T) {
136
+	daemon := &Daemon{}
137
+	// initialize daemon with existing insecure registries: "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000"
138
+	daemon.RegistryService = registry.NewService(registry.ServiceOptions{
139
+		InsecureRegistries: []string{
140
+			"127.0.0.0/8",
141
+			"10.10.1.11:5000",
142
+			"10.10.1.22:5000", // this will be removed when reloading
143
+			"docker1.com",
144
+			"docker2.com", // this will be removed when reloading
145
+		},
146
+	})
147
+
148
+	daemon.configStore = &config.Config{}
149
+
150
+	insecureRegistries := []string{
151
+		"127.0.0.0/8",     // this will be kept
152
+		"10.10.1.11:5000", // this will be kept
153
+		"10.10.1.33:5000", // this will be newly added
154
+		"docker1.com",     // this will be kept
155
+		"docker3.com",     // this will be newly added
156
+	}
157
+
158
+	valuesSets := make(map[string]interface{})
159
+	valuesSets["insecure-registries"] = insecureRegistries
160
+
161
+	newConfig := &config.Config{
162
+		CommonConfig: config.CommonConfig{
163
+			ServiceOptions: registry.ServiceOptions{
164
+				InsecureRegistries: insecureRegistries,
165
+			},
166
+			ValuesSet: valuesSets,
167
+		},
168
+	}
169
+
170
+	if err := daemon.Reload(newConfig); err != nil {
171
+		t.Fatal(err)
172
+	}
173
+
174
+	// After Reload, daemon.RegistryService will be changed which is useful
175
+	// for registry communication in daemon.
176
+	registries := daemon.RegistryService.ServiceConfig()
177
+
178
+	// After Reload(), newConfig has come to registries.InsecureRegistryCIDRs and registries.IndexConfigs in daemon.
179
+	// Then collect registries.InsecureRegistryCIDRs in dataMap.
180
+	// When collecting, we need to convert CIDRS into string as a key,
181
+	// while the times of key appears as value.
182
+	dataMap := map[string]int{}
183
+	for _, value := range registries.InsecureRegistryCIDRs {
184
+		if _, ok := dataMap[value.String()]; !ok {
185
+			dataMap[value.String()] = 1
186
+		} else {
187
+			dataMap[value.String()]++
188
+		}
189
+	}
190
+
191
+	for _, value := range registries.IndexConfigs {
192
+		if _, ok := dataMap[value.Name]; !ok {
193
+			dataMap[value.Name] = 1
194
+		} else {
195
+			dataMap[value.Name]++
196
+		}
197
+	}
198
+
199
+	// Finally compare dataMap with the original insecureRegistries.
200
+	// Each value in insecureRegistries should appear in daemon's insecure registries,
201
+	// and each can only appear exactly ONCE.
202
+	for _, r := range insecureRegistries {
203
+		if value, ok := dataMap[r]; !ok {
204
+			t.Fatalf("Expected daemon insecure registry %s, got none", r)
205
+		} else if value != 1 {
206
+			t.Fatalf("Expected only 1 daemon insecure registry %s, got %d", r, value)
207
+		}
208
+	}
209
+
210
+	// assert if "10.10.1.22:5000" is removed when reloading
211
+	if value, ok := dataMap["10.10.1.22:5000"]; ok {
212
+		t.Fatalf("Expected no insecure registry of 10.10.1.22:5000, got %d", value)
213
+	}
214
+
215
+	// assert if "docker2.com" is removed when reloading
216
+	if value, ok := dataMap["docker2.com"]; ok {
217
+		t.Fatalf("Expected no insecure registry of docker2.com, got %d", value)
218
+	}
219
+}
220
+
221
+func TestDaemonReloadNotAffectOthers(t *testing.T) {
222
+	daemon := &Daemon{}
223
+	daemon.configStore = &config.Config{
224
+		CommonConfig: config.CommonConfig{
225
+			Labels: []string{"foo:bar"},
226
+			Debug:  true,
227
+		},
228
+	}
229
+
230
+	valuesSets := make(map[string]interface{})
231
+	valuesSets["labels"] = "foo:baz"
232
+	newConfig := &config.Config{
233
+		CommonConfig: config.CommonConfig{
234
+			Labels:    []string{"foo:baz"},
235
+			ValuesSet: valuesSets,
236
+		},
237
+	}
238
+
239
+	if err := daemon.Reload(newConfig); err != nil {
240
+		t.Fatal(err)
241
+	}
242
+
243
+	label := daemon.configStore.Labels[0]
244
+	if label != "foo:baz" {
245
+		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
246
+	}
247
+	debug := daemon.configStore.Debug
248
+	if !debug {
249
+		t.Fatalf("Expected debug 'enabled', got 'disabled'")
250
+	}
251
+}
252
+
253
+func TestDaemonDiscoveryReload(t *testing.T) {
254
+	daemon := &Daemon{}
255
+	daemon.configStore = &config.Config{
256
+		CommonConfig: config.CommonConfig{
257
+			ClusterStore:     "memory://127.0.0.1",
258
+			ClusterAdvertise: "127.0.0.1:3333",
259
+		},
260
+	}
261
+
262
+	if err := daemon.initDiscovery(daemon.configStore); err != nil {
263
+		t.Fatal(err)
264
+	}
265
+
266
+	expected := discovery.Entries{
267
+		&discovery.Entry{Host: "127.0.0.1", Port: "3333"},
268
+	}
269
+
270
+	select {
271
+	case <-time.After(10 * time.Second):
272
+		t.Fatal("timeout waiting for discovery")
273
+	case <-daemon.discoveryWatcher.ReadyCh():
274
+	}
275
+
276
+	stopCh := make(chan struct{})
277
+	defer close(stopCh)
278
+	ch, errCh := daemon.discoveryWatcher.Watch(stopCh)
279
+
280
+	select {
281
+	case <-time.After(1 * time.Second):
282
+		t.Fatal("failed to get discovery advertisements in time")
283
+	case e := <-ch:
284
+		if !reflect.DeepEqual(e, expected) {
285
+			t.Fatalf("expected %v, got %v\n", expected, e)
286
+		}
287
+	case e := <-errCh:
288
+		t.Fatal(e)
289
+	}
290
+
291
+	valuesSets := make(map[string]interface{})
292
+	valuesSets["cluster-store"] = "memory://127.0.0.1:2222"
293
+	valuesSets["cluster-advertise"] = "127.0.0.1:5555"
294
+	newConfig := &config.Config{
295
+		CommonConfig: config.CommonConfig{
296
+			ClusterStore:     "memory://127.0.0.1:2222",
297
+			ClusterAdvertise: "127.0.0.1:5555",
298
+			ValuesSet:        valuesSets,
299
+		},
300
+	}
301
+
302
+	expected = discovery.Entries{
303
+		&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
304
+	}
305
+
306
+	if err := daemon.Reload(newConfig); err != nil {
307
+		t.Fatal(err)
308
+	}
309
+
310
+	select {
311
+	case <-time.After(10 * time.Second):
312
+		t.Fatal("timeout waiting for discovery")
313
+	case <-daemon.discoveryWatcher.ReadyCh():
314
+	}
315
+
316
+	ch, errCh = daemon.discoveryWatcher.Watch(stopCh)
317
+
318
+	select {
319
+	case <-time.After(1 * time.Second):
320
+		t.Fatal("failed to get discovery advertisements in time")
321
+	case e := <-ch:
322
+		if !reflect.DeepEqual(e, expected) {
323
+			t.Fatalf("expected %v, got %v\n", expected, e)
324
+		}
325
+	case e := <-errCh:
326
+		t.Fatal(e)
327
+	}
328
+}
329
+
330
+func TestDaemonDiscoveryReloadFromEmptyDiscovery(t *testing.T) {
331
+	daemon := &Daemon{}
332
+	daemon.configStore = &config.Config{}
333
+
334
+	valuesSet := make(map[string]interface{})
335
+	valuesSet["cluster-store"] = "memory://127.0.0.1:2222"
336
+	valuesSet["cluster-advertise"] = "127.0.0.1:5555"
337
+	newConfig := &config.Config{
338
+		CommonConfig: config.CommonConfig{
339
+			ClusterStore:     "memory://127.0.0.1:2222",
340
+			ClusterAdvertise: "127.0.0.1:5555",
341
+			ValuesSet:        valuesSet,
342
+		},
343
+	}
344
+
345
+	expected := discovery.Entries{
346
+		&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
347
+	}
348
+
349
+	if err := daemon.Reload(newConfig); err != nil {
350
+		t.Fatal(err)
351
+	}
352
+
353
+	select {
354
+	case <-time.After(10 * time.Second):
355
+		t.Fatal("timeout waiting for discovery")
356
+	case <-daemon.discoveryWatcher.ReadyCh():
357
+	}
358
+
359
+	stopCh := make(chan struct{})
360
+	defer close(stopCh)
361
+	ch, errCh := daemon.discoveryWatcher.Watch(stopCh)
362
+
363
+	select {
364
+	case <-time.After(1 * time.Second):
365
+		t.Fatal("failed to get discovery advertisements in time")
366
+	case e := <-ch:
367
+		if !reflect.DeepEqual(e, expected) {
368
+			t.Fatalf("expected %v, got %v\n", expected, e)
369
+		}
370
+	case e := <-errCh:
371
+		t.Fatal(e)
372
+	}
373
+}
374
+
375
+func TestDaemonDiscoveryReloadOnlyClusterAdvertise(t *testing.T) {
376
+	daemon := &Daemon{}
377
+	daemon.configStore = &config.Config{
378
+		CommonConfig: config.CommonConfig{
379
+			ClusterStore: "memory://127.0.0.1",
380
+		},
381
+	}
382
+	valuesSets := make(map[string]interface{})
383
+	valuesSets["cluster-advertise"] = "127.0.0.1:5555"
384
+	newConfig := &config.Config{
385
+		CommonConfig: config.CommonConfig{
386
+			ClusterAdvertise: "127.0.0.1:5555",
387
+			ValuesSet:        valuesSets,
388
+		},
389
+	}
390
+	expected := discovery.Entries{
391
+		&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
392
+	}
393
+
394
+	if err := daemon.Reload(newConfig); err != nil {
395
+		t.Fatal(err)
396
+	}
397
+
398
+	select {
399
+	case <-daemon.discoveryWatcher.ReadyCh():
400
+	case <-time.After(10 * time.Second):
401
+		t.Fatal("Timeout waiting for discovery")
402
+	}
403
+	stopCh := make(chan struct{})
404
+	defer close(stopCh)
405
+	ch, errCh := daemon.discoveryWatcher.Watch(stopCh)
406
+
407
+	select {
408
+	case <-time.After(1 * time.Second):
409
+		t.Fatal("failed to get discovery advertisements in time")
410
+	case e := <-ch:
411
+		if !reflect.DeepEqual(e, expected) {
412
+			t.Fatalf("expected %v, got %v\n", expected, e)
413
+		}
414
+	case e := <-errCh:
415
+		t.Fatal(e)
416
+	}
417
+}