github.com/demonoid81/containerd@v1.3.4/snapshots/overlay/overlay_test.go (about) 1 // +build linux 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package overlay 20 21 import ( 22 "context" 23 "fmt" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "syscall" 28 "testing" 29 30 "github.com/containerd/containerd/mount" 31 "github.com/containerd/containerd/pkg/testutil" 32 "github.com/containerd/containerd/snapshots" 33 "github.com/containerd/containerd/snapshots/storage" 34 "github.com/containerd/containerd/snapshots/testsuite" 35 ) 36 37 func newSnapshotter(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error) { 38 snapshotter, err := NewSnapshotter(root) 39 if err != nil { 40 return nil, nil, err 41 } 42 43 return snapshotter, func() error { return snapshotter.Close() }, nil 44 } 45 46 func TestOverlay(t *testing.T) { 47 testutil.RequiresRoot(t) 48 testsuite.SnapshotterSuite(t, "Overlay", newSnapshotter) 49 } 50 51 func TestOverlayMounts(t *testing.T) { 52 ctx := context.TODO() 53 root, err := ioutil.TempDir("", "overlay") 54 if err != nil { 55 t.Fatal(err) 56 } 57 defer os.RemoveAll(root) 58 o, _, err := newSnapshotter(ctx, root) 59 if err != nil { 60 t.Fatal(err) 61 } 62 mounts, err := o.Prepare(ctx, "/tmp/test", "") 63 if err != nil { 64 t.Fatal(err) 65 } 66 if len(mounts) != 1 { 67 t.Errorf("should only have 1 mount but received %d", len(mounts)) 68 } 69 m := mounts[0] 70 if m.Type != "bind" { 71 t.Errorf("mount type should be bind but received %q", m.Type) 72 } 73 expected := filepath.Join(root, "snapshots", "1", "fs") 74 if m.Source != expected { 75 t.Errorf("expected source %q but received %q", expected, m.Source) 76 } 77 if m.Options[0] != "rw" { 78 t.Errorf("expected mount option rw but received %q", m.Options[0]) 79 } 80 if m.Options[1] != "rbind" { 81 t.Errorf("expected mount option rbind but received %q", m.Options[1]) 82 } 83 } 84 85 func TestOverlayCommit(t *testing.T) { 86 ctx := context.TODO() 87 root, err := ioutil.TempDir("", "overlay") 88 if err != nil { 89 t.Fatal(err) 90 } 91 defer os.RemoveAll(root) 92 o, _, err := newSnapshotter(ctx, root) 93 if err != nil { 94 t.Fatal(err) 95 } 96 key := "/tmp/test" 97 mounts, err := o.Prepare(ctx, key, "") 98 if err != nil { 99 t.Fatal(err) 100 } 101 m := mounts[0] 102 if err := ioutil.WriteFile(filepath.Join(m.Source, "foo"), []byte("hi"), 0660); err != nil { 103 t.Fatal(err) 104 } 105 if err := o.Commit(ctx, "base", key); err != nil { 106 t.Fatal(err) 107 } 108 } 109 110 func TestOverlayOverlayMount(t *testing.T) { 111 ctx := context.TODO() 112 root, err := ioutil.TempDir("", "overlay") 113 if err != nil { 114 t.Fatal(err) 115 } 116 defer os.RemoveAll(root) 117 o, _, err := newSnapshotter(ctx, root) 118 if err != nil { 119 t.Fatal(err) 120 } 121 key := "/tmp/test" 122 if _, err = o.Prepare(ctx, key, ""); err != nil { 123 t.Fatal(err) 124 } 125 if err := o.Commit(ctx, "base", key); err != nil { 126 t.Fatal(err) 127 } 128 var mounts []mount.Mount 129 if mounts, err = o.Prepare(ctx, "/tmp/layer2", "base"); err != nil { 130 t.Fatal(err) 131 } 132 if len(mounts) != 1 { 133 t.Errorf("should only have 1 mount but received %d", len(mounts)) 134 } 135 m := mounts[0] 136 if m.Type != "overlay" { 137 t.Errorf("mount type should be overlay but received %q", m.Type) 138 } 139 if m.Source != "overlay" { 140 t.Errorf("expected source %q but received %q", "overlay", m.Source) 141 } 142 var ( 143 bp = getBasePath(ctx, o, root, "/tmp/layer2") 144 work = "workdir=" + filepath.Join(bp, "work") 145 upper = "upperdir=" + filepath.Join(bp, "fs") 146 lower = "lowerdir=" + getParents(ctx, o, root, "/tmp/layer2")[0] 147 ) 148 for i, v := range []string{ 149 work, 150 upper, 151 lower, 152 } { 153 if m.Options[i] != v { 154 t.Errorf("expected %q but received %q", v, m.Options[i]) 155 } 156 } 157 } 158 159 func getBasePath(ctx context.Context, sn snapshots.Snapshotter, root, key string) string { 160 o := sn.(*snapshotter) 161 ctx, t, err := o.ms.TransactionContext(ctx, false) 162 if err != nil { 163 panic(err) 164 } 165 defer t.Rollback() 166 167 s, err := storage.GetSnapshot(ctx, key) 168 if err != nil { 169 panic(err) 170 } 171 172 return filepath.Join(root, "snapshots", s.ID) 173 } 174 175 func getParents(ctx context.Context, sn snapshots.Snapshotter, root, key string) []string { 176 o := sn.(*snapshotter) 177 ctx, t, err := o.ms.TransactionContext(ctx, false) 178 if err != nil { 179 panic(err) 180 } 181 defer t.Rollback() 182 s, err := storage.GetSnapshot(ctx, key) 183 if err != nil { 184 panic(err) 185 } 186 parents := make([]string, len(s.ParentIDs)) 187 for i := range s.ParentIDs { 188 parents[i] = filepath.Join(root, "snapshots", s.ParentIDs[i], "fs") 189 } 190 return parents 191 } 192 193 func TestOverlayOverlayRead(t *testing.T) { 194 testutil.RequiresRoot(t) 195 ctx := context.TODO() 196 root, err := ioutil.TempDir("", "overlay") 197 if err != nil { 198 t.Fatal(err) 199 } 200 defer os.RemoveAll(root) 201 o, _, err := newSnapshotter(ctx, root) 202 if err != nil { 203 t.Fatal(err) 204 } 205 key := "/tmp/test" 206 mounts, err := o.Prepare(ctx, key, "") 207 if err != nil { 208 t.Fatal(err) 209 } 210 m := mounts[0] 211 if err := ioutil.WriteFile(filepath.Join(m.Source, "foo"), []byte("hi"), 0660); err != nil { 212 t.Fatal(err) 213 } 214 if err := o.Commit(ctx, "base", key); err != nil { 215 t.Fatal(err) 216 } 217 if mounts, err = o.Prepare(ctx, "/tmp/layer2", "base"); err != nil { 218 t.Fatal(err) 219 } 220 dest := filepath.Join(root, "dest") 221 if err := os.Mkdir(dest, 0700); err != nil { 222 t.Fatal(err) 223 } 224 if err := mount.All(mounts, dest); err != nil { 225 t.Fatal(err) 226 } 227 defer syscall.Unmount(dest, 0) 228 data, err := ioutil.ReadFile(filepath.Join(dest, "foo")) 229 if err != nil { 230 t.Fatal(err) 231 } 232 if e := string(data); e != "hi" { 233 t.Fatalf("expected file contents hi but got %q", e) 234 } 235 } 236 237 func TestOverlayView(t *testing.T) { 238 ctx := context.TODO() 239 root, err := ioutil.TempDir("", "overlay") 240 if err != nil { 241 t.Fatal(err) 242 } 243 defer os.RemoveAll(root) 244 o, _, err := newSnapshotter(ctx, root) 245 if err != nil { 246 t.Fatal(err) 247 } 248 key := "/tmp/base" 249 mounts, err := o.Prepare(ctx, key, "") 250 if err != nil { 251 t.Fatal(err) 252 } 253 m := mounts[0] 254 if err := ioutil.WriteFile(filepath.Join(m.Source, "foo"), []byte("hi"), 0660); err != nil { 255 t.Fatal(err) 256 } 257 if err := o.Commit(ctx, "base", key); err != nil { 258 t.Fatal(err) 259 } 260 261 key = "/tmp/top" 262 _, err = o.Prepare(ctx, key, "base") 263 if err != nil { 264 t.Fatal(err) 265 } 266 if err := ioutil.WriteFile(filepath.Join(getParents(ctx, o, root, "/tmp/top")[0], "foo"), []byte("hi, again"), 0660); err != nil { 267 t.Fatal(err) 268 } 269 if err := o.Commit(ctx, "top", key); err != nil { 270 t.Fatal(err) 271 } 272 273 mounts, err = o.View(ctx, "/tmp/view1", "base") 274 if err != nil { 275 t.Fatal(err) 276 } 277 if len(mounts) != 1 { 278 t.Fatalf("should only have 1 mount but received %d", len(mounts)) 279 } 280 m = mounts[0] 281 if m.Type != "bind" { 282 t.Errorf("mount type should be bind but received %q", m.Type) 283 } 284 expected := getParents(ctx, o, root, "/tmp/view1")[0] 285 if m.Source != expected { 286 t.Errorf("expected source %q but received %q", expected, m.Source) 287 } 288 if m.Options[0] != "ro" { 289 t.Errorf("expected mount option ro but received %q", m.Options[0]) 290 } 291 if m.Options[1] != "rbind" { 292 t.Errorf("expected mount option rbind but received %q", m.Options[1]) 293 } 294 295 mounts, err = o.View(ctx, "/tmp/view2", "top") 296 if err != nil { 297 t.Fatal(err) 298 } 299 if len(mounts) != 1 { 300 t.Fatalf("should only have 1 mount but received %d", len(mounts)) 301 } 302 m = mounts[0] 303 if m.Type != "overlay" { 304 t.Errorf("mount type should be overlay but received %q", m.Type) 305 } 306 if m.Source != "overlay" { 307 t.Errorf("mount source should be overlay but received %q", m.Source) 308 } 309 if len(m.Options) != 1 { 310 t.Errorf("expected 1 mount option but got %d", len(m.Options)) 311 } 312 lowers := getParents(ctx, o, root, "/tmp/view2") 313 expected = fmt.Sprintf("lowerdir=%s:%s", lowers[0], lowers[1]) 314 if m.Options[0] != expected { 315 t.Errorf("expected option %q but received %q", expected, m.Options[0]) 316 } 317 }