1
0
mirror of https://github.com/regclient/regclient.git synced 2025-04-18 22:44:00 +03:00
regclient/scheme/ocidir/blob_test.go
Brandon Mitchell 95c9152941
Chore: Modernize Go to the 1.22 specs
- Use "any" instead of an empty interface.
- Use range over an integer for for loops.
- Remove shadow variables in loops now that Go no longer reuses the variable.
- Use "slices.Contains", "slices.Delete", "slices.Equal", "slices.Index", "slices.SortFunc".
- Use "cmp.Or", "min", and "max".
- Use "fmt.Appendf" instead of "Sprintf" for generating a byte slice.
- Use "errors.Join" or "fmt.Errorf" with multiple "%w" for multiple errors.

Additionally, use modern regclient features:

- Use "ref.SetTag", "ref.SetDigest", and "ref.AddDigest".
- Call "regclient.ManifestGet" using "WithManifestDesc" instead of setting the digest on the reference.

Signed-off-by: Brandon Mitchell <git@bmitch.net>
2025-02-18 14:32:06 -05:00

190 lines
5.0 KiB
Go

package ocidir
import (
"bytes"
"context"
"fmt"
"io"
"os"
"path/filepath"
"sync"
"testing"
"github.com/regclient/regclient/internal/copyfs"
"github.com/regclient/regclient/types/descriptor"
"github.com/regclient/regclient/types/manifest"
"github.com/regclient/regclient/types/ref"
)
func TestBlob(t *testing.T) {
t.Parallel()
ctx := context.Background()
tempDir := t.TempDir()
err := copyfs.Copy(tempDir+"/testrepo", "../../testdata/testrepo")
if err != nil {
t.Fatalf("failed to copyfs to tempdir: %v", err)
}
o := New()
// get manifest to lookup config digest
rStr := "ocidir://" + tempDir + "/testrepo:v1"
rInd, err := ref.New(rStr)
if err != nil {
t.Fatalf("failed to parse ref %s: %v", rStr, err)
}
ml, err := o.ManifestGet(ctx, rInd)
if err != nil {
t.Fatalf("manifest get: %v", err)
}
if !ml.IsList() {
t.Fatalf("expected manifest list")
}
mli, ok := ml.(manifest.Indexer)
if !ok {
t.Fatalf("manifest doesn't support index methods")
}
dl, err := mli.GetManifestList()
if err != nil || len(dl) < 1 {
t.Fatalf("descriptor list (%d): %v", len(dl), err)
}
rStr = fmt.Sprintf("%s@%s", rStr, dl[0].Digest)
rImg, err := ref.New(rStr)
if err != nil {
t.Fatalf("failed to parse ref %s: %v", rStr, err)
}
m, err := o.ManifestGet(ctx, rImg)
if err != nil {
t.Fatalf("manifest get: %v", err)
}
mi, ok := m.(manifest.Imager)
if !ok {
t.Fatalf("manifest doesn't support image methods")
}
cd, err := mi.GetConfig()
if err != nil {
t.Fatalf("config digest: %v", err)
}
// blob head
bh, err := o.BlobHead(ctx, rImg, cd)
if err != nil {
t.Fatalf("blob head: %v", err)
}
err = bh.Close()
if err != nil {
t.Errorf("blob head close: %v", err)
}
// blob get
bg, err := o.BlobGet(ctx, rImg, cd)
if err != nil {
t.Fatalf("blob get: %v", err)
}
bBytes, err := io.ReadAll(bg)
if err != nil {
t.Fatalf("blob readall: %v", err)
}
if bg.GetDescriptor().Digest != cd.Digest {
t.Errorf("blob digest mismatch, expected %s, received %s", cd.Digest.String(), bg.GetDescriptor().Digest.String())
}
err = bg.Close()
if err != nil {
t.Errorf("blob get close: %v", err)
}
bFS, err := os.ReadFile(filepath.Join(tempDir, "testrepo/blobs", cd.Digest.Algorithm().String(), cd.Digest.Encoded()))
if err != nil {
t.Errorf("blob read file: %v", err)
}
if !bytes.Equal(bBytes, bFS) {
t.Errorf("blob read mismatch, expected %s, received %s", string(bBytes), string(bFS))
}
// toOCIConfig
bg, err = o.BlobGet(ctx, rImg, cd)
if err != nil {
t.Fatalf("blob get 2: %v", err)
}
ociConf, err := bg.ToOCIConfig()
if err != nil {
t.Fatalf("to oci config: %v", err)
}
if ociConf.GetDescriptor().Digest != cd.Digest {
t.Errorf("config digest mismatch, expected %s, received %s", cd.Digest.String(), ociConf.GetDescriptor().Digest.String())
}
// blob put
newDir := "newrepo"
rNew, err := ref.New("ocidir://" + tempDir + "/" + newDir + ":v1")
if err != nil {
t.Fatalf("failed to create new ref: %v", err)
}
bRdr := bytes.NewReader(bBytes)
bpd, err := o.BlobPut(ctx, rNew, cd, bRdr)
if err != nil {
t.Fatalf("blob put: %v", err)
}
if bpd.Size != int64(len(bBytes)) {
t.Errorf("blob put length, expected %d, received %d", len(bBytes), bpd.Size)
}
if bpd.Digest != cd.Digest {
t.Errorf("blob put digest, expected %s, received %s", cd.Digest, bpd.Digest)
}
blobFilename := filepath.Join(tempDir, newDir, "blobs", cd.Digest.Algorithm().String(), cd.Digest.Encoded())
fd, err := os.Open(blobFilename)
if err != nil {
t.Fatalf("blob put open file: %v", err)
}
fBytes, err := io.ReadAll(fd)
_ = fd.Close()
if err != nil {
t.Fatalf("blob put readall: %v", err)
}
if !bytes.Equal(fBytes, bBytes) {
t.Errorf("blob put bytes, expected %s, saw %s", string(bBytes), string(fBytes))
}
// blob delete (from memfs)
err = o.BlobDelete(ctx, rNew, cd)
if err != nil {
t.Errorf("failed to delete blob: %v", err)
}
_, err = os.Stat(blobFilename)
if err == nil {
t.Errorf("stat of a deleted blob did not fail")
}
// concurrent blob put, without the descriptor to test for races
rPut, err := ref.New("ocidir://" + tempDir + "/put@" + dl[0].Digest.String())
if err != nil {
t.Fatalf("failed to parse ref: %v", err)
}
count := 5
var wg sync.WaitGroup
wg.Add(count)
for range 5 {
go func() {
defer wg.Done()
bRdr := bytes.NewReader(bBytes)
bpd, err := o.BlobPut(ctx, rPut, descriptor.Descriptor{}, bRdr)
if err != nil {
t.Errorf("blob put: %v", err)
return
}
if bpd.Size != int64(len(bBytes)) {
t.Errorf("blob put length, expected %d, received %d", len(bBytes), bpd.Size)
}
if bpd.Digest != cd.Digest {
t.Errorf("blob put digest, expected %s, received %s", cd.Digest, bpd.Digest)
}
}()
}
wg.Wait()
fd, err = os.Open(filepath.Join(tempDir, "put/blobs", cd.Digest.Algorithm().String(), cd.Digest.Encoded()))
if err != nil {
t.Fatalf("blob put open file: %v", err)
}
fBytes, err = io.ReadAll(fd)
_ = fd.Close()
if err != nil {
t.Fatalf("blob put readall: %v", err)
}
if !bytes.Equal(fBytes, bBytes) {
t.Errorf("blob put bytes, expected %s, saw %s", string(bBytes), string(fBytes))
}
}