mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
support registry mirror config reload
Signed-off-by: allencloud <allen.sun@daocloud.io>
This commit is contained in:
parent
355f2ae972
commit
5b9348c553
8 changed files with 174 additions and 18 deletions
|
@ -995,14 +995,15 @@ func (daemon *Daemon) initDiscovery(config *Config) error {
|
||||||
// Reload reads configuration changes and modifies the
|
// Reload reads configuration changes and modifies the
|
||||||
// daemon according to those changes.
|
// daemon according to those changes.
|
||||||
// These are the settings that Reload changes:
|
// These are the settings that Reload changes:
|
||||||
// - Daemon labels.
|
// - Daemon labels
|
||||||
// - Daemon debug log level.
|
// - Daemon debug log level
|
||||||
// - Daemon insecure registries.
|
// - Insecure registries
|
||||||
|
// - Registry mirrors
|
||||||
// - Daemon max concurrent downloads
|
// - Daemon max concurrent downloads
|
||||||
// - Daemon max concurrent uploads
|
// - Daemon max concurrent uploads
|
||||||
// - Cluster discovery (reconfigure and restart).
|
// - Cluster discovery (reconfigure and restart)
|
||||||
// - Daemon live restore
|
// - Daemon live restore
|
||||||
// - Daemon shutdown timeout (in seconds).
|
// - Daemon shutdown timeout (in seconds)
|
||||||
func (daemon *Daemon) Reload(config *Config) (err error) {
|
func (daemon *Daemon) Reload(config *Config) (err error) {
|
||||||
|
|
||||||
daemon.configStore.reloadLock.Lock()
|
daemon.configStore.reloadLock.Lock()
|
||||||
|
@ -1035,6 +1036,14 @@ func (daemon *Daemon) Reload(config *Config) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.IsValueSet("registry-mirrors") {
|
||||||
|
daemon.configStore.Mirrors = config.Mirrors
|
||||||
|
if err := daemon.RegistryService.LoadMirrors(config.Mirrors); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if config.IsValueSet("live-restore") {
|
if config.IsValueSet("live-restore") {
|
||||||
daemon.configStore.LiveRestoreEnabled = config.LiveRestoreEnabled
|
daemon.configStore.LiveRestoreEnabled = config.LiveRestoreEnabled
|
||||||
if err := daemon.containerdRemote.UpdateOptions(libcontainerd.WithLiveRestore(config.LiveRestoreEnabled)); err != nil {
|
if err := daemon.containerdRemote.UpdateOptions(libcontainerd.WithLiveRestore(config.LiveRestoreEnabled)); err != nil {
|
||||||
|
@ -1087,6 +1096,16 @@ func (daemon *Daemon) Reload(config *Config) (err error) {
|
||||||
attributes["insecure-registries"] = "[]"
|
attributes["insecure-registries"] = "[]"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if daemon.configStore.Mirrors != nil {
|
||||||
|
mirrors, err := json.Marshal(daemon.configStore.Mirrors)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
attributes["registry-mirrors"] = string(mirrors)
|
||||||
|
} else {
|
||||||
|
attributes["registry-mirrors"] = "[]"
|
||||||
|
}
|
||||||
|
|
||||||
attributes["cluster-store"] = daemon.configStore.ClusterStore
|
attributes["cluster-store"] = daemon.configStore.ClusterStore
|
||||||
if daemon.configStore.ClusterOpts != nil {
|
if daemon.configStore.ClusterOpts != nil {
|
||||||
opts, err := json.Marshal(daemon.configStore.ClusterOpts)
|
opts, err := json.Marshal(daemon.configStore.ClusterOpts)
|
||||||
|
|
|
@ -341,6 +341,100 @@ func TestDaemonReloadLabels(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDaemonReloadMirrors(t *testing.T) {
|
||||||
|
daemon := &Daemon{}
|
||||||
|
|
||||||
|
daemon.RegistryService = registry.NewService(registry.ServiceOptions{
|
||||||
|
InsecureRegistries: []string{},
|
||||||
|
Mirrors: []string{
|
||||||
|
"https://mirror.test1.com",
|
||||||
|
"https://mirror.test2.com", // this will be removed when reloading
|
||||||
|
"https://mirror.test3.com", // this will be removed when reloading
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
daemon.configStore = &Config{}
|
||||||
|
|
||||||
|
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.com"}, // mirrors are invalid
|
||||||
|
after: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
valid: true,
|
||||||
|
mirrors: []string{"https://mirror.test1.com", "https://mirror.test4.com"},
|
||||||
|
after: []string{"https://mirror.test1.com/", "https://mirror.test4.com/"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range loadMirrors {
|
||||||
|
valuesSets := make(map[string]interface{})
|
||||||
|
valuesSets["registry-mirrors"] = value.mirrors
|
||||||
|
|
||||||
|
newConfig := &Config{
|
||||||
|
CommonConfig: 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) {
|
func TestDaemonReloadInsecureRegistries(t *testing.T) {
|
||||||
daemon := &Daemon{}
|
daemon := &Daemon{}
|
||||||
// initialize daemon with existing insecure registries: "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000"
|
// 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:
|
||||||
be used to run containers
|
be used to run containers
|
||||||
- `authorization-plugin`: specifies the authorization plugins to use.
|
- `authorization-plugin`: specifies the authorization plugins to use.
|
||||||
- `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.
|
- `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.
|
||||||
|
- `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.
|
||||||
|
|
||||||
Updating and reloading the cluster configurations such as `--cluster-store`,
|
Updating and reloading the cluster configurations such as `--cluster-store`,
|
||||||
`--cluster-advertise` and `--cluster-store-opts` will take effect only if
|
`--cluster-advertise` and `--cluster-store-opts` will take effect only if
|
||||||
|
|
|
@ -429,7 +429,7 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
|
||||||
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
|
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
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))
|
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
|
func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
|
||||||
|
|
|
@ -84,16 +84,46 @@ func newServiceConfig(options ServiceOptions) *serviceConfig {
|
||||||
IndexConfigs: make(map[string]*registrytypes.IndexInfo, 0),
|
IndexConfigs: make(map[string]*registrytypes.IndexInfo, 0),
|
||||||
// Hack: Bypass setting the mirrors to IndexConfigs since they are going away
|
// Hack: Bypass setting the mirrors to IndexConfigs since they are going away
|
||||||
// and Mirrors are only for the official registry anyways.
|
// and Mirrors are only for the official registry anyways.
|
||||||
Mirrors: options.Mirrors,
|
|
||||||
},
|
},
|
||||||
V2Only: options.V2Only,
|
V2Only: options.V2Only,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.LoadMirrors(options.Mirrors)
|
||||||
config.LoadInsecureRegistries(options.InsecureRegistries)
|
config.LoadInsecureRegistries(options.InsecureRegistries)
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadMirrors loads mirrors to config, after removing duplicates.
|
||||||
|
// Returns an error if mirrors contains an invalid mirror.
|
||||||
|
func (config *serviceConfig) LoadMirrors(mirrors []string) error {
|
||||||
|
mMap := map[string]struct{}{}
|
||||||
|
unique := []string{}
|
||||||
|
|
||||||
|
for _, mirror := range mirrors {
|
||||||
|
m, err := ValidateMirror(mirror)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, exist := mMap[m]; !exist {
|
||||||
|
mMap[m] = struct{}{}
|
||||||
|
unique = append(unique, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Mirrors = unique
|
||||||
|
|
||||||
|
// Configure public registry since mirrors may have changed.
|
||||||
|
config.IndexConfigs[IndexName] = ®istrytypes.IndexInfo{
|
||||||
|
Name: IndexName,
|
||||||
|
Mirrors: config.Mirrors,
|
||||||
|
Secure: true,
|
||||||
|
Official: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// LoadInsecureRegistries loads insecure registries to config
|
// LoadInsecureRegistries loads insecure registries to config
|
||||||
func (config *serviceConfig) LoadInsecureRegistries(registries []string) error {
|
func (config *serviceConfig) LoadInsecureRegistries(registries []string) error {
|
||||||
// Localhost is by default considered as an insecure registry
|
// Localhost is by default considered as an insecure registry
|
||||||
|
@ -208,18 +238,20 @@ func isSecureIndex(config *serviceConfig, indexName string) bool {
|
||||||
func ValidateMirror(val string) (string, error) {
|
func ValidateMirror(val string) (string, error) {
|
||||||
uri, err := url.Parse(val)
|
uri, err := url.Parse(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("%s is not a valid URI", val)
|
return "", fmt.Errorf("invalid mirror: %q is not a valid URI", val)
|
||||||
}
|
}
|
||||||
|
|
||||||
if uri.Scheme != "http" && uri.Scheme != "https" {
|
if uri.Scheme != "http" && uri.Scheme != "https" {
|
||||||
return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
|
return "", fmt.Errorf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri)
|
||||||
}
|
}
|
||||||
|
if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
|
||||||
if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
|
return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri)
|
||||||
return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
|
|
||||||
}
|
}
|
||||||
|
if uri.User != nil {
|
||||||
return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil
|
// strip password from output
|
||||||
|
uri.User = url.UserPassword(uri.User.Username(), "xxxxx")
|
||||||
|
return "", fmt.Errorf("invalid mirror: username/password not allowed in URI %q", uri)
|
||||||
|
}
|
||||||
|
return strings.TrimSuffix(val, "/") + "/", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateIndexName validates an index name.
|
// ValidateIndexName validates an index name.
|
||||||
|
|
|
@ -7,7 +7,9 @@ import (
|
||||||
func TestValidateMirror(t *testing.T) {
|
func TestValidateMirror(t *testing.T) {
|
||||||
valid := []string{
|
valid := []string{
|
||||||
"http://mirror-1.com",
|
"http://mirror-1.com",
|
||||||
|
"http://mirror-1.com/",
|
||||||
"https://mirror-1.com",
|
"https://mirror-1.com",
|
||||||
|
"https://mirror-1.com/",
|
||||||
"http://localhost",
|
"http://localhost",
|
||||||
"https://localhost",
|
"https://localhost",
|
||||||
"http://localhost:5000",
|
"http://localhost:5000",
|
||||||
|
@ -21,15 +23,14 @@ func TestValidateMirror(t *testing.T) {
|
||||||
invalid := []string{
|
invalid := []string{
|
||||||
"!invalid!://%as%",
|
"!invalid!://%as%",
|
||||||
"ftp://mirror-1.com",
|
"ftp://mirror-1.com",
|
||||||
"http://mirror-1.com/",
|
|
||||||
"http://mirror-1.com/?q=foo",
|
"http://mirror-1.com/?q=foo",
|
||||||
"http://mirror-1.com/v1/",
|
"http://mirror-1.com/v1/",
|
||||||
"http://mirror-1.com/v1/?q=foo",
|
"http://mirror-1.com/v1/?q=foo",
|
||||||
"http://mirror-1.com/v1/?q=foo#frag",
|
"http://mirror-1.com/v1/?q=foo#frag",
|
||||||
"http://mirror-1.com?q=foo",
|
"http://mirror-1.com?q=foo",
|
||||||
"https://mirror-1.com#frag",
|
"https://mirror-1.com#frag",
|
||||||
"https://mirror-1.com/",
|
|
||||||
"https://mirror-1.com/#frag",
|
"https://mirror-1.com/#frag",
|
||||||
|
"http://foo:bar@mirror-1.com/",
|
||||||
"https://mirror-1.com/v1/",
|
"https://mirror-1.com/v1/",
|
||||||
"https://mirror-1.com/v1/#",
|
"https://mirror-1.com/v1/#",
|
||||||
"https://mirror-1.com?q",
|
"https://mirror-1.com?q",
|
||||||
|
|
|
@ -663,7 +663,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
s := DefaultService{config: makeServiceConfig([]string{"my.mirror"}, nil)}
|
s := DefaultService{config: makeServiceConfig([]string{"https://my.mirror"}, nil)}
|
||||||
|
|
||||||
imageName, err := reference.WithName(IndexName + "/test/image")
|
imageName, err := reference.WithName(IndexName + "/test/image")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -31,6 +31,7 @@ type Service interface {
|
||||||
Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error)
|
Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error)
|
||||||
ServiceConfig() *registrytypes.ServiceConfig
|
ServiceConfig() *registrytypes.ServiceConfig
|
||||||
TLSConfig(hostname string) (*tls.Config, error)
|
TLSConfig(hostname string) (*tls.Config, error)
|
||||||
|
LoadMirrors([]string) error
|
||||||
LoadInsecureRegistries([]string) error
|
LoadInsecureRegistries([]string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +74,14 @@ func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig {
|
||||||
return &servConfig
|
return &servConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadMirrors loads registry mirrors for Service
|
||||||
|
func (s *DefaultService) LoadMirrors(mirrors []string) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
return s.config.LoadMirrors(mirrors)
|
||||||
|
}
|
||||||
|
|
||||||
// LoadInsecureRegistries loads insecure registries for Service
|
// LoadInsecureRegistries loads insecure registries for Service
|
||||||
func (s *DefaultService) LoadInsecureRegistries(registries []string) error {
|
func (s *DefaultService) LoadInsecureRegistries(registries []string) error {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue