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