github.com/lalkh/containerd@v1.4.3/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 newSnapshotterWithOpts(opts ...Opt) testsuite.SnapshotterFunc { 38 return func(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error) { 39 snapshotter, err := NewSnapshotter(root, opts...) 40 if err != nil { 41 return nil, nil, err 42 } 43 44 return snapshotter, func() error { return snapshotter.Close() }, nil 45 } 46 } 47 48 func TestOverlay(t *testing.T) { 49 testutil.RequiresRoot(t) 50 optTestCases := map[string][]Opt{ 51 "no opt": nil, 52 // default in init() 53 "AsynchronousRemove": {AsynchronousRemove}, 54 } 55 56 for optsName, opts := range optTestCases { 57 t.Run(optsName, func(t *testing.T) { 58 newSnapshotter := newSnapshotterWithOpts(opts...) 59 testsuite.SnapshotterSuite(t, "Overlay", newSnapshotter) 60 t.Run("TestOverlayMounts", func(t *testing.T) { 61 testOverlayMounts(t, newSnapshotter) 62 }) 63 t.Run("TestOverlayCommit", func(t *testing.T) { 64 testOverlayCommit(t, newSnapshotter) 65 }) 66 t.Run("TestOverlayOverlayMount", func(t *testing.T) { 67 testOverlayOverlayMount(t, newSnapshotter) 68 }) 69 t.Run("TestOverlayOverlayRead", func(t *testing.T) { 70 testOverlayOverlayRead(t, newSnapshotter) 71 }) 72 t.Run("TestOverlayView", func(t *testing.T) { 73 testOverlayView(t, newSnapshotter) 74 }) 75 }) 76 } 77 } 78 79 func testOverlayMounts(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) { 80 ctx := context.TODO() 81 root, err := ioutil.TempDir("", "overlay") 82 if err != nil { 83 t.Fatal(err) 84 } 85 defer os.RemoveAll(root) 86 o, _, err := newSnapshotter(ctx, root) 87 if err != nil { 88 t.Fatal(err) 89 } 90 mounts, err := o.Prepare(ctx, "/tmp/test", "") 91 if err != nil { 92 t.Fatal(err) 93 } 94 if len(mounts) != 1 { 95 t.Errorf("should only have 1 mount but received %d", len(mounts)) 96 } 97 m := mounts[0] 98 if m.Type != "bind" { 99 t.Errorf("mount type should be bind but received %q", m.Type) 100 } 101 expected := filepath.Join(root, "snapshots", "1", "fs") 102 if m.Source != expected { 103 t.Errorf("expected source %q but received %q", expected, m.Source) 104 } 105 if m.Options[0] != "rw" { 106 t.Errorf("expected mount option rw but received %q", m.Options[0]) 107 } 108 if m.Options[1] != "rbind" { 109 t.Errorf("expected mount option rbind but received %q", m.Options[1]) 110 } 111 } 112 113 func testOverlayCommit(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) { 114 ctx := context.TODO() 115 root, err := ioutil.TempDir("", "overlay") 116 if err != nil { 117 t.Fatal(err) 118 } 119 defer os.RemoveAll(root) 120 o, _, err := newSnapshotter(ctx, root) 121 if err != nil { 122 t.Fatal(err) 123 } 124 key := "/tmp/test" 125 mounts, err := o.Prepare(ctx, key, "") 126 if err != nil { 127 t.Fatal(err) 128 } 129 m := mounts[0] 130 if err := ioutil.WriteFile(filepath.Join(m.Source, "foo"), []byte("hi"), 0660); err != nil { 131 t.Fatal(err) 132 } 133 if err := o.Commit(ctx, "base", key); err != nil { 134 t.Fatal(err) 135 } 136 } 137 138 func testOverlayOverlayMount(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) { 139 ctx := context.TODO() 140 root, err := ioutil.TempDir("", "overlay") 141 if err != nil { 142 t.Fatal(err) 143 } 144 defer os.RemoveAll(root) 145 o, _, err := newSnapshotter(ctx, root) 146 if err != nil { 147 t.Fatal(err) 148 } 149 key := "/tmp/test" 150 if _, err = o.Prepare(ctx, key, ""); err != nil { 151 t.Fatal(err) 152 } 153 if err := o.Commit(ctx, "base", key); err != nil { 154 t.Fatal(err) 155 } 156 var mounts []mount.Mount 157 if mounts, err = o.Prepare(ctx, "/tmp/layer2", "base"); err != nil { 158 t.Fatal(err) 159 } 160 if len(mounts) != 1 { 161 t.Errorf("should only have 1 mount but received %d", len(mounts)) 162 } 163 m := mounts[0] 164 if m.Type != "overlay" { 165 t.Errorf("mount type should be overlay but received %q", m.Type) 166 } 167 if m.Source != "overlay" { 168 t.Errorf("expected source %q but received %q", "overlay", m.Source) 169 } 170 var ( 171 bp = getBasePath(ctx, o, root, "/tmp/layer2") 172 work = "workdir=" + filepath.Join(bp, "work") 173 upper = "upperdir=" + filepath.Join(bp, "fs") 174 lower = "lowerdir=" + getParents(ctx, o, root, "/tmp/layer2")[0] 175 ) 176 for i, v := range []string{ 177 "index=off", 178 work, 179 upper, 180 lower, 181 } { 182 if m.Options[i] != v { 183 t.Errorf("expected %q but received %q", v, m.Options[i]) 184 } 185 } 186 } 187 188 func getBasePath(ctx context.Context, sn snapshots.Snapshotter, root, key string) string { 189 o := sn.(*snapshotter) 190 ctx, t, err := o.ms.TransactionContext(ctx, false) 191 if err != nil { 192 panic(err) 193 } 194 defer t.Rollback() 195 196 s, err := storage.GetSnapshot(ctx, key) 197 if err != nil { 198 panic(err) 199 } 200 201 return filepath.Join(root, "snapshots", s.ID) 202 } 203 204 func getParents(ctx context.Context, sn snapshots.Snapshotter, root, key string) []string { 205 o := sn.(*snapshotter) 206 ctx, t, err := o.ms.TransactionContext(ctx, false) 207 if err != nil { 208 panic(err) 209 } 210 defer t.Rollback() 211 s, err := storage.GetSnapshot(ctx, key) 212 if err != nil { 213 panic(err) 214 } 215 parents := make([]string, len(s.ParentIDs)) 216 for i := range s.ParentIDs { 217 parents[i] = filepath.Join(root, "snapshots", s.ParentIDs[i], "fs") 218 } 219 return parents 220 } 221 222 func testOverlayOverlayRead(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) { 223 testutil.RequiresRoot(t) 224 ctx := context.TODO() 225 root, err := ioutil.TempDir("", "overlay") 226 if err != nil { 227 t.Fatal(err) 228 } 229 defer os.RemoveAll(root) 230 o, _, err := newSnapshotter(ctx, root) 231 if err != nil { 232 t.Fatal(err) 233 } 234 key := "/tmp/test" 235 mounts, err := o.Prepare(ctx, key, "") 236 if err != nil { 237 t.Fatal(err) 238 } 239 m := mounts[0] 240 if err := ioutil.WriteFile(filepath.Join(m.Source, "foo"), []byte("hi"), 0660); err != nil { 241 t.Fatal(err) 242 } 243 if err := o.Commit(ctx, "base", key); err != nil { 244 t.Fatal(err) 245 } 246 if mounts, err = o.Prepare(ctx, "/tmp/layer2", "base"); err != nil { 247 t.Fatal(err) 248 } 249 dest := filepath.Join(root, "dest") 250 if err := os.Mkdir(dest, 0700); err != nil { 251 t.Fatal(err) 252 } 253 if err := mount.All(mounts, dest); err != nil { 254 t.Fatal(err) 255 } 256 defer syscall.Unmount(dest, 0) 257 data, err := ioutil.ReadFile(filepath.Join(dest, "foo")) 258 if err != nil { 259 t.Fatal(err) 260 } 261 if e := string(data); e != "hi" { 262 t.Fatalf("expected file contents hi but got %q", e) 263 } 264 } 265 266 func testOverlayView(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) { 267 ctx := context.TODO() 268 root, err := ioutil.TempDir("", "overlay") 269 if err != nil { 270 t.Fatal(err) 271 } 272 defer os.RemoveAll(root) 273 o, _, err := newSnapshotter(ctx, root) 274 if err != nil { 275 t.Fatal(err) 276 } 277 key := "/tmp/base" 278 mounts, err := o.Prepare(ctx, key, "") 279 if err != nil { 280 t.Fatal(err) 281 } 282 m := mounts[0] 283 if err := ioutil.WriteFile(filepath.Join(m.Source, "foo"), []byte("hi"), 0660); err != nil { 284 t.Fatal(err) 285 } 286 if err := o.Commit(ctx, "base", key); err != nil { 287 t.Fatal(err) 288 } 289 290 key = "/tmp/top" 291 _, err = o.Prepare(ctx, key, "base") 292 if err != nil { 293 t.Fatal(err) 294 } 295 if err := ioutil.WriteFile(filepath.Join(getParents(ctx, o, root, "/tmp/top")[0], "foo"), []byte("hi, again"), 0660); err != nil { 296 t.Fatal(err) 297 } 298 if err := o.Commit(ctx, "top", key); err != nil { 299 t.Fatal(err) 300 } 301 302 mounts, err = o.View(ctx, "/tmp/view1", "base") 303 if err != nil { 304 t.Fatal(err) 305 } 306 if len(mounts) != 1 { 307 t.Fatalf("should only have 1 mount but received %d", len(mounts)) 308 } 309 m = mounts[0] 310 if m.Type != "bind" { 311 t.Errorf("mount type should be bind but received %q", m.Type) 312 } 313 expected := getParents(ctx, o, root, "/tmp/view1")[0] 314 if m.Source != expected { 315 t.Errorf("expected source %q but received %q", expected, m.Source) 316 } 317 if m.Options[0] != "ro" { 318 t.Errorf("expected mount option ro but received %q", m.Options[0]) 319 } 320 if m.Options[1] != "rbind" { 321 t.Errorf("expected mount option rbind but received %q", m.Options[1]) 322 } 323 324 mounts, err = o.View(ctx, "/tmp/view2", "top") 325 if err != nil { 326 t.Fatal(err) 327 } 328 if len(mounts) != 1 { 329 t.Fatalf("should only have 1 mount but received %d", len(mounts)) 330 } 331 m = mounts[0] 332 if m.Type != "overlay" { 333 t.Errorf("mount type should be overlay but received %q", m.Type) 334 } 335 if m.Source != "overlay" { 336 t.Errorf("mount source should be overlay but received %q", m.Source) 337 } 338 if len(m.Options) != 2 { 339 t.Errorf("expected 1 additional mount option but got %d", len(m.Options)) 340 } 341 lowers := getParents(ctx, o, root, "/tmp/view2") 342 expected = fmt.Sprintf("lowerdir=%s:%s", lowers[0], lowers[1]) 343 if m.Options[1] != expected { 344 t.Errorf("expected option %q but received %q", expected, m.Options[0]) 345 } 346 }