k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/greenhouse/diskcache/cache_test.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package diskcache
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/rand"
    22  	"crypto/sha256"
    23  	"encoding/hex"
    24  	"io"
    25  	"path/filepath"
    26  	"testing"
    27  
    28  	"k8s.io/apimachinery/pkg/util/sets"
    29  )
    30  
    31  func hashBytes(b []byte) string {
    32  	hasher := sha256.New()
    33  	hasher.Write(b)
    34  	return hex.EncodeToString(hasher.Sum(nil))
    35  }
    36  
    37  func makeRandomBytes(n int) (b []byte, err error) {
    38  	b = make([]byte, n)
    39  	_, err = rand.Read(b)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	return b, nil
    44  }
    45  
    46  // test cache.PathToKey and cache.KeyToPath
    47  func TestPathToKeyKeyToPath(t *testing.T) {
    48  	cache := NewCache("/some/dir")
    49  	testCases := []struct {
    50  		Key          string
    51  		ExpectedPath string
    52  	}{
    53  		{
    54  			Key:          "key",
    55  			ExpectedPath: filepath.Join("/some/dir", "key"),
    56  		},
    57  		{
    58  			Key:          "namespaced/key",
    59  			ExpectedPath: filepath.Join("/some/dir", "namespaced/key"),
    60  		},
    61  		{
    62  			Key:          "entry/nested/a/few/times",
    63  			ExpectedPath: filepath.Join("/some/dir", "entry/nested/a/few/times"),
    64  		},
    65  		{
    66  			Key:          "foobar/cas/asdf",
    67  			ExpectedPath: filepath.Join("/some/dir", "foobar/cas/asdf"),
    68  		},
    69  		{
    70  			Key:          "foobar/ac/asdf",
    71  			ExpectedPath: filepath.Join("/some/dir", "foobar/ac/asdf"),
    72  		},
    73  	}
    74  	for _, tc := range testCases {
    75  		path := cache.KeyToPath(tc.Key)
    76  		if path != tc.ExpectedPath {
    77  			t.Fatalf("expected KeyToPath(%s) to be %s", tc.Key, path)
    78  		}
    79  		backToKey := cache.PathToKey(path)
    80  		if backToKey != tc.Key {
    81  			t.Fatalf("cache.KeyToPath(cache.PathToKey(%s)) was not idempotent, got %s", tc.Key, backToKey)
    82  		}
    83  	}
    84  }
    85  
    86  // test stateful cache methods
    87  func TestCacheStorage(t *testing.T) {
    88  	// create a cache in a tempdir
    89  	dir := t.TempDir()
    90  	cache := NewCache(dir)
    91  
    92  	// sanity checks
    93  	if cache.DiskRoot() != dir {
    94  		t.Fatalf("Expected DiskRoot to be %v not %v", dir, cache.DiskRoot())
    95  	}
    96  	// we haven't put anything yet, so get should return exists == false
    97  	err := cache.Get("some/key", func(exists bool, contents io.ReadSeeker) error {
    98  		if exists {
    99  			t.Fatal("no keys should exist yet!")
   100  		}
   101  		return nil
   102  	})
   103  	if err != nil {
   104  		t.Fatalf("Got unexpected error testing non-existent key: %v", err)
   105  	}
   106  
   107  	// Test Put and Get together
   108  	// create 1 MB of random bytes
   109  	lotsOfRandomBytes, err := makeRandomBytes(1000000)
   110  	if err != nil {
   111  		t.Fatalf("Failed to create random test data: %v", err)
   112  	}
   113  	testCases := []struct {
   114  		Name           string
   115  		Key            string
   116  		Contents       []byte
   117  		Hash           string
   118  		PutShouldError bool
   119  	}{
   120  		{
   121  			Name:           "Normal",
   122  			Key:            "foo",
   123  			Contents:       []byte{1, 3, 3, 7},
   124  			Hash:           hashBytes([]byte{1, 3, 3, 7}),
   125  			PutShouldError: false,
   126  		},
   127  		{
   128  			Name:           "Bad Hash",
   129  			Key:            "test/foo/baz",
   130  			Contents:       []byte{1, 3, 3, 7},
   131  			Hash:           hashBytes([]byte{3, 1, 3, 3, 7}),
   132  			PutShouldError: true,
   133  		},
   134  		{
   135  			Name:           "Normal with Path Segments",
   136  			Key:            "test/bar/baz",
   137  			Contents:       []byte{107, 56, 115},
   138  			Hash:           hashBytes([]byte{107, 56, 115}),
   139  			PutShouldError: false,
   140  		},
   141  		{
   142  			Name:           "Lots of Random Bytes",
   143  			Key:            "a/b/c",
   144  			Contents:       lotsOfRandomBytes,
   145  			Hash:           hashBytes(lotsOfRandomBytes),
   146  			PutShouldError: false,
   147  		},
   148  	}
   149  	expectedKeys := sets.NewString()
   150  	for _, tc := range testCases {
   151  		err := cache.Put(tc.Key, bytes.NewReader(tc.Contents), tc.Hash)
   152  		if err != nil && !tc.PutShouldError {
   153  			t.Fatalf("Got error '%v' for test case '%s' and expected none.", err, tc.Name)
   154  		} else if err == nil && tc.PutShouldError {
   155  			t.Fatalf("Did not get error for test case '%s' and expected one.", tc.Name)
   156  		} else if err == nil {
   157  			expectedKeys.Insert(tc.Key)
   158  		}
   159  
   160  		err = cache.Get(tc.Key, func(exists bool, contents io.ReadSeeker) error {
   161  			if exists && tc.PutShouldError {
   162  				t.Fatalf("Got key exists for test case '%s' which should not.", tc.Name)
   163  			} else if !exists && !tc.PutShouldError {
   164  				t.Fatalf("Got key does not exist for test case '%s' which should.", tc.Name)
   165  			}
   166  			if exists {
   167  				read, err2 := io.ReadAll(contents)
   168  				if err2 != nil {
   169  					t.Fatalf("Failed to read contents for test case '%s", tc.Name)
   170  				}
   171  				if !bytes.Equal(read, tc.Contents) {
   172  					t.Fatalf("Contents did not match expected for test case '%s' (got: %v expected: %v)", tc.Name, read, tc.Contents)
   173  				}
   174  			}
   175  			return nil
   176  		})
   177  		if err != nil {
   178  			t.Fatalf("Got unepected error getting cache key for test case '%s': %v", tc.Name, err)
   179  		}
   180  	}
   181  
   182  	// test GetEntries
   183  	entries := cache.GetEntries()
   184  	receivedKeys := sets.NewString()
   185  	for _, entry := range entries {
   186  		receivedKeys.Insert(cache.PathToKey(entry.Path))
   187  	}
   188  	if !expectedKeys.Equal(receivedKeys) {
   189  		t.Fatalf("entries %v does not equal expected: %v", receivedKeys, expectedKeys)
   190  	}
   191  
   192  	// test deleting all keys
   193  	for _, key := range expectedKeys.List() {
   194  		err = cache.Delete(key)
   195  		if err != nil {
   196  			t.Fatalf("failed to delete key: %v", err)
   197  		}
   198  	}
   199  	entries = cache.GetEntries()
   200  	if len(entries) != 0 {
   201  		t.Fatalf("cache.GetEntries() should be empty after deleting all keys, got: %v", entries)
   202  	}
   203  }