diff --git a/builtins/builtins.go b/builtins/builtins.go index 374bd48701..40d421f154 100644 --- a/builtins/builtins.go +++ b/builtins/builtins.go @@ -4,17 +4,23 @@ 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) +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, @@ -32,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 5fe1681448..26ccd24bb4 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -132,7 +132,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 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..1c7a93deac --- /dev/null +++ b/registry/service.go @@ -0,0 +1,104 @@ +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 +// '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) + eng.Register("search", s.Search) + 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 +} + +// 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 f55107d3bd..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, @@ -139,7 +138,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 +146,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 +195,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) @@ -642,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, srv.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) @@ -1457,7 +1381,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 +1604,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 +2482,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}