Browse code

Move graph and tags to graph sub pkg Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/03/08 11:04:38
Showing 11 changed files
1 1
deleted file mode 100644
... ...
@@ -1,408 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"fmt"
5
-	"github.com/dotcloud/docker/archive"
6
-	"github.com/dotcloud/docker/dockerversion"
7
-	"github.com/dotcloud/docker/graphdriver"
8
-	"github.com/dotcloud/docker/image"
9
-	"github.com/dotcloud/docker/runconfig"
10
-	"github.com/dotcloud/docker/utils"
11
-	"io"
12
-	"io/ioutil"
13
-	"os"
14
-	"path"
15
-	"path/filepath"
16
-	"runtime"
17
-	"strings"
18
-	"syscall"
19
-	"time"
20
-)
21
-
22
-// A Graph is a store for versioned filesystem images and the relationship between them.
23
-type Graph struct {
24
-	Root    string
25
-	idIndex *utils.TruncIndex
26
-	driver  graphdriver.Driver
27
-}
28
-
29
-// NewGraph instantiates a new graph at the given root path in the filesystem.
30
-// `root` will be created if it doesn't exist.
31
-func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) {
32
-	abspath, err := filepath.Abs(root)
33
-	if err != nil {
34
-		return nil, err
35
-	}
36
-	// Create the root directory if it doesn't exists
37
-	if err := os.MkdirAll(root, 0700); err != nil && !os.IsExist(err) {
38
-		return nil, err
39
-	}
40
-
41
-	graph := &Graph{
42
-		Root:    abspath,
43
-		idIndex: utils.NewTruncIndex(),
44
-		driver:  driver,
45
-	}
46
-	if err := graph.restore(); err != nil {
47
-		return nil, err
48
-	}
49
-	return graph, nil
50
-}
51
-
52
-func (graph *Graph) restore() error {
53
-	dir, err := ioutil.ReadDir(graph.Root)
54
-	if err != nil {
55
-		return err
56
-	}
57
-	for _, v := range dir {
58
-		id := v.Name()
59
-		if graph.driver.Exists(id) {
60
-			graph.idIndex.Add(id)
61
-		}
62
-	}
63
-	utils.Debugf("Restored %d elements", len(dir))
64
-	return nil
65
-}
66
-
67
-// FIXME: Implement error subclass instead of looking at the error text
68
-// Note: This is the way golang implements os.IsNotExists on Plan9
69
-func (graph *Graph) IsNotExist(err error) bool {
70
-	return err != nil && (strings.Contains(err.Error(), "does not exist") || strings.Contains(err.Error(), "No such"))
71
-}
72
-
73
-// Exists returns true if an image is registered at the given id.
74
-// If the image doesn't exist or if an error is encountered, false is returned.
75
-func (graph *Graph) Exists(id string) bool {
76
-	if _, err := graph.Get(id); err != nil {
77
-		return false
78
-	}
79
-	return true
80
-}
81
-
82
-// Get returns the image with the given id, or an error if the image doesn't exist.
83
-func (graph *Graph) Get(name string) (*image.Image, error) {
84
-	id, err := graph.idIndex.Get(name)
85
-	if err != nil {
86
-		return nil, err
87
-	}
88
-	// FIXME: return nil when the image doesn't exist, instead of an error
89
-	img, err := image.LoadImage(graph.ImageRoot(id))
90
-	if err != nil {
91
-		return nil, err
92
-	}
93
-	if img.ID != id {
94
-		return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
95
-	}
96
-	img.SetGraph(graph)
97
-
98
-	if img.Size < 0 {
99
-		rootfs, err := graph.driver.Get(img.ID)
100
-		if err != nil {
101
-			return nil, fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
102
-		}
103
-		defer graph.driver.Put(img.ID)
104
-
105
-		var size int64
106
-		if img.Parent == "" {
107
-			if size, err = utils.TreeSize(rootfs); err != nil {
108
-				return nil, err
109
-			}
110
-		} else {
111
-			parentFs, err := graph.driver.Get(img.Parent)
112
-			if err != nil {
113
-				return nil, err
114
-			}
115
-			changes, err := archive.ChangesDirs(rootfs, parentFs)
116
-			if err != nil {
117
-				return nil, err
118
-			}
119
-			size = archive.ChangesSize(rootfs, changes)
120
-		}
121
-
122
-		img.Size = size
123
-		if err := img.SaveSize(graph.ImageRoot(id)); err != nil {
124
-			return nil, err
125
-		}
126
-	}
127
-	return img, nil
128
-}
129
-
130
-// Create creates a new image and registers it in the graph.
131
-func (graph *Graph) Create(layerData archive.ArchiveReader, container *Container, comment, author string, config *runconfig.Config) (*image.Image, error) {
132
-	img := &image.Image{
133
-		ID:            utils.GenerateRandomID(),
134
-		Comment:       comment,
135
-		Created:       time.Now().UTC(),
136
-		DockerVersion: dockerversion.VERSION,
137
-		Author:        author,
138
-		Config:        config,
139
-		Architecture:  runtime.GOARCH,
140
-		OS:            runtime.GOOS,
141
-	}
142
-	if container != nil {
143
-		img.Parent = container.Image
144
-		img.Container = container.ID
145
-		img.ContainerConfig = *container.Config
146
-	}
147
-	if err := graph.Register(nil, layerData, img); err != nil {
148
-		return nil, err
149
-	}
150
-	return img, nil
151
-}
152
-
153
-// Register imports a pre-existing image into the graph.
154
-// FIXME: pass img as first argument
155
-func (graph *Graph) Register(jsonData []byte, layerData archive.ArchiveReader, img *image.Image) (err error) {
156
-	defer func() {
157
-		// If any error occurs, remove the new dir from the driver.
158
-		// Don't check for errors since the dir might not have been created.
159
-		// FIXME: this leaves a possible race condition.
160
-		if err != nil {
161
-			graph.driver.Remove(img.ID)
162
-		}
163
-	}()
164
-	if err := utils.ValidateID(img.ID); err != nil {
165
-		return err
166
-	}
167
-	// (This is a convenience to save time. Race conditions are taken care of by os.Rename)
168
-	if graph.Exists(img.ID) {
169
-		return fmt.Errorf("Image %s already exists", img.ID)
170
-	}
171
-
172
-	// Ensure that the image root does not exist on the filesystem
173
-	// when it is not registered in the graph.
174
-	// This is common when you switch from one graph driver to another
175
-	if err := os.RemoveAll(graph.ImageRoot(img.ID)); err != nil && !os.IsNotExist(err) {
176
-		return err
177
-	}
178
-
179
-	// If the driver has this ID but the graph doesn't, remove it from the driver to start fresh.
180
-	// (the graph is the source of truth).
181
-	// Ignore errors, since we don't know if the driver correctly returns ErrNotExist.
182
-	// (FIXME: make that mandatory for drivers).
183
-	graph.driver.Remove(img.ID)
184
-
185
-	tmp, err := graph.Mktemp("")
186
-	defer os.RemoveAll(tmp)
187
-	if err != nil {
188
-		return fmt.Errorf("Mktemp failed: %s", err)
189
-	}
190
-
191
-	// Create root filesystem in the driver
192
-	if err := graph.driver.Create(img.ID, img.Parent); err != nil {
193
-		return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
194
-	}
195
-	// Mount the root filesystem so we can apply the diff/layer
196
-	rootfs, err := graph.driver.Get(img.ID)
197
-	if err != nil {
198
-		return fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
199
-	}
200
-	defer graph.driver.Put(img.ID)
201
-	img.SetGraph(graph)
202
-	if err := image.StoreImage(img, jsonData, layerData, tmp, rootfs); err != nil {
203
-		return err
204
-	}
205
-	// Commit
206
-	if err := os.Rename(tmp, graph.ImageRoot(img.ID)); err != nil {
207
-		return err
208
-	}
209
-	graph.idIndex.Add(img.ID)
210
-	return nil
211
-}
212
-
213
-// TempLayerArchive creates a temporary archive of the given image's filesystem layer.
214
-//   The archive is stored on disk and will be automatically deleted as soon as has been read.
215
-//   If output is not nil, a human-readable progress bar will be written to it.
216
-//   FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives?
217
-func (graph *Graph) TempLayerArchive(id string, compression archive.Compression, sf *utils.StreamFormatter, output io.Writer) (*archive.TempArchive, error) {
218
-	image, err := graph.Get(id)
219
-	if err != nil {
220
-		return nil, err
221
-	}
222
-	tmp, err := graph.Mktemp("")
223
-	if err != nil {
224
-		return nil, err
225
-	}
226
-	a, err := image.TarLayer()
227
-	if err != nil {
228
-		return nil, err
229
-	}
230
-	progress := utils.ProgressReader(a, 0, output, sf, false, utils.TruncateID(id), "Buffering to disk")
231
-	defer progress.Close()
232
-	return archive.NewTempArchive(progress, tmp)
233
-}
234
-
235
-// Mktemp creates a temporary sub-directory inside the graph's filesystem.
236
-func (graph *Graph) Mktemp(id string) (string, error) {
237
-	dir := path.Join(graph.Root, "_tmp", utils.GenerateRandomID())
238
-	if err := os.MkdirAll(dir, 0700); err != nil {
239
-		return "", err
240
-	}
241
-	return dir, nil
242
-}
243
-
244
-// setupInitLayer populates a directory with mountpoints suitable
245
-// for bind-mounting dockerinit into the container. The mountpoint is simply an
246
-// empty file at /.dockerinit
247
-//
248
-// This extra layer is used by all containers as the top-most ro layer. It protects
249
-// the container from unwanted side-effects on the rw layer.
250
-func setupInitLayer(initLayer string) error {
251
-	for pth, typ := range map[string]string{
252
-		"/dev/pts":         "dir",
253
-		"/dev/shm":         "dir",
254
-		"/proc":            "dir",
255
-		"/sys":             "dir",
256
-		"/.dockerinit":     "file",
257
-		"/.dockerenv":      "file",
258
-		"/etc/resolv.conf": "file",
259
-		"/etc/hosts":       "file",
260
-		"/etc/hostname":    "file",
261
-		"/dev/console":     "file",
262
-		// "var/run": "dir",
263
-		// "var/lock": "dir",
264
-	} {
265
-		parts := strings.Split(pth, "/")
266
-		prev := "/"
267
-		for _, p := range parts[1:] {
268
-			prev = path.Join(prev, p)
269
-			syscall.Unlink(path.Join(initLayer, prev))
270
-		}
271
-
272
-		if _, err := os.Stat(path.Join(initLayer, pth)); err != nil {
273
-			if os.IsNotExist(err) {
274
-				switch typ {
275
-				case "dir":
276
-					if err := os.MkdirAll(path.Join(initLayer, pth), 0755); err != nil {
277
-						return err
278
-					}
279
-				case "file":
280
-					if err := os.MkdirAll(path.Join(initLayer, path.Dir(pth)), 0755); err != nil {
281
-						return err
282
-					}
283
-					f, err := os.OpenFile(path.Join(initLayer, pth), os.O_CREATE, 0755)
284
-					if err != nil {
285
-						return err
286
-					}
287
-					f.Close()
288
-				}
289
-			} else {
290
-				return err
291
-			}
292
-		}
293
-	}
294
-
295
-	// Layer is ready to use, if it wasn't before.
296
-	return nil
297
-}
298
-
299
-// Check if given error is "not empty".
300
-// Note: this is the way golang does it internally with os.IsNotExists.
301
-func isNotEmpty(err error) bool {
302
-	switch pe := err.(type) {
303
-	case nil:
304
-		return false
305
-	case *os.PathError:
306
-		err = pe.Err
307
-	case *os.LinkError:
308
-		err = pe.Err
309
-	}
310
-	return strings.Contains(err.Error(), " not empty")
311
-}
312
-
313
-// Delete atomically removes an image from the graph.
314
-func (graph *Graph) Delete(name string) error {
315
-	id, err := graph.idIndex.Get(name)
316
-	if err != nil {
317
-		return err
318
-	}
319
-	tmp, err := graph.Mktemp("")
320
-	if err != nil {
321
-		return err
322
-	}
323
-	graph.idIndex.Delete(id)
324
-	err = os.Rename(graph.ImageRoot(id), tmp)
325
-	if err != nil {
326
-		return err
327
-	}
328
-	// Remove rootfs data from the driver
329
-	graph.driver.Remove(id)
330
-	// Remove the trashed image directory
331
-	return os.RemoveAll(tmp)
332
-}
333
-
334
-// Map returns a list of all images in the graph, addressable by ID.
335
-func (graph *Graph) Map() (map[string]*image.Image, error) {
336
-	images := make(map[string]*image.Image)
337
-	err := graph.walkAll(func(image *image.Image) {
338
-		images[image.ID] = image
339
-	})
340
-	if err != nil {
341
-		return nil, err
342
-	}
343
-	return images, nil
344
-}
345
-
346
-// walkAll iterates over each image in the graph, and passes it to a handler.
347
-// The walking order is undetermined.
348
-func (graph *Graph) walkAll(handler func(*image.Image)) error {
349
-	files, err := ioutil.ReadDir(graph.Root)
350
-	if err != nil {
351
-		return err
352
-	}
353
-	for _, st := range files {
354
-		if img, err := graph.Get(st.Name()); err != nil {
355
-			// Skip image
356
-			continue
357
-		} else if handler != nil {
358
-			handler(img)
359
-		}
360
-	}
361
-	return nil
362
-}
363
-
364
-// ByParent returns a lookup table of images by their parent.
365
-// If an image of id ID has 3 children images, then the value for key ID
366
-// will be a list of 3 images.
367
-// If an image has no children, it will not have an entry in the table.
368
-func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
369
-	byParent := make(map[string][]*image.Image)
370
-	err := graph.walkAll(func(img *image.Image) {
371
-		parent, err := graph.Get(img.Parent)
372
-		if err != nil {
373
-			return
374
-		}
375
-		if children, exists := byParent[parent.ID]; exists {
376
-			byParent[parent.ID] = append(children, img)
377
-		} else {
378
-			byParent[parent.ID] = []*image.Image{img}
379
-		}
380
-	})
381
-	return byParent, err
382
-}
383
-
384
-// Heads returns all heads in the graph, keyed by id.
385
-// A head is an image which is not the parent of another image in the graph.
386
-func (graph *Graph) Heads() (map[string]*image.Image, error) {
387
-	heads := make(map[string]*image.Image)
388
-	byParent, err := graph.ByParent()
389
-	if err != nil {
390
-		return nil, err
391
-	}
392
-	err = graph.walkAll(func(image *image.Image) {
393
-		// If it's not in the byParent lookup table, then
394
-		// it's not a parent -> so it's a head!
395
-		if _, exists := byParent[image.ID]; !exists {
396
-			heads[image.ID] = image
397
-		}
398
-	})
399
-	return heads, err
400
-}
401
-
402
-func (graph *Graph) ImageRoot(id string) string {
403
-	return path.Join(graph.Root, id)
404
-}
405
-
406
-func (graph *Graph) Driver() graphdriver.Driver {
407
-	return graph.driver
408
-}
409 1
new file mode 100644
... ...
@@ -0,0 +1,408 @@
0
+package graph
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/archive"
5
+	"github.com/dotcloud/docker/dockerversion"
6
+	"github.com/dotcloud/docker/graphdriver"
7
+	"github.com/dotcloud/docker/image"
8
+	"github.com/dotcloud/docker/runconfig"
9
+	"github.com/dotcloud/docker/utils"
10
+	"io"
11
+	"io/ioutil"
12
+	"os"
13
+	"path"
14
+	"path/filepath"
15
+	"runtime"
16
+	"strings"
17
+	"syscall"
18
+	"time"
19
+)
20
+
21
+// A Graph is a store for versioned filesystem images and the relationship between them.
22
+type Graph struct {
23
+	Root    string
24
+	idIndex *utils.TruncIndex
25
+	driver  graphdriver.Driver
26
+}
27
+
28
+// NewGraph instantiates a new graph at the given root path in the filesystem.
29
+// `root` will be created if it doesn't exist.
30
+func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) {
31
+	abspath, err := filepath.Abs(root)
32
+	if err != nil {
33
+		return nil, err
34
+	}
35
+	// Create the root directory if it doesn't exists
36
+	if err := os.MkdirAll(root, 0700); err != nil && !os.IsExist(err) {
37
+		return nil, err
38
+	}
39
+
40
+	graph := &Graph{
41
+		Root:    abspath,
42
+		idIndex: utils.NewTruncIndex(),
43
+		driver:  driver,
44
+	}
45
+	if err := graph.restore(); err != nil {
46
+		return nil, err
47
+	}
48
+	return graph, nil
49
+}
50
+
51
+func (graph *Graph) restore() error {
52
+	dir, err := ioutil.ReadDir(graph.Root)
53
+	if err != nil {
54
+		return err
55
+	}
56
+	for _, v := range dir {
57
+		id := v.Name()
58
+		if graph.driver.Exists(id) {
59
+			graph.idIndex.Add(id)
60
+		}
61
+	}
62
+	utils.Debugf("Restored %d elements", len(dir))
63
+	return nil
64
+}
65
+
66
+// FIXME: Implement error subclass instead of looking at the error text
67
+// Note: This is the way golang implements os.IsNotExists on Plan9
68
+func (graph *Graph) IsNotExist(err error) bool {
69
+	return err != nil && (strings.Contains(err.Error(), "does not exist") || strings.Contains(err.Error(), "No such"))
70
+}
71
+
72
+// Exists returns true if an image is registered at the given id.
73
+// If the image doesn't exist or if an error is encountered, false is returned.
74
+func (graph *Graph) Exists(id string) bool {
75
+	if _, err := graph.Get(id); err != nil {
76
+		return false
77
+	}
78
+	return true
79
+}
80
+
81
+// Get returns the image with the given id, or an error if the image doesn't exist.
82
+func (graph *Graph) Get(name string) (*image.Image, error) {
83
+	id, err := graph.idIndex.Get(name)
84
+	if err != nil {
85
+		return nil, err
86
+	}
87
+	// FIXME: return nil when the image doesn't exist, instead of an error
88
+	img, err := image.LoadImage(graph.ImageRoot(id))
89
+	if err != nil {
90
+		return nil, err
91
+	}
92
+	if img.ID != id {
93
+		return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
94
+	}
95
+	img.SetGraph(graph)
96
+
97
+	if img.Size < 0 {
98
+		rootfs, err := graph.driver.Get(img.ID)
99
+		if err != nil {
100
+			return nil, fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
101
+		}
102
+		defer graph.driver.Put(img.ID)
103
+
104
+		var size int64
105
+		if img.Parent == "" {
106
+			if size, err = utils.TreeSize(rootfs); err != nil {
107
+				return nil, err
108
+			}
109
+		} else {
110
+			parentFs, err := graph.driver.Get(img.Parent)
111
+			if err != nil {
112
+				return nil, err
113
+			}
114
+			changes, err := archive.ChangesDirs(rootfs, parentFs)
115
+			if err != nil {
116
+				return nil, err
117
+			}
118
+			size = archive.ChangesSize(rootfs, changes)
119
+		}
120
+
121
+		img.Size = size
122
+		if err := img.SaveSize(graph.ImageRoot(id)); err != nil {
123
+			return nil, err
124
+		}
125
+	}
126
+	return img, nil
127
+}
128
+
129
+// Create creates a new image and registers it in the graph.
130
+func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*image.Image, error) {
131
+	img := &image.Image{
132
+		ID:            utils.GenerateRandomID(),
133
+		Comment:       comment,
134
+		Created:       time.Now().UTC(),
135
+		DockerVersion: dockerversion.VERSION,
136
+		Author:        author,
137
+		Config:        config,
138
+		Architecture:  runtime.GOARCH,
139
+		OS:            runtime.GOOS,
140
+	}
141
+	if containerID != "" {
142
+		img.Parent = containerImage
143
+		img.Container = containerID
144
+		img.ContainerConfig = *containerConfig
145
+	}
146
+	if err := graph.Register(nil, layerData, img); err != nil {
147
+		return nil, err
148
+	}
149
+	return img, nil
150
+}
151
+
152
+// Register imports a pre-existing image into the graph.
153
+// FIXME: pass img as first argument
154
+func (graph *Graph) Register(jsonData []byte, layerData archive.ArchiveReader, img *image.Image) (err error) {
155
+	defer func() {
156
+		// If any error occurs, remove the new dir from the driver.
157
+		// Don't check for errors since the dir might not have been created.
158
+		// FIXME: this leaves a possible race condition.
159
+		if err != nil {
160
+			graph.driver.Remove(img.ID)
161
+		}
162
+	}()
163
+	if err := utils.ValidateID(img.ID); err != nil {
164
+		return err
165
+	}
166
+	// (This is a convenience to save time. Race conditions are taken care of by os.Rename)
167
+	if graph.Exists(img.ID) {
168
+		return fmt.Errorf("Image %s already exists", img.ID)
169
+	}
170
+
171
+	// Ensure that the image root does not exist on the filesystem
172
+	// when it is not registered in the graph.
173
+	// This is common when you switch from one graph driver to another
174
+	if err := os.RemoveAll(graph.ImageRoot(img.ID)); err != nil && !os.IsNotExist(err) {
175
+		return err
176
+	}
177
+
178
+	// If the driver has this ID but the graph doesn't, remove it from the driver to start fresh.
179
+	// (the graph is the source of truth).
180
+	// Ignore errors, since we don't know if the driver correctly returns ErrNotExist.
181
+	// (FIXME: make that mandatory for drivers).
182
+	graph.driver.Remove(img.ID)
183
+
184
+	tmp, err := graph.Mktemp("")
185
+	defer os.RemoveAll(tmp)
186
+	if err != nil {
187
+		return fmt.Errorf("Mktemp failed: %s", err)
188
+	}
189
+
190
+	// Create root filesystem in the driver
191
+	if err := graph.driver.Create(img.ID, img.Parent); err != nil {
192
+		return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
193
+	}
194
+	// Mount the root filesystem so we can apply the diff/layer
195
+	rootfs, err := graph.driver.Get(img.ID)
196
+	if err != nil {
197
+		return fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
198
+	}
199
+	defer graph.driver.Put(img.ID)
200
+	img.SetGraph(graph)
201
+	if err := image.StoreImage(img, jsonData, layerData, tmp, rootfs); err != nil {
202
+		return err
203
+	}
204
+	// Commit
205
+	if err := os.Rename(tmp, graph.ImageRoot(img.ID)); err != nil {
206
+		return err
207
+	}
208
+	graph.idIndex.Add(img.ID)
209
+	return nil
210
+}
211
+
212
+// TempLayerArchive creates a temporary archive of the given image's filesystem layer.
213
+//   The archive is stored on disk and will be automatically deleted as soon as has been read.
214
+//   If output is not nil, a human-readable progress bar will be written to it.
215
+//   FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives?
216
+func (graph *Graph) TempLayerArchive(id string, compression archive.Compression, sf *utils.StreamFormatter, output io.Writer) (*archive.TempArchive, error) {
217
+	image, err := graph.Get(id)
218
+	if err != nil {
219
+		return nil, err
220
+	}
221
+	tmp, err := graph.Mktemp("")
222
+	if err != nil {
223
+		return nil, err
224
+	}
225
+	a, err := image.TarLayer()
226
+	if err != nil {
227
+		return nil, err
228
+	}
229
+	progress := utils.ProgressReader(a, 0, output, sf, false, utils.TruncateID(id), "Buffering to disk")
230
+	defer progress.Close()
231
+	return archive.NewTempArchive(progress, tmp)
232
+}
233
+
234
+// Mktemp creates a temporary sub-directory inside the graph's filesystem.
235
+func (graph *Graph) Mktemp(id string) (string, error) {
236
+	dir := path.Join(graph.Root, "_tmp", utils.GenerateRandomID())
237
+	if err := os.MkdirAll(dir, 0700); err != nil {
238
+		return "", err
239
+	}
240
+	return dir, nil
241
+}
242
+
243
+// setupInitLayer populates a directory with mountpoints suitable
244
+// for bind-mounting dockerinit into the container. The mountpoint is simply an
245
+// empty file at /.dockerinit
246
+//
247
+// This extra layer is used by all containers as the top-most ro layer. It protects
248
+// the container from unwanted side-effects on the rw layer.
249
+func SetupInitLayer(initLayer string) error {
250
+	for pth, typ := range map[string]string{
251
+		"/dev/pts":         "dir",
252
+		"/dev/shm":         "dir",
253
+		"/proc":            "dir",
254
+		"/sys":             "dir",
255
+		"/.dockerinit":     "file",
256
+		"/.dockerenv":      "file",
257
+		"/etc/resolv.conf": "file",
258
+		"/etc/hosts":       "file",
259
+		"/etc/hostname":    "file",
260
+		"/dev/console":     "file",
261
+		// "var/run": "dir",
262
+		// "var/lock": "dir",
263
+	} {
264
+		parts := strings.Split(pth, "/")
265
+		prev := "/"
266
+		for _, p := range parts[1:] {
267
+			prev = path.Join(prev, p)
268
+			syscall.Unlink(path.Join(initLayer, prev))
269
+		}
270
+
271
+		if _, err := os.Stat(path.Join(initLayer, pth)); err != nil {
272
+			if os.IsNotExist(err) {
273
+				switch typ {
274
+				case "dir":
275
+					if err := os.MkdirAll(path.Join(initLayer, pth), 0755); err != nil {
276
+						return err
277
+					}
278
+				case "file":
279
+					if err := os.MkdirAll(path.Join(initLayer, path.Dir(pth)), 0755); err != nil {
280
+						return err
281
+					}
282
+					f, err := os.OpenFile(path.Join(initLayer, pth), os.O_CREATE, 0755)
283
+					if err != nil {
284
+						return err
285
+					}
286
+					f.Close()
287
+				}
288
+			} else {
289
+				return err
290
+			}
291
+		}
292
+	}
293
+
294
+	// Layer is ready to use, if it wasn't before.
295
+	return nil
296
+}
297
+
298
+// Check if given error is "not empty".
299
+// Note: this is the way golang does it internally with os.IsNotExists.
300
+func isNotEmpty(err error) bool {
301
+	switch pe := err.(type) {
302
+	case nil:
303
+		return false
304
+	case *os.PathError:
305
+		err = pe.Err
306
+	case *os.LinkError:
307
+		err = pe.Err
308
+	}
309
+	return strings.Contains(err.Error(), " not empty")
310
+}
311
+
312
+// Delete atomically removes an image from the graph.
313
+func (graph *Graph) Delete(name string) error {
314
+	id, err := graph.idIndex.Get(name)
315
+	if err != nil {
316
+		return err
317
+	}
318
+	tmp, err := graph.Mktemp("")
319
+	if err != nil {
320
+		return err
321
+	}
322
+	graph.idIndex.Delete(id)
323
+	err = os.Rename(graph.ImageRoot(id), tmp)
324
+	if err != nil {
325
+		return err
326
+	}
327
+	// Remove rootfs data from the driver
328
+	graph.driver.Remove(id)
329
+	// Remove the trashed image directory
330
+	return os.RemoveAll(tmp)
331
+}
332
+
333
+// Map returns a list of all images in the graph, addressable by ID.
334
+func (graph *Graph) Map() (map[string]*image.Image, error) {
335
+	images := make(map[string]*image.Image)
336
+	err := graph.walkAll(func(image *image.Image) {
337
+		images[image.ID] = image
338
+	})
339
+	if err != nil {
340
+		return nil, err
341
+	}
342
+	return images, nil
343
+}
344
+
345
+// walkAll iterates over each image in the graph, and passes it to a handler.
346
+// The walking order is undetermined.
347
+func (graph *Graph) walkAll(handler func(*image.Image)) error {
348
+	files, err := ioutil.ReadDir(graph.Root)
349
+	if err != nil {
350
+		return err
351
+	}
352
+	for _, st := range files {
353
+		if img, err := graph.Get(st.Name()); err != nil {
354
+			// Skip image
355
+			continue
356
+		} else if handler != nil {
357
+			handler(img)
358
+		}
359
+	}
360
+	return nil
361
+}
362
+
363
+// ByParent returns a lookup table of images by their parent.
364
+// If an image of id ID has 3 children images, then the value for key ID
365
+// will be a list of 3 images.
366
+// If an image has no children, it will not have an entry in the table.
367
+func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
368
+	byParent := make(map[string][]*image.Image)
369
+	err := graph.walkAll(func(img *image.Image) {
370
+		parent, err := graph.Get(img.Parent)
371
+		if err != nil {
372
+			return
373
+		}
374
+		if children, exists := byParent[parent.ID]; exists {
375
+			byParent[parent.ID] = append(children, img)
376
+		} else {
377
+			byParent[parent.ID] = []*image.Image{img}
378
+		}
379
+	})
380
+	return byParent, err
381
+}
382
+
383
+// Heads returns all heads in the graph, keyed by id.
384
+// A head is an image which is not the parent of another image in the graph.
385
+func (graph *Graph) Heads() (map[string]*image.Image, error) {
386
+	heads := make(map[string]*image.Image)
387
+	byParent, err := graph.ByParent()
388
+	if err != nil {
389
+		return nil, err
390
+	}
391
+	err = graph.walkAll(func(image *image.Image) {
392
+		// If it's not in the byParent lookup table, then
393
+		// it's not a parent -> so it's a head!
394
+		if _, exists := byParent[image.ID]; !exists {
395
+			heads[image.ID] = image
396
+		}
397
+	})
398
+	return heads, err
399
+}
400
+
401
+func (graph *Graph) ImageRoot(id string) string {
402
+	return path.Join(graph.Root, id)
403
+}
404
+
405
+func (graph *Graph) Driver() graphdriver.Driver {
406
+	return graph.driver
407
+}
0 408
new file mode 100644
... ...
@@ -0,0 +1,235 @@
0
+package graph
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"github.com/dotcloud/docker/image"
6
+	"github.com/dotcloud/docker/utils"
7
+	"io/ioutil"
8
+	"os"
9
+	"path/filepath"
10
+	"sort"
11
+	"strings"
12
+)
13
+
14
+const DEFAULTTAG = "latest"
15
+
16
+type TagStore struct {
17
+	path         string
18
+	graph        *Graph
19
+	Repositories map[string]Repository
20
+}
21
+
22
+type Repository map[string]string
23
+
24
+func NewTagStore(path string, graph *Graph) (*TagStore, error) {
25
+	abspath, err := filepath.Abs(path)
26
+	if err != nil {
27
+		return nil, err
28
+	}
29
+	store := &TagStore{
30
+		path:         abspath,
31
+		graph:        graph,
32
+		Repositories: make(map[string]Repository),
33
+	}
34
+	// Load the json file if it exists, otherwise create it.
35
+	if err := store.Reload(); os.IsNotExist(err) {
36
+		if err := store.Save(); err != nil {
37
+			return nil, err
38
+		}
39
+	} else if err != nil {
40
+		return nil, err
41
+	}
42
+	return store, nil
43
+}
44
+
45
+func (store *TagStore) Save() error {
46
+	// Store the json ball
47
+	jsonData, err := json.Marshal(store)
48
+	if err != nil {
49
+		return err
50
+	}
51
+	if err := ioutil.WriteFile(store.path, jsonData, 0600); err != nil {
52
+		return err
53
+	}
54
+	return nil
55
+}
56
+
57
+func (store *TagStore) Reload() error {
58
+	jsonData, err := ioutil.ReadFile(store.path)
59
+	if err != nil {
60
+		return err
61
+	}
62
+	if err := json.Unmarshal(jsonData, store); err != nil {
63
+		return err
64
+	}
65
+	return nil
66
+}
67
+
68
+func (store *TagStore) LookupImage(name string) (*image.Image, error) {
69
+	// FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
70
+	// (so we can pass all errors here)
71
+	repos, tag := utils.ParseRepositoryTag(name)
72
+	if tag == "" {
73
+		tag = DEFAULTTAG
74
+	}
75
+	img, err := store.GetImage(repos, tag)
76
+	if err != nil {
77
+		return nil, err
78
+	} else if img == nil {
79
+		if img, err = store.graph.Get(name); err != nil {
80
+			return nil, err
81
+		}
82
+	}
83
+	return img, nil
84
+}
85
+
86
+// Return a reverse-lookup table of all the names which refer to each image
87
+// Eg. {"43b5f19b10584": {"base:latest", "base:v1"}}
88
+func (store *TagStore) ByID() map[string][]string {
89
+	byID := make(map[string][]string)
90
+	for repoName, repository := range store.Repositories {
91
+		for tag, id := range repository {
92
+			name := repoName + ":" + tag
93
+			if _, exists := byID[id]; !exists {
94
+				byID[id] = []string{name}
95
+			} else {
96
+				byID[id] = append(byID[id], name)
97
+				sort.Strings(byID[id])
98
+			}
99
+		}
100
+	}
101
+	return byID
102
+}
103
+
104
+func (store *TagStore) ImageName(id string) string {
105
+	if names, exists := store.ByID()[id]; exists && len(names) > 0 {
106
+		return names[0]
107
+	}
108
+	return utils.TruncateID(id)
109
+}
110
+
111
+func (store *TagStore) DeleteAll(id string) error {
112
+	names, exists := store.ByID()[id]
113
+	if !exists || len(names) == 0 {
114
+		return nil
115
+	}
116
+	for _, name := range names {
117
+		if strings.Contains(name, ":") {
118
+			nameParts := strings.Split(name, ":")
119
+			if _, err := store.Delete(nameParts[0], nameParts[1]); err != nil {
120
+				return err
121
+			}
122
+		} else {
123
+			if _, err := store.Delete(name, ""); err != nil {
124
+				return err
125
+			}
126
+		}
127
+	}
128
+	return nil
129
+}
130
+
131
+func (store *TagStore) Delete(repoName, tag string) (bool, error) {
132
+	deleted := false
133
+	if err := store.Reload(); err != nil {
134
+		return false, err
135
+	}
136
+	if r, exists := store.Repositories[repoName]; exists {
137
+		if tag != "" {
138
+			if _, exists2 := r[tag]; exists2 {
139
+				delete(r, tag)
140
+				if len(r) == 0 {
141
+					delete(store.Repositories, repoName)
142
+				}
143
+				deleted = true
144
+			} else {
145
+				return false, fmt.Errorf("No such tag: %s:%s", repoName, tag)
146
+			}
147
+		} else {
148
+			delete(store.Repositories, repoName)
149
+			deleted = true
150
+		}
151
+	} else {
152
+		fmt.Errorf("No such repository: %s", repoName)
153
+	}
154
+	return deleted, store.Save()
155
+}
156
+
157
+func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
158
+	img, err := store.LookupImage(imageName)
159
+	if err != nil {
160
+		return err
161
+	}
162
+	if tag == "" {
163
+		tag = DEFAULTTAG
164
+	}
165
+	if err := validateRepoName(repoName); err != nil {
166
+		return err
167
+	}
168
+	if err := validateTagName(tag); err != nil {
169
+		return err
170
+	}
171
+	if err := store.Reload(); err != nil {
172
+		return err
173
+	}
174
+	var repo Repository
175
+	if r, exists := store.Repositories[repoName]; exists {
176
+		repo = r
177
+	} else {
178
+		repo = make(map[string]string)
179
+		if old, exists := store.Repositories[repoName]; exists && !force {
180
+			return fmt.Errorf("Conflict: Tag %s:%s is already set to %s", repoName, tag, old)
181
+		}
182
+		store.Repositories[repoName] = repo
183
+	}
184
+	repo[tag] = img.ID
185
+	return store.Save()
186
+}
187
+
188
+func (store *TagStore) Get(repoName string) (Repository, error) {
189
+	if err := store.Reload(); err != nil {
190
+		return nil, err
191
+	}
192
+	if r, exists := store.Repositories[repoName]; exists {
193
+		return r, nil
194
+	}
195
+	return nil, nil
196
+}
197
+
198
+func (store *TagStore) GetImage(repoName, tagOrID string) (*image.Image, error) {
199
+	repo, err := store.Get(repoName)
200
+	if err != nil {
201
+		return nil, err
202
+	} else if repo == nil {
203
+		return nil, nil
204
+	}
205
+	if revision, exists := repo[tagOrID]; exists {
206
+		return store.graph.Get(revision)
207
+	}
208
+	// If no matching tag is found, search through images for a matching image id
209
+	for _, revision := range repo {
210
+		if strings.HasPrefix(revision, tagOrID) {
211
+			return store.graph.Get(revision)
212
+		}
213
+	}
214
+	return nil, nil
215
+}
216
+
217
+// Validate the name of a repository
218
+func validateRepoName(name string) error {
219
+	if name == "" {
220
+		return fmt.Errorf("Repository name can't be empty")
221
+	}
222
+	return nil
223
+}
224
+
225
+// Validate the name of a tag
226
+func validateTagName(name string) error {
227
+	if name == "" {
228
+		return fmt.Errorf("Tag name can't be empty")
229
+	}
230
+	if strings.Contains(name, "/") || strings.Contains(name, ":") {
231
+		return fmt.Errorf("Illegal tag name: %s", name)
232
+	}
233
+	return nil
234
+}
0 235
new file mode 100644
... ...
@@ -0,0 +1,110 @@
0
+package graph
1
+
2
+import (
3
+	"bytes"
4
+	"github.com/dotcloud/docker/graphdriver"
5
+	_ "github.com/dotcloud/docker/graphdriver/vfs" // import the vfs driver so it is used in the tests
6
+	"github.com/dotcloud/docker/image"
7
+	"github.com/dotcloud/docker/utils"
8
+	"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
9
+	"io"
10
+	"os"
11
+	"path"
12
+	"testing"
13
+)
14
+
15
+const (
16
+	testImageName = "myapp"
17
+	testImageID   = "foo"
18
+)
19
+
20
+func fakeTar() (io.Reader, error) {
21
+	content := []byte("Hello world!\n")
22
+	buf := new(bytes.Buffer)
23
+	tw := tar.NewWriter(buf)
24
+	for _, name := range []string{"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} {
25
+		hdr := new(tar.Header)
26
+		hdr.Size = int64(len(content))
27
+		hdr.Name = name
28
+		if err := tw.WriteHeader(hdr); err != nil {
29
+			return nil, err
30
+		}
31
+		tw.Write([]byte(content))
32
+	}
33
+	tw.Close()
34
+	return buf, nil
35
+}
36
+
37
+func mkTestTagStore(root string, t *testing.T) *TagStore {
38
+	driver, err := graphdriver.New(root)
39
+	if err != nil {
40
+		t.Fatal(err)
41
+	}
42
+	graph, err := NewGraph(root, driver)
43
+	if err != nil {
44
+		t.Fatal(err)
45
+	}
46
+	store, err := NewTagStore(path.Join(root, "tags"), graph)
47
+	if err != nil {
48
+		t.Fatal(err)
49
+	}
50
+	archive, err := fakeTar()
51
+	if err != nil {
52
+		t.Fatal(err)
53
+	}
54
+	img := &image.Image{ID: testImageID}
55
+	// FIXME: this fails on Darwin with:
56
+	// tags_unit_test.go:36: mkdir /var/folders/7g/b3ydb5gx4t94ndr_cljffbt80000gq/T/docker-test569b-tRunner-075013689/vfs/dir/foo/etc/postgres: permission denied
57
+	if err := graph.Register(nil, archive, img); err != nil {
58
+		t.Fatal(err)
59
+	}
60
+	if err := store.Set(testImageName, "", testImageID, false); err != nil {
61
+		t.Fatal(err)
62
+	}
63
+	return store
64
+}
65
+
66
+func TestLookupImage(t *testing.T) {
67
+	tmp, err := utils.TestDirectory("")
68
+	if err != nil {
69
+		t.Fatal(err)
70
+	}
71
+	defer os.RemoveAll(tmp)
72
+	store := mkTestTagStore(tmp, t)
73
+	defer store.graph.driver.Cleanup()
74
+
75
+	if img, err := store.LookupImage(testImageName); err != nil {
76
+		t.Fatal(err)
77
+	} else if img == nil {
78
+		t.Errorf("Expected 1 image, none found")
79
+	}
80
+	if img, err := store.LookupImage(testImageName + ":" + DEFAULTTAG); err != nil {
81
+		t.Fatal(err)
82
+	} else if img == nil {
83
+		t.Errorf("Expected 1 image, none found")
84
+	}
85
+
86
+	if img, err := store.LookupImage(testImageName + ":" + "fail"); err == nil {
87
+		t.Errorf("Expected error, none found")
88
+	} else if img != nil {
89
+		t.Errorf("Expected 0 image, 1 found")
90
+	}
91
+
92
+	if img, err := store.LookupImage("fail:fail"); err == nil {
93
+		t.Errorf("Expected error, none found")
94
+	} else if img != nil {
95
+		t.Errorf("Expected 0 image, 1 found")
96
+	}
97
+
98
+	if img, err := store.LookupImage(testImageID); err != nil {
99
+		t.Fatal(err)
100
+	} else if img == nil {
101
+		t.Errorf("Expected 1 image, none found")
102
+	}
103
+
104
+	if img, err := store.LookupImage(testImageName + ":" + testImageID); err != nil {
105
+		t.Fatal(err)
106
+	} else if img == nil {
107
+		t.Errorf("Expected 1 image, none found")
108
+	}
109
+}
... ...
@@ -2,9 +2,9 @@ package docker
2 2
 
