-
Notifications
You must be signed in to change notification settings - Fork 0
/
service.go
176 lines (167 loc) · 5.52 KB
/
service.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package graph
import (
"fmt"
"io"
"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,
"image_get": s.CmdGet,
"image_inspect": s.CmdLookup,
"image_tarlayer": s.CmdTarLayer,
"image_export": s.CmdImageExport,
"viz": s.CmdViz,
"load": s.CmdLoad,
"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) error {
if len(job.Args) != 1 {
return fmt.Errorf("usage: %s NAME", job.Name)
}
var (
imgJSON = []byte(job.Getenv("json"))
layer = job.Stdin
)
if len(imgJSON) == 0 {
return fmt.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 redundant.
img, err := image.NewImgJSON(imgJSON)
if err != nil {
return err
}
if err := s.graph.Register(img, layer); err != nil {
return err
}
return nil
}
// 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) error {
if len(job.Args) != 1 {
return fmt.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 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 nil
}
// CmdLookup return an image encoded in JSON
func (s *TagStore) CmdLookup(job *engine.Job) error {
if len(job.Args) != 1 {
return fmt.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 err
}
job.Stdout.Write(b)
return nil
}
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 err
}
return nil
}
return fmt.Errorf("No such image: %s", name)
}
// CmdTarLayer return the tarLayer of the image
func (s *TagStore) CmdTarLayer(job *engine.Job) error {
if len(job.Args) != 1 {
return fmt.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 err
}
defer fs.Close()
written, err := io.Copy(job.Stdout, fs)
if err != nil {
return err
}
logrus.Debugf("rendered layer for %s of [%d] size", image.ID, written)
return nil
}
return fmt.Errorf("No such image: %s", name)
}