Skip to content

Commit e23ad87

Browse files
authored
Workaround for container registry push/pull errors (#21862) (#22069)
Backport of #21862
1 parent e93a4a0 commit e23ad87

File tree

5 files changed

+100
-4
lines changed

5 files changed

+100
-4
lines changed

Diff for: integrations/api_packages_container_test.go

+28
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ package integrations
66

77
import (
88
"bytes"
9+
"crypto/sha256"
910
"encoding/base64"
1011
"fmt"
1112
"net/http"
1213
"strings"
14+
"sync"
1315
"testing"
1416

1517
"code.gitea.io/gitea/models/db"
@@ -549,6 +551,32 @@ func TestPackageContainer(t *testing.T) {
549551
})
550552
}
551553

554+
// https://github.com/go-gitea/gitea/issues/19586
555+
t.Run("ParallelUpload", func(t *testing.T) {
556+
defer PrintCurrentTest(t)()
557+
558+
url := fmt.Sprintf("%sv2/%s/parallel", setting.AppURL, user.Name)
559+
560+
var wg sync.WaitGroup
561+
for i := 0; i < 10; i++ {
562+
wg.Add(1)
563+
564+
content := []byte{byte(i)}
565+
digest := fmt.Sprintf("sha256:%x", sha256.Sum256(content))
566+
567+
go func() {
568+
defer wg.Done()
569+
570+
req := NewRequestWithBody(t, "POST", fmt.Sprintf("%s/blobs/uploads?digest=%s", url, digest), bytes.NewReader(content))
571+
addTokenAuthHeader(req, userToken)
572+
resp := MakeRequest(t, req, http.StatusCreated)
573+
574+
assert.Equal(t, digest, resp.Header().Get("Docker-Content-Digest"))
575+
}()
576+
}
577+
wg.Wait()
578+
})
579+
552580
t.Run("OwnerNameChange", func(t *testing.T) {
553581
defer PrintCurrentTest(t)()
554582

Diff for: modules/packages/content_store.go

+7
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) {
3030
return s.store.Open(KeyToRelativePath(key))
3131
}
3232

33+
// FIXME: Workaround to be removed in v1.20
34+
// https://github.com/go-gitea/gitea/issues/19586
35+
func (s *ContentStore) Has(key BlobHash256Key) error {
36+
_, err := s.store.Stat(KeyToRelativePath(key))
37+
return err
38+
}
39+
3340
// Save stores a package blob
3441
func (s *ContentStore) Save(key BlobHash256Key, r io.Reader, size int64) error {
3542
_, err := s.store.Save(KeyToRelativePath(key), r, size)

Diff for: routers/api/packages/container/blob.go

+30-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ package container
77
import (
88
"context"
99
"encoding/hex"
10+
"errors"
1011
"fmt"
12+
"os"
1113
"strings"
14+
"sync"
1215

1316
"code.gitea.io/gitea/models/db"
1417
packages_model "code.gitea.io/gitea/models/packages"
@@ -19,6 +22,8 @@ import (
1922
packages_service "code.gitea.io/gitea/services/packages"
2023
)
2124

25+
var uploadVersionMutex sync.Mutex
26+
2227
// saveAsPackageBlob creates a package blob from an upload
2328
// The uploaded blob gets stored in a special upload version to link them to the package/image
2429
func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_service.PackageInfo) (*packages_model.PackageBlob, error) {
@@ -28,6 +33,11 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic
2833

2934
contentStore := packages_module.NewContentStore()
3035

36+
var uploadVersion *packages_model.PackageVersion
37+
38+
// FIXME: Replace usage of mutex with database transaction
39+
// https://github.com/go-gitea/gitea/pull/21862
40+
uploadVersionMutex.Lock()
3141
err := db.WithTx(func(ctx context.Context) error {
3242
created := true
3343
p := &packages_model.Package{
@@ -68,11 +78,30 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic
6878
}
6979
}
7080

81+
uploadVersion = pv
82+
83+
return nil
84+
})
85+
uploadVersionMutex.Unlock()
86+
if err != nil {
87+
return nil, err
88+
}
89+
90+
err = db.WithTx(func(ctx context.Context) error {
7191
pb, exists, err = packages_model.GetOrInsertBlob(ctx, pb)
7292
if err != nil {
7393
log.Error("Error inserting package blob: %v", err)
7494
return err
7595
}
96+
// FIXME: Workaround to be removed in v1.20
97+
// https://github.com/go-gitea/gitea/issues/19586
98+
if exists {
99+
err = contentStore.Has(packages_module.BlobHash256Key(pb.HashSHA256))
100+
if err != nil && errors.Is(err, os.ErrNotExist) {
101+
log.Debug("Package registry inconsistent: blob %s does not exist on file system", pb.HashSHA256)
102+
exists = false
103+
}
104+
}
76105
if !exists {
77106
if err := contentStore.Save(packages_module.BlobHash256Key(pb.HashSHA256), hsr, hsr.Size()); err != nil {
78107
log.Error("Error saving package blob in content store: %v", err)
@@ -83,7 +112,7 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic
83112
filename := strings.ToLower(fmt.Sprintf("sha256_%s", pb.HashSHA256))
84113

85114
pf := &packages_model.PackageFile{
86-
VersionID: pv.ID,
115+
VersionID: uploadVersion.ID,
87116
BlobID: pb.ID,
88117
Name: filename,
89118
LowerName: filename,

Diff for: routers/api/packages/container/container.go

+24-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"io"
1111
"net/http"
1212
"net/url"
13+
"os"
1314
"regexp"
1415
"strconv"
1516
"strings"
@@ -193,7 +194,7 @@ func InitiateUploadBlob(ctx *context.Context) {
193194
mount := ctx.FormTrim("mount")
194195
from := ctx.FormTrim("from")
195196
if mount != "" {
196-
blob, _ := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
197+
blob, _ := workaroundGetContainerBlob(ctx, &container_model.BlobSearchOptions{
197198
Image: from,
198199
Digest: mount,
199200
})
@@ -361,7 +362,7 @@ func getBlobFromContext(ctx *context.Context) (*packages_model.PackageFileDescri
361362
return nil, container_model.ErrContainerBlobNotExist
362363
}
363364

364-
return container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
365+
return workaroundGetContainerBlob(ctx, &container_model.BlobSearchOptions{
365366
OwnerID: ctx.Package.Owner.ID,
366367
Image: ctx.Params("image"),
367368
Digest: digest,
@@ -503,7 +504,7 @@ func getManifestFromContext(ctx *context.Context) (*packages_model.PackageFileDe
503504
return nil, container_model.ErrContainerBlobNotExist
504505
}
505506

506-
return container_model.GetContainerBlob(ctx, opts)
507+
return workaroundGetContainerBlob(ctx, opts)
507508
}
508509

509510
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#checking-if-content-exists-in-the-registry
@@ -643,3 +644,23 @@ func GetTagList(ctx *context.Context) {
643644
Tags: tags,
644645
})
645646
}
647+
648+
// FIXME: Workaround to be removed in v1.20
649+
// https://github.com/go-gitea/gitea/issues/19586
650+
func workaroundGetContainerBlob(ctx *context.Context, opts *container_model.BlobSearchOptions) (*packages_model.PackageFileDescriptor, error) {
651+
blob, err := container_model.GetContainerBlob(ctx, opts)
652+
if err != nil {
653+
return nil, err
654+
}
655+
656+
err = packages_module.NewContentStore().Has(packages_module.BlobHash256Key(blob.Blob.HashSHA256))
657+
if err != nil {
658+
if errors.Is(err, os.ErrNotExist) {
659+
log.Debug("Package registry inconsistent: blob %s does not exist on file system", blob.Blob.HashSHA256)
660+
return nil, container_model.ErrContainerBlobNotExist
661+
}
662+
return nil, err
663+
}
664+
665+
return blob, nil
666+
}

Diff for: routers/api/packages/container/manifest.go

+11
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ package container
66

77
import (
88
"context"
9+
"errors"
910
"fmt"
1011
"io"
12+
"os"
1113
"strings"
1214

1315
"code.gitea.io/gitea/models/db"
@@ -403,6 +405,15 @@ func createManifestBlob(ctx context.Context, mci *manifestCreationInfo, pv *pack
403405
log.Error("Error inserting package blob: %v", err)
404406
return nil, false, "", err
405407
}
408+
// FIXME: Workaround to be removed in v1.20
409+
// https://github.com/go-gitea/gitea/issues/19586
410+
if exists {
411+
err = packages_module.NewContentStore().Has(packages_module.BlobHash256Key(pb.HashSHA256))
412+
if err != nil && errors.Is(err, os.ErrNotExist) {
413+
log.Debug("Package registry inconsistent: blob %s does not exist on file system", pb.HashSHA256)
414+
exists = false
415+
}
416+
}
406417
if !exists {
407418
contentStore := packages_module.NewContentStore()
408419
if err := contentStore.Save(packages_module.BlobHash256Key(pb.HashSHA256), buf, buf.Size()); err != nil {

0 commit comments

Comments
 (0)