| package graph |
| |
| import ( |
| "fmt" |
| "io" |
| |
| log "github.com/Sirupsen/logrus" |
| "github.com/docker/docker/engine" |
| "github.com/docker/docker/image" |
| ) |
| |
| func (s *TagStore) Install(eng *engine.Engine) error { |
| for name, handler := range map[string]engine.Handler{ |
| "image_set": s.CmdSet, |
| "tag": s.CmdTag, |
| "image_get": s.CmdGet, |
| "image_inspect": s.CmdLookup, |
| "image_tarlayer": s.CmdTarLayer, |
| "image_export": s.CmdImageExport, |
| "history": s.CmdHistory, |
| "images": s.CmdImages, |
| "viz": s.CmdViz, |
| "load": s.CmdLoad, |
| "import": s.CmdImport, |
| "pull": s.CmdPull, |
| "push": s.CmdPush, |
| } { |
| if err := eng.Register(name, handler); err != nil { |
| return fmt.Errorf("Could not register %q: %v", name, err) |
| } |
| } |
| return nil |
| } |
| |
| // CmdSet stores a new image in the graph. |
| // Images are stored in the graph using 4 elements: |
| // - A user-defined ID |
| // - A collection of metadata describing the image |
| // - A directory tree stored as a tar archive (also called the "layer") |
| // - A reference to a "parent" ID on top of which the layer should be applied |
| // |
| // NOTE: even though the parent ID is only useful in relation to the layer and how |
| // to apply it (ie you could represent the full directory tree as 'parent_layer + layer', |
| // it is treated as a top-level property of the image. This is an artifact of early |
| // design and should probably be cleaned up in the future to simplify the design. |
| // |
| // Syntax: image_set ID |
| // Input: |
| // - Layer content must be streamed in tar format on stdin. An empty input is |
| // valid and represents a nil layer. |
| // |
| // - Image metadata must be passed in the command environment. |
| // 'json': a json-encoded object with all image metadata. |
| // It will be stored as-is, without any encoding/decoding artifacts. |
| // That is a requirement of the current registry client implementation, |
| // because a re-encoded json might invalidate the image checksum at |
| // the next upload, even with functionaly identical content. |
| func (s *TagStore) CmdSet(job *engine.Job) engine.Status { |
| if len(job.Args) != 1 { |
| return job.Errorf("usage: %s NAME", job.Name) |
| } |
| var ( |
| imgJSON = []byte(job.Getenv("json")) |
| layer = job.Stdin |
| ) |
| if len(imgJSON) == 0 { |
| return job.Errorf("mandatory key 'json' is not set") |
| } |
| // We have to pass an *image.Image object, even though it will be completely |
| // ignored in favor of the redundant json data. |
| // FIXME: the current prototype of Graph.Register is stupid and redundant. |
| img, err := image.NewImgJSON(imgJSON) |
| if err != nil { |
| return job.Error(err) |
| } |
| if err := s.graph.Register(img, layer); err != nil { |
| return job.Error(err) |
| } |
| return engine.StatusOK |
| } |
| |
| // CmdGet returns information about an image. |
| // If the image doesn't exist, an empty object is returned, to allow |
| // checking for an image's existence. |
| func (s *TagStore) CmdGet(job *engine.Job) engine.Status { |
| if len(job.Args) != 1 { |
| return job.Errorf("usage: %s NAME", job.Name) |
| } |
| name := job.Args[0] |
| res := &engine.Env{} |
| img, err := s.LookupImage(name) |
| // Note: if the image doesn't exist, LookupImage returns |
| // nil, nil. |
| if err != nil { |
| return job.Error(err) |
| } |
| if img != nil { |
| // We don't directly expose all fields of the Image objects, |
| // to maintain a clean public API which we can maintain over |
| // time even if the underlying structure changes. |
| // We should have done this with the Image object to begin with... |
| // but we didn't, so now we're doing it here. |
| // |
| // Fields that we're probably better off not including: |
| // - 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" |
| // metaphor, in practice people either ignore it or use it as a |
| // generic description field which it isn't. On deprecation shortlist. |
| res.SetAuto("Created", img.Created) |
| res.SetJson("Author", img.Author) |
| res.Set("Os", img.OS) |
| res.Set("Architecture", img.Architecture) |
| res.Set("DockerVersion", img.DockerVersion) |
| res.SetJson("Id", img.ID) |
| res.SetJson("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 { |
| if job.GetenvBool("raw") { |
| b, err := image.RawJson() |
| if err != nil { |
| return job.Error(err) |
| } |
| job.Stdout.Write(b) |
| return engine.StatusOK |
| } |
| |
| out := &engine.Env{} |
| out.SetJson("Id", image.ID) |
| out.SetJson("Parent", image.Parent) |
| out.SetJson("Comment", image.Comment) |
| out.SetAuto("Created", image.Created) |
| out.SetJson("Container", image.Container) |
| out.SetJson("ContainerConfig", image.ContainerConfig) |
| out.Set("DockerVersion", image.DockerVersion) |
| out.SetJson("Author", image.Author) |
| out.SetJson("Config", image.Config) |
| out.Set("Architecture", image.Architecture) |
| out.Set("Os", image.OS) |
| out.SetInt64("Size", image.Size) |
| out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size) |
| if _, err = out.WriteTo(job.Stdout); err != nil { |
| return job.Error(err) |
| } |
| 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() |
| |
| written, err := io.Copy(job.Stdout, fs) |
| if err != nil { |
| return job.Error(err) |
| } |
| log.Debugf("rendered layer for %s of [%d] size", image.ID, written) |
| return engine.StatusOK |
| } |
| return job.Errorf("No such image: %s", name) |
| } |