Skip to content

Refactor container package #34877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions models/db/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,15 @@ func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error
return txWithNoCheck(parentCtx, f)
}

// WithTx2 is similar to WithTx, but it has two return values: result and error.
func WithTx2[T any](parentCtx context.Context, f func(ctx context.Context) (T, error)) (ret T, errRet error) {
errRet = WithTx(parentCtx, func(ctx context.Context) (errInner error) {
ret, errInner = f(ctx)
return errInner
})
return ret, errRet
}

func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error) error {
sess := xormEngine.NewSession()
defer sess.Close()
Expand Down
2 changes: 1 addition & 1 deletion routers/api/packages/container/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func (a *Auth) Name() string {
}

// Verify extracts the user from the Bearer token
// If it's an anonymous session a ghost user is returned
// If it's an anonymous session, a ghost user is returned
func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) (*user_model.User, error) {
packageMeta, err := packages.ParseAuthorizationRequest(req)
if err != nil {
Expand Down
17 changes: 5 additions & 12 deletions routers/api/packages/container/blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,13 @@ func containerGlobalLockKey(piOwnerID int64, piName, usage string) string {
}

func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageInfo) (*packages_model.PackageVersion, error) {
var uploadVersion *packages_model.PackageVersion

releaser, err := globallock.Lock(ctx, containerGlobalLockKey(pi.Owner.ID, pi.Name, "package"))
if err != nil {
return nil, err
}
defer releaser()

err = db.WithTx(ctx, func(ctx context.Context) error {
return db.WithTx2(ctx, func(ctx context.Context) (*packages_model.PackageVersion, error) {
created := true
p := &packages_model.Package{
OwnerID: pi.Owner.ID,
Expand All @@ -115,15 +113,15 @@ func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageI
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
if !errors.Is(err, packages_model.ErrDuplicatePackage) {
log.Error("Error inserting package: %v", err)
return err
return nil, err
}
created = false
}

if created {
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, strings.ToLower(pi.Owner.LowerName+"/"+pi.Name)); err != nil {
log.Error("Error setting package property: %v", err)
return err
return nil, err
}
}

Expand All @@ -138,16 +136,11 @@ func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageI
if pv, err = packages_model.GetOrInsertVersion(ctx, pv); err != nil {
if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
log.Error("Error inserting package: %v", err)
return err
return nil, err
}
}

uploadVersion = pv

return nil
return pv, nil
})

return uploadVersion, err
}

func createFileForBlob(ctx context.Context, pv *packages_model.PackageVersion, pb *packages_model.PackageBlob) error {
Expand Down
28 changes: 16 additions & 12 deletions routers/api/packages/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"regexp"
"strconv"
"strings"
"sync"

auth_model "code.gitea.io/gitea/models/auth"
packages_model "code.gitea.io/gitea/models/packages"
Expand All @@ -39,10 +40,14 @@ import (
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests
const maxManifestSize = 10 * 1024 * 1024

var (
imageNamePattern = regexp.MustCompile(`\A[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*\z`)
referencePattern = regexp.MustCompile(`\A[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}\z`)
)
var globalVars = sync.OnceValue(func() (ret struct {
imageNamePattern, referencePattern *regexp.Regexp
},
) {
ret.imageNamePattern = regexp.MustCompile(`\A[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*\z`)
ret.referencePattern = regexp.MustCompile(`\A[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}\z`)
return ret
})

type containerHeaders struct {
Status int
Expand Down Expand Up @@ -84,9 +89,7 @@ func jsonResponse(ctx *context.Context, status int, obj any) {
Status: status,
ContentType: "application/json",
})
if err := json.NewEncoder(ctx.Resp).Encode(obj); err != nil {
log.Error("JSON encode: %v", err)
}
_ = json.NewEncoder(ctx.Resp).Encode(obj) // ignore network errors
}

func apiError(ctx *context.Context, status int, err error) {
Expand Down Expand Up @@ -134,7 +137,7 @@ func ReqContainerAccess(ctx *context.Context) {

// VerifyImageName is a middleware which checks if the image name is allowed
func VerifyImageName(ctx *context.Context) {
if !imageNamePattern.MatchString(ctx.PathParam("image")) {
if !globalVars().imageNamePattern.MatchString(ctx.PathParam("image")) {
apiErrorDefined(ctx, errNameInvalid)
}
}
Expand Down Expand Up @@ -216,7 +219,7 @@ func GetRepositoryList(ctx *context.Context) {
if len(repositories) == n {
v := url.Values{}
if n > 0 {
v.Add("n", strconv.Itoa(n))
v.Add("n", strconv.Itoa(n)) // FIXME: "n" can't be zero here, the logic is inconsistent with GetTagsList
}
v.Add("last", repositories[len(repositories)-1])

Expand Down Expand Up @@ -565,7 +568,7 @@ func PutManifest(ctx *context.Context) {
IsTagged: digest.Digest(reference).Validate() != nil,
}

if mci.IsTagged && !referencePattern.MatchString(reference) {
if mci.IsTagged && !globalVars().referencePattern.MatchString(reference) {
apiErrorDefined(ctx, errManifestInvalid.WithMessage("Tag is invalid"))
return
}
Expand Down Expand Up @@ -618,7 +621,7 @@ func getBlobSearchOptionsFromContext(ctx *context.Context) (*container_model.Blo
reference := ctx.PathParam("reference")
if d := digest.Digest(reference); d.Validate() == nil {
opts.Digest = string(d)
} else if referencePattern.MatchString(reference) {
} else if globalVars().referencePattern.MatchString(reference) {
opts.Tag = reference
opts.OnlyLead = true
} else {
Expand Down Expand Up @@ -782,7 +785,8 @@ func GetTagsList(ctx *context.Context) {
})
}

// FIXME: Workaround to be removed in v1.20
// FIXME: Workaround to be removed in v1.20.
// Update maybe we should never really remote it, as long as there is legacy data?
// https://github.com/go-gitea/gitea/issues/19586
func workaroundGetContainerBlob(ctx *context.Context, opts *container_model.BlobSearchOptions) (*packages_model.PackageFileDescriptor, error) {
blob, err := container_model.GetContainerBlob(ctx, opts)
Expand Down
Loading