From 3d605683b3d272982399635a55ee81b2a7535e81 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Sun, 27 Apr 2014 15:06:09 -0700 Subject: [PATCH 1/3] Move 'auth' to the registry subsystem This is the first step towards separating the registry subsystem from the deprecated `Server` object. * New service `github.com/dotcloud/docker/registry/Service` * The service is installed by default in `builtins` * The service only exposes `auth` for now... * ...Soon to be followed by `pull`, `push` and `search`. Docker-DCO-1.1-Signed-off-by: Solomon Hykes (github: shykes) --- builtins/builtins.go | 4 +++ registry/registry.go | 39 ++++++++++++++++++++++++++ registry/service.go | 54 ++++++++++++++++++++++++++++++++++++ server/server.go | 66 ++------------------------------------------ 4 files changed, 100 insertions(+), 63 deletions(-) create mode 100644 registry/service.go diff --git a/builtins/builtins.go b/builtins/builtins.go index 374bd48701..bd3b33d0d3 100644 --- a/builtins/builtins.go +++ b/builtins/builtins.go @@ -4,12 +4,16 @@ import ( api "github.com/dotcloud/docker/api/server" "github.com/dotcloud/docker/daemon/networkdriver/bridge" "github.com/dotcloud/docker/engine" + "github.com/dotcloud/docker/registry" "github.com/dotcloud/docker/server" ) func Register(eng *engine.Engine) { daemon(eng) remote(eng) + // FIXME: engine.Installer.Install can fail. These errors + // should be passed up. + registry.NewService().Install(eng) } // remote: a RESTful api for cross-docker communication diff --git a/registry/registry.go b/registry/registry.go index 1bd73cdeb5..55154e364b 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -13,10 +13,12 @@ import ( "net/http/cookiejar" "net/url" "regexp" + "runtime" "strconv" "strings" "time" + "github.com/dotcloud/docker/dockerversion" "github.com/dotcloud/docker/utils" ) @@ -757,3 +759,40 @@ func NewRegistry(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, inde r.reqFactory = factory return r, nil } + +func HTTPRequestFactory(metaHeaders map[string][]string) *utils.HTTPRequestFactory { + // FIXME: this replicates the 'info' job. + httpVersion := make([]utils.VersionInfo, 0, 4) + httpVersion = append(httpVersion, &simpleVersionInfo{"docker", dockerversion.VERSION}) + httpVersion = append(httpVersion, &simpleVersionInfo{"go", runtime.Version()}) + httpVersion = append(httpVersion, &simpleVersionInfo{"git-commit", dockerversion.GITCOMMIT}) + if kernelVersion, err := utils.GetKernelVersion(); err == nil { + httpVersion = append(httpVersion, &simpleVersionInfo{"kernel", kernelVersion.String()}) + } + httpVersion = append(httpVersion, &simpleVersionInfo{"os", runtime.GOOS}) + httpVersion = append(httpVersion, &simpleVersionInfo{"arch", runtime.GOARCH}) + ud := utils.NewHTTPUserAgentDecorator(httpVersion...) + md := &utils.HTTPMetaHeadersDecorator{ + Headers: metaHeaders, + } + factory := utils.NewHTTPRequestFactory(ud, md) + return factory +} + +// simpleVersionInfo is a simple implementation of +// the interface VersionInfo, which is used +// to provide version information for some product, +// component, etc. It stores the product name and the version +// in string and returns them on calls to Name() and Version(). +type simpleVersionInfo struct { + name string + version string +} + +func (v *simpleVersionInfo) Name() string { + return v.name +} + +func (v *simpleVersionInfo) Version() string { + return v.version +} diff --git a/registry/service.go b/registry/service.go new file mode 100644 index 0000000000..530a7f7afe --- /dev/null +++ b/registry/service.go @@ -0,0 +1,54 @@ +package registry + +import ( + "github.com/dotcloud/docker/engine" +) + +// Service exposes registry capabilities in the standard Engine +// interface. Once installed, it extends the engine with the +// following calls: +// +// 'auth': Authenticate against the public registry +// 'search': Search for images on the public registry (TODO) +// 'pull': Download images from any registry (TODO) +// 'push': Upload images to any registry (TODO) +type Service struct { +} + +// NewService returns a new instance of Service ready to be +// installed no an engine. +func NewService() *Service { + return &Service{} +} + +// Install installs registry capabilities to eng. +func (s *Service) Install(eng *engine.Engine) error { + eng.Register("auth", s.Auth) + return nil +} + +// Auth contacts the public registry with the provided credentials, +// and returns OK if authentication was sucessful. +// It can be used to verify the validity of a client's credentials. +func (s *Service) Auth(job *engine.Job) engine.Status { + var ( + err error + authConfig = &AuthConfig{} + ) + + job.GetenvJson("authConfig", authConfig) + // TODO: this is only done here because auth and registry need to be merged into one pkg + if addr := authConfig.ServerAddress; addr != "" && addr != IndexServerAddress() { + addr, err = ExpandAndVerifyRegistryUrl(addr) + if err != nil { + return job.Error(err) + } + authConfig.ServerAddress = addr + } + status, err := Login(authConfig, HTTPRequestFactory(nil)) + if err != nil { + return job.Error(err) + } + job.Printf("%s\n", status) + return engine.StatusOK +} diff --git a/server/server.go b/server/server.go index f55107d3bd..16f9129311 100644 --- a/server/server.go +++ b/server/server.go @@ -139,7 +139,6 @@ func InitServer(job *engine.Job) engine.Status { "events": srv.Events, "push": srv.ImagePush, "containers": srv.Containers, - "auth": srv.Auth, } { if err := job.Eng.Register(name, handler); err != nil { return job.Error(err) @@ -148,24 +147,6 @@ func InitServer(job *engine.Job) engine.Status { return engine.StatusOK } -// simpleVersionInfo is a simple implementation of -// the interface VersionInfo, which is used -// to provide version information for some product, -// component, etc. It stores the product name and the version -// in string and returns them on calls to Name() and Version(). -type simpleVersionInfo struct { - name string - version string -} - -func (v *simpleVersionInfo) Name() string { - return v.name -} - -func (v *simpleVersionInfo) Version() string { - return v.version -} - // ContainerKill send signal to the container // If no signal is given (sig 0), then Kill with SIGKILL and wait // for the container to exit. @@ -215,29 +196,6 @@ func (srv *Server) ContainerKill(job *engine.Job) engine.Status { return engine.StatusOK } -func (srv *Server) Auth(job *engine.Job) engine.Status { - var ( - err error - authConfig = ®istry.AuthConfig{} - ) - - job.GetenvJson("authConfig", authConfig) - // TODO: this is only done here because auth and registry need to be merged into one pkg - if addr := authConfig.ServerAddress; addr != "" && addr != registry.IndexServerAddress() { - addr, err = registry.ExpandAndVerifyRegistryUrl(addr) - if err != nil { - return job.Error(err) - } - authConfig.ServerAddress = addr - } - status, err := registry.Login(authConfig, srv.HTTPRequestFactory(nil)) - if err != nil { - return job.Error(err) - } - job.Printf("%s\n", status) - return engine.StatusOK -} - func (srv *Server) Events(job *engine.Job) engine.Status { if len(job.Args) != 1 { return job.Errorf("Usage: %s FROM", job.Name) @@ -654,7 +612,7 @@ func (srv *Server) ImagesSearch(job *engine.Job) engine.Status { job.GetenvJson("authConfig", authConfig) job.GetenvJson("metaHeaders", metaHeaders) - r, err := registry.NewRegistry(authConfig, srv.HTTPRequestFactory(metaHeaders), registry.IndexServerAddress()) + r, err := registry.NewRegistry(authConfig, registry.HTTPRequestFactory(metaHeaders), registry.IndexServerAddress()) if err != nil { return job.Error(err) } @@ -1457,7 +1415,7 @@ func (srv *Server) ImagePull(job *engine.Job) engine.Status { return job.Error(err) } - r, err := registry.NewRegistry(&authConfig, srv.HTTPRequestFactory(metaHeaders), endpoint) + r, err := registry.NewRegistry(&authConfig, registry.HTTPRequestFactory(metaHeaders), endpoint) if err != nil { return job.Error(err) } @@ -1680,7 +1638,7 @@ func (srv *Server) ImagePush(job *engine.Job) engine.Status { } img, err := srv.daemon.Graph().Get(localName) - r, err2 := registry.NewRegistry(authConfig, srv.HTTPRequestFactory(metaHeaders), endpoint) + r, err2 := registry.NewRegistry(authConfig, registry.HTTPRequestFactory(metaHeaders), endpoint) if err2 != nil { return job.Error(err2) } @@ -2558,24 +2516,6 @@ func NewServer(eng *engine.Engine, config *daemonconfig.Config) (*Server, error) return srv, nil } -func (srv *Server) HTTPRequestFactory(metaHeaders map[string][]string) *utils.HTTPRequestFactory { - httpVersion := make([]utils.VersionInfo, 0, 4) - httpVersion = append(httpVersion, &simpleVersionInfo{"docker", dockerversion.VERSION}) - httpVersion = append(httpVersion, &simpleVersionInfo{"go", goruntime.Version()}) - httpVersion = append(httpVersion, &simpleVersionInfo{"git-commit", dockerversion.GITCOMMIT}) - if kernelVersion, err := utils.GetKernelVersion(); err == nil { - httpVersion = append(httpVersion, &simpleVersionInfo{"kernel", kernelVersion.String()}) - } - httpVersion = append(httpVersion, &simpleVersionInfo{"os", goruntime.GOOS}) - httpVersion = append(httpVersion, &simpleVersionInfo{"arch", goruntime.GOARCH}) - ud := utils.NewHTTPUserAgentDecorator(httpVersion...) - md := &utils.HTTPMetaHeadersDecorator{ - Headers: metaHeaders, - } - factory := utils.NewHTTPRequestFactory(ud, md) - return factory -} - func (srv *Server) LogEvent(action, id, from string) *utils.JSONMessage { now := time.Now().UTC().Unix() jm := utils.JSONMessage{Status: action, ID: id, From: from, Time: now} From c4089ad80bcc1466535696ac0b11d388df529391 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Sun, 27 Apr 2014 15:21:42 -0700 Subject: [PATCH 2/3] Move 'search' to the registry subsystem This continues the effort to separate all registry logic from the deprecated `Server` object. * 'search' is exposed by `github.com/dotcloud/docker/registry/Service` * Added proper documentation of Search while I was at it Docker-DCO-1.1-Signed-off-by: Solomon Hykes (github: shykes) --- registry/service.go | 52 ++++++++++++++++++++++++++++++++++++++++++++- server/server.go | 34 ----------------------------- 2 files changed, 51 insertions(+), 35 deletions(-) diff --git a/registry/service.go b/registry/service.go index 530a7f7afe..1c7a93deac 100644 --- a/registry/service.go +++ b/registry/service.go @@ -9,7 +9,7 @@ import ( // following calls: // // 'auth': Authenticate against the public registry -// 'search': Search for images on the public registry (TODO) +// 'search': Search for images on the public registry // 'pull': Download images from any registry (TODO) // 'push': Upload images to any registry (TODO) type Service struct { @@ -24,6 +24,7 @@ func NewService() *Service { // Install installs registry capabilities to eng. func (s *Service) Install(eng *engine.Engine) error { eng.Register("auth", s.Auth) + eng.Register("search", s.Search) return nil } @@ -52,3 +53,52 @@ func (s *Service) Auth(job *engine.Job) engine.Status { job.Printf("%s\n", status) return engine.StatusOK } + +// Search queries the public registry for images matching the specified +// search terms, and returns the results. +// +// Argument syntax: search TERM +// +// Option environment: +// 'authConfig': json-encoded credentials to authenticate against the registry. +// The search extends to images only accessible via the credentials. +// +// 'metaHeaders': extra HTTP headers to include in the request to the registry. +// The headers should be passed as a json-encoded dictionary. +// +// Output: +// Results are sent as a collection of structured messages (using engine.Table). +// Each result is sent as a separate message. +// Results are ordered by number of stars on the public registry. +func (s *Service) Search(job *engine.Job) engine.Status { + if n := len(job.Args); n != 1 { + return job.Errorf("Usage: %s TERM", job.Name) + } + var ( + term = job.Args[0] + metaHeaders = map[string][]string{} + authConfig = &AuthConfig{} + ) + job.GetenvJson("authConfig", authConfig) + job.GetenvJson("metaHeaders", metaHeaders) + + r, err := NewRegistry(authConfig, HTTPRequestFactory(metaHeaders), IndexServerAddress()) + if err != nil { + return job.Error(err) + } + results, err := r.SearchRepositories(term) + if err != nil { + return job.Error(err) + } + outs := engine.NewTable("star_count", 0) + for _, result := range results.Results { + out := &engine.Env{} + out.Import(result) + outs.Add(out) + } + outs.ReverseSort() + if _, err := outs.WriteListTo(job.Stdout); err != nil { + return job.Error(err) + } + return engine.StatusOK +} diff --git a/server/server.go b/server/server.go index 16f9129311..04cc17a35a 100644 --- a/server/server.go +++ b/server/server.go @@ -126,7 +126,6 @@ func InitServer(job *engine.Job) engine.Status { "insert": srv.ImageInsert, "attach": srv.ContainerAttach, "logs": srv.ContainerLogs, - "search": srv.ImagesSearch, "changes": srv.ContainerChanges, "top": srv.ContainerTop, "version": srv.DockerVersion, @@ -600,39 +599,6 @@ func (srv *Server) recursiveLoad(address, tmpImageDir string) error { return nil } -func (srv *Server) ImagesSearch(job *engine.Job) engine.Status { - if n := len(job.Args); n != 1 { - return job.Errorf("Usage: %s TERM", job.Name) - } - var ( - term = job.Args[0] - metaHeaders = map[string][]string{} - authConfig = ®istry.AuthConfig{} - ) - job.GetenvJson("authConfig", authConfig) - job.GetenvJson("metaHeaders", metaHeaders) - - r, err := registry.NewRegistry(authConfig, registry.HTTPRequestFactory(metaHeaders), registry.IndexServerAddress()) - if err != nil { - return job.Error(err) - } - results, err := r.SearchRepositories(term) - if err != nil { - return job.Error(err) - } - outs := engine.NewTable("star_count", 0) - for _, result := range results.Results { - out := &engine.Env{} - out.Import(result) - outs.Add(out) - } - outs.ReverseSort() - if _, err := outs.WriteListTo(job.Stdout); err != nil { - return job.Error(err) - } - return engine.StatusOK -} - // FIXME: 'insert' is deprecated and should be removed in a future version. func (srv *Server) ImageInsert(job *engine.Job) engine.Status { fmt.Fprintf(job.Stderr, "Warning: '%s' is deprecated and will be removed in a future version. Please use 'build' and 'ADD' instead.\n", job.Name) From 328d65dcff423b14e76f03ee65445032da31ed42 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Sat, 3 May 2014 00:54:52 +0000 Subject: [PATCH 3/3] remove fixme Docker-DCO-1.1-Signed-off-by: Victor Vieux (github: vieux) --- builtins/builtins.go | 26 +++++++++++++++----------- docker/docker.go | 4 +++- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/builtins/builtins.go b/builtins/builtins.go index bd3b33d0d3..40d421f154 100644 --- a/builtins/builtins.go +++ b/builtins/builtins.go @@ -8,17 +8,19 @@ import ( "github.com/dotcloud/docker/server" ) -func Register(eng *engine.Engine) { - daemon(eng) - remote(eng) - // FIXME: engine.Installer.Install can fail. These errors - // should be passed up. - registry.NewService().Install(eng) +func Register(eng *engine.Engine) error { + if err := daemon(eng); err != nil { + return err + } + if err := remote(eng); err != nil { + return err + } + return registry.NewService().Install(eng) } // remote: a RESTful api for cross-docker communication -func remote(eng *engine.Engine) { - eng.Register("serveapi", api.ServeApi) +func remote(eng *engine.Engine) error { + return eng.Register("serveapi", api.ServeApi) } // daemon: a default execution and storage backend for Docker on Linux, @@ -36,7 +38,9 @@ func remote(eng *engine.Engine) { // // These components should be broken off into plugins of their own. // -func daemon(eng *engine.Engine) { - eng.Register("initserver", server.InitServer) - eng.Register("init_networkdriver", bridge.InitDriver) +func daemon(eng *engine.Engine) error { + if err := eng.Register("initserver", server.InitServer); err != nil { + return err + } + return eng.Register("init_networkdriver", bridge.InitDriver) } diff --git a/docker/docker.go b/docker/docker.go index 7c366001b7..db33341413 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -128,7 +128,9 @@ func main() { eng := engine.New() // Load builtins - builtins.Register(eng) + if err := builtins.Register(eng); err != nil { + log.Fatal(err) + } // load the daemon in the background so we can immediately start // the http api so that connections don't fail while the daemon // is booting