From 59586d02b1cc004f14cd7ff6b454211f562da326 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 8 Mar 2016 16:03:37 -0500 Subject: [PATCH] Move registry service options to the daemon configuration. Allowing to set their values in the daemon configuration file. Signed-off-by: David Calavera --- daemon/config.go | 4 ++ daemon/info.go | 2 +- docker/daemon.go | 18 +++---- docker/daemon_test.go | 59 ++++++++++++++++++--- docs/reference/commandline/daemon.md | 5 +- registry/config.go | 76 +++++++++++++++------------- registry/registry.go | 7 --- registry/registry_mock_test.go | 21 ++------ registry/registry_test.go | 6 +-- registry/service.go | 21 +++++--- registry/service_v2.go | 2 +- 11 files changed, 133 insertions(+), 88 deletions(-) diff --git a/daemon/config.go b/daemon/config.go index 50e814ba4e..3e6257fefd 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/opts" "github.com/docker/docker/pkg/discovery" flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/registry" "github.com/imdario/mergo" ) @@ -96,6 +97,7 @@ type CommonConfig struct { CommonTLSOptions LogConfig bridgeConfig // bridgeConfig holds bridge network specific configuration. + registry.ServiceOptions reloadLock sync.Mutex valuesSet map[string]interface{} @@ -106,6 +108,8 @@ type CommonConfig struct { // Subsequent calls to `flag.Parse` will populate config with values parsed // from the command-line. func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string) string) { + config.ServiceOptions.InstallCliFlags(cmd, usageFn) + cmd.Var(opts.NewNamedListOptsRef("storage-opts", &config.GraphOptions, nil), []string{"-storage-opt"}, usageFn("Set storage driver options")) cmd.Var(opts.NewNamedListOptsRef("authorization-plugins", &config.AuthorizationPlugins, nil), []string{"-authorization-plugin"}, usageFn("List authorization plugins in order from first evaluator to last")) cmd.Var(opts.NewNamedListOptsRef("exec-opts", &config.ExecOptions, nil), []string{"-exec-opt"}, usageFn("Set exec driver options")) diff --git a/daemon/info.go b/daemon/info.go index 1ee367add1..f3c753b959 100644 --- a/daemon/info.go +++ b/daemon/info.go @@ -90,7 +90,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) { IndexServerAddress: registry.IndexServer, OSType: platform.OSType, Architecture: platform.Architecture, - RegistryConfig: daemon.RegistryService.Config, + RegistryConfig: daemon.RegistryService.ServiceConfig(), NCPU: runtime.NumCPU(), MemTotal: meminfo.MemTotal, DockerRootDir: daemon.configStore.Root, diff --git a/docker/daemon.go b/docker/daemon.go index efb459f6f9..9846e6280b 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -8,6 +8,7 @@ import ( "io" "os" "path/filepath" + "runtime" "strings" "time" @@ -51,8 +52,7 @@ var ( // DaemonCli represents the daemon CLI. type DaemonCli struct { *daemon.Config - registryOptions *registry.Options - flags *flag.FlagSet + flags *flag.FlagSet } func presentInHelp(usage string) string { return usage } @@ -67,17 +67,17 @@ func NewDaemonCli() *DaemonCli { daemonConfig.LogConfig.Config = make(map[string]string) daemonConfig.ClusterOpts = make(map[string]string) + if runtime.GOOS != "linux" { + daemonConfig.V2Only = true + } + daemonConfig.InstallFlags(daemonFlags, presentInHelp) daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp) - registryOptions := new(registry.Options) - registryOptions.InstallFlags(daemonFlags, presentInHelp) - registryOptions.InstallFlags(flag.CommandLine, absentFromHelp) daemonFlags.Require(flag.Exact, 0) return &DaemonCli{ - Config: daemonConfig, - registryOptions: registryOptions, - flags: daemonFlags, + Config: daemonConfig, + flags: daemonFlags, } } @@ -263,7 +263,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error { } cli.TrustKeyPath = commonFlags.TrustKey - registryService := registry.NewService(cli.registryOptions) + registryService := registry.NewService(cli.Config.ServiceOptions) d, err := daemon.NewDaemon(cli.Config, registryService) if err != nil { if pfile != nil { diff --git a/docker/daemon_test.go b/docker/daemon_test.go index 322e0b7604..c568bdb1ce 100644 --- a/docker/daemon_test.go +++ b/docker/daemon_test.go @@ -4,6 +4,7 @@ package main import ( "io/ioutil" + "os" "strings" "testing" @@ -63,8 +64,9 @@ func TestLoadDaemonCliConfigWithConflicts(t *testing.T) { if err != nil { t.Fatal(err) } - configFile := f.Name() + defer os.Remove(configFile) + f.Write([]byte(`{"labels": ["l3=foo"]}`)) f.Close() @@ -103,8 +105,9 @@ func TestLoadDaemonCliConfigWithTLSVerify(t *testing.T) { if err != nil { t.Fatal(err) } - configFile := f.Name() + defer os.Remove(configFile) + f.Write([]byte(`{"tlsverify": true}`)) f.Close() @@ -135,8 +138,9 @@ func TestLoadDaemonCliConfigWithExplicitTLSVerifyFalse(t *testing.T) { if err != nil { t.Fatal(err) } - configFile := f.Name() + defer os.Remove(configFile) + f.Write([]byte(`{"tlsverify": false}`)) f.Close() @@ -167,8 +171,9 @@ func TestLoadDaemonCliConfigWithoutTLSVerify(t *testing.T) { if err != nil { t.Fatal(err) } - configFile := f.Name() + defer os.Remove(configFile) + f.Write([]byte(`{}`)) f.Close() @@ -194,8 +199,9 @@ func TestLoadDaemonCliConfigWithLogLevel(t *testing.T) { if err != nil { t.Fatal(err) } - configFile := f.Name() + defer os.Remove(configFile) + f.Write([]byte(`{"log-level": "warn"}`)) f.Close() @@ -220,6 +226,7 @@ func TestLoadDaemonCliConfigWithLogLevel(t *testing.T) { func TestLoadDaemonConfigWithEmbeddedOptions(t *testing.T) { c := &daemon.Config{} common := &cli.CommonFlags{} + flags := mflag.NewFlagSet("test", mflag.ContinueOnError) flags.String([]string{"-tlscacert"}, "", "") flags.String([]string{"-log-driver"}, "", "") @@ -228,8 +235,9 @@ func TestLoadDaemonConfigWithEmbeddedOptions(t *testing.T) { if err != nil { t.Fatal(err) } - configFile := f.Name() + defer os.Remove(configFile) + f.Write([]byte(`{"tlscacert": "/etc/certs/ca.pem", "log-driver": "syslog"}`)) f.Close() @@ -247,3 +255,42 @@ func TestLoadDaemonConfigWithEmbeddedOptions(t *testing.T) { t.Fatalf("expected LogConfig type syslog, got %v", loadedConfig.LogConfig.Type) } } + +func TestLoadDaemonConfigWithRegistryOptions(t *testing.T) { + c := &daemon.Config{} + common := &cli.CommonFlags{} + flags := mflag.NewFlagSet("test", mflag.ContinueOnError) + c.ServiceOptions.InstallCliFlags(flags, absentFromHelp) + + f, err := ioutil.TempFile("", "docker-config-") + if err != nil { + t.Fatal(err) + } + configFile := f.Name() + defer os.Remove(configFile) + + f.Write([]byte(`{"registry-mirrors": ["https://mirrors.docker.com"], "insecure-registries": ["https://insecure.docker.com"], "disable-legacy-registry": true}`)) + f.Close() + + loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile) + if err != nil { + t.Fatal(err) + } + if loadedConfig == nil { + t.Fatal("expected configuration, got nil") + } + + m := loadedConfig.Mirrors + if len(m) != 1 { + t.Fatalf("expected 1 mirror, got %d", len(m)) + } + + r := loadedConfig.InsecureRegistries + if len(r) != 1 { + t.Fatalf("expected 1 insecure registries, got %d", len(r)) + } + + if !loadedConfig.V2Only { + t.Fatal("expected disable-legacy-registry to be true, got false") + } +} diff --git a/docs/reference/commandline/daemon.md b/docs/reference/commandline/daemon.md index ccd72c1034..6082ecaa62 100644 --- a/docs/reference/commandline/daemon.md +++ b/docs/reference/commandline/daemon.md @@ -883,7 +883,10 @@ This is a full example of the allowed configuration options in the file: "default-gateway": "", "default-gateway-v6": "", "icc": false, - "raw-logs": false + "raw-logs": false, + "registry-mirrors": [], + "insecure-registries": [], + "disable-legacy-registry": false } ``` diff --git a/registry/config.go b/registry/config.go index 7d8b6301aa..ab6f071589 100644 --- a/registry/config.go +++ b/registry/config.go @@ -13,10 +13,20 @@ import ( registrytypes "github.com/docker/engine-api/types/registry" ) -// Options holds command line options. -type Options struct { - Mirrors opts.ListOpts - InsecureRegistries opts.ListOpts +// ServiceOptions holds command line options. +type ServiceOptions struct { + Mirrors []string `json:"registry-mirrors,omitempty"` + InsecureRegistries []string `json:"insecure-registries,omitempty"` + + // V2Only controls access to legacy registries. If it is set to true via the + // command line flag the daemon will not attempt to contact v1 legacy registries + V2Only bool `json:"disable-legacy-registry,omitempty"` +} + +// serviceConfig holds daemon configuration for the registry service. +type serviceConfig struct { + registrytypes.ServiceConfig + V2Only bool } var ( @@ -42,51 +52,45 @@ var ( // not have the correct form ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")") - emptyServiceConfig = NewServiceConfig(nil) - - // V2Only controls access to legacy registries. If it is set to true via the - // command line flag the daemon will not attempt to contact v1 legacy registries - V2Only = false + emptyServiceConfig = newServiceConfig(ServiceOptions{}) ) // for mocking in unit tests var lookupIP = net.LookupIP -// InstallFlags adds command-line options to the top-level flag parser for +// InstallCliFlags adds command-line options to the top-level flag parser for // the current process. -func (options *Options) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) { - options.Mirrors = opts.NewListOpts(ValidateMirror) - cmd.Var(&options.Mirrors, []string{"-registry-mirror"}, usageFn("Preferred Docker registry mirror")) - options.InsecureRegistries = opts.NewListOpts(ValidateIndexName) - cmd.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, usageFn("Enable insecure registry communication")) - cmd.BoolVar(&V2Only, []string{"-disable-legacy-registry"}, false, usageFn("Do not contact legacy registries")) +func (options *ServiceOptions) InstallCliFlags(cmd *flag.FlagSet, usageFn func(string) string) { + mirrors := opts.NewNamedListOptsRef("registry-mirrors", &options.Mirrors, ValidateMirror) + cmd.Var(mirrors, []string{"-registry-mirror"}, usageFn("Preferred Docker registry mirror")) + + insecureRegistries := opts.NewNamedListOptsRef("insecure-registries", &options.InsecureRegistries, ValidateIndexName) + cmd.Var(insecureRegistries, []string{"-insecure-registry"}, usageFn("Enable insecure registry communication")) + + cmd.BoolVar(&options.V2Only, []string{"-disable-legacy-registry"}, false, usageFn("Do not contact legacy registries")) } -// NewServiceConfig returns a new instance of ServiceConfig -func NewServiceConfig(options *Options) *registrytypes.ServiceConfig { - if options == nil { - options = &Options{ - Mirrors: opts.NewListOpts(nil), - InsecureRegistries: opts.NewListOpts(nil), - } - } - +// newServiceConfig returns a new instance of ServiceConfig +func newServiceConfig(options ServiceOptions) *serviceConfig { // Localhost is by default considered as an insecure registry // This is a stop-gap for people who are running a private registry on localhost (especially on Boot2docker). // // TODO: should we deprecate this once it is easier for people to set up a TLS registry or change // daemon flags on boot2docker? - options.InsecureRegistries.Set("127.0.0.0/8") + options.InsecureRegistries = append(options.InsecureRegistries, "127.0.0.0/8") - config := ®istrytypes.ServiceConfig{ - InsecureRegistryCIDRs: make([]*registrytypes.NetIPNet, 0), - 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.GetAll(), + config := &serviceConfig{ + ServiceConfig: registrytypes.ServiceConfig{ + InsecureRegistryCIDRs: make([]*registrytypes.NetIPNet, 0), + 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, } // Split --insecure-registry into CIDR and registry-specific settings. - for _, r := range options.InsecureRegistries.GetAll() { + for _, r := range options.InsecureRegistries { // Check if CIDR was passed to --insecure-registry _, ipnet, err := net.ParseCIDR(r) if err == nil { @@ -125,7 +129,7 @@ func NewServiceConfig(options *Options) *registrytypes.ServiceConfig { // or an IP address. If it is a domain name, then it will be resolved in order to check if the IP is contained // in a subnet. If the resolving is not successful, isSecureIndex will only try to match hostname to any element // of insecureRegistries. -func isSecureIndex(config *registrytypes.ServiceConfig, indexName string) bool { +func isSecureIndex(config *serviceConfig, indexName string) bool { // Check for configured index, first. This is needed in case isSecureIndex // is called from anything besides newIndexInfo, in order to honor per-index configurations. if index, ok := config.IndexConfigs[indexName]; ok { @@ -201,7 +205,7 @@ func validateNoSchema(reposName string) error { } // newIndexInfo returns IndexInfo configuration from indexName -func newIndexInfo(config *registrytypes.ServiceConfig, indexName string) (*registrytypes.IndexInfo, error) { +func newIndexInfo(config *serviceConfig, indexName string) (*registrytypes.IndexInfo, error) { var err error indexName, err = ValidateIndexName(indexName) if err != nil { @@ -233,7 +237,7 @@ func GetAuthConfigKey(index *registrytypes.IndexInfo) string { } // newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo -func newRepositoryInfo(config *registrytypes.ServiceConfig, name reference.Named) (*RepositoryInfo, error) { +func newRepositoryInfo(config *serviceConfig, name reference.Named) (*RepositoryInfo, error) { index, err := newIndexInfo(config, name.Hostname()) if err != nil { return nil, err diff --git a/registry/registry.go b/registry/registry.go index 9071d9dc14..8fdfe3b0a4 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -11,7 +11,6 @@ import ( "net/http" "os" "path/filepath" - "runtime" "strings" "time" @@ -26,12 +25,6 @@ var ( ErrAlreadyExists = errors.New("Image already exists") ) -func init() { - if runtime.GOOS != "linux" { - V2Only = true - } -} - func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) { // PreferredServerCipherSuites should have no effect tlsConfig := tlsconfig.ServerDefault diff --git a/registry/registry_mock_test.go b/registry/registry_mock_test.go index 057afac10d..828f48fc92 100644 --- a/registry/registry_mock_test.go +++ b/registry/registry_mock_test.go @@ -15,7 +15,6 @@ import ( "testing" "time" - "github.com/docker/docker/opts" "github.com/docker/docker/reference" registrytypes "github.com/docker/engine-api/types/registry" "github.com/gorilla/mux" @@ -174,23 +173,13 @@ func makePublicIndex() *registrytypes.IndexInfo { return index } -func makeServiceConfig(mirrors []string, insecureRegistries []string) *registrytypes.ServiceConfig { - options := &Options{ - Mirrors: opts.NewListOpts(nil), - InsecureRegistries: opts.NewListOpts(nil), - } - if mirrors != nil { - for _, mirror := range mirrors { - options.Mirrors.Set(mirror) - } - } - if insecureRegistries != nil { - for _, insecureRegistries := range insecureRegistries { - options.InsecureRegistries.Set(insecureRegistries) - } +func makeServiceConfig(mirrors []string, insecureRegistries []string) *serviceConfig { + options := ServiceOptions{ + Mirrors: mirrors, + InsecureRegistries: insecureRegistries, } - return NewServiceConfig(options) + return newServiceConfig(options) } func writeHeaders(w http.ResponseWriter) { diff --git a/registry/registry_test.go b/registry/registry_test.go index 02eb683d05..7f9cc8e4c4 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -523,7 +523,7 @@ func TestParseRepositoryInfo(t *testing.T) { } func TestNewIndexInfo(t *testing.T) { - testIndexInfo := func(config *registrytypes.ServiceConfig, expectedIndexInfos map[string]*registrytypes.IndexInfo) { + testIndexInfo := func(config *serviceConfig, expectedIndexInfos map[string]*registrytypes.IndexInfo) { for indexName, expectedIndexInfo := range expectedIndexInfos { index, err := newIndexInfo(config, indexName) if err != nil { @@ -537,7 +537,7 @@ func TestNewIndexInfo(t *testing.T) { } } - config := NewServiceConfig(nil) + config := newServiceConfig(ServiceOptions{}) noMirrors := []string{} expectedIndexInfos := map[string]*registrytypes.IndexInfo{ IndexName: { @@ -661,7 +661,7 @@ func TestMirrorEndpointLookup(t *testing.T) { } return false } - s := Service{Config: makeServiceConfig([]string{"my.mirror"}, nil)} + s := Service{config: makeServiceConfig([]string{"my.mirror"}, nil)} imageName, err := reference.WithName(IndexName + "/test/image") if err != nil { diff --git a/registry/service.go b/registry/service.go index 2124da6d9f..47bffed308 100644 --- a/registry/service.go +++ b/registry/service.go @@ -15,17 +15,22 @@ import ( // Service is a registry service. It tracks configuration data such as a list // of mirrors. type Service struct { - Config *registrytypes.ServiceConfig + config *serviceConfig } // NewService returns a new instance of Service ready to be // installed into an engine. -func NewService(options *Options) *Service { +func NewService(options ServiceOptions) *Service { return &Service{ - Config: NewServiceConfig(options), + config: newServiceConfig(options), } } +// ServiceConfig returns the public registry service configuration. +func (s *Service) ServiceConfig() *registrytypes.ServiceConfig { + return &s.config.ServiceConfig +} + // Auth contacts the public registry with the provided credentials, // and returns OK if authentication was successful. // It can be used to verify the validity of a client's credentials. @@ -82,7 +87,7 @@ func (s *Service) Search(term string, authConfig *types.AuthConfig, userAgent st indexName, remoteName := splitReposSearchTerm(term) - index, err := newIndexInfo(s.Config, indexName) + index, err := newIndexInfo(s.config, indexName) if err != nil { return nil, err } @@ -113,12 +118,12 @@ func (s *Service) Search(term string, authConfig *types.AuthConfig, userAgent st // ResolveRepository splits a repository name into its components // and configuration of the associated registry. func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, error) { - return newRepositoryInfo(s.Config, name) + return newRepositoryInfo(s.config, name) } // ResolveIndex takes indexName and returns index info func (s *Service) ResolveIndex(name string) (*registrytypes.IndexInfo, error) { - return newIndexInfo(s.Config, name) + return newIndexInfo(s.config, name) } // APIEndpoint represents a remote API endpoint @@ -138,7 +143,7 @@ func (e APIEndpoint) ToV1Endpoint(userAgent string, metaHeaders http.Header) (*V // TLSConfig constructs a client TLS configuration based on server defaults func (s *Service) TLSConfig(hostname string) (*tls.Config, error) { - return newTLSConfig(hostname, isSecureIndex(s.Config, hostname)) + return newTLSConfig(hostname, isSecureIndex(s.config, hostname)) } func (s *Service) tlsConfigForMirror(mirrorURL *url.URL) (*tls.Config, error) { @@ -173,7 +178,7 @@ func (s *Service) lookupEndpoints(hostname string) (endpoints []APIEndpoint, err return nil, err } - if V2Only { + if s.config.V2Only { return endpoints, nil } diff --git a/registry/service_v2.go b/registry/service_v2.go index 9c909f186e..744be691e8 100644 --- a/registry/service_v2.go +++ b/registry/service_v2.go @@ -12,7 +12,7 @@ func (s *Service) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, e tlsConfig := &cfg if hostname == DefaultNamespace { // v2 mirrors - for _, mirror := range s.Config.Mirrors { + for _, mirror := range s.config.Mirrors { if !strings.HasPrefix(mirror, "http://") && !strings.HasPrefix(mirror, "https://") { mirror = "https://" + mirror }