Repository: buchgr/bazel-remote
Pull request #571 (2022-08-21 15:18:06)

Move ensureDirExists to test source file where it's used


Patch:
diff --git a/cache/disk/casblob/casblob.go b/cache/disk/casblob/casblob.go
index 367a0fc..c3b9838 100644
--- a/cache/disk/casblob/casblob.go
+++ b/cache/disk/casblob/casblob.go
@@ -8,7 +8,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"log"
 	"os"
 
@@ -293,7 +292,7 @@ func GetUncompressedReadCloser(zstd zstdimpl.ZstdImpl, f *os.File, expectedSize
 		// Last chunk in the file.
 		r := bytes.NewReader(uncompressedFirstChunk[remainder:])
 		f.Close()
-		return ioutil.NopCloser(r), nil
+		return io.NopCloser(r), nil
 	}
 
 	z, err := zstd.GetDecoder(f)
@@ -387,7 +386,7 @@ func GetZstdReadCloser(zstd zstdimpl.ZstdImpl, f *os.File, expectedSize int64, o
 	br := bytes.NewReader(recompressedChunk)
 	if chunkNum == int64(len(h.chunkOffsets)-2) {
 		f.Close()
-		return ioutil.NopCloser(br), nil
+		return io.NopCloser(br), nil
 	}
 
 	return &readCloserWrapper{
diff --git a/cache/disk/disk.go b/cache/disk/disk.go
index ee30397..daaf04c 100644
--- a/cache/disk/disk.go
+++ b/cache/disk/disk.go
@@ -7,7 +7,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"log"
 	"math"
 	"net/http"
@@ -357,7 +356,7 @@ func migrateDirectory(baseDir string, kind cache.EntryKind) error {
 
 	log.Println("Migrating files (if any) to new directory structure:", sourceDir)
 
-	listing, err := ioutil.ReadDir(sourceDir)
+	listing, err := os.ReadDir(sourceDir)
 	if err != nil {
 		return err
 	}
@@ -371,7 +370,7 @@ func migrateDirectory(baseDir string, kind cache.EntryKind) error {
 
 	targetDir := path.Join(baseDir, kind.DirName())
 
-	itemChan := make(chan os.FileInfo)
+	itemChan := make(chan os.DirEntry)
 	errChan := make(chan error)
 
 	var wg sync.WaitGroup
@@ -402,7 +401,7 @@ func migrateDirectory(baseDir string, kind cache.EntryKind) error {
 					continue
 				}
 
-				if !item.Mode().IsRegular() {
+				if !item.Type().IsRegular() {
 					log.Println("Warning: skipping non-regular file:", oldNamePath)
 					continue
 				}
@@ -456,7 +455,7 @@ func migrateDirectory(baseDir string, kind cache.EntryKind) error {
 }
 
 func migrateV1Subdir(oldDir string, destDir string, kind cache.EntryKind) error {
-	listing, err := ioutil.ReadDir(oldDir)
+	listing, err := os.ReadDir(oldDir)
 	if err != nil {
 		return err
 	}
@@ -612,7 +611,7 @@ func (c *diskCache) loadExistingFiles() error {
 func (c *diskCache) Put(ctx context.Context, kind cache.EntryKind, hash string, size int64, r io.Reader) (rErr error) {
 	defer func() {
 		if r != nil {
-			_, _ = io.Copy(ioutil.Discard, r)
+			_, _ = io.Copy(io.Discard, r)
 		}
 	}()
 
@@ -944,10 +943,10 @@ func (c *diskCache) get(ctx context.Context, kind cache.EntryKind, hash string,
 
 	if kind == cache.CAS && size <= 0 && hash == emptySha256 {
 		if zstd {
-			return ioutil.NopCloser(bytes.NewReader(emptyZstdBlob)), 0, nil
+			return io.NopCloser(bytes.NewReader(emptyZstdBlob)), 0, nil
 		}
 
-		return ioutil.NopCloser(bytes.NewReader([]byte{})), 0, nil
+		return io.NopCloser(bytes.NewReader([]byte{})), 0, nil
 	}
 
 	if kind != cache.CAS && zstd {
@@ -1147,15 +1146,6 @@ func isSizeMismatch(requestedSize int64, foundSize int64) bool {
 	return requestedSize > -1 && foundSize > -1 && requestedSize != foundSize
 }
 
-func ensureDirExists(path string) {
-	if _, err := os.Stat(path); os.IsNotExist(err) {
-		err = os.MkdirAll(path, os.ModePerm)
-		if err != nil {
-			log.Fatal(err)
-		}
-	}
-}
-
 // GetValidatedActionResult returns a valid ActionResult and its serialized
 // value from the CAS if it and all its dependencies are also available. If
 // not, nil values are returned. If something unexpected went wrong, return
@@ -1173,7 +1163,7 @@ func (c *diskCache) GetValidatedActionResult(ctx context.Context, hash string) (
 		return nil, nil, nil // aka "not found"
 	}
 
-	acdata, err := ioutil.ReadAll(rc)
+	acdata, err := io.ReadAll(rc)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1216,7 +1206,7 @@ func (c *diskCache) GetValidatedActionResult(ctx context.Context, hash string) (
 		}
 
 		var oddata []byte
-		oddata, err = ioutil.ReadAll(r)
+		oddata, err = io.ReadAll(r)
 		r.Close()
 		if err != nil {
 			return nil, nil, err
diff --git a/cache/gcsproxy/gcsproxy.go b/cache/gcsproxy/gcsproxy.go
index a562f44..d4df750 100644
--- a/cache/gcsproxy/gcsproxy.go
+++ b/cache/gcsproxy/gcsproxy.go
@@ -5,9 +5,9 @@ package gcsproxy
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
 	"net/http"
 	"net/url"
+	"os"
 
 	"github.com/buchgr/bazel-remote/cache"
 	"github.com/buchgr/bazel-remote/cache/httpproxy"
@@ -29,7 +29,7 @@ func New(bucket string, useDefaultCredentials bool, jsonCredentialsFile string,
 			return nil, err
 		}
 	} else if jsonCredentialsFile != "" {
-		jsonConfig, err := ioutil.ReadFile(jsonCredentialsFile)
+		jsonConfig, err := os.ReadFile(jsonCredentialsFile)
 		if err != nil {
 			err = fmt.Errorf("Failed to read Google Credentials file '%s': %v", jsonCredentialsFile, err)
 			return nil, err
diff --git a/cache/httpproxy/httpproxy.go b/cache/httpproxy/httpproxy.go
index b05213a..bdb463c 100644
--- a/cache/httpproxy/httpproxy.go
+++ b/cache/httpproxy/httpproxy.go
@@ -7,7 +7,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net/http"
 	"net/url"
 	"strconv"
@@ -90,7 +89,7 @@ func (r *remoteHTTPProxyCache) uploadFile(item uploadReq) {
 		r.errorLogger.Printf("HTTP %s UPLOAD: %s", url, err.Error())
 		return
 	}
-	_, err = io.Copy(ioutil.Discard, rsp.Body)
+	_, err = io.Copy(io.Discard, rsp.Body)
 	if err != nil {
 		r.errorLogger.Printf("HTTP %s UPLOAD: %s", url, err.Error())
 		return
@@ -201,7 +200,7 @@ func (r *remoteHTTPProxyCache) Get(ctx context.Context, kind cache.EntryKind, ha
 		// If the failed http response contains some data then
 		// forward up to 1 KiB.
 		var errorBytes []byte
-		errorBytes, err = ioutil.ReadAll(io.LimitReader(rsp.Body, 1024))
+		errorBytes, err = io.ReadAll(io.LimitReader(rsp.Body, 1024))
 		var errorText string
 		if err == nil {
 			errorText = string(errorBytes)
diff --git a/config/config.go b/config/config.go
index 11675ab..d330742 100644
--- a/config/config.go
+++ b/config/config.go
@@ -4,7 +4,7 @@ import (
 	"crypto/tls"
 	"errors"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"log"
 	"math"
 	"net"
@@ -170,7 +170,7 @@ func newFromYamlFile(path string) (*Config, error) {
 	}
 	defer file.Close()
 
-	data, err := ioutil.ReadAll(file)
+	data, err := io.ReadAll(file)
 	if err != nil {
 		return nil, fmt.Errorf("Failed to read config file '%s': %v", path, err)
 	}
diff --git a/config/logger.go b/config/logger.go
index 980e939..4779dc5 100644
--- a/config/logger.go
+++ b/config/logger.go
@@ -1,7 +1,7 @@
 package config
 
 import (
-	"io/ioutil"
+	"io"
 	"log"
 	"os"
 )
@@ -15,7 +15,7 @@ func (c *Config) setLogger() error {
 	c.ErrorLogger = log.New(os.Stderr, "", LogFlags)
 
 	if c.AccessLogLevel == "none" {
-		c.AccessLogger.SetOutput(ioutil.Discard)
+		c.AccessLogger.SetOutput(io.Discard)
 	}
 
 	return nil
diff --git a/config/tls.go b/config/tls.go
index 699e892..c233955 100644
--- a/config/tls.go
+++ b/config/tls.go
@@ -4,13 +4,13 @@ import (
 	"crypto/tls"
 	"crypto/x509"
 	"fmt"
-	"io/ioutil"
+	"os"
 )
 
 func (c *Config) setTLSConfig() error {
 	if len(c.TLSCaFile) != 0 {
 		caCertPool := x509.NewCertPool()
-		caCert, err := ioutil.ReadFile(c.TLSCaFile)
+		caCert, err := os.ReadFile(c.TLSCaFile)
 		if err != nil {
 			return fmt.Errorf("Error reading TLS CA File: %w", err)
 		}
diff --git a/server/grpc_ac.go b/server/grpc_ac.go
index 5312be4..339f0df 100644
--- a/server/grpc_ac.go
+++ b/server/grpc_ac.go
@@ -7,7 +7,6 @@ import (
 	"encoding/hex"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net"
 	"strings"
 
@@ -71,7 +70,7 @@ func (s *grpcServer) GetActionResult(ctx context.Context,
 		}
 		defer rdr.Close()
 
-		acdata, err := ioutil.ReadAll(rdr)
+		acdata, err := io.ReadAll(rdr)
 		if err != nil {
 			s.accessLogger.Printf("%s %s %s", logPrefix, req.ActionDigest.Hash, err)
 			return nil, status.Error(codes.Unknown, err.Error())
diff --git a/server/grpc_asset.go b/server/grpc_asset.go
index 685f15f..c96c2eb 100644
--- a/server/grpc_asset.go
+++ b/server/grpc_asset.go
@@ -7,7 +7,6 @@ import (
 	"encoding/base64"
 	"encoding/hex"
 	"io"
-	"io/ioutil"
 	"net/http"
 	"net/url"
 	"strings"
@@ -147,7 +146,7 @@ func (s *grpcServer) fetchItem(ctx context.Context, uri string, expectedHash str
 	if expectedHash == "" || expectedSize < 0 {
 		// We can't call Put until we know the hash and size.
 
-		data, err := ioutil.ReadAll(resp.Body)
+		data, err := io.ReadAll(resp.Body)
 		if err != nil {
 			s.errorLogger.Printf("failed to read data: %v", uri)
 			return false, "", int64(-1)
@@ -164,7 +163,7 @@ func (s *grpcServer) fetchItem(ctx context.Context, uri string, expectedHash str
 		}
 
 		expectedHash = hashStr
-		rc = ioutil.NopCloser(bytes.NewReader(data))
+		rc = io.NopCloser(bytes.NewReader(data))
 	}
 
 	err = s.cache.Put(ctx, cache.CAS, expectedHash, expectedSize, rc)
diff --git a/server/grpc_cas.go b/server/grpc_cas.go
index 26ca580..e6f13e5 100644
--- a/server/grpc_cas.go
+++ b/server/grpc_cas.go
@@ -5,7 +5,6 @@ import (
 	"context"
 	"errors"
 	"io"
-	"io/ioutil"
 
 	"google.golang.org/genproto/googleapis/rpc/code"
 	"google.golang.org/genproto/googleapis/rpc/status"
@@ -126,7 +125,7 @@ func (s *grpcServer) getBlobData(ctx context.Context, hash string, size int64) (
 		return []byte{}, errBadSize
 	}
 
-	data, err := ioutil.ReadAll(rdr)
+	data, err := io.ReadAll(rdr)
 	if err != nil {
 		rdr.Close()
 		return []byte{}, err
@@ -158,7 +157,7 @@ func (s *grpcServer) getBlobResponse(ctx context.Context, digest *pb.Digest, all
 			return &r
 		}
 
-		data, err := ioutil.ReadAll(rc)
+		data, err := io.ReadAll(rc)
 		if err != nil {
 			s.errorLogger.Printf("GRPC CAS GET %s INTERNAL ERROR: %v", digest.Hash, err)
 			r.Status = &status.Status{Code: int32(code.Code_INTERNAL)}
diff --git a/server/http.go b/server/http.go
index 4c99b91..53330e4 100644
--- a/server/http.go
+++ b/server/http.go
@@ -6,7 +6,6 @@ import (
 	"fmt"
 	"html"
 	"io"
-	"io/ioutil"
 	"net"
 	"net/http"
 	"regexp"
@@ -325,7 +324,7 @@ func (h *httpCache) CacheHandler(w http.ResponseWriter, r *http.Request) {
 		if h.validateAC && kind == cache.AC {
 			// verify that this is a valid ActionResult
 
-			data, err := ioutil.ReadAll(rdr)
+			data, err := io.ReadAll(rdr)
 			if err != nil {
 				msg := "failed to read request body"
 				http.Error(w, msg, http.StatusInternalServerError)
diff --git a/utils/grpcreadclient/grpcreadclient.go b/utils/grpcreadclient/grpcreadclient.go
index 98a451f..8d4b994 100644
--- a/utils/grpcreadclient/grpcreadclient.go
+++ b/utils/grpcreadclient/grpcreadclient.go
@@ -14,7 +14,6 @@ import (
 	"flag"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"time"
 
@@ -124,7 +123,7 @@ func dial(serverAddr string, caCertFile string, clientCertFile string, clientKey
 	} else {
 		fmt.Println("reading", caCertFile)
 
-		caCertData, err := ioutil.ReadFile(caCertFile)
+		caCertData, err := os.ReadFile(caCertFile)
 		if err != nil {
 			return nil, fmt.Errorf("Failed to read CA cert file %q: %w",
 				caCertFile, err), nil, nil
diff --git a/utils/tempfile/tempfile.go b/utils/tempfile/tempfile.go
index f71e9d6..6fbbba1 100644
--- a/utils/tempfile/tempfile.go
+++ b/utils/tempfile/tempfile.go
@@ -23,7 +23,7 @@ func NewCreator() *Creator {
 // Fast "quick and dirty" linear congruential (pseudo-random) number
 // generator from Numerical Recipes. Excerpt here:
 // https://www.unf.edu/~cwinton/html/cop4300/s09/class.notes/LCGinfo.pdf
-// This is the same algorithm as used in the ioutil.TempFile go standard
+// This is the same algorithm as used in the old ioutil.TempFile go standard
 // library function.
 func (c *Creator) ranqd1() string {
 	c.mu.Lock()


Test patch:
diff --git a/cache/disk/disk_test.go b/cache/disk/disk_test.go
index 4c9e6de..b56b017 100644
--- a/cache/disk/disk_test.go
+++ b/cache/disk/disk_test.go
@@ -7,7 +7,6 @@ import (
 	"encoding/hex"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"math"
 	"net/http"
 	"net/http/httptest"
@@ -33,7 +32,7 @@ import (
 )
 
 func tempDir(t *testing.T) string {
-	dir, err := ioutil.TempDir("", "bazel-remote")
+	dir, err := os.MkdirTemp("", "bazel-remote")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -81,7 +80,7 @@ func TestCacheBasics(t *testing.T) {
 
 	// Add an item.
 	err = testCache.Put(ctx, cache.CAS, hash, itemSize,
-		ioutil.NopCloser(bytes.NewReader(data)))
+		io.NopCloser(bytes.NewReader(data)))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -240,7 +239,7 @@ func (d proxyStub) Get(ctx context.Context, kind cache.EntryKind, hash string) (
 		return nil, -1, nil
 	}
 
-	tmpfile, err := ioutil.TempFile("", "proxyStubGet")
+	tmpfile, err := os.CreateTemp("", "proxyStubGet")
 	if err != nil {
 		return nil, -1, err
 	}
@@ -255,7 +254,7 @@ func (d proxyStub) Get(ctx context.Context, kind cache.EntryKind, hash string) (
 
 	_, err = casblob.WriteAndClose(
 		zi,
-		ioutil.NopCloser(
+		io.NopCloser(
 			strings.NewReader(contents)), tmpfile, casblob.Zstandard,
 		hash, contentsLength)
 	if err != nil {
@@ -282,7 +281,7 @@ func expectContentEquals(rdr io.ReadCloser, sizeBytes int64, expectedContent []b
 	if rdr == nil {
 		return fmt.Errorf("expected the item to exist")
 	}
-	data, err := ioutil.ReadAll(rdr)
+	data, err := io.ReadAll(rdr)
 	if err != nil {
 		return err
 	}
@@ -367,6 +366,15 @@ func TestOverwrite(t *testing.T) {
 	}
 }
 
+func ensureDirExists(path string, t *testing.T) {
+	if _, err := os.Stat(path); os.IsNotExist(err) {
+		err = os.MkdirAll(path, os.ModePerm)
+		if err != nil {
+			t.Fatal(err)
+		}
+	}
+}
+
 func TestCacheExistingFiles(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
@@ -410,7 +418,7 @@ func TestCacheExistingFiles(t *testing.T) {
 	for _, it := range items {
 
 		fp := path.Join(cacheDir, it.file)
-		ensureDirExists(path.Dir(fp))
+		ensureDirExists(path.Dir(fp), t)
 
 		if strings.HasPrefix(it.file, "cas.v2/") {
 			r := bytes.NewReader([]byte(it.contents))
@@ -426,7 +434,7 @@ func TestCacheExistingFiles(t *testing.T) {
 					it.hash, int64(len(it.contents)))
 			}
 		} else {
-			err = ioutil.WriteFile(fp, []byte(it.contents), os.ModePerm)
+			err = os.WriteFile(fp, []byte(it.contents), os.ModePerm)
 		}
 		if err != nil {
 			t.Fatal(err)
@@ -558,7 +566,7 @@ func createRandomFile(dir string, size int64) (string, error) {
 	}
 	filepath := dir + "/" + hash
 
-	return hash, ioutil.WriteFile(filepath, data, os.ModePerm)
+	return hash, os.WriteFile(filepath, data, os.ModePerm)
 }
 
 func createRandomV1CASFile(dir string, size int64) (string, error) {
@@ -569,7 +577,7 @@ func createRandomV1CASFile(dir string, size int64) (string, error) {
 	}
 	filePath := dir + "/" + hash
 
-	err = ioutil.WriteFile(filePath, data, os.ModePerm)
+	err = os.WriteFile(filePath, data, os.ModePerm)
 	if err != nil {
 		return "", err
 	}
@@ -649,7 +657,7 @@ func TestLoadExistingEntries(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	err = ioutil.WriteFile(path.Join(cacheDir, "ac", acHash), acData, 0644)
+	err = os.WriteFile(path.Join(cacheDir, "ac", acHash), acData, 0644)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -660,7 +668,7 @@ func TestLoadExistingEntries(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	err = ioutil.WriteFile(path.Join(cacheDir, "cas", casHash), casData, 0644)
+	err = os.WriteFile(path.Join(cacheDir, "cas", casHash), casData, 0644)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -671,7 +679,7 @@ func TestLoadExistingEntries(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	err = ioutil.WriteFile(path.Join(cacheDir, "cas", casV1Hash[:2], casV1Hash), casV1Data, 0644)
+	err = os.WriteFile(path.Join(cacheDir, "cas", casV1Hash[:2], casV1Hash), casV1Data, 0644)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -682,7 +690,7 @@ func TestLoadExistingEntries(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	err = ioutil.WriteFile(path.Join(cacheDir, "ac", acV1Hash[:2], acV1Hash), acV1Data, 0644)
+	err = os.WriteFile(path.Join(cacheDir, "ac", acV1Hash[:2], acV1Hash), acV1Data, 0644)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -693,7 +701,7 @@ func TestLoadExistingEntries(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	err = ioutil.WriteFile(path.Join(cacheDir, "raw", rawHash[:2], rawHash), rawData, 0644)
+	err = os.WriteFile(path.Join(cacheDir, "raw", rawHash[:2], rawHash), rawData, 0644)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -817,7 +825,7 @@ func (s *testServer) handler(w http.ResponseWriter, r *http.Request) {
 		return
 
 	case http.MethodPut:
-		data, err := ioutil.ReadAll(r.Body)
+		data, err := io.ReadAll(r.Body)
 		if err != nil {
 			http.Error(w, "failed to read body", http.StatusInternalServerError)
 			return
@@ -973,7 +981,7 @@ func TestHttpProxyBackend(t *testing.T) {
 		t.Fatalf("Expected a blob of size %d, got %d", blobSize, fetchedSize)
 	}
 
-	retrievedData, err := ioutil.ReadAll(r)
+	retrievedData, err := io.ReadAll(r)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1182,7 +1190,7 @@ func TestGetWithOffset(t *testing.T) {
 	data, hash := testutils.RandomDataAndHash(blobSize)
 
 	err = testCache.Put(ctx, cache.CAS, hash, blobSize,
-		ioutil.NopCloser(bytes.NewReader(data)))
+		io.NopCloser(bytes.NewReader(data)))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1197,7 +1205,7 @@ func TestGetWithOffset(t *testing.T) {
 	}
 
 	// Read back the full blob, confirm the test state is OK.
-	foundData, err := ioutil.ReadAll(rc)
+	foundData, err := io.ReadAll(rc)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1217,7 +1225,7 @@ func TestGetWithOffset(t *testing.T) {
 				foundSize, int64(len(data)))
 		}
 
-		foundData, err = ioutil.ReadAll(rc)
+		foundData, err = io.ReadAll(rc)
 		if err != nil {
 			t.Fatal(err)
 		}
diff --git a/cache/disk/findmissing_test.go b/cache/disk/findmissing_test.go
index 3d3cd62..9cc79b6 100644
--- a/cache/disk/findmissing_test.go
+++ b/cache/disk/findmissing_test.go
@@ -6,7 +6,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"sync"
 	"testing"
@@ -213,8 +212,8 @@ func TestFindMissingCasBlobsWithProxy(t *testing.T) {
 	data3, digest3 := testutils.RandomDataAndDigest(300)
 	_, digest4 := testutils.RandomDataAndDigest(400)
 
-	proxy.Put(ctx, cache.CAS, digest1.Hash, digest1.SizeBytes, ioutil.NopCloser(bytes.NewReader(data1)))
-	proxy.Put(ctx, cache.CAS, digest3.Hash, digest3.SizeBytes, ioutil.NopCloser(bytes.NewReader(data3)))
+	proxy.Put(ctx, cache.CAS, digest1.Hash, digest1.SizeBytes, io.NopCloser(bytes.NewReader(data1)))
+	proxy.Put(ctx, cache.CAS, digest3.Hash, digest3.SizeBytes, io.NopCloser(bytes.NewReader(data3)))
 
 	missing, err := testCache.FindMissingCasBlobs(ctx, []*pb.Digest{
 		&digest1,
@@ -279,8 +278,8 @@ func TestFindMissingCasBlobsWithProxyFailFast(t *testing.T) {
 	data3, digest3 := testutils.RandomDataAndDigest(300)
 	_, digest4 := testutils.RandomDataAndDigest(400)
 
-	proxy.Put(ctx, cache.CAS, digest1.Hash, digest1.SizeBytes, ioutil.NopCloser(bytes.NewReader(data1)))
-	proxy.Put(ctx, cache.CAS, digest3.Hash, digest3.SizeBytes, ioutil.NopCloser(bytes.NewReader(data3)))
+	proxy.Put(ctx, cache.CAS, digest1.Hash, digest1.SizeBytes, io.NopCloser(bytes.NewReader(data1)))
+	proxy.Put(ctx, cache.CAS, digest3.Hash, digest3.SizeBytes, io.NopCloser(bytes.NewReader(data3)))
 
 	blobs := []*pb.Digest{
 		&digest1,
@@ -338,10 +337,10 @@ func TestFindMissingCasBlobsWithProxyFailFastNoneMissing(t *testing.T) {
 	data3, digest3 := testutils.RandomDataAndDigest(300)
 	data4, digest4 := testutils.RandomDataAndDigest(400)
 
-	proxy.Put(ctx, cache.CAS, digest1.Hash, digest1.SizeBytes, ioutil.NopCloser(bytes.NewReader(data1)))
-	proxy.Put(ctx, cache.CAS, digest2.Hash, digest2.SizeBytes, ioutil.NopCloser(bytes.NewReader(data2)))
-	proxy.Put(ctx, cache.CAS, digest3.Hash, digest3.SizeBytes, ioutil.NopCloser(bytes.NewReader(data3)))
-	proxy.Put(ctx, cache.CAS, digest4.Hash, digest4.SizeBytes, ioutil.NopCloser(bytes.NewReader(data4)))
+	proxy.Put(ctx, cache.CAS, digest1.Hash, digest1.SizeBytes, io.NopCloser(bytes.NewReader(data1)))
+	proxy.Put(ctx, cache.CAS, digest2.Hash, digest2.SizeBytes, io.NopCloser(bytes.NewReader(data2)))
+	proxy.Put(ctx, cache.CAS, digest3.Hash, digest3.SizeBytes, io.NopCloser(bytes.NewReader(data3)))
+	proxy.Put(ctx, cache.CAS, digest4.Hash, digest4.SizeBytes, io.NopCloser(bytes.NewReader(data4)))
 
 	blobs := []*pb.Digest{
 		&digest1,
@@ -412,9 +411,9 @@ func TestFindMissingCasBlobsWithProxyFailFastMaxProxyBlobSize(t *testing.T) {
 	data3, digest3 := testutils.RandomDataAndDigest(300) // We expect this blob to not be found.
 
 	// Put blobs directly into proxy backend, where it will not be filtered out.
-	proxy.Put(ctx, cache.CAS, digest1.Hash, digest1.SizeBytes, ioutil.NopCloser(bytes.NewReader(data1)))
-	proxy.Put(ctx, cache.CAS, digest2.Hash, digest2.SizeBytes, ioutil.NopCloser(bytes.NewReader(data2)))
-	proxy.Put(ctx, cache.CAS, digest3.Hash, digest3.SizeBytes, ioutil.NopCloser(bytes.NewReader(data3)))
+	proxy.Put(ctx, cache.CAS, digest1.Hash, digest1.SizeBytes, io.NopCloser(bytes.NewReader(data1)))
+	proxy.Put(ctx, cache.CAS, digest2.Hash, digest2.SizeBytes, io.NopCloser(bytes.NewReader(data2)))
+	proxy.Put(ctx, cache.CAS, digest3.Hash, digest3.SizeBytes, io.NopCloser(bytes.NewReader(data3)))
 
 	blobs := []*pb.Digest{
 		&digest1,
@@ -468,8 +467,8 @@ func TestFindMissingCasBlobsWithProxyMaxProxyBlobSize(t *testing.T) {
 	data1, digest1 := testutils.RandomDataAndDigest(100)
 	data2, digest2 := testutils.RandomDataAndDigest(600)
 
-	proxy.Put(ctx, cache.CAS, digest1.Hash, digest1.SizeBytes, ioutil.NopCloser(bytes.NewReader(data1)))
-	proxy.Put(ctx, cache.CAS, digest2.Hash, digest2.SizeBytes, ioutil.NopCloser(bytes.NewReader(data2)))
+	proxy.Put(ctx, cache.CAS, digest1.Hash, digest1.SizeBytes, io.NopCloser(bytes.NewReader(data1)))
+	proxy.Put(ctx, cache.CAS, digest2.Hash, digest2.SizeBytes, io.NopCloser(bytes.NewReader(data2)))
 
 	missing, err := testCache.FindMissingCasBlobs(ctx, []*pb.Digest{
 		&digest1,
diff --git a/cache/httpproxy/httpproxy_test.go b/cache/httpproxy/httpproxy_test.go
index a975211..2727db5 100644
--- a/cache/httpproxy/httpproxy_test.go
+++ b/cache/httpproxy/httpproxy_test.go
@@ -5,7 +5,6 @@ import (
 	"context"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"log"
 	"net/http"
 	"net/http/httptest"
@@ -63,7 +62,7 @@ func (s *testServer) handler(w http.ResponseWriter, r *http.Request) {
 		return
 
 	case http.MethodPut:
-		data, err := ioutil.ReadAll(r.Body)
+		data, err := io.ReadAll(r.Body)
 		if err != nil {
 			http.Error(w, "failed to read body", http.StatusInternalServerError)
 			return
@@ -173,7 +172,7 @@ func TestEverything(t *testing.T) {
 
 		// TODO: tweak the GetUncompressedReadCloser API to accept more than os.File.
 
-		tmpfile, err := ioutil.TempFile("", "bazel-remote-httpproxy-test")
+		tmpfile, err := os.CreateTemp("", "bazel-remote-httpproxy-test")
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -202,7 +201,7 @@ func TestEverything(t *testing.T) {
 			t.Fatal(err)
 		}
 		defer rc.Close()
-		vData, err := ioutil.ReadAll(rc)
+		vData, err := io.ReadAll(rc)
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -252,7 +251,7 @@ func TestEverything(t *testing.T) {
 			len(acData), size)
 	}
 
-	data, err = ioutil.ReadAll(rc)
+	data, err = io.ReadAll(rc)
 	if err != nil {
 		t.Error(err)
 	}
@@ -272,7 +271,7 @@ func TestEverything(t *testing.T) {
 			len(casData), size)
 	}
 
-	data, err = ioutil.ReadAll(rc)
+	data, err = io.ReadAll(rc)
 	if err != nil {
 		t.Error(err)
 	}
@@ -330,7 +329,7 @@ func TestEverything(t *testing.T) {
 			len(acData), size)
 	}
 
-	data, err = ioutil.ReadAll(rc)
+	data, err = io.ReadAll(rc)
 	if err != nil {
 		t.Error(err)
 	}
@@ -350,7 +349,7 @@ func TestEverything(t *testing.T) {
 			len(casData), size)
 	}
 
-	data, err = ioutil.ReadAll(rc)
+	data, err = io.ReadAll(rc)
 	if err != nil {
 		t.Error(err)
 	}
diff --git a/server/grpc_test.go b/server/grpc_test.go
index 79b27f1..6fd0b7a 100644
--- a/server/grpc_test.go
+++ b/server/grpc_test.go
@@ -8,7 +8,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net"
 	"os"
 	"testing"
@@ -71,7 +70,7 @@ func bufDialer(context.Context, string) (net.Conn, error) {
 }
 
 func TestMain(m *testing.M) {
-	dir, err := ioutil.TempDir("", "bazel-remote-grpc-tests")
+	dir, err := os.MkdirTemp("", "bazel-remote-grpc-tests")
 	if err != nil {
 		fmt.Println("Test setup failed")
 		os.Exit(1)
diff --git a/server/http_test.go b/server/http_test.go
index 9739e43..47c4107 100644
--- a/server/http_test.go
+++ b/server/http_test.go
@@ -7,7 +7,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
@@ -185,7 +184,7 @@ func TestUploadSameFileConcurrently(t *testing.T) {
 			handler.ServeHTTP(rr, request)
 
 			if status := rr.Code; status != http.StatusOK {
-				resp, _ := ioutil.ReadAll(rr.Body)
+				resp, _ := io.ReadAll(rr.Body)
 				t.Error("Handler returned wrong status code",
 					"expected", http.StatusOK,
 					"got", status,
@@ -269,7 +268,7 @@ func TestUploadEmptyActionResult(t *testing.T) {
 	rr2 := httptest.NewRecorder()
 	handler.ServeHTTP(rr2, getReq)
 
-	cachedData, err := ioutil.ReadAll(rr2.Result().Body)
+	cachedData, err := io.ReadAll(rr2.Result().Body)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -472,7 +471,7 @@ func (r *fakeResponseWriter) WriteHeader(statusCode int) {
 }
 
 func TestRemoteReturnsNotFound(t *testing.T) {
-	cacheDir, err := ioutil.TempDir("", "bazel-remote")
+	cacheDir, err := os.MkdirTemp("", "bazel-remote")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -487,7 +486,7 @@ func TestRemoteReturnsNotFound(t *testing.T) {
 	_, hash := testutils.RandomDataAndHash(1024)
 	url, _ := url.Parse(fmt.Sprintf("http://localhost:8080/ac/%s", hash))
 	reader := bytes.NewReader([]byte{})
-	body := ioutil.NopCloser(reader)
+	body := io.NopCloser(reader)
 	req := &http.Request{
 		Method:     "GET",
 		URL:        url,
diff --git a/utils/tempfile/tempfile_test.go b/utils/tempfile/tempfile_test.go
index e758500..5628cd6 100644
--- a/utils/tempfile/tempfile_test.go
+++ b/utils/tempfile/tempfile_test.go
@@ -1,7 +1,6 @@
 package tempfile_test
 
 import (
-	"io/ioutil"
 	"os"
 	"path"
 	"strings"
@@ -13,7 +12,7 @@ import (
 func TestTempfileCreator(t *testing.T) {
 	tfc := tempfile.NewCreator()
 
-	dir, err := ioutil.TempDir("", "foo")
+	dir, err := os.MkdirTemp("", "foo")
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/utils/testutils.go b/utils/testutils.go
index e06d28d..6410e33 100644
--- a/utils/testutils.go
+++ b/utils/testutils.go
@@ -4,8 +4,9 @@ import (
 	"crypto/rand"
 	"crypto/sha256"
 	"encoding/hex"
-	"io/ioutil"
+	"io"
 	"log"
+	"os"
 	"testing"
 
 	pb "github.com/buchgr/bazel-remote/genproto/build/bazel/remote/execution/v2"
@@ -14,7 +15,7 @@ import (
 // TempDir creates a temporary directory and returns its name. If an error
 // occurs, then it panics.
 func TempDir(t *testing.T) string {
-	dir, err := ioutil.TempDir("", "bazel-remote")
+	dir, err := os.MkdirTemp("", "bazel-remote")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -51,5 +52,5 @@ func RandomDataAndDigest(size int64) ([]byte, pb.Digest) {
 // NewSilentLogger returns a cheap logger that doesn't print anything, useful
 // for tests.
 func NewSilentLogger() *log.Logger {
-	return log.New(ioutil.Discard, "", 0)
+	return log.New(io.Discard, "", 0)
 }