Files
.gitea
.github
assets
build
cmd
contrib
custom
docker
docs
models
modules
options
public
routers
api
packages
composer
conan
container
auth.go
blob.go
container.go
errors.go
manifest.go
generic
helm
helper
maven
npm
nuget
pub
pypi
rubygems
vagrant
api.go
v1
common
install
private
utils
web
init.go
services
snap
templates
tests
tools
web_src
.air.toml
.changelog.yml
.dockerignore
.drone.yml
.editorconfig
.eslintrc.yaml
.gitattributes
.gitignore
.gitpod.yml
.golangci.yml
.ignore
.lgtm
.markdownlint.yaml
.npmrc
.spectral.yaml
.stylelintrc.yaml
BSDmakefile
CHANGELOG.md
CONTRIBUTING.md
DCO
Dockerfile
Dockerfile.rootless
LICENSE
MAINTAINERS
Makefile
README.md
README_ZH.md
SECURITY.md
build.go
go.mod
go.sum
main.go
package-lock.json
package.json
playwright.config.js
vitest.config.js
webpack.config.js
gitea/routers/api/packages/container/blob.go
flynnnnnnnnnn e81ccc406b Implement FSFE REUSE for golang files ()
Change all license headers to comply with REUSE specification.

Fix 

Co-authored-by: flynnnnnnnnnn <flynnnnnnnnnn@github>
Co-authored-by: John Olheiser <john.olheiser@gmail.com>
2022-11-27 18:20:29 +00:00

176 lines
5.0 KiB
Go

// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
"context"
"encoding/hex"
"errors"
"fmt"
"os"
"strings"
"sync"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
container_model "code.gitea.io/gitea/models/packages/container"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
container_module "code.gitea.io/gitea/modules/packages/container"
"code.gitea.io/gitea/modules/util"
packages_service "code.gitea.io/gitea/services/packages"
)
var uploadVersionMutex sync.Mutex
// saveAsPackageBlob creates a package blob from an upload
// The uploaded blob gets stored in a special upload version to link them to the package/image
func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_service.PackageInfo) (*packages_model.PackageBlob, error) {
pb := packages_service.NewPackageBlob(hsr)
exists := false
contentStore := packages_module.NewContentStore()
var uploadVersion *packages_model.PackageVersion
// FIXME: Replace usage of mutex with database transaction
// https://github.com/go-gitea/gitea/pull/21862
uploadVersionMutex.Lock()
err := db.WithTx(db.DefaultContext, func(ctx context.Context) error {
created := true
p := &packages_model.Package{
OwnerID: pi.Owner.ID,
Type: packages_model.TypeContainer,
Name: strings.ToLower(pi.Name),
LowerName: strings.ToLower(pi.Name),
}
var err error
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
if err == packages_model.ErrDuplicatePackage {
created = false
} else {
log.Error("Error inserting package: %v", err)
return err
}
}
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
}
}
pv := &packages_model.PackageVersion{
PackageID: p.ID,
CreatorID: pi.Owner.ID,
Version: container_model.UploadVersion,
LowerVersion: container_model.UploadVersion,
IsInternal: true,
MetadataJSON: "null",
}
if pv, err = packages_model.GetOrInsertVersion(ctx, pv); err != nil {
if err != packages_model.ErrDuplicatePackageVersion {
log.Error("Error inserting package: %v", err)
return err
}
}
uploadVersion = pv
return nil
})
uploadVersionMutex.Unlock()
if err != nil {
return nil, err
}
err = db.WithTx(db.DefaultContext, func(ctx context.Context) error {
pb, exists, err = packages_model.GetOrInsertBlob(ctx, pb)
if err != nil {
log.Error("Error inserting package blob: %v", err)
return err
}
// FIXME: Workaround to be removed in v1.20
// https://github.com/go-gitea/gitea/issues/19586
if exists {
err = contentStore.Has(packages_module.BlobHash256Key(pb.HashSHA256))
if err != nil && (errors.Is(err, util.ErrNotExist) || errors.Is(err, os.ErrNotExist)) {
log.Debug("Package registry inconsistent: blob %s does not exist on file system", pb.HashSHA256)
exists = false
}
}
if !exists {
if err := contentStore.Save(packages_module.BlobHash256Key(pb.HashSHA256), hsr, hsr.Size()); err != nil {
log.Error("Error saving package blob in content store: %v", err)
return err
}
}
filename := strings.ToLower(fmt.Sprintf("sha256_%s", pb.HashSHA256))
pf := &packages_model.PackageFile{
VersionID: uploadVersion.ID,
BlobID: pb.ID,
Name: filename,
LowerName: filename,
CompositeKey: packages_model.EmptyFileKey,
}
if pf, err = packages_model.TryInsertFile(ctx, pf); err != nil {
if err == packages_model.ErrDuplicatePackageFile {
return nil
}
log.Error("Error inserting package file: %v", err)
return err
}
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeFile, pf.ID, container_module.PropertyDigest, digestFromPackageBlob(pb)); err != nil {
log.Error("Error setting package file property: %v", err)
return err
}
return nil
})
if err != nil {
if !exists {
if err := contentStore.Delete(packages_module.BlobHash256Key(pb.HashSHA256)); err != nil {
log.Error("Error deleting package blob from content store: %v", err)
}
}
return nil, err
}
return pb, nil
}
func deleteBlob(ownerID int64, image, digest string) error {
return db.WithTx(db.DefaultContext, func(ctx context.Context) error {
pfds, err := container_model.GetContainerBlobs(ctx, &container_model.BlobSearchOptions{
OwnerID: ownerID,
Image: image,
Digest: digest,
})
if err != nil {
return err
}
for _, file := range pfds {
if err := packages_service.DeletePackageFile(ctx, file.File); err != nil {
return err
}
}
return nil
})
}
func digestFromHashSummer(h packages_module.HashSummer) string {
_, _, hashSHA256, _ := h.Sums()
return "sha256:" + hex.EncodeToString(hashSHA256)
}
func digestFromPackageBlob(pb *packages_model.PackageBlob) string {
return "sha256:" + pb.HashSHA256
}