Browse code

support registry mirror config reload

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

allencloud authored on 2016/12/21 19:31:05
Showing 8 changed files
... ...
@@ -995,14 +995,15 @@ func (daemon *Daemon) initDiscovery(config *Config) error {
995 995
 // Reload reads configuration changes and modifies the
996 996
 // daemon according to those changes.
997 997
 // These are the settings that Reload changes:
998
-// - Daemon labels.
999
-// - Daemon debug log level.
1000
-// - Daemon insecure registries.
998
+// - Daemon labels
999
+// - Daemon debug log level
1000
+// - Insecure registries
1001
+// - Registry mirrors
1001 1002
 // - Daemon max concurrent downloads
1002 1003
 // - Daemon max concurrent uploads
1003
-// - Cluster discovery (reconfigure and restart).
1004
+// - Cluster discovery (reconfigure and restart)
1004 1005
 // - Daemon live restore
1005
-// - Daemon shutdown timeout (in seconds).
1006
+// - Daemon shutdown timeout (in seconds)
1006 1007
 func (daemon *Daemon) Reload(config *Config) (err error) {
1007 1008
 
1008 1009
 	daemon.configStore.reloadLock.Lock()
... ...
@@ -1035,6 +1036,14 @@ func (daemon *Daemon) Reload(config *Config) (err error) {
1035 1035
 			return err
1036 1036
 		}
1037 1037
 	}
1038
+
1039
+	if config.IsValueSet("registry-mirrors") {
1040
+		daemon.configStore.Mirrors = config.Mirrors
1041
+		if err := daemon.RegistryService.LoadMirrors(config.Mirrors); err != nil {
1042
+			return err
1043
+		}
1044
+	}
1045
+
1038 1046
 	if config.IsValueSet("live-restore") {
1039 1047
 		daemon.configStore.LiveRestoreEnabled = config.LiveRestoreEnabled
1040 1048
 		if err := daemon.containerdRemote.UpdateOptions(libcontainerd.WithLiveRestore(config.LiveRestoreEnabled)); err != nil {
... ...
@@ -1087,6 +1096,16 @@ func (daemon *Daemon) Reload(config *Config) (err error) {
1087 1087
 		attributes["insecure-registries"] = "[]"
1088 1088
 	}
1089 1089
 
1090
+	if daemon.configStore.Mirrors != nil {
1091
+		mirrors, err := json.Marshal(daemon.configStore.Mirrors)
1092
+		if err != nil {
1093
+			return err
1094
+		}
1095
+		attributes["registry-mirrors"] = string(mirrors)
1096
+	} else {
1097
+		attributes["registry-mirrors"] = "[]"
1098
+	}
1099
+
1090 1100
 	attributes["cluster-store"] = daemon.configStore.ClusterStore
1091 1101
 	if daemon.configStore.ClusterOpts != nil {
1092 1102
 		opts, err := json.Marshal(daemon.configStore.ClusterOpts)
... ...
@@ -341,6 +341,100 @@ func TestDaemonReloadLabels(t *testing.T) {
341 341
 	}
342 342
 }
343 343
 
344
+func TestDaemonReloadMirrors(t *testing.T) {
345
+	daemon := &Daemon{}
346
+
347
+	daemon.RegistryService = registry.NewService(registry.ServiceOptions{
348
+		InsecureRegistries: []string{},
349
+		Mirrors: []string{
350
+			"https://mirror.test1.com",
351
+			"https://mirror.test2.com", // this will be removed when reloading
352
+			"https://mirror.test3.com", // this will be removed when reloading
353
+		},
354
+	})
355
+
356
+	daemon.configStore = &Config{}
357
+
358
+	type pair struct {
359
+		valid   bool
360
+		mirrors []string
361
+		after   []string
362
+	}
363
+
364
+	loadMirrors := []pair{
365
+		{
366
+			valid:   false,
367
+			mirrors: []string{"10.10.1.11:5000"}, // this mirror is invalid
368
+			after:   []string{},
369
+		},
370
+		{
371
+			valid:   false,
372
+			mirrors: []string{"mirror.test1.com"}, // this mirror is invalid
373
+			after:   []string{},
374
+		},
375
+		{
376
+			valid:   false,
377
+			mirrors: []string{"10.10.1.11:5000", "mirror.test1.com"}, // mirrors are invalid
378
+			after:   []string{},
379
+		},
380
+		{
381
+			valid:   true,
382
+			mirrors: []string{"https://mirror.test1.com", "https://mirror.test4.com"},
383
+			after:   []string{"https://mirror.test1.com/", "https://mirror.test4.com/"},
384
+		},
385
+	}
386
+
387
+	for _, value := range loadMirrors {
388
+		valuesSets := make(map[string]interface{})
389
+		valuesSets["registry-mirrors"] = value.mirrors
390
+
391
+		newConfig := &Config{
392
+			CommonConfig: CommonConfig{
393
+				ServiceOptions: registry.ServiceOptions{
394
+					Mirrors: value.mirrors,
395
+				},
396
+				valuesSet: valuesSets,
397
+			},
398
+		}
399
+
400
+		err := daemon.Reload(newConfig)
401
+		if !value.valid && err == nil {
402
+			// mirrors should be invalid, should be a non-nil error
403
+			t.Fatalf("Expected daemon reload error with invalid mirrors: %s, while get nil", value.mirrors)
404
+		}
405
+
406
+		if value.valid {
407
+			if err != nil {
408
+				// mirrors should be valid, should be no error
409
+				t.Fatal(err)
410
+			}
411
+			registryService := daemon.RegistryService.ServiceConfig()
412
+
413
+			if len(registryService.Mirrors) != len(value.after) {
414
+				t.Fatalf("Expected %d daemon mirrors %s while get %d with %s",
415
+					len(value.after),
416
+					value.after,
417
+					len(registryService.Mirrors),
418
+					registryService.Mirrors)
419
+			}
420
+
421
+			dataMap := map[string]struct{}{}
422
+
423
+			for _, mirror := range registryService.Mirrors {
424
+				if _, exist := dataMap[mirror]; !exist {
425
+					dataMap[mirror] = struct{}{}
426
+				}
427
+			}
428
+
429
+			for _, address := range value.after {
430
+				if _, exist := dataMap[address]; !exist {
431
+					t.Fatalf("Expected %s in daemon mirrors, while get none", address)
432
+				}
433
+			}
434
+		}
435
+	}
436
+}
437
+
344 438
 func TestDaemonReloadInsecureRegistries(t *testing.T) {
345 439
 	daemon := &Daemon{}
346 440
 	// initialize daemon with existing insecure registries: "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000"
... ...
@@ -1289,6 +1289,7 @@ The list of currently supported options that can be reconfigured is this:
1289 1289
   be used to run containers
1290 1290
 - `authorization-plugin`: specifies the authorization plugins to use.
1291 1291
 - `insecure-registries`: it replaces the daemon insecure registries with a new set of insecure registries. If some existing insecure registries in daemon's configuration are not in newly reloaded insecure resgitries, these existing ones will be removed from daemon's config.
1292
+- `registry-mirrors`: it replaces the daemon registry mirrors with a new set of registry mirrors. If some existing registry mirrors in daemon's configuration are not in newly reloaded registry mirrors, these existing ones will be removed from daemon's config.
1292 1293
 
1293 1294
 Updating and reloading the cluster configurations such as `--cluster-store`,
1294 1295
 `--cluster-advertise` and `--cluster-store-opts` will take effect only if
... ...
@@ -429,7 +429,7 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
429 429
 	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
430 430
 	c.Assert(err, checker.IsNil)
431 431
 
432
-	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, default-runtime=runc, insecure-registries=[], labels=[\"bar=foo\"], live-restore=false, max-concurrent-downloads=1, max-concurrent-uploads=5, name=%s, runtimes=runc:{docker-runc []}, shutdown-timeout=10)", daemonID, daemonName))
432
+	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, default-runtime=runc, insecure-registries=[], labels=[\"bar=foo\"], live-restore=false, max-concurrent-downloads=1, max-concurrent-uploads=5, name=%s, registry-mirrors=[], runtimes=runc:{docker-runc []}, shutdown-timeout=10)", daemonID, daemonName))
433 433
 }
434 434
 
435 435
 func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
... ...
@@ -84,16 +84,46 @@ func newServiceConfig(options ServiceOptions) *serviceConfig {
84 84
 			IndexConfigs:          make(map[string]*registrytypes.IndexInfo, 0),
85 85
 			// Hack: Bypass setting the mirrors to IndexConfigs since they are going away
86 86
 			// and Mirrors are only for the official registry anyways.
87
-			Mirrors: options.Mirrors,
88 87
 		},
89 88
 		V2Only: options.V2Only,
90 89
 	}
