mirror of
https://github.com/containers/podman.git
synced 2025-10-16 18:53:19 +08:00
Merge pull request #26524 from mheon/libartifact_locking
Add basic locking to Libartifact
This commit is contained in:
@ -29,6 +29,7 @@ import (
|
|||||||
"github.com/containers/podman/v5/pkg/libartifact"
|
"github.com/containers/podman/v5/pkg/libartifact"
|
||||||
libartTypes "github.com/containers/podman/v5/pkg/libartifact/types"
|
libartTypes "github.com/containers/podman/v5/pkg/libartifact/types"
|
||||||
"github.com/containers/storage/pkg/fileutils"
|
"github.com/containers/storage/pkg/fileutils"
|
||||||
|
"github.com/containers/storage/pkg/lockfile"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
"github.com/opencontainers/image-spec/specs-go"
|
"github.com/opencontainers/image-spec/specs-go"
|
||||||
specV1 "github.com/opencontainers/image-spec/specs-go/v1"
|
specV1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
@ -44,6 +45,7 @@ const ManifestSchemaVersion = 2
|
|||||||
type ArtifactStore struct {
|
type ArtifactStore struct {
|
||||||
SystemContext *types.SystemContext
|
SystemContext *types.SystemContext
|
||||||
storePath string
|
storePath string
|
||||||
|
lock *lockfile.LockFile
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewArtifactStore is a constructor for artifact stores. Most artifact dealings depend on this. Store path is
|
// NewArtifactStore is a constructor for artifact stores. Most artifact dealings depend on this. Store path is
|
||||||
@ -68,7 +70,14 @@ func NewArtifactStore(storePath string, sc *types.SystemContext) (*ArtifactStore
|
|||||||
if err := os.MkdirAll(baseDir, 0700); err != nil {
|
if err := os.MkdirAll(baseDir, 0700); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Open the lockfile, creating if necessary
|
||||||
|
lock, err := lockfile.GetLockFile(filepath.Join(storePath, "index.lock"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
artifactStore.lock = lock
|
||||||
// if the index file is not present we need to create an empty one
|
// if the index file is not present we need to create an empty one
|
||||||
|
// Do so after the lock to try and prevent races around store creation.
|
||||||
if err := fileutils.Exists(artifactStore.indexPath()); err != nil && errors.Is(err, os.ErrNotExist) {
|
if err := fileutils.Exists(artifactStore.indexPath()); err != nil && errors.Is(err, os.ErrNotExist) {
|
||||||
if createErr := artifactStore.createEmptyManifest(); createErr != nil {
|
if createErr := artifactStore.createEmptyManifest(); createErr != nil {
|
||||||
return nil, createErr
|
return nil, createErr
|
||||||
@ -83,6 +92,9 @@ func (as ArtifactStore) Remove(ctx context.Context, name string) (*digest.Digest
|
|||||||
return nil, ErrEmptyArtifactName
|
return nil, ErrEmptyArtifactName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
as.lock.Lock()
|
||||||
|
defer as.lock.Unlock()
|
||||||
|
|
||||||
// validate and see if the input is a digest
|
// validate and see if the input is a digest
|
||||||
artifacts, err := as.getArtifacts(ctx, nil)
|
artifacts, err := as.getArtifacts(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -112,6 +124,10 @@ func (as ArtifactStore) Inspect(ctx context.Context, nameOrDigest string) (*liba
|
|||||||
if len(nameOrDigest) == 0 {
|
if len(nameOrDigest) == 0 {
|
||||||
return nil, ErrEmptyArtifactName
|
return nil, ErrEmptyArtifactName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
as.lock.RLock()
|
||||||
|
defer as.lock.Unlock()
|
||||||
|
|
||||||
artifacts, err := as.getArtifacts(ctx, nil)
|
artifacts, err := as.getArtifacts(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -122,6 +138,9 @@ func (as ArtifactStore) Inspect(ctx context.Context, nameOrDigest string) (*liba
|
|||||||
|
|
||||||
// List artifacts in the local store
|
// List artifacts in the local store
|
||||||
func (as ArtifactStore) List(ctx context.Context) (libartifact.ArtifactList, error) {
|
func (as ArtifactStore) List(ctx context.Context) (libartifact.ArtifactList, error) {
|
||||||
|
as.lock.RLock()
|
||||||
|
defer as.lock.Unlock()
|
||||||
|
|
||||||
return as.getArtifacts(ctx, nil)
|
return as.getArtifacts(ctx, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +153,10 @@ func (as ArtifactStore) Pull(ctx context.Context, name string, opts libimage.Cop
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
as.lock.Lock()
|
||||||
|
defer as.lock.Unlock()
|
||||||
|
|
||||||
destRef, err := layout.NewReference(as.storePath, name)
|
destRef, err := layout.NewReference(as.storePath, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -162,6 +185,10 @@ func (as ArtifactStore) Push(ctx context.Context, src, dest string, opts libimag
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
as.lock.Lock()
|
||||||
|
defer as.lock.Unlock()
|
||||||
|
|
||||||
srcRef, err := layout.NewReference(as.storePath, src)
|
srcRef, err := layout.NewReference(as.storePath, src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -201,6 +228,14 @@ func (as ArtifactStore) Add(ctx context.Context, dest string, artifactBlobs []en
|
|||||||
return nil, fmt.Errorf("cannot override filename with %s annotation", specV1.AnnotationTitle)
|
return nil, fmt.Errorf("cannot override filename with %s annotation", specV1.AnnotationTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
locked := true
|
||||||
|
as.lock.Lock()
|
||||||
|
defer func() {
|
||||||
|
if locked {
|
||||||
|
as.lock.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Check if artifact already exists
|
// Check if artifact already exists
|
||||||
artifacts, err := as.getArtifacts(ctx, nil)
|
artifacts, err := as.getArtifacts(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -263,6 +298,11 @@ func (as ArtifactStore) Add(ctx context.Context, dest string, artifactBlobs []en
|
|||||||
}
|
}
|
||||||
defer imageDest.Close()
|
defer imageDest.Close()
|
||||||
|
|
||||||
|
// Unlock around the actual pull of the blobs.
|
||||||
|
// This is ugly as hell, but should be safe.
|
||||||
|
locked = false
|
||||||
|
as.lock.Unlock()
|
||||||
|
|
||||||
// ImageDestination, in general, requires the caller to write a full image; here we may write only the added layers.
|
// ImageDestination, in general, requires the caller to write a full image; here we may write only the added layers.
|
||||||
// This works for the oci/layout transport we hard-code.
|
// This works for the oci/layout transport we hard-code.
|
||||||
for _, artifactBlob := range artifactBlobs {
|
for _, artifactBlob := range artifactBlobs {
|
||||||
@ -307,6 +347,9 @@ func (as ArtifactStore) Add(ctx context.Context, dest string, artifactBlobs []en
|
|||||||
artifactManifest.Layers = append(artifactManifest.Layers, newLayer)
|
artifactManifest.Layers = append(artifactManifest.Layers, newLayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
as.lock.Lock()
|
||||||
|
locked = true
|
||||||
|
|
||||||
rawData, err := json.Marshal(artifactManifest)
|
rawData, err := json.Marshal(artifactManifest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -669,6 +712,8 @@ func copyTrustedImageBlobToTarStream(ctx context.Context, imgSrc types.ImageSour
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (as ArtifactStore) createEmptyManifest() error {
|
func (as ArtifactStore) createEmptyManifest() error {
|
||||||
|
as.lock.Lock()
|
||||||
|
defer as.lock.Unlock()
|
||||||
index := specV1.Index{
|
index := specV1.Index{
|
||||||
MediaType: specV1.MediaTypeImageIndex,
|
MediaType: specV1.MediaTypeImageIndex,
|
||||||
Versioned: specs.Versioned{SchemaVersion: ManifestSchemaVersion},
|
Versioned: specs.Versioned{SchemaVersion: ManifestSchemaVersion},
|
||||||
|
Reference in New Issue
Block a user