github.com/microsoft/docker@v1.5.0-rc2/graph/service.go (about) 1 package graph 2 3 import ( 4 "fmt" 5 "io" 6 7 log "github.com/Sirupsen/logrus" 8 "github.com/docker/docker/engine" 9 "github.com/docker/docker/image" 10 ) 11 12 func (s *TagStore) Install(eng *engine.Engine) error { 13 for name, handler := range map[string]engine.Handler{ 14 "image_set": s.CmdSet, 15 "image_tag": s.CmdTag, 16 "tag": s.CmdTagLegacy, // FIXME merge with "image_tag" 17 "image_get": s.CmdGet, 18 "image_inspect": s.CmdLookup, 19 "image_tarlayer": s.CmdTarLayer, 20 "image_export": s.CmdImageExport, 21 "history": s.CmdHistory, 22 "images": s.CmdImages, 23 "viz": s.CmdViz, 24 "load": s.CmdLoad, 25 "import": s.CmdImport, 26 "pull": s.CmdPull, 27 "push": s.CmdPush, 28 "image_manifest": s.CmdManifest, 29 } { 30 if err := eng.Register(name, handler); err != nil { 31 return fmt.Errorf("Could not register %q: %v", name, err) 32 } 33 } 34 return nil 35 } 36 37 // CmdSet stores a new image in the graph. 38 // Images are stored in the graph using 4 elements: 39 // - A user-defined ID 40 // - A collection of metadata describing the image 41 // - A directory tree stored as a tar archive (also called the "layer") 42 // - A reference to a "parent" ID on top of which the layer should be applied 43 // 44 // NOTE: even though the parent ID is only useful in relation to the layer and how 45 // to apply it (ie you could represent the full directory tree as 'parent_layer + layer', 46 // it is treated as a top-level property of the image. This is an artifact of early 47 // design and should probably be cleaned up in the future to simplify the design. 48 // 49 // Syntax: image_set ID 50 // Input: 51 // - Layer content must be streamed in tar format on stdin. An empty input is 52 // valid and represents a nil layer. 53 // 54 // - Image metadata must be passed in the command environment. 55 // 'json': a json-encoded object with all image metadata. 56 // It will be stored as-is, without any encoding/decoding artifacts. 57 // That is a requirement of the current registry client implementation, 58 // because a re-encoded json might invalidate the image checksum at 59 // the next upload, even with functionaly identical content. 60 func (s *TagStore) CmdSet(job *engine.Job) engine.Status { 61 if len(job.Args) != 1 { 62 return job.Errorf("usage: %s NAME", job.Name) 63 } 64 var ( 65 imgJSON = []byte(job.Getenv("json")) 66 layer = job.Stdin 67 ) 68 if len(imgJSON) == 0 { 69 return job.Errorf("mandatory key 'json' is not set") 70 } 71 // We have to pass an *image.Image object, even though it will be completely 72 // ignored in favor of the redundant json data. 73 // FIXME: the current prototype of Graph.Register is stupid and redundant. 74 img, err := image.NewImgJSON(imgJSON) 75 if err != nil { 76 return job.Error(err) 77 } 78 if err := s.graph.Register(img, layer); err != nil { 79 return job.Error(err) 80 } 81 return engine.StatusOK 82 } 83 84 // CmdGet returns information about an image. 85 // If the image doesn't exist, an empty object is returned, to allow 86 // checking for an image's existence. 87 func (s *TagStore) CmdGet(job *engine.Job) engine.Status { 88 if len(job.Args) != 1 { 89 return job.Errorf("usage: %s NAME", job.Name) 90 } 91 name := job.Args[0] 92 res := &engine.Env{} 93 img, err := s.LookupImage(name) 94 // Note: if the image doesn't exist, LookupImage returns 95 // nil, nil. 96 if err != nil { 97 return job.Error(err) 98 } 99 if img != nil { 100 // We don't directly expose all fields of the Image objects, 101 // to maintain a clean public API which we can maintain over 102 // time even if the underlying structure changes. 103 // We should have done this with the Image object to begin with... 104 // but we didn't, so now we're doing it here. 105 // 106 // Fields that we're probably better off not including: 107 // - Config/ContainerConfig. Those structs have the same sprawl problem, 108 // so we shouldn't include them wholesale either. 109 // - Comment: initially created to fulfill the "every image is a git commit" 110 // metaphor, in practice people either ignore it or use it as a 111 // generic description field which it isn't. On deprecation shortlist. 112 res.SetAuto("Created", img.Created) 113 res.SetJson("Author", img.Author) 114 res.Set("Os", img.OS) 115 res.Set("Architecture", img.Architecture) 116 res.Set("DockerVersion", img.DockerVersion) 117 res.SetJson("Id", img.ID) 118 res.SetJson("Parent", img.Parent) 119 } 120 res.WriteTo(job.Stdout) 121 return engine.StatusOK 122 } 123 124 // CmdLookup return an image encoded in JSON 125 func (s *TagStore) CmdLookup(job *engine.Job) engine.Status { 126 if len(job.Args) != 1 { 127 return job.Errorf("usage: %s NAME", job.Name) 128 } 129 name := job.Args[0] 130 if image, err := s.LookupImage(name); err == nil && image != nil { 131 if job.GetenvBool("raw") { 132 b, err := image.RawJson() 133 if err != nil { 134 return job.Error(err) 135 } 136 job.Stdout.Write(b) 137 return engine.StatusOK 138 } 139 140 out := &engine.Env{} 141 out.SetJson("Id", image.ID) 142 out.SetJson("Parent", image.Parent) 143 out.SetJson("Comment", image.Comment) 144 out.SetAuto("Created", image.Created) 145 out.SetJson("Container", image.Container) 146 out.SetJson("ContainerConfig", image.ContainerConfig) 147 out.Set("DockerVersion", image.DockerVersion) 148 out.SetJson("Author", image.Author) 149 out.SetJson("Config", image.Config) 150 out.Set("Architecture", image.Architecture) 151 out.Set("Os", image.OS) 152 out.SetInt64("Size", image.Size) 153 out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size) 154 out.Set("Checksum", image.Checksum) 155 if _, err = out.WriteTo(job.Stdout); err != nil { 156 return job.Error(err) 157 } 158 return engine.StatusOK 159 } 160 return job.Errorf("No such image: %s", name) 161 } 162 163 // CmdTarLayer return the tarLayer of the image 164 func (s *TagStore) CmdTarLayer(job *engine.Job) engine.Status { 165 if len(job.Args) != 1 { 166 return job.Errorf("usage: %s NAME", job.Name) 167 } 168 name := job.Args[0] 169 if image, err := s.LookupImage(name); err == nil && image != nil { 170 fs, err := image.TarLayer() 171 if err != nil { 172 return job.Error(err) 173 } 174 defer fs.Close() 175 176 written, err := io.Copy(job.Stdout, fs) 177 if err != nil { 178 return job.Error(err) 179 } 180 log.Debugf("rendered layer for %s of [%d] size", image.ID, written) 181 return engine.StatusOK 182 } 183 return job.Errorf("No such image: %s", name) 184 }