91 90
 
91
+	config.LoadMirrors(options.Mirrors)
92 92
 	config.LoadInsecureRegistries(options.InsecureRegistries)
93 93
 
94 94
 	return config
95 95
 }
96 96
 
97
+// LoadMirrors loads mirrors to config, after removing duplicates.
98
+// Returns an error if mirrors contains an invalid mirror.
99
+func (config *serviceConfig) LoadMirrors(mirrors []string) error {
100
+	mMap := map[string]struct{}{}
101
+	unique := []string{}
102
+
103
+	for _, mirror := range mirrors {
104
+		m, err := ValidateMirror(mirror)
105
+		if err != nil {
106
+			return err
107
+		}
108
+		if _, exist := mMap[m]; !exist {
109
+			mMap[m] = struct{}{}
110
+			unique = append(unique, m)
111
+		}
112
+	}
113
+
114
+	config.Mirrors = unique
115
+
116
+	// Configure public registry since mirrors may have changed.
117
+	config.IndexConfigs[IndexName] = &registrytypes.IndexInfo{
118
+		Name:     IndexName,
119
+		Mirrors:  config.Mirrors,
120
+		Secure:   true,
121
+		Official: true,
122
+	}
123
+
124
+	return nil
125
+}
126
+
97 127
 // LoadInsecureRegistries loads insecure registries to config
