diff options
Diffstat (limited to 'pkg/v1/cache/fs_test.go')
-rw-r--r-- | pkg/v1/cache/fs_test.go | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/pkg/v1/cache/fs_test.go b/pkg/v1/cache/fs_test.go new file mode 100644 index 0000000..2e05d29 --- /dev/null +++ b/pkg/v1/cache/fs_test.go @@ -0,0 +1,213 @@ +// Copyright 2021 Google LLC All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cache + +import ( + "errors" + "io" + "os" + "testing" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/random" + "github.com/google/go-containerregistry/pkg/v1/tarball" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +func TestFilesystemCache(t *testing.T) { + dir := t.TempDir() + + numLayers := 5 + img, err := random.Image(10, int64(numLayers)) + if err != nil { + t.Fatalf("random.Image: %v", err) + } + c := NewFilesystemCache(dir) + img = Image(img, c) + + // Read all the (compressed) layers to populate the cache. + ls, err := img.Layers() + if err != nil { + t.Fatalf("Layers: %v", err) + } + for i, l := range ls { + rc, err := l.Compressed() + if err != nil { + t.Fatalf("layer[%d].Compressed: %v", i, err) + } + if _, err := io.Copy(io.Discard, rc); err != nil { + t.Fatalf("Error reading contents: %v", err) + } + rc.Close() + } + + // Check that layers exist in the fs cache. + dirEntries, err := os.ReadDir(dir) + if err != nil { + t.Fatalf("ReadDir: %v", err) + } + if got, want := len(dirEntries), numLayers; got != want { + t.Errorf("Got %d cached files, want %d", got, want) + } + for _, de := range dirEntries { + fi, err := de.Info() + if err != nil { + t.Fatal(err) + } + if fi.Size() == 0 { + t.Errorf("Cached file %q is empty", fi.Name()) + } + } + + // Read all (uncompressed) layers, those populate the cache too. + for i, l := range ls { + rc, err := l.Uncompressed() + if err != nil { + t.Fatalf("layer[%d].Compressed: %v", i, err) + } + if _, err := io.Copy(io.Discard, rc); err != nil { + t.Fatalf("Error reading contents: %v", err) + } + rc.Close() + } + + // Check that double the layers are present now, both compressed and + // uncompressed. + dirEntries, err = os.ReadDir(dir) + if err != nil { + t.Fatalf("ReadDir: %v", err) + } + if got, want := len(dirEntries), numLayers*2; got != want { + t.Errorf("Got %d cached files, want %d", got, want) + } + for _, de := range dirEntries { + fi, err := de.Info() + if err != nil { + t.Fatal(err) + } + if fi.Size() == 0 { + t.Errorf("Cached file %q is empty", fi.Name()) + } + } + + // Delete a cached layer, see it disappear. + l := ls[0] + h, err := l.Digest() + if err != nil { + t.Fatalf("layer.Digest: %v", err) + } + if err := c.Delete(h); err != nil { + t.Errorf("cache.Delete: %v", err) + } + dirEntries, err = os.ReadDir(dir) + if err != nil { + t.Fatalf("ReadDir: %v", err) + } + if got, want := len(dirEntries), numLayers*2-1; got != want { + t.Errorf("Got %d cached files, want %d", got, want) + } + + // Read the image again, see the layer reappear. + for i, l := range ls { + rc, err := l.Compressed() + if err != nil { + t.Fatalf("layer[%d].Compressed: %v", i, err) + } + if _, err := io.Copy(io.Discard, rc); err != nil { + t.Fatalf("Error reading contents: %v", err) + } + rc.Close() + } + + // Check that layers exist in the fs cache. + dirEntries, err = os.ReadDir(dir) + if err != nil { + t.Fatalf("ReadDir: %v", err) + } + if got, want := len(dirEntries), numLayers*2; got != want { + t.Errorf("Got %d cached files, want %d", got, want) + } + for _, de := range dirEntries { + fi, err := de.Info() + if err != nil { + t.Fatal(err) + } + if fi.Size() == 0 { + t.Errorf("Cached file %q is empty", fi.Name()) + } + } +} + +func TestErrNotFound(t *testing.T) { + dir := t.TempDir() + + c := NewFilesystemCache(dir) + h := v1.Hash{Algorithm: "fake", Hex: "not-found"} + if _, err := c.Get(h); !errors.Is(err, ErrNotFound) { + t.Errorf("Get(%q): %v", h, err) + } + if err := c.Delete(h); !errors.Is(err, ErrNotFound) { + t.Errorf("Delete(%q): %v", h, err) + } +} + +func TestErrUnexpectedEOF(t *testing.T) { + dir := t.TempDir() + + // create a random layer + l, err := random.Layer(10, types.DockerLayer) + if err != nil { + t.Fatalf("random.Layer: %v", err) + } + rc, err := l.Compressed() + if err != nil { + t.Fatalf("layer.Compressed(): %v", err) + } + + h, err := l.Digest() + if err != nil { + t.Fatalf("layer.Digest(): %v", err) + } + p := cachepath(dir, h) + + // Write only the first segment of the compressed layer to produce an + // UnexpectedEOF error when reading it + buf := make([]byte, 10) + n, err := rc.Read(buf) + if err != nil { + t.Fatalf("Read(buf): %v", err) + } + if err := os.WriteFile(p, buf[:n], 0644); err != nil { + t.Fatalf("os.WriteFile(%s, buf[:%d]): %v", p, n, err) + } + + c := NewFilesystemCache(dir) + + // make sure LayerFromFile returns UnexpectedEOF + if _, err := tarball.LayerFromFile(p); !errors.Is(err, io.ErrUnexpectedEOF) { + t.Fatalf("tarball.LayerFromFile(%s): expected %v, got %v", p, io.ErrUnexpectedEOF, err) + } + + // Try to Get the layer + if _, err := c.Get(h); !errors.Is(err, ErrNotFound) { + t.Errorf("Get(%q): %v", h, err) + } + + // If we had an UnexpectedEOF and the cache deleted the broken layer no file + // should exist + if _, err := os.Stat(p); !os.IsNotExist(err) { + t.Errorf("os.Stat(%q): %v", p, err) + } +} |