Merge pull request #29650 from allencloud/support-registry-mirror-config-reload

support registry mirror config reload
This commit is contained in:
Sebastiaan van Stijn 2017-01-04 13:46:19 +01:00 committed by GitHub
commit d3f30d6a67
8 changed files with 174 additions and 18 deletions

View File

@ -995,14 +995,15 @@ func (daemon *Daemon) initDiscovery(config *Config) error {
// Reload reads configuration changes and modifies the
// daemon according to those changes.
// These are the settings that Reload changes:
// - Daemon labels.
// - Daemon debug log level.
// - Daemon insecure registries.
// - Daemon labels
// - Daemon debug log level
// - Insecure registries
// - Registry mirrors
// - Daemon max concurrent downloads
// - Daemon max concurrent uploads
// - Cluster discovery (reconfigure and restart).
// - Cluster discovery (reconfigure and restart)
// - Daemon live restore
// - Daemon shutdown timeout (in seconds).
// - Daemon shutdown timeout (in seconds)
func (daemon *Daemon) Reload(config *Config) (err error) {
daemon.configStore.reloadLock.Lock()
@ -1035,6 +1036,14 @@ func (daemon *Daemon) Reload(config *Config) (err error) {
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") {
daemon.configStore.LiveRestoreEnabled = config.LiveRestoreEnabled
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"] = "[]"
}
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
if daemon.configStore.ClusterOpts != nil {
opts, err := json.Marshal(daemon.configStore.ClusterOpts)

View File

@ -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) {
daemon := &Daemon{}
// initialize daemon with existing insecure registries: "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000"

View File

@ -1287,6 +1287,7 @@ The list of currently supported options that can be reconfigured is this:
be used to run containers
- `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.
- `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`,
`--cluster-advertise` and `--cluster-store-opts` will take effect only if

View File

@ -429,7 +429,7 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
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) {

View File

@ -84,16 +84,46 @@ func newServiceConfig(options ServiceOptions) *serviceConfig {
IndexConfigs: make(map[string]*registrytypes.IndexInfo, 0),
// Hack: Bypass setting the mirrors to IndexConfigs since they are going away
// and Mirrors are only for the official registry anyways.
Mirrors: options.Mirrors,
},
V2Only: options.V2Only,
}
config.LoadMirrors(options.Mirrors)
config.LoadInsecureRegistries(options.InsecureRegistries)
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] = &registrytypes.IndexInfo{
Name: IndexName,
Mirrors: config.Mirrors,
Secure: true,
Official: true,
}
return nil
}
// LoadInsecureRegistries loads insecure registries to config
func (config *serviceConfig) LoadInsecureRegistries(registries []string) error {
// 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) {
uri, err := url.Parse(val)
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" {
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.RawQuery != "" || uri.Fragment != "" {
return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri)
}
return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil
if uri.User != 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.

View File

@ -7,7 +7,9 @@ import (
func TestValidateMirror(t *testing.T) {
valid := []string{
"http://mirror-1.com",
"http://mirror-1.com/",
"https://mirror-1.com",
"https://mirror-1.com/",
"http://localhost",
"https://localhost",
"http://localhost:5000",
@ -21,15 +23,14 @@ func TestValidateMirror(t *testing.T) {
invalid := []string{
"!invalid!://%as%",
"ftp://mirror-1.com",
"http://mirror-1.com/",
"http://mirror-1.com/?q=foo",
"http://mirror-1.com/v1/",
"http://mirror-1.com/v1/?q=foo",
"http://mirror-1.com/v1/?q=foo#frag",
"http://mirror-1.com?q=foo",
"https://mirror-1.com#frag",
"https://mirror-1.com/",
"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?q",

View File

@ -663,7 +663,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
}
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")
if err != nil {

View File

@ -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)
ServiceConfig() *registrytypes.ServiceConfig
TLSConfig(hostname string) (*tls.Config, error)
LoadMirrors([]string) error
LoadInsecureRegistries([]string) error
}
@ -73,6 +74,14 @@ func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig {
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
func (s *DefaultService) LoadInsecureRegistries(registries []string) error {
s.mu.Lock()