1package daemon // import "github.com/docker/docker/daemon"
2
3import (
4	"os"
5	"reflect"
6	"sort"
7	"testing"
8	"time"
9
10	"github.com/docker/docker/daemon/config"
11	"github.com/docker/docker/daemon/images"
12	"github.com/docker/docker/pkg/discovery"
13	_ "github.com/docker/docker/pkg/discovery/memory"
14	"github.com/docker/docker/registry"
15	"github.com/docker/libnetwork"
16	"github.com/sirupsen/logrus"
17	"gotest.tools/v3/assert"
18	is "gotest.tools/v3/assert/cmp"
19)
20
21// muteLogs suppresses logs that are generated during the test
22func muteLogs() {
23	logrus.SetLevel(logrus.ErrorLevel)
24}
25
26func TestDaemonReloadLabels(t *testing.T) {
27	daemon := &Daemon{
28		configStore: &config.Config{
29			CommonConfig: config.CommonConfig{
30				Labels: []string{"foo:bar"},
31			},
32		},
33		imageService: images.NewImageService(images.ImageServiceConfig{}),
34	}
35	muteLogs()
36
37	valuesSets := make(map[string]interface{})
38	valuesSets["labels"] = "foo:baz"
39	newConfig := &config.Config{
40		CommonConfig: config.CommonConfig{
41			Labels:    []string{"foo:baz"},
42			ValuesSet: valuesSets,
43		},
44	}
45
46	if err := daemon.Reload(newConfig); err != nil {
47		t.Fatal(err)
48	}
49
50	label := daemon.configStore.Labels[0]
51	if label != "foo:baz" {
52		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
53	}
54}
55
56func TestDaemonReloadAllowNondistributableArtifacts(t *testing.T) {
57	daemon := &Daemon{
58		configStore:  &config.Config{},
59		imageService: images.NewImageService(images.ImageServiceConfig{}),
60	}
61	muteLogs()
62
63	var err error
64	// Initialize daemon with some registries.
65	daemon.RegistryService, err = registry.NewService(registry.ServiceOptions{
66		AllowNondistributableArtifacts: []string{
67			"127.0.0.0/8",
68			"10.10.1.11:5000",
69			"10.10.1.22:5000", // This will be removed during reload.
70			"docker1.com",
71			"docker2.com", // This will be removed during reload.
72		},
73	})
74	if err != nil {
75		t.Fatal(err)
76	}
77
78	registries := []string{
79		"127.0.0.0/8",
80		"10.10.1.11:5000",
81		"10.10.1.33:5000", // This will be added during reload.
82		"docker1.com",
83		"docker3.com", // This will be added during reload.
84	}
85
86	newConfig := &config.Config{
87		CommonConfig: config.CommonConfig{
88			ServiceOptions: registry.ServiceOptions{
89				AllowNondistributableArtifacts: registries,
90			},
91			ValuesSet: map[string]interface{}{
92				"allow-nondistributable-artifacts": registries,
93			},
94		},
95	}
96
97	if err := daemon.Reload(newConfig); err != nil {
98		t.Fatal(err)
99	}
100
101	var actual []string
102	serviceConfig := daemon.RegistryService.ServiceConfig()
103	for _, value := range serviceConfig.AllowNondistributableArtifactsCIDRs {
104		actual = append(actual, value.String())
105	}
106	actual = append(actual, serviceConfig.AllowNondistributableArtifactsHostnames...)
107
108	sort.Strings(registries)
109	sort.Strings(actual)
110	assert.Check(t, is.DeepEqual(registries, actual))
111}
112
113func TestDaemonReloadMirrors(t *testing.T) {
114	daemon := &Daemon{
115		imageService: images.NewImageService(images.ImageServiceConfig{}),
116	}
117	muteLogs()
118
119	var err error
120	daemon.RegistryService, err = registry.NewService(registry.ServiceOptions{
121		InsecureRegistries: []string{},
122		Mirrors: []string{
123			"https://mirror.test1.com",
124			"https://mirror.test2.com", // this will be removed when reloading
125			"https://mirror.test3.com", // this will be removed when reloading
126		},
127	})
128	if err != nil {
129		t.Fatal(err)
130	}
131
132	daemon.configStore = &config.Config{}
133
134	type pair struct {
135		valid   bool
136		mirrors []string
137		after   []string
138	}
139
140	loadMirrors := []pair{
141		{
142			valid:   false,
143			mirrors: []string{"10.10.1.11:5000"}, // this mirror is invalid
144			after:   []string{},
145		},
146		{
147			valid:   false,
148			mirrors: []string{"mirror.test1.com"}, // this mirror is invalid
149			after:   []string{},
150		},
151		{
152			valid:   false,
153			mirrors: []string{"10.10.1.11:5000", "mirror.test1.com"}, // mirrors are invalid
154			after:   []string{},
155		},
156		{
157			valid:   true,
158			mirrors: []string{"https://mirror.test1.com", "https://mirror.test4.com"},
159			after:   []string{"https://mirror.test1.com/", "https://mirror.test4.com/"},
160		},
161	}
162
163	for _, value := range loadMirrors {
164		valuesSets := make(map[string]interface{})
165		valuesSets["registry-mirrors"] = value.mirrors
166
167		newConfig := &config.Config{
168			CommonConfig: config.CommonConfig{
169				ServiceOptions: registry.ServiceOptions{
170					Mirrors: value.mirrors,
171				},
172				ValuesSet: valuesSets,
173			},
174		}
175
176		err := daemon.Reload(newConfig)
177		if !value.valid && err == nil {
178			// mirrors should be invalid, should be a non-nil error
179			t.Fatalf("Expected daemon reload error with invalid mirrors: %s, while get nil", value.mirrors)
180		}
181
182		if value.valid {
183			if err != nil {
184				// mirrors should be valid, should be no error
185				t.Fatal(err)
186			}
187			registryService := daemon.RegistryService.ServiceConfig()
188
189			if len(registryService.Mirrors) != len(value.after) {
190				t.Fatalf("Expected %d daemon mirrors %s while get %d with %s",
191					len(value.after),
192					value.after,
193					len(registryService.Mirrors),
194					registryService.Mirrors)
195			}
196
197			dataMap := map[string]struct{}{}
198
199			for _, mirror := range registryService.Mirrors {
200				if _, exist := dataMap[mirror]; !exist {
201					dataMap[mirror] = struct{}{}
202				}
203			}
204
205			for _, address := range value.after {
206				if _, exist := dataMap[address]; !exist {
207					t.Fatalf("Expected %s in daemon mirrors, while get none", address)
208				}
209			}
210		}
211	}
212}
213
214func TestDaemonReloadInsecureRegistries(t *testing.T) {
215	daemon := &Daemon{
216		imageService: images.NewImageService(images.ImageServiceConfig{}),
217	}
218	muteLogs()
219
220	var err error
221	// initialize daemon with existing insecure registries: "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000"
222	daemon.RegistryService, err = registry.NewService(registry.ServiceOptions{
223		InsecureRegistries: []string{
224			"127.0.0.0/8",
225			"10.10.1.11:5000",
226			"10.10.1.22:5000", // this will be removed when reloading
227			"docker1.com",
228			"docker2.com", // this will be removed when reloading
229		},
230	})
231	if err != nil {
232		t.Fatal(err)
233	}
234
235	daemon.configStore = &config.Config{}
236
237	insecureRegistries := []string{
238		"127.0.0.0/8",     // this will be kept
239		"10.10.1.11:5000", // this will be kept
240		"10.10.1.33:5000", // this will be newly added
241		"docker1.com",     // this will be kept
242		"docker3.com",     // this will be newly added
243	}
244
245	valuesSets := make(map[string]interface{})
246	valuesSets["insecure-registries"] = insecureRegistries
247
248	newConfig := &config.Config{
249		CommonConfig: config.CommonConfig{
250			ServiceOptions: registry.ServiceOptions{
251				InsecureRegistries: insecureRegistries,
252			},
253			ValuesSet: valuesSets,
254		},
255	}
256
257	if err := daemon.Reload(newConfig); err != nil {
258		t.Fatal(err)
259	}
260
261	// After Reload, daemon.RegistryService will be changed which is useful
262	// for registry communication in daemon.
263	registries := daemon.RegistryService.ServiceConfig()
264
265	// After Reload(), newConfig has come to registries.InsecureRegistryCIDRs and registries.IndexConfigs in daemon.
266	// Then collect registries.InsecureRegistryCIDRs in dataMap.
267	// When collecting, we need to convert CIDRS into string as a key,
268	// while the times of key appears as value.
269	dataMap := map[string]int{}
270	for _, value := range registries.InsecureRegistryCIDRs {
271		if _, ok := dataMap[value.String()]; !ok {
272			dataMap[value.String()] = 1
273		} else {
274			dataMap[value.String()]++
275		}
276	}
277
278	for _, value := range registries.IndexConfigs {
279		if _, ok := dataMap[value.Name]; !ok {
280			dataMap[value.Name] = 1
281		} else {
282			dataMap[value.Name]++
283		}
284	}
285
286	// Finally compare dataMap with the original insecureRegistries.
287	// Each value in insecureRegistries should appear in daemon's insecure registries,
288	// and each can only appear exactly ONCE.
289	for _, r := range insecureRegistries {
290		if value, ok := dataMap[r]; !ok {
291			t.Fatalf("Expected daemon insecure registry %s, got none", r)
292		} else if value != 1 {
293			t.Fatalf("Expected only 1 daemon insecure registry %s, got %d", r, value)
294		}
295	}
296
297	// assert if "10.10.1.22:5000" is removed when reloading
298	if value, ok := dataMap["10.10.1.22:5000"]; ok {
299		t.Fatalf("Expected no insecure registry of 10.10.1.22:5000, got %d", value)
300	}
301
302	// assert if "docker2.com" is removed when reloading
303	if value, ok := dataMap["docker2.com"]; ok {
304		t.Fatalf("Expected no insecure registry of docker2.com, got %d", value)
305	}
306}
307
308func TestDaemonReloadNotAffectOthers(t *testing.T) {
309	daemon := &Daemon{
310		imageService: images.NewImageService(images.ImageServiceConfig{}),
311	}
312	muteLogs()
313
314	daemon.configStore = &config.Config{
315		CommonConfig: config.CommonConfig{
316			Labels: []string{"foo:bar"},
317			Debug:  true,
318		},
319	}
320
321	valuesSets := make(map[string]interface{})
322	valuesSets["labels"] = "foo:baz"
323	newConfig := &config.Config{
324		CommonConfig: config.CommonConfig{
325			Labels:    []string{"foo:baz"},
326			ValuesSet: valuesSets,
327		},
328	}
329
330	if err := daemon.Reload(newConfig); err != nil {
331		t.Fatal(err)
332	}
333
334	label := daemon.configStore.Labels[0]
335	if label != "foo:baz" {
336		t.Fatalf("Expected daemon label `foo:baz`, got %s", label)
337	}
338	debug := daemon.configStore.Debug
339	if !debug {
340		t.Fatal("Expected debug 'enabled', got 'disabled'")
341	}
342}
343
344func TestDaemonDiscoveryReload(t *testing.T) {
345	daemon := &Daemon{
346		imageService: images.NewImageService(images.ImageServiceConfig{}),
347	}
348	muteLogs()
349	daemon.configStore = &config.Config{
350		CommonConfig: config.CommonConfig{
351			ClusterStore:     "memory://127.0.0.1",
352			ClusterAdvertise: "127.0.0.1:3333",
353		},
354	}
355
356	if err := daemon.initDiscovery(daemon.configStore); err != nil {
357		t.Fatal(err)
358	}
359
360	expected := discovery.Entries{
361		&discovery.Entry{Host: "127.0.0.1", Port: "3333"},
362	}
363
364	select {
365	case <-time.After(10 * time.Second):
366		t.Fatal("timeout waiting for discovery")
367	case <-daemon.discoveryWatcher.ReadyCh():
368	}
369
370	stopCh := make(chan struct{})
371	defer close(stopCh)
372	ch, errCh := daemon.discoveryWatcher.Watch(stopCh)
373
374	select {
375	case <-time.After(1 * time.Second):
376		t.Fatal("failed to get discovery advertisements in time")
377	case e := <-ch:
378		if !reflect.DeepEqual(e, expected) {
379			t.Fatalf("expected %v, got %v\n", expected, e)
380		}
381	case e := <-errCh:
382		t.Fatal(e)
383	}
384
385	valuesSets := make(map[string]interface{})
386	valuesSets["cluster-store"] = "memory://127.0.0.1:2222"
387	valuesSets["cluster-advertise"] = "127.0.0.1:5555"
388	newConfig := &config.Config{
389		CommonConfig: config.CommonConfig{
390			ClusterStore:     "memory://127.0.0.1:2222",
391			ClusterAdvertise: "127.0.0.1:5555",
392			ValuesSet:        valuesSets,
393		},
394	}
395
396	expected = discovery.Entries{
397		&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
398	}
399
400	if err := daemon.Reload(newConfig); err != nil {
401		t.Fatal(err)
402	}
403
404	select {
405	case <-time.After(10 * time.Second):
406		t.Fatal("timeout waiting for discovery")
407	case <-daemon.discoveryWatcher.ReadyCh():
408	}
409
410	ch, errCh = daemon.discoveryWatcher.Watch(stopCh)
411
412	select {
413	case <-time.After(1 * time.Second):
414		t.Fatal("failed to get discovery advertisements in time")
415	case e := <-ch:
416		if !reflect.DeepEqual(e, expected) {
417			t.Fatalf("expected %v, got %v\n", expected, e)
418		}
419	case e := <-errCh:
420		t.Fatal(e)
421	}
422}
423
424func TestDaemonDiscoveryReloadFromEmptyDiscovery(t *testing.T) {
425	daemon := &Daemon{
426		imageService: images.NewImageService(images.ImageServiceConfig{}),
427	}
428	daemon.configStore = &config.Config{}
429	muteLogs()
430
431	valuesSet := make(map[string]interface{})
432	valuesSet["cluster-store"] = "memory://127.0.0.1:2222"
433	valuesSet["cluster-advertise"] = "127.0.0.1:5555"
434	newConfig := &config.Config{
435		CommonConfig: config.CommonConfig{
436			ClusterStore:     "memory://127.0.0.1:2222",
437			ClusterAdvertise: "127.0.0.1:5555",
438			ValuesSet:        valuesSet,
439		},
440	}
441
442	expected := discovery.Entries{
443		&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
444	}
445
446	if err := daemon.Reload(newConfig); err != nil {
447		t.Fatal(err)
448	}
449
450	select {
451	case <-time.After(10 * time.Second):
452		t.Fatal("timeout waiting for discovery")
453	case <-daemon.discoveryWatcher.ReadyCh():
454	}
455
456	stopCh := make(chan struct{})
457	defer close(stopCh)
458	ch, errCh := daemon.discoveryWatcher.Watch(stopCh)
459
460	select {
461	case <-time.After(1 * time.Second):
462		t.Fatal("failed to get discovery advertisements in time")
463	case e := <-ch:
464		if !reflect.DeepEqual(e, expected) {
465			t.Fatalf("expected %v, got %v\n", expected, e)
466		}
467	case e := <-errCh:
468		t.Fatal(e)
469	}
470}
471
472func TestDaemonDiscoveryReloadOnlyClusterAdvertise(t *testing.T) {
473	daemon := &Daemon{
474		imageService: images.NewImageService(images.ImageServiceConfig{}),
475	}
476	daemon.configStore = &config.Config{
477		CommonConfig: config.CommonConfig{
478			ClusterStore: "memory://127.0.0.1",
479		},
480	}
481	valuesSets := make(map[string]interface{})
482	valuesSets["cluster-advertise"] = "127.0.0.1:5555"
483	newConfig := &config.Config{
484		CommonConfig: config.CommonConfig{
485			ClusterAdvertise: "127.0.0.1:5555",
486			ValuesSet:        valuesSets,
487		},
488	}
489	expected := discovery.Entries{
490		&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
491	}
492
493	if err := daemon.Reload(newConfig); err != nil {
494		t.Fatal(err)
495	}
496
497	select {
498	case <-daemon.discoveryWatcher.ReadyCh():
499	case <-time.After(10 * time.Second):
500		t.Fatal("Timeout waiting for discovery")
501	}
502	stopCh := make(chan struct{})
503	defer close(stopCh)
504	ch, errCh := daemon.discoveryWatcher.Watch(stopCh)
505
506	select {
507	case <-time.After(1 * time.Second):
508		t.Fatal("failed to get discovery advertisements in time")
509	case e := <-ch:
510		if !reflect.DeepEqual(e, expected) {
511			t.Fatalf("expected %v, got %v\n", expected, e)
512		}
513	case e := <-errCh:
514		t.Fatal(e)
515	}
516}
517
518func TestDaemonReloadNetworkDiagnosticPort(t *testing.T) {
519	if os.Getuid() != 0 {
520		t.Skip("root required")
521	}
522	daemon := &Daemon{
523		imageService: images.NewImageService(images.ImageServiceConfig{}),
524	}
525	daemon.configStore = &config.Config{}
526
527	valuesSet := make(map[string]interface{})
528	valuesSet["network-diagnostic-port"] = 2000
529	enableConfig := &config.Config{
530		CommonConfig: config.CommonConfig{
531			NetworkDiagnosticPort: 2000,
532			ValuesSet:             valuesSet,
533		},
534	}
535	disableConfig := &config.Config{
536		CommonConfig: config.CommonConfig{},
537	}
538
539	netOptions, err := daemon.networkOptions(enableConfig, nil, nil)
540	if err != nil {
541		t.Fatal(err)
542	}
543	controller, err := libnetwork.New(netOptions...)
544	if err != nil {
545		t.Fatal(err)
546	}
547	daemon.netController = controller
548
549	// Enable/Disable the server for some iterations
550	for i := 0; i < 10; i++ {
551		enableConfig.CommonConfig.NetworkDiagnosticPort++
552		if err := daemon.Reload(enableConfig); err != nil {
553			t.Fatal(err)
554		}
555		// Check that the diagnostic is enabled
556		if !daemon.netController.IsDiagnosticEnabled() {
557			t.Fatalf("diagnostic should be enable")
558		}
559
560		// Reload
561		if err := daemon.Reload(disableConfig); err != nil {
562			t.Fatal(err)
563		}
564		// Check that the diagnostic is disabled
565		if daemon.netController.IsDiagnosticEnabled() {
566			t.Fatalf("diagnostic should be disable")
567		}
568	}
569
570	enableConfig.CommonConfig.NetworkDiagnosticPort++
571	// 2 times the enable should not create problems
572	if err := daemon.Reload(enableConfig); err != nil {
573		t.Fatal(err)
574	}
575	// Check that the diagnostic is enabled
576	if !daemon.netController.IsDiagnosticEnabled() {
577		t.Fatalf("diagnostic should be enable")
578	}
579
580	// Check that another reload does not cause issues
581	if err := daemon.Reload(enableConfig); err != nil {
582		t.Fatal(err)
583	}
584	// Check that the diagnostic is enable
585	if !daemon.netController.IsDiagnosticEnabled() {
586		t.Fatalf("diagnostic should be enable")
587	}
588
589}
590