Skip to content

Commit 5b9348c

Browse files
committed
support registry mirror config reload
Signed-off-by: allencloud <[email protected]>
1 parent 355f2ae commit 5b9348c

8 files changed

Lines changed: 174 additions & 18 deletions

File tree

daemon/daemon.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -995,14 +995,15 @@ func (daemon *Daemon) initDiscovery(config *Config) error {
995995
// Reload reads configuration changes and modifies the
996996
// daemon according to those changes.
997997
// 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
10011002
// - Daemon max concurrent downloads
10021003
// - Daemon max concurrent uploads
1003-
// - Cluster discovery (reconfigure and restart).
1004+
// - Cluster discovery (reconfigure and restart)
10041005
// - Daemon live restore
1005-
// - Daemon shutdown timeout (in seconds).
1006+
// - Daemon shutdown timeout (in seconds)
10061007
func (daemon *Daemon) Reload(config *Config) (err error) {
10071008

10081009
daemon.configStore.reloadLock.Lock()
@@ -1035,6 +1036,14 @@ func (daemon *Daemon) Reload(config *Config) (err error) {
10351036
return err
10361037
}
10371038
}
1039+
1040+
if config.IsValueSet("registry-mirrors") {
1041+
daemon.configStore.Mirrors = config.Mirrors
1042+
if err := daemon.RegistryService.LoadMirrors(config.Mirrors); err != nil {
1043+
return err
1044+
}
1045+
}
1046+
10381047
if config.IsValueSet("live-restore") {
10391048
daemon.configStore.LiveRestoreEnabled = config.LiveRestoreEnabled
10401049
if err := daemon.containerdRemote.UpdateOptions(libcontainerd.WithLiveRestore(config.LiveRestoreEnabled)); err != nil {
@@ -1087,6 +1096,16 @@ func (daemon *Daemon) Reload(config *Config) (err error) {
10871096
attributes["insecure-registries"] = "[]"
10881097
}
10891098

1099+
if daemon.configStore.Mirrors != nil {
1100+
mirrors, err := json.Marshal(daemon.configStore.Mirrors)
1101+
if err != nil {
1102+
return err
1103+
}
1104+
attributes["registry-mirrors"] = string(mirrors)
1105+
} else {
1106+
attributes["registry-mirrors"] = "[]"
1107+
}
1108+
10901109
attributes["cluster-store"] = daemon.configStore.ClusterStore
10911110
if daemon.configStore.ClusterOpts != nil {
10921111
opts, err := json.Marshal(daemon.configStore.ClusterOpts)

daemon/daemon_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,100 @@ func TestDaemonReloadLabels(t *testing.T) {
341341
}
342342
}
343343

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+
344438
func TestDaemonReloadInsecureRegistries(t *testing.T) {
345439
daemon := &Daemon{}
346440
// initialize daemon with existing insecure registries: "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000"

docs/reference/commandline/dockerd.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,7 @@ The list of currently supported options that can be reconfigured is this:
12891289
be used to run containers
12901290
- `authorization-plugin`: specifies the authorization plugins to use.
12911291
- `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.
12921293

12931294
Updating and reloading the cluster configurations such as `--cluster-store`,
12941295
`--cluster-advertise` and `--cluster-store-opts` will take effect only if

integration-cli/docker_cli_events_unix_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
429429
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
430430
c.Assert(err, checker.IsNil)
431431

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))
433433
}
434434

435435
func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {

registry/config.go

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,46 @@ func newServiceConfig(options ServiceOptions) *serviceConfig {
8484
IndexConfigs: make(map[string]*registrytypes.IndexInfo, 0),
8585
// Hack: Bypass setting the mirrors to IndexConfigs since they are going away
8686
// and Mirrors are only for the official registry anyways.
87-
Mirrors: options.Mirrors,
8887
},
8988
V2Only: options.V2Only,
9089
}
9190

91+
config.LoadMirrors(options.Mirrors)
9292
config.LoadInsecureRegistries(options.InsecureRegistries)
9393

9494
return config
9595
}
9696

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+
97127
// LoadInsecureRegistries loads insecure registries to config
98128
func (config *serviceConfig) LoadInsecureRegistries(registries []string) error {
99129
// Localhost is by default considered as an insecure registry
@@ -208,18 +238,20 @@ func isSecureIndex(config *serviceConfig, indexName string) bool {
208238
func ValidateMirror(val string) (string, error) {
209239
uri, err := url.Parse(val)
210240
if err != nil {
211-
return "", fmt.Errorf("%s is not a valid URI", val)
241+
return "", fmt.Errorf("invalid mirror: %q is not a valid URI", val)
212242
}
213-
214243
if uri.Scheme != "http" && uri.Scheme != "https" {
215-
return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
244+
return "", fmt.Errorf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri)
216245
}
217-
218-
if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
219-
return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
246+
if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
247+
return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri)
220248
}
221-
222-
return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil
249+
if uri.User != nil {
250+
// strip password from output
251+
uri.User = url.UserPassword(uri.User.Username(), "xxxxx")
252+
return "", fmt.Errorf("invalid mirror: username/password not allowed in URI %q", uri)
253+
}
254+
return strings.TrimSuffix(val, "/") + "/", nil
223255
}
224256

225257
// ValidateIndexName validates an index name.

registry/config_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import (
77
func TestValidateMirror(t *testing.T) {
88
valid := []string{
99
"http://mirror-1.com",
10+
"http://mirror-1.com/",
1011
"https://mirror-1.com",
12+
"https://mirror-1.com/",
1113
"http://localhost",
1214
"https://localhost",
1315
"http://localhost:5000",
@@ -21,15 +23,14 @@ func TestValidateMirror(t *testing.T) {
2123
invalid := []string{
2224
"!invalid!://%as%",
2325
"ftp://mirror-1.com",
24-
"http://mirror-1.com/",
2526
"http://mirror-1.com/?q=foo",
2627
"http://mirror-1.com/v1/",
2728
"http://mirror-1.com/v1/?q=foo",
2829
"http://mirror-1.com/v1/?q=foo#frag",
2930
"http://mirror-1.com?q=foo",
3031
"https://mirror-1.com#frag",
31-
"https://mirror-1.com/",
3232
"https://mirror-1.com/#frag",
33+
"http://foo:[email protected]/",
3334
"https://mirror-1.com/v1/",
3435
"https://mirror-1.com/v1/#",
3536
"https://mirror-1.com?q",

registry/registry_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
663663
}
664664
return false
665665
}
666-
s := DefaultService{config: makeServiceConfig([]string{"my.mirror"}, nil)}
666+
s := DefaultService{config: makeServiceConfig([]string{"https://my.mirror"}, nil)}
667667

668668
imageName, err := reference.WithName(IndexName + "/test/image")
669669
if err != nil {

registry/service.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type Service interface {
3131
Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error)
3232
ServiceConfig() *registrytypes.ServiceConfig
3333
TLSConfig(hostname string) (*tls.Config, error)
34+
LoadMirrors([]string) error
3435
LoadInsecureRegistries([]string) error
3536
}
3637

@@ -73,6 +74,14 @@ func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig {
7374
return &servConfig
7475
}
7576

77+
// LoadMirrors loads registry mirrors for Service
78+
func (s *DefaultService) LoadMirrors(mirrors []string) error {
79+
s.mu.Lock()
80+
defer s.mu.Unlock()
81+
82+
return s.config.LoadMirrors(mirrors)
83+
}
84+
7685
// LoadInsecureRegistries loads insecure registries for Service
7786
func (s *DefaultService) LoadInsecureRegistries(registries []string) error {
7887
s.mu.Lock()

0 commit comments

Comments
 (0)