3 3
 import (
4 4
 	"errors"
5
-	"github.com/dotcloud/docker"
6 5
 	"github.com/dotcloud/docker/archive"
7 6
 	"github.com/dotcloud/docker/dockerversion"
7
+	"github.com/dotcloud/docker/graph"
8 8
 	"github.com/dotcloud/docker/graphdriver"
9 9
 	"github.com/dotcloud/docker/image"
10 10
 	"github.com/dotcloud/docker/utils"
... ...
@@ -25,7 +25,7 @@ func TestMount(t *testing.T) {
25 25
 	if err != nil {
26 26
 		t.Fatal(err)
27 27
 	}
28
-	image, err := graph.Create(archive, nil, "Testing", "", nil)
28
+	image, err := graph.Create(archive, "", "", "Testing", "", nil, nil)
29 29
 	if err != nil {
30 30
 		t.Fatal(err)
31 31
 	}
... ...
@@ -97,7 +97,7 @@ func TestGraphCreate(t *testing.T) {
97 97
 	if err != nil {
98 98
 		t.Fatal(err)
99 99
 	}
100
-	img, err := graph.Create(archive, nil, "Testing", "", nil)
100
+	img, err := graph.Create(archive, "", "", "Testing", "", nil, nil)
101 101
 	if err != nil {
102 102
 		t.Fatal(err)
103 103
 	}
... ...
@@ -165,12 +165,12 @@ func TestDeletePrefix(t *testing.T) {
165 165
 	assertNImages(graph, t, 0)
166 166
 }
167 167
 
168
-func createTestImage(graph *docker.Graph, t *testing.T) *image.Image {
168
+func createTestImage(graph *graph.Graph, t *testing.T) *image.Image {
169 169
 	archive, err := fakeTar()
170 170
 	if err != nil {
171 171
 		t.Fatal(err)
172 172
 	}
173
-	img, err := graph.Create(archive, nil, "Test image", "", nil)
173
+	img, err := graph.Create(archive, "", "", "Test image", "", nil, nil)
174 174
 	if err != nil {
175 175
 		t.Fatal(err)
176 176
 	}
... ...
@@ -185,7 +185,7 @@ func TestDelete(t *testing.T) {
185 185
 		t.Fatal(err)
186 186
 	}
187 187
 	assertNImages(graph, t, 0)
188
-	img, err := graph.Create(archive, nil, "Bla bla", "", nil)
188
+	img, err := graph.Create(archive, "", "", "Bla bla", "", nil, nil)
189 189
 	if err != nil {
190 190
 		t.Fatal(err)
191 191
 	}
... ...
@@ -200,7 +200,7 @@ func TestDelete(t *testing.T) {
200 200
 		t.Fatal(err)
201 201
 	}
202 202
 	// Test 2 create (same name) / 1 delete
203
-	img1, err := graph.Create(archive, nil, "Testing", "", nil)
203
+	img1, err := graph.Create(archive, "", "", "Testing", "", nil, nil)
204 204
 	if err != nil {
205 205
 		t.Fatal(err)
206 206
 	}
... ...
@@ -208,7 +208,7 @@ func TestDelete(t *testing.T) {
208 208
 	if err != nil {
209 209
 		t.Fatal(err)
210 210
 	}
211
-	if _, err = graph.Create(archive, nil, "Testing", "", nil); err != nil {
211
+	if _, err = graph.Create(archive, "", "", "Testing", "", nil, nil); err != nil {
212 212
 		t.Fatal(err)
213 213
 	}
214 214
 	assertNImages(graph, t, 2)
... ...
@@ -280,7 +280,7 @@ func TestByParent(t *testing.T) {
280 280
  * HELPER FUNCTIONS
281 281
  */
282 282
 
283
-func assertNImages(graph *docker.Graph, t *testing.T, n int) {
283
+func assertNImages(graph *graph.Graph, t *testing.T, n int) {
284 284
 	if images, err := graph.Map(); err != nil {
285 285
 		t.Fatal(err)
286 286
 	} else if actualN := len(images); actualN != n {
... ...
@@ -288,7 +288,7 @@ func assertNImages(graph *docker.Graph, t *testing.T, n int) {
288 288
 	}
289 289
 }
290 290
 
291
-func tempGraph(t *testing.T) (*docker.Graph, graphdriver.Driver) {
291
+func tempGraph(t *testing.T) (*graph.Graph, graphdriver.Driver) {
292 292
 	tmp, err := ioutil.TempDir("", "docker-graph-")
293 293
 	if err != nil {
294 294
 		t.Fatal(err)
... ...
@@ -297,14 +297,14 @@ func tempGraph(t *testing.T) (*docker.Graph, graphdriver.Driver) {
297 297
 	if err != nil {
298 298
 		t.Fatal(err)
299 299
 	}
300
-	graph, err := docker.NewGraph(tmp, driver)
300
+	graph, err := graph.NewGraph(tmp, driver)
301 301
 	if err != nil {
302 302
 		t.Fatal(err)
303 303
 	}
304 304
 	return graph, driver
305 305
 }
306 306
 
307
-func nukeGraph(graph *docker.Graph) {
307
+func nukeGraph(graph *graph.Graph) {
308 308
 	graph.Driver().Cleanup()
309 309
 	os.RemoveAll(graph.Root)
310 310
 }
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"github.com/dotcloud/docker/execdriver"
11 11
 	"github.com/dotcloud/docker/execdriver/lxc"
12 12
 	"github.com/dotcloud/docker/execdriver/native"
13
+	"github.com/dotcloud/docker/graph"
13 14
 	"github.com/dotcloud/docker/graphdriver"
14 15
 	"github.com/dotcloud/docker/graphdriver/aufs"
15 16
 	_ "github.com/dotcloud/docker/graphdriver/btrfs"
... ...
@@ -48,11 +49,11 @@ type Runtime struct {
48 48
 	repository     string
49 49
 	sysInitPath    string
50 50
 	containers     *list.List
51
-	graph          *Graph
52
-	repositories   *TagStore
51
+	graph          *graph.Graph
52
+	repositories   *graph.TagStore
53 53
 	idIndex        *utils.TruncIndex
54 54
 	sysInfo        *sysinfo.SysInfo
55
-	volumes        *Graph
55
+	volumes        *graph.Graph
56 56
 	srv            *Server
57 57
 	eng            *engine.Engine
58 58
 	config         *daemonconfig.Config
... ...
@@ -486,7 +487,7 @@ func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Containe
486 486
 	}
487 487
 	defer runtime.driver.Put(initID)
488 488
 
489
-	if err := setupInitLayer(initPath); err != nil {
489
+	if err := graph.SetupInitLayer(initPath); err != nil {
490 490
 		return nil, nil, err
491 491
 	}
492 492
 
... ...
@@ -555,7 +556,16 @@ func (runtime *Runtime) Commit(container *Container, repository, tag, comment, a
555 555
 	defer rwTar.Close()
556 556
 
557 557
 	// Create a new image from the container's base layers + a new layer from container changes
558
-	img, err := runtime.graph.Create(rwTar, container, comment, author, config)
558
+	var (
559
+		containerID, containerImage string
560
+		containerConfig             *runconfig.Config
561
+	)
562
+	if container != nil {
563
+		containerID = container.ID
564
+		containerImage = container.Image
565
+		containerConfig = container.Config
566
+	}
567
+	img, err := runtime.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config)
559 568
 	if err != nil {
560 569
 		return nil, err
561 570
 	}
... ...
@@ -654,13 +664,13 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
654 654
 
655 655
 	if ad, ok := driver.(*aufs.Driver); ok {
656 656
 		utils.Debugf("Migrating existing containers")
657
-		if err := ad.Migrate(config.Root, setupInitLayer); err != nil {
657
+		if err := ad.Migrate(config.Root, graph.SetupInitLayer); err != nil {
658 658
 			return nil, err
659 659
 		}
660 660
 	}
661 661
 
662 662
 	utils.Debugf("Creating images graph")
663
-	g, err := NewGraph(path.Join(config.Root, "graph"), driver)
663
+	g, err := graph.NewGraph(path.Join(config.Root, "graph"), driver)
664 664
 	if err != nil {
665 665
 		return nil, err
666 666
 	}
... ...
@@ -672,12 +682,12 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
672 672
 		return nil, err
673 673
 	}
674 674
 	utils.Debugf("Creating volumes graph")
675
-	volumes, err := NewGraph(path.Join(config.Root, "volumes"), volumesDriver)
675
+	volumes, err := graph.NewGraph(path.Join(config.Root, "volumes"), volumesDriver)
676 676
 	if err != nil {
677 677
 		return nil, err
678 678
 	}
679 679
 	utils.Debugf("Creating repository list")
680
-	repositories, err := NewTagStore(path.Join(config.Root, "repositories-"+driver.String()), g)
680
+	repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+driver.String()), g)
681 681
 	if err != nil {
682 682
 		return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
683 683
 	}
... ...
@@ -878,7 +888,7 @@ func (runtime *Runtime) Nuke() error {
878 878
 // which need direct access to runtime.graph.
879 879
 // Once the tests switch to using engine and jobs, this method
880 880
 // can go away.
881
-func (runtime *Runtime) Graph() *Graph {
881
+func (runtime *Runtime) Graph() *graph.Graph {
882 882
 	return runtime.graph
883 883
 }
884 884
 
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"github.com/dotcloud/docker/daemonconfig"
9 9
 	"github.com/dotcloud/docker/dockerversion"
10 10
 	"github.com/dotcloud/docker/engine"
11
+	"github.com/dotcloud/docker/graph"
11 12
 	"github.com/dotcloud/docker/image"
12 13
 	"github.com/dotcloud/docker/pkg/graphdb"
13 14
 	"github.com/dotcloud/docker/registry"
... ...
@@ -334,7 +335,7 @@ func (srv *Server) ImageExport(job *engine.Job) engine.Status {
334 334
 		}
335 335
 
336 336
 		// write repositories
337
-		rootRepoMap := map[string]Repository{}
337
+		rootRepoMap := map[string]graph.Repository{}
338 338
 		rootRepoMap[name] = rootRepo
339 339
 		rootRepoJson, _ := json.Marshal(rootRepoMap)
340 340
 
... ...
@@ -547,7 +548,7 @@ func (srv *Server) ImageLoad(job *engine.Job) engine.Status {
547 547
 
548 548
 	repositoriesJson, err := ioutil.ReadFile(path.Join(tmpImageDir, "repo", "repositories"))
549 549
 	if err == nil {
550
-		repositories := map[string]Repository{}
550
+		repositories := map[string]graph.Repository{}
551 551
 		if err := json.Unmarshal(repositoriesJson, &repositories); err != nil {
552 552
 			return job.Error(err)
553 553
 		}
... ...
@@ -1617,7 +1618,7 @@ func (srv *Server) ImageImport(job *engine.Job) engine.Status {
1617 1617
 		defer progressReader.Close()
1618 1618
 		archive = progressReader
1619 1619
 	}
1620
-	img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
1620
+	img, err := srv.runtime.graph.Create(archive, "", "", "Imported from "+src, "", nil, nil)
1621 1621
 	if err != nil {
1622 1622
 		return job.Error(err)
1623 1623
 	}
... ...
@@ -1664,7 +1665,7 @@ func (srv *Server) ContainerCreate(job *engine.Job) engine.Status {
1664 1664
 		if srv.runtime.graph.IsNotExist(err) {
1665 1665
 			_, tag := utils.ParseRepositoryTag(config.Image)
1666 1666
 			if tag == "" {
1667
-				tag = DEFAULTTAG
1667
+				tag = graph.DEFAULTTAG
1668 1668
 			}
1669 1669
 			return job.Errorf("No such image: %s (tag: %s)", config.Image, tag)
1670 1670
 		}
... ...
@@ -1837,7 +1838,7 @@ func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force boo
1837 1837
 
1838 1838
 	repoName, tag = utils.ParseRepositoryTag(name)
1839 1839
 	if tag == "" {
1840
-		tag = DEFAULTTAG
1840
+		tag = graph.DEFAULTTAG
1841 1841
 	}
1842 1842
 
1843 1843
 	img, err := srv.runtime.repositories.LookupImage(name)
1844 1844
deleted file mode 100644
... ...
@@ -1,235 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"encoding/json"
5
-	"fmt"
6
-	"github.com/dotcloud/docker/image"
7
-	"github.com/dotcloud/docker/utils"
8
-	"io/ioutil"
9
-	"os"
10
-	"path/filepath"
11
-	"sort"
12
-	"strings"
13
-)
14
-
15
-const DEFAULTTAG = "latest"
16
-
17
-type TagStore struct {
18
-	path         string
19
-	graph        *Graph
20
-	Repositories map[string]Repository
21
-}
22
-
23
-type Repository map[string]string
24
-
25
-func NewTagStore(path string, graph *Graph) (*TagStore, error) {
26
-	abspath, err := filepath.Abs(path)
27
-	if err != nil {
28
-		return nil, err
29
-	}
30
-	store := &TagStore{
31
-		path:         abspath,
32
-		graph:        graph,
33
-		Repositories: make(map[string]Repository),
34
-	}
35
-	// Load the json file if it exists, otherwise create it.
36
-	if err := store.Reload(); os.IsNotExist(err) {
37
-		if err := store.Save(); err != nil {
38
-			return nil, err
39
-		}
40
-	} else if err != nil {
41
-		return nil, err
42
-	}
43
-	return store, nil
44
-}
45
-
46
-func (store *TagStore) Save() error {
47
-	// Store the json ball
48
-	jsonData, err := json.Marshal(store)
49
-	if err != nil {
50
-		return err
51
-	}
52
-	if err := ioutil.WriteFile(store.path, jsonData, 0600); err != nil {
53
-		return err
54
-	}
55
-	return nil
56
-}
57
-
58
-func (store *TagStore) Reload() error {
59
-	jsonData, err := ioutil.ReadFile(store.path)
60
-	if err != nil {
61
-		return err
62
-	}
63
-	if err := json.Unmarshal(jsonData, store); err != nil {
64
-		return err
65
-	}
66
-	return nil
67
-}
68
-
69
-func (store *TagStore) LookupImage(name string) (*image.Image, error) {
70
-	// FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
71
-	// (so we can pass all errors here)
72
-	repos, tag := utils.ParseRepositoryTag(name)
73
-	if tag == "" {
74
-		tag = DEFAULTTAG
75
-	}
76
-	img, err := store.GetImage(repos, tag)
77
-	if err != nil {
78
-		return nil, err
79
-	} else if img == nil {
80
-		if img, err = store.graph.Get(name); err != nil {
81
-			return nil, err
82
-		}
83
-	}
84
-	return img, nil
85
-}
86
-
87
-// Return a reverse-lookup table of all the names which refer to each image
88
-// Eg. {"43b5f19b10584": {"base:latest", "base:v1"}}
89
-func (store *TagStore) ByID() map[string][]string {
90
-	byID := make(map[string][]string)
91
-	for repoName, repository := range store.Repositories {
92
-		for tag, id := range repository {
93
-			name := repoName + ":" + tag
94
-			if _, exists := byID[id]; !exists {
95
-				byID[id] = []string{name}
96
-			} else {
97
-				byID[id] = append(byID[id], name)
98
-				sort.Strings(byID[id])
99
-			}
100
-		}
101
-	}
102
-	return byID
103
-}
104
-
105
-func (store *TagStore) ImageName(id string) string {
106
-	if names, exists := store.ByID()[id]; exists && len(names) > 0 {
107
-		return names[0]
108
-	}
109
-	return utils.TruncateID(id)
110
-}
111
-
112
-func (store *TagStore) DeleteAll(id string) error {
113
-	names, exists := store.ByID()[id]
114
-	if !exists || len(names) == 0 {
115
-		return nil
116
-	}
117
-	for _, name := range names {
118
-		if strings.Contains(name, ":") {
119
-			nameParts := strings.Split(name, ":")
120
-			if _, err := store.Delete(nameParts[0], nameParts[1]); err != nil {
121
-				return err
122
-			}
123
-		} else {
124
-			if _, err := store.Delete(name, ""); err != nil {
125
-				return err
126
-			}
127
-		}
128
-	}
129
-	return nil
130
-}
131
-
132
-func (store *TagStore) Delete(repoName, tag string) (bool, error) {
133
-	deleted := false
134
-	if err := store.Reload(); err != nil {
135
-		return false, err
136
-	}
137
-	if r, exists := store.Repositories[repoName]; exists {
138
-		if tag != "" {
139
-			if _, exists2 := r[tag]; exists2 {
140
-				delete(r, tag)
141
-				if len(r) == 0 {
142
-					delete(store.Repositories, repoName)
143
-				}
144
-				deleted = true
145
-			} else {
146
-				return false, fmt.Errorf("No such tag: %s:%s", repoName, tag)
147
-			}
148
-		} else {
149
-			delete(store.Repositories, repoName)
150
-			deleted = true
151
-		}
152
-	} else {
153
-		fmt.Errorf("No such repository: %s", repoName)
154
-	}
155
-	return deleted, store.Save()
156
-}
157
-
158
-func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
159
-	img, err := store.LookupImage(imageName)
160
-	if err != nil {
161
-		return err
162
-	}
163
-	if tag == "" {
164
-		tag = DEFAULTTAG
165
-	}
166
-	if err := validateRepoName(repoName); err != nil {
167
-		return err
168
-	}
169
-	if err := validateTagName(tag); err != nil {
170
-		return err
171
-	}
172
-	if err := store.Reload(); err != nil {
173
-		return err
174
-	}
175
-	var repo Repository
176
-	if r, exists := store.Repositories[repoName]; exists {
177
-		repo = r
178
-	} else {
179
-		repo = make(map[string]string)
180
-		if old, exists := store.Repositories[repoName]; exists && !force {
181
-			return fmt.Errorf("Conflict: Tag %s:%s is already set to %s", repoName, tag, old)
182
-		}
183
-		store.Repositories[repoName] = repo
184
-	}
185
-	repo[tag] = img.ID
186
-	return store.Save()
187
-}
188
-
189
-func (store *TagStore) Get(repoName string) (Repository, error) {
190
-	if err := store.Reload(); err != nil {
191
-		return nil, err
192
-	}
193
-	if r, exists := store.Repositories[repoName]; exists {
194
-		return r, nil
195
-	}
196
-	return nil, nil
197
-}
198
-
199
-func (store *TagStore) GetImage(repoName, tagOrID string) (*image.Image, error) {
200
-	repo, err := store.Get(repoName)
201
-	if err != nil {
202
-		return nil, err
203
-	} else if repo == nil {
204
-		return nil, nil
205
-	}
206
-	if revision, exists := repo[tagOrID]; exists {
207
-		return store.graph.Get(revision)
208
-	}
209
-	// If no matching tag is found, search through images for a matching image id
210
-	for _, revision := range repo {
211
-		if strings.HasPrefix(revision, tagOrID) {
212
-			return store.graph.Get(revision)
213
-		}
214
-	}
215
-	return nil, nil
216
-}
217
-
218
-// Validate the name of a repository
219
-func validateRepoName(name string) error {
220
-	if name == "" {
221
-		return fmt.Errorf("Repository name can't be empty")
222
-	}
223
-	return nil
224
-}
225
-
226
-// Validate the name of a tag
227
-func validateTagName(name string) error {
228
-	if name == "" {
229
-		return fmt.Errorf("Tag name can't be empty")
230
-	}
231
-	if strings.Contains(name, "/") || strings.Contains(name, ":") {
232
-		return fmt.Errorf("Illegal tag name: %s", name)
233
-	}
234
-	return nil
235
-}
236 1
deleted file mode 100644
... ...
@@ -1,89 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"github.com/dotcloud/docker/graphdriver"
5
-	"github.com/dotcloud/docker/image"
6
-	"github.com/dotcloud/docker/utils"
7
-	"os"
8
-	"path"
9
-	"testing"
10
-)
11
-
12
-const (
13
-	testImageName = "myapp"
14
-	testImageID   = "foo"
15
-)
16
-
17
-func mkTestTagStore(root string, t *testing.T) *TagStore {
18
-	driver, err := graphdriver.New(root)
19
-	if err != nil {
20
-		t.Fatal(err)
21
-	}
22
-	graph, err := NewGraph(root, driver)
23
-	if err != nil {
24
-		t.Fatal(err)
25
-	}
26
-	store, err := NewTagStore(path.Join(root, "tags"), graph)
27
-	if err != nil {
28
-		t.Fatal(err)
29
-	}
30
-	archive, err := fakeTar()
31
-	if err != nil {
32
-		t.Fatal(err)
33
-	}
34
-	img := &image.Image{ID: testImageID}
35
-	// FIXME: this fails on Darwin with:
36
-	// tags_unit_test.go:36: mkdir /var/folders/7g/b3ydb5gx4t94ndr_cljffbt80000gq/T/docker-test569b-tRunner-075013689/vfs/dir/foo/etc/postgres: permission denied
37
-	if err := graph.Register(nil, archive, img); err != nil {
38
-		t.Fatal(err)
39
-	}
40
-	if err := store.Set(testImageName, "", testImageID, false); err != nil {
41
-		t.Fatal(err)
42
-	}
43
-	return store
44
-}
45
-
46
-func TestLookupImage(t *testing.T) {
47
-	tmp, err := utils.TestDirectory("")
48
-	if err != nil {
49
-		t.Fatal(err)
50
-	}
51
-	defer os.RemoveAll(tmp)
52
-	store := mkTestTagStore(tmp, t)
53
-	defer store.graph.driver.Cleanup()
54
-
55
-	if img, err := store.LookupImage(testImageName); err != nil {
56
-		t.Fatal(err)
57
-	} else if img == nil {
58
-		t.Errorf("Expected 1 image, none found")
59
-	}
60
-	if img, err := store.LookupImage(testImageName + ":" + DEFAULTTAG); err != nil {
61
-		t.Fatal(err)
62
-	} else if img == nil {
63
-		t.Errorf("Expected 1 image, none found")
64
-	}
65
-
66
-	if img, err := store.LookupImage(testImageName + ":" + "fail"); err == nil {
67
-		t.Errorf("Expected error, none found")
68
-	} else if img != nil {
69
-		t.Errorf("Expected 0 image, 1 found")
70
-	}
71
-
72
-	if img, err := store.LookupImage("fail:fail"); err == nil {
73
-		t.Errorf("Expected error, none found")
74
-	} else if img != nil {
75
-		t.Errorf("Expected 0 image, 1 found")
76
-	}
77
-
78
-	if img, err := store.LookupImage(testImageID); err != nil {
79
-		t.Fatal(err)
80
-	} else if img == nil {
81
-		t.Errorf("Expected 1 image, none found")
82
-	}
83
-
84
-	if img, err := store.LookupImage(testImageName + ":" + testImageID); err != nil {
85
-		t.Fatal(err)
86
-	} else if img == nil {
87
-		t.Errorf("Expected 1 image, none found")
88
-	}
89
-}
90 1
deleted file mode 100644
... ...
@@ -1,24 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"bytes"
5
-	"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
6
-	"io"
7
-)
8
-
9
-func fakeTar() (io.Reader, error) {
10
-	content := []byte("Hello world!\n")
11
-	buf := new(bytes.Buffer)
12
-	tw := tar.NewWriter(buf)
13
-	for _, name := range []string{"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} {
14
-		hdr := new(tar.Header)
15
-		hdr.Size = int64(len(content))
16
-		hdr.Name = name
17
-		if err := tw.WriteHeader(hdr); err != nil {
18
-			return nil, err
19
-		}
20
-		tw.Write([]byte(content))
21
-	}
22
-	tw.Close()
23
-	return buf, nil
24
-}
... ...
@@ -216,7 +216,7 @@ func createVolumes(container *Container) error {
216 216
 		return err
217 217
 	}
218 218
 
219
-	volumesDriver := container.runtime.volumes.driver
219
+	volumesDriver := container.runtime.volumes.Driver()
220 220
 	// Create the requested volumes if they don't exist
221 221
 	for volPath := range container.Config.Volumes {
222 222
 		volPath = filepath.Clean(volPath)
... ...
@@ -246,7 +246,7 @@ func createVolumes(container *Container) error {
246 246
 			// Do not pass a container as the parameter for the volume creation.
247 247
 			// The graph driver using the container's information ( Image ) to
248 248
 			// create the parent.
249
-			c, err := container.runtime.volumes.Create(nil, nil, "", "", nil)
249
+			c, err := container.runtime.volumes.Create(nil, "", "", "", "", nil, nil)
250 250
 			if err != nil {
251 251
 				return err
252 252
 			}