Browse code

Adding readOnly parameter to graphdriver Create method

Since the layer store was introduced, the level above the graphdriver
now differentiates between read/write and read-only layers. This
distinction is useful for graphdrivers that need to take special steps
when creating a layer based on whether it is read-only or not.
Adding this parameter allows the graphdrivers to differentiate, which
in the case of the Windows graphdriver, removes our dependence on parsing
the id of the parent for "-init" in order to infer this information.

This will also set the stage for unblocking some of the layer store
unit tests in the next preview build of Windows.

Signed-off-by: Stefan J. Wernli <swernli@microsoft.com>

Stefan J. Wernli authored on 2016/02/19 10:24:59
Showing 14 changed files
... ...
@@ -194,6 +194,12 @@ func (a *Driver) Exists(id string) bool {
194 194
 	return true
195 195
 }
196 196
 
197
+// CreateReadWrite creates a layer that is writable for use as a container
198
+// file system.
199
+func (a *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error {
200
+	return a.Create(id, parent, mountLabel, storageOpt)
201
+}
202
+
197 203
 // Create three folders for each id
198 204
 // mnt, layers, and diff
199 205
 func (a *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
... ...
@@ -320,7 +320,7 @@ func TestGetDiff(t *testing.T) {
320 320
 	d := newDriver(t)
321 321
 	defer os.RemoveAll(tmp)
322 322
 
323
-	if err := d.Create("1", "", "", nil); err != nil {
323
+	if err := d.CreateReadWrite("1", "", "", nil); err != nil {
324 324
 		t.Fatal(err)
325 325
 	}
326 326
 
... ...
@@ -357,7 +357,7 @@ func TestChanges(t *testing.T) {
357 357
 	if err := d.Create("1", "", "", nil); err != nil {
358 358
 		t.Fatal(err)
359 359
 	}
360
-	if err := d.Create("2", "1", "", nil); err != nil {
360
+	if err := d.CreateReadWrite("2", "1", "", nil); err != nil {
361 361
 		t.Fatal(err)
362 362
 	}
363 363
 
... ...
@@ -403,7 +403,7 @@ func TestChanges(t *testing.T) {
403 403
 		t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
404 404
 	}
405 405
 
406
-	if err := d.Create("3", "2", "", nil); err != nil {
406
+	if err := d.CreateReadWrite("3", "2", "", nil); err != nil {
407 407
 		t.Fatal(err)
408 408
 	}
409 409
 	mntPoint, err = d.Get("3", "")
... ...
@@ -448,7 +448,7 @@ func TestDiffSize(t *testing.T) {
448 448
 	d := newDriver(t)
449 449
 	defer os.RemoveAll(tmp)
450 450
 
451
-	if err := d.Create("1", "", "", nil); err != nil {
451
+	if err := d.CreateReadWrite("1", "", "", nil); err != nil {
452 452
 		t.Fatal(err)
453 453
 	}
454 454
 
... ...
@@ -490,7 +490,7 @@ func TestChildDiffSize(t *testing.T) {
490 490
 	defer os.RemoveAll(tmp)
491 491
 	defer d.Cleanup()
492 492
 
493
-	if err := d.Create("1", "", "", nil); err != nil {
493
+	if err := d.CreateReadWrite("1", "", "", nil); err != nil {
494 494
 		t.Fatal(err)
495 495
 	}
496 496
 
... ...
@@ -592,7 +592,7 @@ func TestApplyDiff(t *testing.T) {
592 592
 	defer os.RemoveAll(tmp)
593 593
 	defer d.Cleanup()
594 594
 
595
-	if err := d.Create("1", "", "", nil); err != nil {
595
+	if err := d.CreateReadWrite("1", "", "", nil); err != nil {
596 596
 		t.Fatal(err)
597 597
 	}
598 598
 
... ...
@@ -671,7 +671,7 @@ func testMountMoreThan42Layers(t *testing.T, mountPath string) {
671 671
 		}
672 672
 		current = hash(current)
673 673
 
674
-		if err := d.Create(current, parent, "", nil); err != nil {
674
+		if err := d.CreateReadWrite(current, parent, "", nil); err != nil {
675 675
 			t.Logf("Current layer %d", i)
676 676
 			t.Error(err)
677 677
 		}
... ...
@@ -241,6 +241,12 @@ func (d *Driver) subvolumesDirID(id string) string {
241 241
 	return path.Join(d.subvolumesDir(), id)
242 242
 }
243 243
 
244
+// CreateReadWrite creates a layer that is writable for use as a container
245
+// file system.
246
+func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error {
247
+	return d.Create(id, parent, mountLabel, storageOpt)
248
+}
249
+
244 250
 // Create the filesystem with given id.
245 251
 func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
246 252
 
... ...
@@ -30,7 +30,7 @@ func TestBtrfsCreateSnap(t *testing.T) {
30 30
 
31 31
 func TestBtrfsSubvolDelete(t *testing.T) {
32 32
 	d := graphtest.GetDriver(t, "btrfs")
33
-	if err := d.Create("test", "", "", nil); err != nil {
33
+	if err := d.CreateReadWrite("test", "", "", nil); err != nil {
34 34
 		t.Fatal(err)
35 35
 	}
36 36
 	defer graphtest.PutDriver(t)
... ...
@@ -117,6 +117,12 @@ func (d *Driver) Cleanup() error {
117 117
 	return err
118 118
 }
119 119
 
120
+// CreateReadWrite creates a layer that is writable for use as a container
121
+// file system.
122
+func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error {
123
+	return d.Create(id, parent, mountLabel, storageOpt)
124
+}
125
+
120 126
 // Create adds a device with a given id and the parent.
121 127
 func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
122 128
 	if err := d.DeviceSet.AddDevice(id, parent, storageOpt); err != nil {
... ...
@@ -46,6 +46,9 @@ type InitFunc func(root string, options []string, uidMaps, gidMaps []idtools.IDM
46 46
 type ProtoDriver interface {
47 47
 	// String returns a string representation of this driver.
48 48
 	String() string
49
+	// CreateReadWrite creates a new, empty filesystem layer that is ready
50
+	// to be used as the storage for a container.
51
+	CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error
49 52
 	// Create creates a new, empty, filesystem layer with the
50 53
 	// specified id and parent and mountLabel. Parent and mountLabel may be "".
51 54
 	Create(id, parent, mountLabel string, storageOpt map[string]string) error
... ...
@@ -215,7 +215,7 @@ func createBase(t *testing.T, driver graphdriver.Driver, name string) {
215 215
 	oldmask := syscall.Umask(0)
216 216
 	defer syscall.Umask(oldmask)
217 217
 
218
-	if err := driver.Create(name, "", "", nil); err != nil {
218
+	if err := driver.CreateReadWrite(name, "", "", nil); err != nil {
219 219
 		t.Fatal(err)
220 220
 	}
221 221
 
... ...
@@ -220,6 +220,12 @@ func (d *Driver) Cleanup() error {
220 220
 	return nil
221 221
 }
222 222
 
223
+// CreateReadWrite creates a layer that is writable for use as a container
224
+// file system.
225
+func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error {
226
+	return d.Create(id, parent, mountLabel, storageOpt)
227
+}
228
+
223 229
 // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id.
224 230
 // The parent filesystem is used to configure these directories for the overlay.
225 231
 func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) (retErr error) {
... ...
@@ -54,6 +54,22 @@ func (d *graphDriverProxy) String() string {
54 54
 	return d.name
55 55
 }
56 56
 
57
+func (d *graphDriverProxy) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error {
58
+	args := &graphDriverRequest{
59
+		ID:         id,
60
+		Parent:     parent,
61
+		MountLabel: mountLabel,
62
+	}
63
+	var ret graphDriverResponse
64
+	if err := d.client.Call("GraphDriver.CreateReadWrite", args, &ret); err != nil {
65
+		return err
66
+	}
67
+	if ret.Err != "" {
68
+		return errors.New(ret.Err)
69
+	}
70
+	return nil
71
+}
72
+
57 73
 func (d *graphDriverProxy) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
58 74
 	args := &graphDriverRequest{
59 75
 		ID:         id,
... ...
@@ -68,6 +68,12 @@ func (d *Driver) Cleanup() error {
68 68
 	return nil
69 69
 }
70 70
 
71
+// CreateReadWrite creates a layer that is writable for use as a container
72
+// file system.
73
+func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error {
74
+	return d.Create(id, parent, mountLabel, storageOpt)
75
+}
76
+
71 77
 // Create prepares the filesystem for the VFS driver and copies the directory for the given id under the parent.
72 78
 func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
73 79
 	if len(storageOpt) != 0 {
... ...
@@ -106,8 +106,18 @@ func (d *Driver) Exists(id string) bool {
106 106
 	return result
107 107
 }
108 108
 
109
-// Create creates a new layer with the given id.
109
+// CreateReadWrite creates a layer that is writable for use as a container
110
+// file system.
111
+func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error {
112
+	return d.create(id, parent, mountLabel, false, storageOpt)
113
+}
114
+
115
+// Create creates a new read-only layer with the given id.
110 116
 func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
117
+	return d.create(id, parent, mountLabel, true, storageOpt)
118
+}
119
+
120
+func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt map[string]string) error {
111 121
 	if len(storageOpt) != 0 {
112 122
 		return fmt.Errorf("--storage-opt is not supported for windows")
113 123
 	}
... ...
@@ -124,27 +134,30 @@ func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]str
124 124
 
125 125
 	var layerChain []string
126 126
 
127
-	parentIsInit := strings.HasSuffix(rPId, "-init")
128
-
129
-	if !parentIsInit && rPId != "" {
127
+	if rPId != "" {
130 128
 		parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId)
131 129
 		if err != nil {
132 130
 			return err
133 131
 		}
134
-		layerChain = []string{parentPath}
132
+		if _, err := os.Stat(filepath.Join(parentPath, "Files")); err == nil {
133
+			// This is a legitimate parent layer (not the empty "-init" layer),
134
+			// so include it in the layer chain.
135
+			layerChain = []string{parentPath}
136
+		}
135 137
 	}
136 138
 
137 139
 	layerChain = append(layerChain, parentChain...)
138 140
 
139
-	if parentIsInit {
140
-		if len(layerChain) == 0 {
141
-			return fmt.Errorf("Cannot create a read/write layer without a parent layer.")
142
-		}
143
-		if err := hcsshim.CreateSandboxLayer(d.info, id, layerChain[0], layerChain); err != nil {
141
+	if readOnly {
142
+		if err := hcsshim.CreateLayer(d.info, id, rPId); err != nil {
144 143
 			return err
145 144
 		}
146 145
 	} else {
147
-		if err := hcsshim.CreateLayer(d.info, id, rPId); err != nil {
146
+		var parentPath string
147
+		if len(layerChain) != 0 {
148
+			parentPath = layerChain[0]
149
+		}
150
+		if err := hcsshim.CreateSandboxLayer(d.info, id, parentPath, layerChain); err != nil {
148 151
 			return err
149 152
 		}
150 153
 	}
... ...
@@ -240,6 +240,12 @@ func (d *Driver) mountPath(id string) string {
240 240
 	return path.Join(d.options.mountPath, "graph", getMountpoint(id))
241 241
 }
242 242
 
243
+// CreateReadWrite creates a layer that is writable for use as a container
244
+// file system.
245
+func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error {
246
+	return d.Create(id, parent, mountLabel, storageOpt)
247
+}
248
+
243 249
 // Create prepares the dataset and filesystem for the ZFS driver for the given id under the parent.
244 250
 func (d *Driver) Create(id string, parent string, mountLabel string, storageOpt map[string]string) error {
245 251
 	if len(storageOpt) != 0 {
... ...
@@ -67,6 +67,7 @@ func (s *DockerExternalGraphdriverSuite) SetUpSuite(c *check.C) {
67 67
 		ID         string `json:",omitempty"`
68 68
 		Parent     string `json:",omitempty"`
69 69
 		MountLabel string `json:",omitempty"`
70
+		ReadOnly   bool   `json:",omitempty"`
70 71
 	}
71 72
 
72 73
 	type graphDriverResponse struct {
... ...
@@ -115,6 +116,20 @@ func (s *DockerExternalGraphdriverSuite) SetUpSuite(c *check.C) {
115 115
 		respond(w, "{}")
116 116
 	})
117 117
 
118
+	mux.HandleFunc("/GraphDriver.CreateReadWrite", func(w http.ResponseWriter, r *http.Request) {
119
+		s.ec.creations++
120
+
121
+		var req graphDriverRequest
122
+		if err := decReq(r.Body, &req, w); err != nil {
123
+			return
124
+		}
125
+		if err := driver.CreateReadWrite(req.ID, req.Parent, "", nil); err != nil {
126
+			respond(w, err)
127
+			return
128
+		}
129
+		respond(w, "{}")
130
+	})
131
+
118 132
 	mux.HandleFunc("/GraphDriver.Create", func(w http.ResponseWriter, r *http.Request) {
119 133
 		s.ec.creations++
120 134
 
... ...
@@ -461,7 +461,7 @@ func (ls *layerStore) CreateRWLayer(name string, parent ChainID, mountLabel stri
461 461
 		m.initID = pid
462 462
 	}
463 463
 
464
-	if err = ls.driver.Create(m.mountID, pid, "", storageOpt); err != nil {
464
+	if err = ls.driver.CreateReadWrite(m.mountID, pid, "", storageOpt); err != nil {
465 465
 		return nil, err
466 466
 	}
467 467