98 128
 func (config *serviceConfig) LoadInsecureRegistries(registries []string) error {
99 129
 	// Localhost is by default considered as an insecure registry
... ...
@@ -208,18 +238,20 @@ func isSecureIndex(config *serviceConfig, indexName string) bool {
208 208
 func ValidateMirror(val string) (string, error) {
209 209
 	uri, err := url.Parse(val)
210 210
 	if err != nil {
211
-		return "", fmt.Errorf("%s is not a valid URI", val)
211
+		return "", fmt.Errorf("invalid mirror: %q is not a valid URI", val)
212 212
 	}
213
-
214 213
 	if uri.Scheme != "http" && uri.Scheme != "https" {
215
-		return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
214
+		return "", fmt.Errorf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri)
216 215
 	}
217
-
218
-	if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
219
-		return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
216
+	if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
217
+		return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri)
220 218
 	}
221
-
222
-	return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil
219
+	if uri.User != nil {
220
+		// strip password from output
221
+		uri.User = url.UserPassword(uri.User.Username(), "xxxxx")
222
+		return "", fmt.Errorf("invalid mirror: username/password not allowed in URI %q", uri)
223
+	}
224
+	return strings.TrimSuffix(val, "/") + "/", nil
223 225
 }
224 226
 
225 227
 // ValidateIndexName validates an index name.
... ...
@@ -7,7 +7,9 @@ import (
7 7
 func TestValidateMirror(t *testing.T) {
8 8
 	valid := []string{
9 9
 		"http://mirror-1.com",
10
+		"http://mirror-1.com/",
10 11
 		"https://mirror-1.com",
12
+		"https://mirror-1.com/",
11 13
 		"http://localhost",
12 14
 		"https://localhost",
13 15
 		"http://localhost:5000",
... ...
@@ -21,15 +23,14 @@ func TestValidateMirror(t *testing.T) {
21 21
 	invalid := []string{
22 22
 		"!invalid!://%as%",
23 23
 		"ftp://mirror-1.com",
24
-		"http://mirror-1.com/",
25 24
 		"http://mirror-1.com/?q=foo",
26 25
 		"http://mirror-1.com/v1/",
27 26
 		"http://mirror-1.com/v1/?q=foo",
28 27
 		"http://mirror-1.com/v1/?q=foo#frag",
29 28
 		"http://mirror-1.com?q=foo",
30 29
 		"https://mirror-1.com#frag",
31
-		"https://mirror-1.com/",
32 30
 		"https://mirror-1.com/#frag",
31
+		"http://foo:bar@mirror-1.com/",
33 32
 		"https://mirror-1.com/v1/",
34 33
 		"https://mirror-1.com/v1/#",
35 34
 		"https://mirror-1.com?q",
... ...
@@ -663,7 +663,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
663 663
 		}
664 664
 		return false
665 665
 	}
666
-	s := DefaultService{config: makeServiceConfig([]string{"my.mirror"}, nil)}
666
+	s := DefaultService{config: makeServiceConfig([]string{"https://my.mirror"}, nil)}
667 667
 
668 668
 	imageName, err := reference.WithName(IndexName + "/test/image")
669 669
 	if err != nil {
... ...
@@ -31,6 +31,7 @@ type Service interface {
31 31
 	Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error)
32 32
 	ServiceConfig() *registrytypes.ServiceConfig
33 33
 	TLSConfig(hostname string) (*tls.Config, error)
34
+	LoadMirrors([]string) error
34 35
 	LoadInsecureRegistries([]string) error
35 36
 }
36 37
 
... ...
@@ -73,6 +74,14 @@ func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig {
73 73
 	return &servConfig
74 74
 }
75 75
 
76
+// LoadMirrors loads registry mirrors for Service
77
+func (s *DefaultService) LoadMirrors(mirrors []string) error {
78
+	s.mu.Lock()
79
+	defer s.mu.Unlock()
80
+
81
+	return s.config.LoadMirrors(mirrors)
82
+}
83
+
76 84
 // LoadInsecureRegistries loads insecure registries for Service
77 85
 func (s *DefaultService) LoadInsecureRegistries(registries []string) error {
78 86
 	s.mu.Lock()