diff --git a/api/server/server.go b/api/server/server.go index f08f395400..19986058c6 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -338,7 +338,7 @@ func getContainersLogs(eng *engine.Engine, version version.Version, w http.Respo } var ( - job = eng.Job("inspect", vars["name"], "container") + job = eng.Job("container_inspect", vars["name"]) c, err = job.Stdout.AddEnv() ) if err != nil { @@ -755,7 +755,7 @@ func postContainersAttach(eng *engine.Engine, version version.Version, w http.Re } var ( - job = eng.Job("inspect", vars["name"], "container") + job = eng.Job("container_inspect", vars["name"]) c, err = job.Stdout.AddEnv() ) if err != nil { @@ -819,7 +819,7 @@ func wsContainersAttach(eng *engine.Engine, version version.Version, w http.Resp return fmt.Errorf("Missing parameter") } - if err := eng.Job("inspect", vars["name"], "container").Run(); err != nil { + if err := eng.Job("container_inspect", vars["name"]).Run(); err != nil { return err } @@ -847,9 +847,8 @@ func getContainersByName(eng *engine.Engine, version version.Version, w http.Res if vars == nil { return fmt.Errorf("Missing parameter") } - var job = eng.Job("inspect", vars["name"], "container") + var job = eng.Job("container_inspect", vars["name"]) streamJSON(job, w, false) - job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job return job.Run() } @@ -857,9 +856,8 @@ func getImagesByName(eng *engine.Engine, version version.Version, w http.Respons if vars == nil { return fmt.Errorf("Missing parameter") } - var job = eng.Job("inspect", vars["name"], "image") + var job = eng.Job("image_inspect", vars["name"]) streamJSON(job, w, false) - job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job return job.Run() } diff --git a/daemon/daemon.go b/daemon/daemon.go index 6737bab6ae..210e1a540e 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -64,6 +64,11 @@ type Daemon struct { execDriver execdriver.Driver } +// Install installs daemon capabilities to eng. +func (daemon *Daemon) Install(eng *engine.Engine) error { + return eng.Register("container_inspect", daemon.ContainerInspect) +} + // Mountpoints should be private to the container func remountPrivate(mountPoint string) error { mounted, err := mount.Mounted(mountPoint) diff --git a/daemon/inspect.go b/daemon/inspect.go new file mode 100644 index 0000000000..0f771a3ca2 --- /dev/null +++ b/daemon/inspect.go @@ -0,0 +1,27 @@ +package daemon + +import ( + "encoding/json" + + "github.com/dotcloud/docker/engine" + "github.com/dotcloud/docker/runconfig" +) + +func (daemon *Daemon) ContainerInspect(job *engine.Job) engine.Status { + if len(job.Args) != 1 { + return job.Errorf("usage: %s NAME", job.Name) + } + name := job.Args[0] + if container := daemon.Get(name); container != nil { + b, err := json.Marshal(&struct { + *Container + HostConfig *runconfig.HostConfig + }{container, container.HostConfig()}) + if err != nil { + return job.Error(err) + } + job.Stdout.Write(b) + return engine.StatusOK + } + return job.Errorf("No such container: %s", name) +} diff --git a/graph/service.go b/graph/service.go index 211babd0bd..881a199043 100644 --- a/graph/service.go +++ b/graph/service.go @@ -1,7 +1,10 @@ package graph import ( + "encoding/json" "fmt" + "io" + "github.com/dotcloud/docker/engine" "github.com/dotcloud/docker/image" "github.com/dotcloud/docker/utils" @@ -11,6 +14,8 @@ func (s *TagStore) Install(eng *engine.Engine) error { eng.Register("image_set", s.CmdSet) eng.Register("image_tag", s.CmdTag) eng.Register("image_get", s.CmdGet) + eng.Register("image_inspect", s.CmdLookup) + eng.Register("image_tarlayer", s.CmdTarLayer) return nil } @@ -107,11 +112,6 @@ func (s *TagStore) CmdGet(job *engine.Job) engine.Status { // but we didn't, so now we're doing it here. // // Fields that we're probably better off not including: - // - ID (the caller already knows it, and we stay more flexible on - // naming down the road) - // - Parent. That field is really an implementation detail of - // layer storage ("layer is a diff against this other layer). - // It doesn't belong at the same level as author/description/etc. // - Config/ContainerConfig. Those structs have the same sprawl problem, // so we shouldn't include them wholesale either. // - Comment: initially created to fulfill the "every image is a git commit" @@ -122,7 +122,50 @@ func (s *TagStore) CmdGet(job *engine.Job) engine.Status { res.Set("os", img.OS) res.Set("architecture", img.Architecture) res.Set("docker_version", img.DockerVersion) + res.Set("ID", img.ID) + res.Set("Parent", img.Parent) } res.WriteTo(job.Stdout) return engine.StatusOK } + +// CmdLookup return an image encoded in JSON +func (s *TagStore) CmdLookup(job *engine.Job) engine.Status { + if len(job.Args) != 1 { + return job.Errorf("usage: %s NAME", job.Name) + } + name := job.Args[0] + if image, err := s.LookupImage(name); err == nil && image != nil { + b, err := json.Marshal(image) + if err != nil { + return job.Error(err) + } + job.Stdout.Write(b) + return engine.StatusOK + } + return job.Errorf("No such image: %s", name) +} + +// CmdTarLayer return the tarLayer of the image +func (s *TagStore) CmdTarLayer(job *engine.Job) engine.Status { + if len(job.Args) != 1 { + return job.Errorf("usage: %s NAME", job.Name) + } + name := job.Args[0] + if image, err := s.LookupImage(name); err == nil && image != nil { + fs, err := image.TarLayer() + if err != nil { + return job.Error(err) + } + defer fs.Close() + + if written, err := io.Copy(job.Stdout, fs); err != nil { + return job.Error(err) + } else { + utils.Debugf("rendered layer for %s of [%d] size", image.ID, written) + } + + return engine.StatusOK + } + return job.Errorf("No such image: %s", name) +} diff --git a/integration/api_test.go b/integration/api_test.go index 04611dfe3d..969e0fbaf2 100644 --- a/integration/api_test.go +++ b/integration/api_test.go @@ -536,7 +536,6 @@ func TestGetContainersByName(t *testing.T) { func TestPostCommit(t *testing.T) { eng := NewTestEngine(t) defer mkDaemonFromEngine(eng, t).Nuke() - srv := mkServerFromEngine(eng, t) // Create a container and remove a file containerID := createTestContainer(eng, @@ -567,7 +566,7 @@ func TestPostCommit(t *testing.T) { if err := env.Decode(r.Body); err != nil { t.Fatal(err) } - if _, err := srv.ImageInspect(env.Get("Id")); err != nil { + if err := eng.Job("image_inspect", env.Get("Id")).Run(); err != nil { t.Fatalf("The image has not been committed") } } diff --git a/integration/buildfile_test.go b/integration/buildfile_test.go index b87fa116eb..268e9eaf4f 100644 --- a/integration/buildfile_test.go +++ b/integration/buildfile_test.go @@ -1,19 +1,22 @@ package docker import ( + "bytes" + "encoding/json" "fmt" - "github.com/dotcloud/docker/archive" - "github.com/dotcloud/docker/engine" - "github.com/dotcloud/docker/image" - "github.com/dotcloud/docker/nat" - "github.com/dotcloud/docker/server" - "github.com/dotcloud/docker/utils" "io/ioutil" "net" "net/http" "net/http/httptest" "strings" "testing" + + "github.com/dotcloud/docker/archive" + "github.com/dotcloud/docker/engine" + "github.com/dotcloud/docker/image" + "github.com/dotcloud/docker/nat" + "github.com/dotcloud/docker/server" + "github.com/dotcloud/docker/utils" ) // A testContextTemplate describes a build context and how to test it @@ -400,7 +403,15 @@ func buildImage(context testContextTemplate, t *testing.T, eng *engine.Engine, u return nil, err } - return srv.ImageInspect(id) + job := eng.Job("image_inspect", id) + buffer := bytes.NewBuffer(nil) + image := &image.Image{} + job.Stdout.Add(buffer) + if err := job.Run(); err != nil { + return nil, err + } + err = json.NewDecoder(buffer).Decode(image) + return image, err } func TestVolume(t *testing.T) { diff --git a/integration/commands_test.go b/integration/commands_test.go index b91ba603a7..2ee5842e1c 100644 --- a/integration/commands_test.go +++ b/integration/commands_test.go @@ -1052,11 +1052,12 @@ func TestContainerOrphaning(t *testing.T) { if err := cli.CmdBuild("-t", image, tmpDir); err != nil { t.Fatal(err) } - img, err := srv.ImageInspect(image) - if err != nil { + job := globalEngine.Job("image_get", image) + info, _ := job.Stdout.AddEnv() + if err := job.Run(); err != nil { t.Fatal(err) } - return img.ID + return info.Get("ID") } // build an image diff --git a/integration/server_test.go b/integration/server_test.go index 4da3e6e368..3752c9b7e6 100644 --- a/integration/server_test.go +++ b/integration/server_test.go @@ -81,13 +81,13 @@ func TestMergeConfigOnCommit(t *testing.T) { container2, _, _ := mkContainer(runtime, []string{engine.Tail(outputBuffer, 1)}, t) defer runtime.Destroy(container2) - job = eng.Job("inspect", container1.Name, "container") + job = eng.Job("container_inspect", container1.Name) baseContainer, _ := job.Stdout.AddEnv() if err := job.Run(); err != nil { t.Error(err) } - job = eng.Job("inspect", container2.Name, "container") + job = eng.Job("container_inspect", container2.Name) commitContainer, _ := job.Stdout.AddEnv() if err := job.Run(); err != nil { t.Error(err) diff --git a/server/server.go b/server/server.go index 490f482596..25aa1b0058 100644 --- a/server/server.go +++ b/server/server.go @@ -132,7 +132,6 @@ func InitServer(job *engine.Job) engine.Status { "pull": srv.ImagePull, "import": srv.ImageImport, "image_delete": srv.ImageDelete, - "inspect": srv.JobInspect, "events": srv.Events, "push": srv.ImagePush, "containers": srv.Containers, @@ -146,6 +145,11 @@ func InitServer(job *engine.Job) engine.Status { if err := srv.daemon.Repositories().Install(job.Eng); err != nil { return job.Error(err) } + // Install daemon-related commands from the daemon subsystem. + // See `daemon/` + if err := srv.daemon.Install(job.Eng); err != nil { + return job.Error(err) + } return engine.StatusOK } @@ -327,12 +331,7 @@ func (srv *Server) ImageExport(job *engine.Job) engine.Status { } if rootRepo != nil { for _, id := range rootRepo { - image, err := srv.ImageInspect(id) - if err != nil { - return job.Error(err) - } - - if err := srv.exportImage(image, tempdir); err != nil { + if err := srv.exportImage(job.Eng, id, tempdir); err != nil { return job.Error(err) } } @@ -346,11 +345,7 @@ func (srv *Server) ImageExport(job *engine.Job) engine.Status { return job.Error(err) } } else { - image, err := srv.ImageInspect(name) - if err != nil { - return job.Error(err) - } - if err := srv.exportImage(image, tempdir); err != nil { + if err := srv.exportImage(job.Eng, name, tempdir); err != nil { return job.Error(err) } } @@ -364,13 +359,14 @@ func (srv *Server) ImageExport(job *engine.Job) engine.Status { if _, err := io.Copy(job.Stdout, fs); err != nil { return job.Error(err) } + utils.Debugf("End Serializing %s", name) return engine.StatusOK } -func (srv *Server) exportImage(img *image.Image, tempdir string) error { - for i := img; i != nil; { +func (srv *Server) exportImage(eng *engine.Engine, name, tempdir string) error { + for n := name; n != ""; { // temporary directory - tmpImageDir := path.Join(tempdir, i.ID) + tmpImageDir := path.Join(tempdir, n) if err := os.Mkdir(tmpImageDir, os.FileMode(0755)); err != nil { if os.IsExist(err) { return nil @@ -386,44 +382,34 @@ func (srv *Server) exportImage(img *image.Image, tempdir string) error { } // serialize json - b, err := json.Marshal(i) + json, err := os.Create(path.Join(tmpImageDir, "json")) if err != nil { return err } - if err := ioutil.WriteFile(path.Join(tmpImageDir, "json"), b, os.FileMode(0644)); err != nil { + job := eng.Job("image_inspect", n) + job.Stdout.Add(json) + if err := job.Run(); err != nil { return err } // serialize filesystem - fs, err := i.TarLayer() - if err != nil { - return err - } - defer fs.Close() - fsTar, err := os.Create(path.Join(tmpImageDir, "layer.tar")) if err != nil { return err } - if written, err := io.Copy(fsTar, fs); err != nil { - return err - } else { - utils.Debugf("rendered layer for %s of [%d] size", i.ID, written) - } - - if err = fsTar.Close(); err != nil { + job = eng.Job("image_tarlayer", n) + job.Stdout.Add(fsTar) + if err := job.Run(); err != nil { return err } // find parent - if i.Parent != "" { - i, err = srv.ImageInspect(i.Parent) - if err != nil { - return err - } - } else { - i = nil + job = eng.Job("image_get", n) + info, _ := job.Stdout.AddEnv() + if err := job.Run(); err != nil { + return err } + n = info.Get("Parent") } return nil } @@ -548,7 +534,7 @@ func (srv *Server) ImageLoad(job *engine.Job) engine.Status { for _, d := range dirs { if d.IsDir() { - if err := srv.recursiveLoad(d.Name(), tmpImageDir); err != nil { + if err := srv.recursiveLoad(job.Eng, d.Name(), tmpImageDir); err != nil { return job.Error(err) } } @@ -575,8 +561,8 @@ func (srv *Server) ImageLoad(job *engine.Job) engine.Status { return engine.StatusOK } -func (srv *Server) recursiveLoad(address, tmpImageDir string) error { - if _, err := srv.ImageInspect(address); err != nil { +func (srv *Server) recursiveLoad(eng *engine.Engine, address, tmpImageDir string) error { + if err := eng.Job("image_get", address).Run(); err != nil { utils.Debugf("Loading %s", address) imageJson, err := ioutil.ReadFile(path.Join(tmpImageDir, "repo", address, "json")) @@ -597,7 +583,7 @@ func (srv *Server) recursiveLoad(address, tmpImageDir string) error { } if img.Parent != "" { if !srv.daemon.Graph().Exists(img.Parent) { - if err := srv.recursiveLoad(img.Parent, tmpImageDir); err != nil { + if err := srv.recursiveLoad(eng, img.Parent, tmpImageDir); err != nil { return err } } @@ -2341,64 +2327,6 @@ func (srv *Server) ContainerAttach(job *engine.Job) engine.Status { return engine.StatusOK } -func (srv *Server) ContainerInspect(name string) (*daemon.Container, error) { - if container := srv.daemon.Get(name); container != nil { - return container, nil - } - return nil, fmt.Errorf("No such container: %s", name) -} - -func (srv *Server) ImageInspect(name string) (*image.Image, error) { - if image, err := srv.daemon.Repositories().LookupImage(name); err == nil && image != nil { - return image, nil - } - return nil, fmt.Errorf("No such image: %s", name) -} - -func (srv *Server) JobInspect(job *engine.Job) engine.Status { - // TODO: deprecate KIND/conflict - if n := len(job.Args); n != 2 { - return job.Errorf("Usage: %s CONTAINER|IMAGE KIND", job.Name) - } - var ( - name = job.Args[0] - kind = job.Args[1] - object interface{} - conflict = job.GetenvBool("conflict") //should the job detect conflict between containers and images - image, errImage = srv.ImageInspect(name) - container, errContainer = srv.ContainerInspect(name) - ) - - if conflict && image != nil && container != nil { - return job.Errorf("Conflict between containers and images") - } - - switch kind { - case "image": - if errImage != nil { - return job.Error(errImage) - } - object = image - case "container": - if errContainer != nil { - return job.Error(errContainer) - } - object = &struct { - *daemon.Container - HostConfig *runconfig.HostConfig - }{container, container.HostConfig()} - default: - return job.Errorf("Unknown kind: %s", kind) - } - - b, err := json.Marshal(object) - if err != nil { - return job.Error(err) - } - job.Stdout.Write(b) - return engine.StatusOK -} - func (srv *Server) ContainerCopy(job *engine.Job) engine.Status { if len(job.Args) != 2 { return job.Errorf("Usage: %s CONTAINER RESOURCE\n", job.Name)