package daemon // import "github.com/docker/docker/daemon" import ( "os" "sort" "testing" "github.com/containerd/log" "github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/images" "github.com/docker/docker/libnetwork" "github.com/docker/docker/registry" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) // muteLogs suppresses logs that are generated during the test func muteLogs(t *testing.T) { t.Helper() err := log.SetLevel("error") if err != nil { t.Error(err) } } func newDaemonForReloadT(t *testing.T, cfg *config.Config) *Daemon { t.Helper() daemon := &Daemon{ imageService: images.NewImageService(images.ImageServiceConfig{}), } var err error daemon.registryService, err = registry.NewService(registry.ServiceOptions{}) assert.Assert(t, err) daemon.configStore.Store(&configStore{Config: *cfg}) return daemon } func TestDaemonReloadLabels(t *testing.T) { daemon := newDaemonForReloadT(t, &config.Config{ CommonConfig: config.CommonConfig{ Labels: []string{"foo:bar"}, }, }) muteLogs(t) 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.config().Labels[0] if label != "foo:baz" { t.Fatalf("Expected daemon label `foo:baz`, got %s", label) } } func TestDaemonReloadAllowNondistributableArtifacts(t *testing.T) { daemon := newDaemonForReloadT(t, &config.Config{}) muteLogs(t) var err error // Initialize daemon with some registries. daemon.registryService, err = registry.NewService(registry.ServiceOptions{ 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. }, }) if err != nil { t.Fatal(err) } 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) } var actual []string serviceConfig := daemon.registryService.ServiceConfig() for _, value := range serviceConfig.AllowNondistributableArtifactsCIDRs { actual = append(actual, value.String()) } actual = append(actual, serviceConfig.AllowNondistributableArtifactsHostnames...) sort.Strings(registries) sort.Strings(actual) assert.Check(t, is.DeepEqual(registries, actual)) } func TestDaemonReloadMirrors(t *testing.T) { daemon := &Daemon{ imageService: images.NewImageService(images.ImageServiceConfig{}), } muteLogs(t) var err error daemon.registryService, err = registry.NewService(registry.ServiceOptions{ InsecureRegistries: []string{}, Mirrors: []string{ "https://mirror.test1.example.com", "https://mirror.test2.example.com", // this will be removed when reloading "https://mirror.test3.example.com", // this will be removed when reloading }, }) if err != nil { t.Fatal(err) } 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.example.com"}, // mirrors are invalid after: []string{}, }, { valid: true, mirrors: []string{"https://mirror.test1.example.com", "https://mirror.test4.example.com"}, after: []string{"https://mirror.test1.example.com/", "https://mirror.test4.example.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) { daemon := &Daemon{ imageService: images.NewImageService(images.ImageServiceConfig{}), } muteLogs(t) var err error // initialize daemon with existing insecure registries: "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000" daemon.registryService, err = registry.NewService(registry.ServiceOptions{ InsecureRegistries: []string{ "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000", // this will be removed when reloading "docker1.example.com", "docker2.example.com", // this will be removed when reloading }, }) if err != nil { t.Fatal(err) } 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.example.com", // this will be kept "docker3.example.com", // this will be newly added } mirrors := []string{ "https://mirror.test.example.com", } valuesSets := make(map[string]interface{}) valuesSets["insecure-registries"] = insecureRegistries valuesSets["registry-mirrors"] = mirrors newConfig := &config.Config{ CommonConfig: config.CommonConfig{ ServiceOptions: registry.ServiceOptions{ InsecureRegistries: insecureRegistries, Mirrors: mirrors, }, 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.example.com"]; ok { t.Fatalf("Expected no insecure registry of docker2.com, got %d", value) } } func TestDaemonReloadNotAffectOthers(t *testing.T) { daemon := newDaemonForReloadT(t, &config.Config{ CommonConfig: config.CommonConfig{ Labels: []string{"foo:bar"}, Debug: true, }, }) muteLogs(t) 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.config().Labels[0] if label != "foo:baz" { t.Fatalf("Expected daemon label `foo:baz`, got %s", label) } debug := daemon.config().Debug if !debug { t.Fatal("Expected debug 'enabled', got 'disabled'") } } func TestDaemonReloadNetworkDiagnosticPort(t *testing.T) { if os.Getuid() != 0 { t.Skip("root required") } daemon := newDaemonForReloadT(t, &config.Config{}) enableConfig := &config.Config{ CommonConfig: config.CommonConfig{ NetworkDiagnosticPort: 2000, ValuesSet: map[string]interface{}{ "network-diagnostic-port": 2000, }, }, } netOptions, err := daemon.networkOptions(&config.Config{CommonConfig: config.CommonConfig{Root: t.TempDir()}}, 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) } // Check that the diagnostic is enabled if !daemon.netController.IsDiagnosticEnabled() { t.Fatalf("diagnostic should be enabled") } // Reload if err := daemon.Reload(&config.Config{}); err != nil { t.Fatal(err) } // Check that the diagnostic is disabled if daemon.netController.IsDiagnosticEnabled() { t.Fatalf("diagnostic should be disabled") } } enableConfig.CommonConfig.NetworkDiagnosticPort++ // 2 times the enable should not create problems if err := daemon.Reload(enableConfig); err != nil { t.Fatal(err) } // Check that the diagnostic is enabled if !daemon.netController.IsDiagnosticEnabled() { t.Fatalf("diagnostic should be enable") } // Check that another reload does not cause issues if err := daemon.Reload(enableConfig); err != nil { t.Fatal(err) } // Check that the diagnostic is enable if !daemon.netController.IsDiagnosticEnabled() { t.Fatalf("diagnostic should be enable") } }