Signed-off-by: allencloud <allen.sun@daocloud.io>
| ... | ... |
@@ -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] = ®istrytypes.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() |