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>
| ... | ... |
@@ -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 |
|