mirror of
https://github.com/containers/skopeo.git
synced 2025-06-26 06:42:21 +00:00
Vendor in latest containers/storage and containers/image
Update containers/storage and containers/image to define location of local storage. Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
parent
d93a581fb8
commit
032309941b
@ -2,13 +2,13 @@
|
|||||||
github.com/urfave/cli v1.20.0
|
github.com/urfave/cli v1.20.0
|
||||||
github.com/kr/pretty v0.1.0
|
github.com/kr/pretty v0.1.0
|
||||||
github.com/kr/text v0.1.0
|
github.com/kr/text v0.1.0
|
||||||
github.com/containers/image 4153c049af593f5abeab913595b288d109c6f2a7
|
github.com/containers/image f52cf78ebfa1916da406f8b6210d8f7764ec1185
|
||||||
github.com/vbauerster/mpb v3.3.4
|
github.com/vbauerster/mpb v3.3.4
|
||||||
github.com/mattn/go-isatty v0.0.4
|
github.com/mattn/go-isatty v0.0.4
|
||||||
github.com/VividCortex/ewma v1.1.1
|
github.com/VividCortex/ewma v1.1.1
|
||||||
golang.org/x/sync 42b317875d0fa942474b76e1b46a6060d720ae6e
|
golang.org/x/sync 42b317875d0fa942474b76e1b46a6060d720ae6e
|
||||||
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
|
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
|
||||||
github.com/containers/storage 3c88d700b4daf494818633be242f02ccaccc6e72
|
github.com/containers/storage v1.12.1
|
||||||
github.com/sirupsen/logrus v1.0.0
|
github.com/sirupsen/logrus v1.0.0
|
||||||
github.com/go-check/check v1
|
github.com/go-check/check v1
|
||||||
github.com/stretchr/testify v1.1.3
|
github.com/stretchr/testify v1.1.3
|
||||||
|
2
vendor/github.com/containers/image/docker/docker_client.go
generated
vendored
2
vendor/github.com/containers/image/docker/docker_client.go
generated
vendored
@ -197,7 +197,7 @@ func dockerCertDir(sys *types.SystemContext, hostPort string) (string, error) {
|
|||||||
// “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection)
|
// “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection)
|
||||||
func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, write bool, actions string) (*dockerClient, error) {
|
func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, write bool, actions string) (*dockerClient, error) {
|
||||||
registry := reference.Domain(ref.ref)
|
registry := reference.Domain(ref.ref)
|
||||||
username, password, err := config.GetAuthentication(sys, reference.Domain(ref.ref))
|
username, password, err := config.GetAuthentication(sys, registry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error getting username and password")
|
return nil, errors.Wrapf(err, "error getting username and password")
|
||||||
}
|
}
|
||||||
|
4
vendor/github.com/containers/image/docker/docker_image_dest.go
generated
vendored
4
vendor/github.com/containers/image/docker/docker_image_dest.go
generated
vendored
@ -16,7 +16,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/image/docker/reference"
|
"github.com/containers/image/docker/reference"
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/pkg/blobinfocache"
|
"github.com/containers/image/pkg/blobinfocache/none"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/docker/distribution/registry/api/errcode"
|
"github.com/docker/distribution/registry/api/errcode"
|
||||||
"github.com/docker/distribution/registry/api/v2"
|
"github.com/docker/distribution/registry/api/v2"
|
||||||
@ -129,7 +129,7 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader,
|
|||||||
// This should not really be necessary, at least the copy code calls TryReusingBlob automatically.
|
// This should not really be necessary, at least the copy code calls TryReusingBlob automatically.
|
||||||
// Still, we need to check, if only because the "initiate upload" endpoint does not have a documented "blob already exists" return value.
|
// Still, we need to check, if only because the "initiate upload" endpoint does not have a documented "blob already exists" return value.
|
||||||
// But we do that with NoCache, so that it _only_ checks the primary destination, instead of trying all mount candidates _again_.
|
// But we do that with NoCache, so that it _only_ checks the primary destination, instead of trying all mount candidates _again_.
|
||||||
haveBlob, reusedInfo, err := d.TryReusingBlob(ctx, inputInfo, blobinfocache.NoCache, false)
|
haveBlob, reusedInfo, err := d.TryReusingBlob(ctx, inputInfo, none.NoCache, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.BlobInfo{}, err
|
return types.BlobInfo{}, err
|
||||||
}
|
}
|
||||||
|
6
vendor/github.com/containers/image/image/docker_schema2.go
generated
vendored
6
vendor/github.com/containers/image/image/docker_schema2.go
generated
vendored
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/image/docker/reference"
|
"github.com/containers/image/docker/reference"
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/pkg/blobinfocache"
|
"github.com/containers/image/pkg/blobinfocache/none"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
@ -96,7 +96,7 @@ func (m *manifestSchema2) ConfigBlob(ctx context.Context) ([]byte, error) {
|
|||||||
if m.src == nil {
|
if m.src == nil {
|
||||||
return nil, errors.Errorf("Internal error: neither src nor configBlob set in manifestSchema2")
|
return nil, errors.Errorf("Internal error: neither src nor configBlob set in manifestSchema2")
|
||||||
}
|
}
|
||||||
stream, _, err := m.src.GetBlob(ctx, manifest.BlobInfoFromSchema2Descriptor(m.m.ConfigDescriptor), blobinfocache.NoCache)
|
stream, _, err := m.src.GetBlob(ctx, manifest.BlobInfoFromSchema2Descriptor(m.m.ConfigDescriptor), none.NoCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -252,7 +252,7 @@ func (m *manifestSchema2) convertToManifestSchema1(ctx context.Context, dest typ
|
|||||||
logrus.Debugf("Uploading empty layer during conversion to schema 1")
|
logrus.Debugf("Uploading empty layer during conversion to schema 1")
|
||||||
// Ideally we should update the relevant BlobInfoCache about this layer, but that would require passing it down here,
|
// Ideally we should update the relevant BlobInfoCache about this layer, but that would require passing it down here,
|
||||||
// and anyway this blob is so small that it’s easier to just copy it than to worry about figuring out another location where to get it.
|
// and anyway this blob is so small that it’s easier to just copy it than to worry about figuring out another location where to get it.
|
||||||
info, err := dest.PutBlob(ctx, bytes.NewReader(GzippedEmptyLayer), types.BlobInfo{Digest: GzippedEmptyLayerDigest, Size: int64(len(GzippedEmptyLayer))}, blobinfocache.NoCache, false)
|
info, err := dest.PutBlob(ctx, bytes.NewReader(GzippedEmptyLayer), types.BlobInfo{Digest: GzippedEmptyLayerDigest, Size: int64(len(GzippedEmptyLayer))}, none.NoCache, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Error uploading empty layer")
|
return nil, errors.Wrap(err, "Error uploading empty layer")
|
||||||
}
|
}
|
||||||
|
4
vendor/github.com/containers/image/image/oci.go
generated
vendored
4
vendor/github.com/containers/image/image/oci.go
generated
vendored
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/image/docker/reference"
|
"github.com/containers/image/docker/reference"
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/pkg/blobinfocache"
|
"github.com/containers/image/pkg/blobinfocache/none"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
@ -61,7 +61,7 @@ func (m *manifestOCI1) ConfigBlob(ctx context.Context) ([]byte, error) {
|
|||||||
if m.src == nil {
|
if m.src == nil {
|
||||||
return nil, errors.Errorf("Internal error: neither src nor configBlob set in manifestOCI1")
|
return nil, errors.Errorf("Internal error: neither src nor configBlob set in manifestOCI1")
|
||||||
}
|
}
|
||||||
stream, _, err := m.src.GetBlob(ctx, manifest.BlobInfoFromOCI1Descriptor(m.m.Config), blobinfocache.NoCache)
|
stream, _, err := m.src.GetBlob(ctx, manifest.BlobInfoFromOCI1Descriptor(m.m.Config), none.NoCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
package blobinfocache
|
// Package boltdb implements a BlobInfoCache backed by BoltDB.
|
||||||
|
package boltdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -7,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
"github.com/boltdb/bolt"
|
||||||
|
"github.com/containers/image/pkg/blobinfocache/internal/prioritize"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -81,22 +83,23 @@ func unlockPath(path string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// boltDBCache si a BlobInfoCache implementation which uses a BoltDB file at the specified path.
|
// cache is a BlobInfoCache implementation which uses a BoltDB file at the specified path.
|
||||||
//
|
//
|
||||||
// Note that we don’t keep the database open across operations, because that would lock the file and block any other
|
// Note that we don’t keep the database open across operations, because that would lock the file and block any other
|
||||||
// users; instead, we need to open/close it for every single write or lookup.
|
// users; instead, we need to open/close it for every single write or lookup.
|
||||||
type boltDBCache struct {
|
type cache struct {
|
||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBoltDBCache returns a BlobInfoCache implementation which uses a BoltDB file at path.
|
// New returns a BlobInfoCache implementation which uses a BoltDB file at path.
|
||||||
// Most users should call DefaultCache instead.
|
//
|
||||||
func NewBoltDBCache(path string) types.BlobInfoCache {
|
// Most users should call blobinfocache.DefaultCache instead.
|
||||||
return &boltDBCache{path: path}
|
func New(path string) types.BlobInfoCache {
|
||||||
|
return &cache{path: path}
|
||||||
}
|
}
|
||||||
|
|
||||||
// view returns runs the specified fn within a read-only transaction on the database.
|
// view returns runs the specified fn within a read-only transaction on the database.
|
||||||
func (bdc *boltDBCache) view(fn func(tx *bolt.Tx) error) (retErr error) {
|
func (bdc *cache) view(fn func(tx *bolt.Tx) error) (retErr error) {
|
||||||
// bolt.Open(bdc.path, 0600, &bolt.Options{ReadOnly: true}) will, if the file does not exist,
|
// bolt.Open(bdc.path, 0600, &bolt.Options{ReadOnly: true}) will, if the file does not exist,
|
||||||
// nevertheless create it, but with an O_RDONLY file descriptor, try to initialize it, and fail — while holding
|
// nevertheless create it, but with an O_RDONLY file descriptor, try to initialize it, and fail — while holding
|
||||||
// a read lock, blocking any future writes.
|
// a read lock, blocking any future writes.
|
||||||
@ -122,7 +125,7 @@ func (bdc *boltDBCache) view(fn func(tx *bolt.Tx) error) (retErr error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update returns runs the specified fn within a read-write transaction on the database.
|
// update returns runs the specified fn within a read-write transaction on the database.
|
||||||
func (bdc *boltDBCache) update(fn func(tx *bolt.Tx) error) (retErr error) {
|
func (bdc *cache) update(fn func(tx *bolt.Tx) error) (retErr error) {
|
||||||
lockPath(bdc.path)
|
lockPath(bdc.path)
|
||||||
defer unlockPath(bdc.path)
|
defer unlockPath(bdc.path)
|
||||||
db, err := bolt.Open(bdc.path, 0600, nil)
|
db, err := bolt.Open(bdc.path, 0600, nil)
|
||||||
@ -139,7 +142,7 @@ func (bdc *boltDBCache) update(fn func(tx *bolt.Tx) error) (retErr error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// uncompressedDigest implements BlobInfoCache.UncompressedDigest within the provided read-only transaction.
|
// uncompressedDigest implements BlobInfoCache.UncompressedDigest within the provided read-only transaction.
|
||||||
func (bdc *boltDBCache) uncompressedDigest(tx *bolt.Tx, anyDigest digest.Digest) digest.Digest {
|
func (bdc *cache) uncompressedDigest(tx *bolt.Tx, anyDigest digest.Digest) digest.Digest {
|
||||||
if b := tx.Bucket(uncompressedDigestBucket); b != nil {
|
if b := tx.Bucket(uncompressedDigestBucket); b != nil {
|
||||||
if uncompressedBytes := b.Get([]byte(anyDigest.String())); uncompressedBytes != nil {
|
if uncompressedBytes := b.Get([]byte(anyDigest.String())); uncompressedBytes != nil {
|
||||||
d, err := digest.Parse(string(uncompressedBytes))
|
d, err := digest.Parse(string(uncompressedBytes))
|
||||||
@ -166,7 +169,7 @@ func (bdc *boltDBCache) uncompressedDigest(tx *bolt.Tx, anyDigest digest.Digest)
|
|||||||
// UncompressedDigest returns an uncompressed digest corresponding to anyDigest.
|
// UncompressedDigest returns an uncompressed digest corresponding to anyDigest.
|
||||||
// May return anyDigest if it is known to be uncompressed.
|
// May return anyDigest if it is known to be uncompressed.
|
||||||
// Returns "" if nothing is known about the digest (it may be compressed or uncompressed).
|
// Returns "" if nothing is known about the digest (it may be compressed or uncompressed).
|
||||||
func (bdc *boltDBCache) UncompressedDigest(anyDigest digest.Digest) digest.Digest {
|
func (bdc *cache) UncompressedDigest(anyDigest digest.Digest) digest.Digest {
|
||||||
var res digest.Digest
|
var res digest.Digest
|
||||||
if err := bdc.view(func(tx *bolt.Tx) error {
|
if err := bdc.view(func(tx *bolt.Tx) error {
|
||||||
res = bdc.uncompressedDigest(tx, anyDigest)
|
res = bdc.uncompressedDigest(tx, anyDigest)
|
||||||
@ -182,7 +185,7 @@ func (bdc *boltDBCache) UncompressedDigest(anyDigest digest.Digest) digest.Diges
|
|||||||
// WARNING: Only call this for LOCALLY VERIFIED data; don’t record a digest pair just because some remote author claims so (e.g.
|
// WARNING: Only call this for LOCALLY VERIFIED data; don’t record a digest pair just because some remote author claims so (e.g.
|
||||||
// because a manifest/config pair exists); otherwise the cache could be poisoned and allow substituting unexpected blobs.
|
// because a manifest/config pair exists); otherwise the cache could be poisoned and allow substituting unexpected blobs.
|
||||||
// (Eventually, the DiffIDs in image config could detect the substitution, but that may be too late, and not all image formats contain that data.)
|
// (Eventually, the DiffIDs in image config could detect the substitution, but that may be too late, and not all image formats contain that data.)
|
||||||
func (bdc *boltDBCache) RecordDigestUncompressedPair(anyDigest digest.Digest, uncompressed digest.Digest) {
|
func (bdc *cache) RecordDigestUncompressedPair(anyDigest digest.Digest, uncompressed digest.Digest) {
|
||||||
_ = bdc.update(func(tx *bolt.Tx) error {
|
_ = bdc.update(func(tx *bolt.Tx) error {
|
||||||
b, err := tx.CreateBucketIfNotExists(uncompressedDigestBucket)
|
b, err := tx.CreateBucketIfNotExists(uncompressedDigestBucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -219,7 +222,7 @@ func (bdc *boltDBCache) RecordDigestUncompressedPair(anyDigest digest.Digest, un
|
|||||||
|
|
||||||
// RecordKnownLocation records that a blob with the specified digest exists within the specified (transport, scope) scope,
|
// RecordKnownLocation records that a blob with the specified digest exists within the specified (transport, scope) scope,
|
||||||
// and can be reused given the opaque location data.
|
// and can be reused given the opaque location data.
|
||||||
func (bdc *boltDBCache) RecordKnownLocation(transport types.ImageTransport, scope types.BICTransportScope, blobDigest digest.Digest, location types.BICLocationReference) {
|
func (bdc *cache) RecordKnownLocation(transport types.ImageTransport, scope types.BICTransportScope, blobDigest digest.Digest, location types.BICLocationReference) {
|
||||||
_ = bdc.update(func(tx *bolt.Tx) error {
|
_ = bdc.update(func(tx *bolt.Tx) error {
|
||||||
b, err := tx.CreateBucketIfNotExists(knownLocationsBucket)
|
b, err := tx.CreateBucketIfNotExists(knownLocationsBucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -248,8 +251,8 @@ func (bdc *boltDBCache) RecordKnownLocation(transport types.ImageTransport, scop
|
|||||||
}) // FIXME? Log error (but throttle the log volume on repeated accesses)?
|
}) // FIXME? Log error (but throttle the log volume on repeated accesses)?
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendReplacementCandiates creates candidateWithTime values for digest in scopeBucket, and returns the result of appending them to candidates.
|
// appendReplacementCandiates creates prioritize.CandidateWithTime values for digest in scopeBucket, and returns the result of appending them to candidates.
|
||||||
func (bdc *boltDBCache) appendReplacementCandidates(candidates []candidateWithTime, scopeBucket *bolt.Bucket, digest digest.Digest) []candidateWithTime {
|
func (bdc *cache) appendReplacementCandidates(candidates []prioritize.CandidateWithTime, scopeBucket *bolt.Bucket, digest digest.Digest) []prioritize.CandidateWithTime {
|
||||||
b := scopeBucket.Bucket([]byte(digest.String()))
|
b := scopeBucket.Bucket([]byte(digest.String()))
|
||||||
if b == nil {
|
if b == nil {
|
||||||
return candidates
|
return candidates
|
||||||
@ -259,12 +262,12 @@ func (bdc *boltDBCache) appendReplacementCandidates(candidates []candidateWithTi
|
|||||||
if err := t.UnmarshalBinary(v); err != nil {
|
if err := t.UnmarshalBinary(v); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
candidates = append(candidates, candidateWithTime{
|
candidates = append(candidates, prioritize.CandidateWithTime{
|
||||||
candidate: types.BICReplacementCandidate{
|
Candidate: types.BICReplacementCandidate{
|
||||||
Digest: digest,
|
Digest: digest,
|
||||||
Location: types.BICLocationReference{Opaque: string(k)},
|
Location: types.BICLocationReference{Opaque: string(k)},
|
||||||
},
|
},
|
||||||
lastSeen: t,
|
LastSeen: t,
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}) // FIXME? Log error (but throttle the log volume on repeated accesses)?
|
}) // FIXME? Log error (but throttle the log volume on repeated accesses)?
|
||||||
@ -277,8 +280,8 @@ func (bdc *boltDBCache) appendReplacementCandidates(candidates []candidateWithTi
|
|||||||
// If !canSubstitute, the returned cadidates will match the submitted digest exactly; if canSubstitute,
|
// If !canSubstitute, the returned cadidates will match the submitted digest exactly; if canSubstitute,
|
||||||
// data from previous RecordDigestUncompressedPair calls is used to also look up variants of the blob which have the same
|
// data from previous RecordDigestUncompressedPair calls is used to also look up variants of the blob which have the same
|
||||||
// uncompressed digest.
|
// uncompressed digest.
|
||||||
func (bdc *boltDBCache) CandidateLocations(transport types.ImageTransport, scope types.BICTransportScope, primaryDigest digest.Digest, canSubstitute bool) []types.BICReplacementCandidate {
|
func (bdc *cache) CandidateLocations(transport types.ImageTransport, scope types.BICTransportScope, primaryDigest digest.Digest, canSubstitute bool) []types.BICReplacementCandidate {
|
||||||
res := []candidateWithTime{}
|
res := []prioritize.CandidateWithTime{}
|
||||||
var uncompressedDigestValue digest.Digest // = ""
|
var uncompressedDigestValue digest.Digest // = ""
|
||||||
if err := bdc.view(func(tx *bolt.Tx) error {
|
if err := bdc.view(func(tx *bolt.Tx) error {
|
||||||
scopeBucket := tx.Bucket(knownLocationsBucket)
|
scopeBucket := tx.Bucket(knownLocationsBucket)
|
||||||
@ -325,5 +328,5 @@ func (bdc *boltDBCache) CandidateLocations(transport types.ImageTransport, scope
|
|||||||
return []types.BICReplacementCandidate{} // FIXME? Log err (but throttle the log volume on repeated accesses)?
|
return []types.BICReplacementCandidate{} // FIXME? Log err (but throttle the log volume on repeated accesses)?
|
||||||
}
|
}
|
||||||
|
|
||||||
return destructivelyPrioritizeReplacementCandidates(res, primaryDigest, uncompressedDigestValue)
|
return prioritize.DestructivelyPrioritizeReplacementCandidates(res, primaryDigest, uncompressedDigestValue)
|
||||||
}
|
}
|
8
vendor/github.com/containers/image/pkg/blobinfocache/default.go
generated
vendored
8
vendor/github.com/containers/image/pkg/blobinfocache/default.go
generated
vendored
@ -5,6 +5,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containers/image/pkg/blobinfocache/boltdb"
|
||||||
|
"github.com/containers/image/pkg/blobinfocache/memory"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -50,14 +52,14 @@ func DefaultCache(sys *types.SystemContext) types.BlobInfoCache {
|
|||||||
dir, err := blobInfoCacheDir(sys, os.Geteuid())
|
dir, err := blobInfoCacheDir(sys, os.Geteuid())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Error determining a location for %s, using a memory-only cache", blobInfoCacheFilename)
|
logrus.Debugf("Error determining a location for %s, using a memory-only cache", blobInfoCacheFilename)
|
||||||
return NewMemoryCache()
|
return memory.New()
|
||||||
}
|
}
|
||||||
path := filepath.Join(dir, blobInfoCacheFilename)
|
path := filepath.Join(dir, blobInfoCacheFilename)
|
||||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||||
logrus.Debugf("Error creating parent directories for %s, using a memory-only cache: %v", blobInfoCacheFilename, err)
|
logrus.Debugf("Error creating parent directories for %s, using a memory-only cache: %v", blobInfoCacheFilename, err)
|
||||||
return NewMemoryCache()
|
return memory.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("Using blob info cache at %s", path)
|
logrus.Debugf("Using blob info cache at %s", path)
|
||||||
return NewBoltDBCache(path)
|
return boltdb.New(path)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
package blobinfocache
|
// Package prioritize provides utilities for prioritizing locations in
|
||||||
|
// types.BlobInfoCache.CandidateLocations.
|
||||||
|
package prioritize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
@ -13,16 +15,16 @@ import (
|
|||||||
// This is a heuristic/guess, and could well use a different value.
|
// This is a heuristic/guess, and could well use a different value.
|
||||||
const replacementAttempts = 5
|
const replacementAttempts = 5
|
||||||
|
|
||||||
// candidateWithTime is the input to types.BICReplacementCandidate prioritization.
|
// CandidateWithTime is the input to types.BICReplacementCandidate prioritization.
|
||||||
type candidateWithTime struct {
|
type CandidateWithTime struct {
|
||||||
candidate types.BICReplacementCandidate // The replacement candidate
|
Candidate types.BICReplacementCandidate // The replacement candidate
|
||||||
lastSeen time.Time // Time the candidate was last known to exist (either read or written)
|
LastSeen time.Time // Time the candidate was last known to exist (either read or written)
|
||||||
}
|
}
|
||||||
|
|
||||||
// candidateSortState is a local state implementing sort.Interface on candidates to prioritize,
|
// candidateSortState is a local state implementing sort.Interface on candidates to prioritize,
|
||||||
// along with the specially-treated digest values for the implementation of sort.Interface.Less
|
// along with the specially-treated digest values for the implementation of sort.Interface.Less
|
||||||
type candidateSortState struct {
|
type candidateSortState struct {
|
||||||
cs []candidateWithTime // The entries to sort
|
cs []CandidateWithTime // The entries to sort
|
||||||
primaryDigest digest.Digest // The digest the user actually asked for
|
primaryDigest digest.Digest // The digest the user actually asked for
|
||||||
uncompressedDigest digest.Digest // The uncompressed digest corresponding to primaryDigest. May be "", or even equal to primaryDigest
|
uncompressedDigest digest.Digest // The uncompressed digest corresponding to primaryDigest. May be "", or even equal to primaryDigest
|
||||||
}
|
}
|
||||||
@ -40,35 +42,35 @@ func (css *candidateSortState) Less(i, j int) bool {
|
|||||||
// Other digest values are primarily sorted by time (more recent first), secondarily by digest (to provide a deterministic order)
|
// Other digest values are primarily sorted by time (more recent first), secondarily by digest (to provide a deterministic order)
|
||||||
|
|
||||||
// First, deal with the primaryDigest/uncompressedDigest cases:
|
// First, deal with the primaryDigest/uncompressedDigest cases:
|
||||||
if xi.candidate.Digest != xj.candidate.Digest {
|
if xi.Candidate.Digest != xj.Candidate.Digest {
|
||||||
// - The two digests are different, and one (or both) of the digests is primaryDigest or uncompressedDigest: time does not matter
|
// - The two digests are different, and one (or both) of the digests is primaryDigest or uncompressedDigest: time does not matter
|
||||||
if xi.candidate.Digest == css.primaryDigest {
|
if xi.Candidate.Digest == css.primaryDigest {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if xj.candidate.Digest == css.primaryDigest {
|
if xj.Candidate.Digest == css.primaryDigest {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if css.uncompressedDigest != "" {
|
if css.uncompressedDigest != "" {
|
||||||
if xi.candidate.Digest == css.uncompressedDigest {
|
if xi.Candidate.Digest == css.uncompressedDigest {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if xj.candidate.Digest == css.uncompressedDigest {
|
if xj.Candidate.Digest == css.uncompressedDigest {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // xi.candidate.Digest == xj.candidate.Digest
|
} else { // xi.Candidate.Digest == xj.Candidate.Digest
|
||||||
// The two digests are the same, and are either primaryDigest or uncompressedDigest: order by time
|
// The two digests are the same, and are either primaryDigest or uncompressedDigest: order by time
|
||||||
if xi.candidate.Digest == css.primaryDigest || (css.uncompressedDigest != "" && xi.candidate.Digest == css.uncompressedDigest) {
|
if xi.Candidate.Digest == css.primaryDigest || (css.uncompressedDigest != "" && xi.Candidate.Digest == css.uncompressedDigest) {
|
||||||
return xi.lastSeen.After(xj.lastSeen)
|
return xi.LastSeen.After(xj.LastSeen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Neither of the digests are primaryDigest/uncompressedDigest:
|
// Neither of the digests are primaryDigest/uncompressedDigest:
|
||||||
if !xi.lastSeen.Equal(xj.lastSeen) { // Order primarily by time
|
if !xi.LastSeen.Equal(xj.LastSeen) { // Order primarily by time
|
||||||
return xi.lastSeen.After(xj.lastSeen)
|
return xi.LastSeen.After(xj.LastSeen)
|
||||||
}
|
}
|
||||||
// Fall back to digest, if timestamps end up _exactly_ the same (how?!)
|
// Fall back to digest, if timestamps end up _exactly_ the same (how?!)
|
||||||
return xi.candidate.Digest < xj.candidate.Digest
|
return xi.Candidate.Digest < xj.Candidate.Digest
|
||||||
}
|
}
|
||||||
|
|
||||||
func (css *candidateSortState) Swap(i, j int) {
|
func (css *candidateSortState) Swap(i, j int) {
|
||||||
@ -77,7 +79,7 @@ func (css *candidateSortState) Swap(i, j int) {
|
|||||||
|
|
||||||
// destructivelyPrioritizeReplacementCandidatesWithMax is destructivelyPrioritizeReplacementCandidates with a parameter for the
|
// destructivelyPrioritizeReplacementCandidatesWithMax is destructivelyPrioritizeReplacementCandidates with a parameter for the
|
||||||
// number of entries to limit, only to make testing simpler.
|
// number of entries to limit, only to make testing simpler.
|
||||||
func destructivelyPrioritizeReplacementCandidatesWithMax(cs []candidateWithTime, primaryDigest, uncompressedDigest digest.Digest, maxCandidates int) []types.BICReplacementCandidate {
|
func destructivelyPrioritizeReplacementCandidatesWithMax(cs []CandidateWithTime, primaryDigest, uncompressedDigest digest.Digest, maxCandidates int) []types.BICReplacementCandidate {
|
||||||
// We don't need to use sort.Stable() because nanosecond timestamps are (presumably?) unique, so no two elements should
|
// We don't need to use sort.Stable() because nanosecond timestamps are (presumably?) unique, so no two elements should
|
||||||
// compare equal.
|
// compare equal.
|
||||||
sort.Sort(&candidateSortState{
|
sort.Sort(&candidateSortState{
|
||||||
@ -92,17 +94,17 @@ func destructivelyPrioritizeReplacementCandidatesWithMax(cs []candidateWithTime,
|
|||||||
}
|
}
|
||||||
res := make([]types.BICReplacementCandidate, resLength)
|
res := make([]types.BICReplacementCandidate, resLength)
|
||||||
for i := range res {
|
for i := range res {
|
||||||
res[i] = cs[i].candidate
|
res[i] = cs[i].Candidate
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// destructivelyPrioritizeReplacementCandidates consumes AND DESTROYS an array of possible replacement candidates with their last known existence times,
|
// DestructivelyPrioritizeReplacementCandidates consumes AND DESTROYS an array of possible replacement candidates with their last known existence times,
|
||||||
// the primary digest the user actually asked for, and the corresponding uncompressed digest (if known, possibly equal to the primary digest),
|
// the primary digest the user actually asked for, and the corresponding uncompressed digest (if known, possibly equal to the primary digest),
|
||||||
// and returns an appropriately prioritized and/or trimmed result suitable for a return value from types.BlobInfoCache.CandidateLocations.
|
// and returns an appropriately prioritized and/or trimmed result suitable for a return value from types.BlobInfoCache.CandidateLocations.
|
||||||
//
|
//
|
||||||
// WARNING: The array of candidates is destructively modified. (The implementation of this function could of course
|
// WARNING: The array of candidates is destructively modified. (The implementation of this function could of course
|
||||||
// make a copy, but all CandidateLocations implementations build the slice of candidates only for the single purpose of calling this function anyway.)
|
// make a copy, but all CandidateLocations implementations build the slice of candidates only for the single purpose of calling this function anyway.)
|
||||||
func destructivelyPrioritizeReplacementCandidates(cs []candidateWithTime, primaryDigest, uncompressedDigest digest.Digest) []types.BICReplacementCandidate {
|
func DestructivelyPrioritizeReplacementCandidates(cs []CandidateWithTime, primaryDigest, uncompressedDigest digest.Digest) []types.BICReplacementCandidate {
|
||||||
return destructivelyPrioritizeReplacementCandidatesWithMax(cs, primaryDigest, uncompressedDigest, replacementAttempts)
|
return destructivelyPrioritizeReplacementCandidatesWithMax(cs, primaryDigest, uncompressedDigest, replacementAttempts)
|
||||||
}
|
}
|
@ -1,9 +1,11 @@
|
|||||||
package blobinfocache
|
// Package memory implements an in-memory BlobInfoCache.
|
||||||
|
package memory
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/image/pkg/blobinfocache/internal/prioritize"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -16,8 +18,8 @@ type locationKey struct {
|
|||||||
blobDigest digest.Digest
|
blobDigest digest.Digest
|
||||||
}
|
}
|
||||||
|
|
||||||
// memoryCache implements an in-memory-only BlobInfoCache
|
// cache implements an in-memory-only BlobInfoCache
|
||||||
type memoryCache struct {
|
type cache struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
// The following fields can only be accessed with mutex held.
|
// The following fields can only be accessed with mutex held.
|
||||||
uncompressedDigests map[digest.Digest]digest.Digest
|
uncompressedDigests map[digest.Digest]digest.Digest
|
||||||
@ -25,12 +27,16 @@ type memoryCache struct {
|
|||||||
knownLocations map[locationKey]map[types.BICLocationReference]time.Time // stores last known existence time for each location reference
|
knownLocations map[locationKey]map[types.BICLocationReference]time.Time // stores last known existence time for each location reference
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMemoryCache returns a BlobInfoCache implementation which is in-memory only.
|
// New returns a BlobInfoCache implementation which is in-memory only.
|
||||||
// This is primarily intended for tests, but also used as a fallback if DefaultCache
|
//
|
||||||
// can’t determine, or set up, the location for a persistent cache.
|
// This is primarily intended for tests, but also used as a fallback
|
||||||
// Manual users of types.{ImageSource,ImageDestination} might also use this instead of a persistent cache.
|
// if blobinfocache.DefaultCache can’t determine, or set up, the
|
||||||
func NewMemoryCache() types.BlobInfoCache {
|
// location for a persistent cache. Most users should use
|
||||||
return &memoryCache{
|
// blobinfocache.DefaultCache. instead of calling this directly.
|
||||||
|
// Manual users of types.{ImageSource,ImageDestination} might also use
|
||||||
|
// this instead of a persistent cache.
|
||||||
|
func New() types.BlobInfoCache {
|
||||||
|
return &cache{
|
||||||
uncompressedDigests: map[digest.Digest]digest.Digest{},
|
uncompressedDigests: map[digest.Digest]digest.Digest{},
|
||||||
digestsByUncompressed: map[digest.Digest]map[digest.Digest]struct{}{},
|
digestsByUncompressed: map[digest.Digest]map[digest.Digest]struct{}{},
|
||||||
knownLocations: map[locationKey]map[types.BICLocationReference]time.Time{},
|
knownLocations: map[locationKey]map[types.BICLocationReference]time.Time{},
|
||||||
@ -40,14 +46,14 @@ func NewMemoryCache() types.BlobInfoCache {
|
|||||||
// UncompressedDigest returns an uncompressed digest corresponding to anyDigest.
|
// UncompressedDigest returns an uncompressed digest corresponding to anyDigest.
|
||||||
// May return anyDigest if it is known to be uncompressed.
|
// May return anyDigest if it is known to be uncompressed.
|
||||||
// Returns "" if nothing is known about the digest (it may be compressed or uncompressed).
|
// Returns "" if nothing is known about the digest (it may be compressed or uncompressed).
|
||||||
func (mem *memoryCache) UncompressedDigest(anyDigest digest.Digest) digest.Digest {
|
func (mem *cache) UncompressedDigest(anyDigest digest.Digest) digest.Digest {
|
||||||
mem.mutex.Lock()
|
mem.mutex.Lock()
|
||||||
defer mem.mutex.Unlock()
|
defer mem.mutex.Unlock()
|
||||||
return mem.uncompressedDigestLocked(anyDigest)
|
return mem.uncompressedDigestLocked(anyDigest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// uncompressedDigestLocked implements types.BlobInfoCache.UncompressedDigest, but must be called only with mem.mutex held.
|
// uncompressedDigestLocked implements types.BlobInfoCache.UncompressedDigest, but must be called only with mem.mutex held.
|
||||||
func (mem *memoryCache) uncompressedDigestLocked(anyDigest digest.Digest) digest.Digest {
|
func (mem *cache) uncompressedDigestLocked(anyDigest digest.Digest) digest.Digest {
|
||||||
if d, ok := mem.uncompressedDigests[anyDigest]; ok {
|
if d, ok := mem.uncompressedDigests[anyDigest]; ok {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
@ -65,7 +71,7 @@ func (mem *memoryCache) uncompressedDigestLocked(anyDigest digest.Digest) digest
|
|||||||
// WARNING: Only call this for LOCALLY VERIFIED data; don’t record a digest pair just because some remote author claims so (e.g.
|
// WARNING: Only call this for LOCALLY VERIFIED data; don’t record a digest pair just because some remote author claims so (e.g.
|
||||||
// because a manifest/config pair exists); otherwise the cache could be poisoned and allow substituting unexpected blobs.
|
// because a manifest/config pair exists); otherwise the cache could be poisoned and allow substituting unexpected blobs.
|
||||||
// (Eventually, the DiffIDs in image config could detect the substitution, but that may be too late, and not all image formats contain that data.)
|
// (Eventually, the DiffIDs in image config could detect the substitution, but that may be too late, and not all image formats contain that data.)
|
||||||
func (mem *memoryCache) RecordDigestUncompressedPair(anyDigest digest.Digest, uncompressed digest.Digest) {
|
func (mem *cache) RecordDigestUncompressedPair(anyDigest digest.Digest, uncompressed digest.Digest) {
|
||||||
mem.mutex.Lock()
|
mem.mutex.Lock()
|
||||||
defer mem.mutex.Unlock()
|
defer mem.mutex.Unlock()
|
||||||
if previous, ok := mem.uncompressedDigests[anyDigest]; ok && previous != uncompressed {
|
if previous, ok := mem.uncompressedDigests[anyDigest]; ok && previous != uncompressed {
|
||||||
@ -83,7 +89,7 @@ func (mem *memoryCache) RecordDigestUncompressedPair(anyDigest digest.Digest, un
|
|||||||
|
|
||||||
// RecordKnownLocation records that a blob with the specified digest exists within the specified (transport, scope) scope,
|
// RecordKnownLocation records that a blob with the specified digest exists within the specified (transport, scope) scope,
|
||||||
// and can be reused given the opaque location data.
|
// and can be reused given the opaque location data.
|
||||||
func (mem *memoryCache) RecordKnownLocation(transport types.ImageTransport, scope types.BICTransportScope, blobDigest digest.Digest, location types.BICLocationReference) {
|
func (mem *cache) RecordKnownLocation(transport types.ImageTransport, scope types.BICTransportScope, blobDigest digest.Digest, location types.BICLocationReference) {
|
||||||
mem.mutex.Lock()
|
mem.mutex.Lock()
|
||||||
defer mem.mutex.Unlock()
|
defer mem.mutex.Unlock()
|
||||||
key := locationKey{transport: transport.Name(), scope: scope, blobDigest: blobDigest}
|
key := locationKey{transport: transport.Name(), scope: scope, blobDigest: blobDigest}
|
||||||
@ -95,16 +101,16 @@ func (mem *memoryCache) RecordKnownLocation(transport types.ImageTransport, scop
|
|||||||
locationScope[location] = time.Now() // Possibly overwriting an older entry.
|
locationScope[location] = time.Now() // Possibly overwriting an older entry.
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendReplacementCandiates creates candidateWithTime values for (transport, scope, digest), and returns the result of appending them to candidates.
|
// appendReplacementCandiates creates prioritize.CandidateWithTime values for (transport, scope, digest), and returns the result of appending them to candidates.
|
||||||
func (mem *memoryCache) appendReplacementCandidates(candidates []candidateWithTime, transport types.ImageTransport, scope types.BICTransportScope, digest digest.Digest) []candidateWithTime {
|
func (mem *cache) appendReplacementCandidates(candidates []prioritize.CandidateWithTime, transport types.ImageTransport, scope types.BICTransportScope, digest digest.Digest) []prioritize.CandidateWithTime {
|
||||||
locations := mem.knownLocations[locationKey{transport: transport.Name(), scope: scope, blobDigest: digest}] // nil if not present
|
locations := mem.knownLocations[locationKey{transport: transport.Name(), scope: scope, blobDigest: digest}] // nil if not present
|
||||||
for l, t := range locations {
|
for l, t := range locations {
|
||||||
candidates = append(candidates, candidateWithTime{
|
candidates = append(candidates, prioritize.CandidateWithTime{
|
||||||
candidate: types.BICReplacementCandidate{
|
Candidate: types.BICReplacementCandidate{
|
||||||
Digest: digest,
|
Digest: digest,
|
||||||
Location: l,
|
Location: l,
|
||||||
},
|
},
|
||||||
lastSeen: t,
|
LastSeen: t,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return candidates
|
return candidates
|
||||||
@ -116,10 +122,10 @@ func (mem *memoryCache) appendReplacementCandidates(candidates []candidateWithTi
|
|||||||
// If !canSubstitute, the returned cadidates will match the submitted digest exactly; if canSubstitute,
|
// If !canSubstitute, the returned cadidates will match the submitted digest exactly; if canSubstitute,
|
||||||
// data from previous RecordDigestUncompressedPair calls is used to also look up variants of the blob which have the same
|
// data from previous RecordDigestUncompressedPair calls is used to also look up variants of the blob which have the same
|
||||||
// uncompressed digest.
|
// uncompressed digest.
|
||||||
func (mem *memoryCache) CandidateLocations(transport types.ImageTransport, scope types.BICTransportScope, primaryDigest digest.Digest, canSubstitute bool) []types.BICReplacementCandidate {
|
func (mem *cache) CandidateLocations(transport types.ImageTransport, scope types.BICTransportScope, primaryDigest digest.Digest, canSubstitute bool) []types.BICReplacementCandidate {
|
||||||
mem.mutex.Lock()
|
mem.mutex.Lock()
|
||||||
defer mem.mutex.Unlock()
|
defer mem.mutex.Unlock()
|
||||||
res := []candidateWithTime{}
|
res := []prioritize.CandidateWithTime{}
|
||||||
res = mem.appendReplacementCandidates(res, transport, scope, primaryDigest)
|
res = mem.appendReplacementCandidates(res, transport, scope, primaryDigest)
|
||||||
var uncompressedDigest digest.Digest // = ""
|
var uncompressedDigest digest.Digest // = ""
|
||||||
if canSubstitute {
|
if canSubstitute {
|
||||||
@ -135,5 +141,5 @@ func (mem *memoryCache) CandidateLocations(transport types.ImageTransport, scope
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return destructivelyPrioritizeReplacementCandidates(res, primaryDigest, uncompressedDigest)
|
return prioritize.DestructivelyPrioritizeReplacementCandidates(res, primaryDigest, uncompressedDigest)
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
package blobinfocache
|
// Package none implements a dummy BlobInfoCache which records no data.
|
||||||
|
package none
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
@ -11,9 +12,10 @@ type noCache struct {
|
|||||||
|
|
||||||
// NoCache implements BlobInfoCache by not recording any data.
|
// NoCache implements BlobInfoCache by not recording any data.
|
||||||
//
|
//
|
||||||
// This exists primarily for implementations of configGetter for Manifest.Inspect,
|
// This exists primarily for implementations of configGetter for
|
||||||
// because configs only have one representation.
|
// Manifest.Inspect, because configs only have one representation.
|
||||||
// Any use of BlobInfoCache with blobs should usually use at least a short-lived cache.
|
// Any use of BlobInfoCache with blobs should usually use at least a
|
||||||
|
// short-lived cache, ideally blobinfocache.DefaultCache.
|
||||||
var NoCache types.BlobInfoCache = noCache{}
|
var NoCache types.BlobInfoCache = noCache{}
|
||||||
|
|
||||||
// UncompressedDigest returns an uncompressed digest corresponding to anyDigest.
|
// UncompressedDigest returns an uncompressed digest corresponding to anyDigest.
|
6
vendor/github.com/containers/image/storage/storage_image.go
generated
vendored
6
vendor/github.com/containers/image/storage/storage_image.go
generated
vendored
@ -18,7 +18,7 @@ import (
|
|||||||
"github.com/containers/image/image"
|
"github.com/containers/image/image"
|
||||||
"github.com/containers/image/internal/tmpdir"
|
"github.com/containers/image/internal/tmpdir"
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/pkg/blobinfocache"
|
"github.com/containers/image/pkg/blobinfocache/none"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
@ -595,12 +595,12 @@ func (s *storageImageDestination) Commit(ctx context.Context) error {
|
|||||||
if !haveDiffID {
|
if !haveDiffID {
|
||||||
// Check if it's elsewhere and the caller just forgot to pass it to us in a PutBlob(),
|
// Check if it's elsewhere and the caller just forgot to pass it to us in a PutBlob(),
|
||||||
// or to even check if we had it.
|
// or to even check if we had it.
|
||||||
// Use blobinfocache.NoCache to avoid a repeated DiffID lookup in the BlobInfoCache; a caller
|
// Use none.NoCache to avoid a repeated DiffID lookup in the BlobInfoCache; a caller
|
||||||
// that relies on using a blob digest that has never been seeen by the store had better call
|
// that relies on using a blob digest that has never been seeen by the store had better call
|
||||||
// TryReusingBlob; not calling PutBlob already violates the documented API, so there’s only
|
// TryReusingBlob; not calling PutBlob already violates the documented API, so there’s only
|
||||||
// so far we are going to accommodate that (if we should be doing that at all).
|
// so far we are going to accommodate that (if we should be doing that at all).
|
||||||
logrus.Debugf("looking for diffID for blob %+v", blob.Digest)
|
logrus.Debugf("looking for diffID for blob %+v", blob.Digest)
|
||||||
has, _, err := s.TryReusingBlob(ctx, blob.BlobInfo, blobinfocache.NoCache, false)
|
has, _, err := s.TryReusingBlob(ctx, blob.BlobInfo, none.NoCache, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error checking for a layer based on blob %q", blob.Digest.String())
|
return errors.Wrapf(err, "error checking for a layer based on blob %q", blob.Digest.String())
|
||||||
}
|
}
|
||||||
|
6
vendor/github.com/containers/image/storage/storage_transport.go
generated
vendored
6
vendor/github.com/containers/image/storage/storage_transport.go
generated
vendored
@ -4,6 +4,7 @@ package storage
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -180,7 +181,10 @@ func (s *storageTransport) GetStore() (storage.Store, error) {
|
|||||||
// Return the transport's previously-set store. If we don't have one
|
// Return the transport's previously-set store. If we don't have one
|
||||||
// of those, initialize one now.
|
// of those, initialize one now.
|
||||||
if s.store == nil {
|
if s.store == nil {
|
||||||
options := storage.DefaultStoreOptions
|
options, err := storage.DefaultStoreOptions(os.Getuid() != 0, os.Getuid())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
options.UIDMap = s.defaultUIDMap
|
options.UIDMap = s.defaultUIDMap
|
||||||
options.GIDMap = s.defaultGIDMap
|
options.GIDMap = s.defaultGIDMap
|
||||||
store, err := storage.GetStore(options)
|
store, err := storage.GetStore(options)
|
||||||
|
2
vendor/github.com/containers/image/vendor.conf
generated
vendored
2
vendor/github.com/containers/image/vendor.conf
generated
vendored
@ -1,7 +1,7 @@
|
|||||||
github.com/containers/image
|
github.com/containers/image
|
||||||
|
|
||||||
github.com/sirupsen/logrus v1.0.0
|
github.com/sirupsen/logrus v1.0.0
|
||||||
github.com/containers/storage master
|
github.com/containers/storage v1.12.1
|
||||||
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
||||||
github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1
|
github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1
|
||||||
github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716
|
github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716
|
||||||
|
3
vendor/github.com/containers/storage/drivers/copy/copy.go
generated
vendored
3
vendor/github.com/containers/storage/drivers/copy/copy.go
generated
vendored
@ -19,6 +19,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/storage/pkg/idtools"
|
||||||
"github.com/containers/storage/pkg/pools"
|
"github.com/containers/storage/pkg/pools"
|
||||||
"github.com/containers/storage/pkg/system"
|
"github.com/containers/storage/pkg/system"
|
||||||
rsystem "github.com/opencontainers/runc/libcontainer/system"
|
rsystem "github.com/opencontainers/runc/libcontainer/system"
|
||||||
@ -212,7 +213,7 @@ func DirCopy(srcDir, dstDir string, copyMode Mode, copyXattrs bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Lchown(dstPath, int(stat.Uid), int(stat.Gid)); err != nil {
|
if err := idtools.SafeLchown(dstPath, int(stat.Uid), int(stat.Gid)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
vendor/github.com/containers/storage/pkg/archive/archive.go
generated
vendored
2
vendor/github.com/containers/storage/pkg/archive/archive.go
generated
vendored
@ -636,7 +636,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
|
|||||||
if chownOpts == nil {
|
if chownOpts == nil {
|
||||||
chownOpts = &idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
|
chownOpts = &idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
|
||||||
}
|
}
|
||||||
if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
|
if err := idtools.SafeLchown(path, chownOpts.UID, chownOpts.GID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
vendor/github.com/containers/storage/pkg/archive/archive_linux.go
generated
vendored
3
vendor/github.com/containers/storage/pkg/archive/archive_linux.go
generated
vendored
@ -7,6 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containers/storage/pkg/idtools"
|
||||||
"github.com/containers/storage/pkg/system"
|
"github.com/containers/storage/pkg/system"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
@ -130,7 +131,7 @@ func (overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool,
|
|||||||
if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil {
|
if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if err := os.Chown(originalPath, hdr.Uid, hdr.Gid); err != nil {
|
if err := idtools.SafeChown(originalPath, hdr.Uid, hdr.Gid); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
vendor/github.com/containers/storage/pkg/idtools/idtools.go
generated
vendored
18
vendor/github.com/containers/storage/pkg/idtools/idtools.go
generated
vendored
@ -7,6 +7,9 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IDMap contains a single entry for user namespace range remapping. An array
|
// IDMap contains a single entry for user namespace range remapping. An array
|
||||||
@ -277,3 +280,18 @@ func parseSubidFile(path, username string) (ranges, error) {
|
|||||||
}
|
}
|
||||||
return rangeList, nil
|
return rangeList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkChownErr(err error, name string, uid, gid int) error {
|
||||||
|
if e, ok := err.(*os.PathError); ok && e.Err == syscall.EINVAL {
|
||||||
|
return errors.Wrapf(err, "there might not be enough IDs available in the namespace (requested %d:%d for %s)", uid, gid, name)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SafeChown(name string, uid, gid int) error {
|
||||||
|
return checkChownErr(os.Chown(name, uid, gid), name, uid, gid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SafeLchown(name string, uid, gid int) error {
|
||||||
|
return checkChownErr(os.Lchown(name, uid, gid), name, uid, gid)
|
||||||
|
}
|
||||||
|
4
vendor/github.com/containers/storage/pkg/idtools/idtools_unix.go
generated
vendored
4
vendor/github.com/containers/storage/pkg/idtools/idtools_unix.go
generated
vendored
@ -30,7 +30,7 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
|
|||||||
paths = []string{path}
|
paths = []string{path}
|
||||||
} else if err == nil && chownExisting {
|
} else if err == nil && chownExisting {
|
||||||
// short-circuit--we were called with an existing directory and chown was requested
|
// short-circuit--we were called with an existing directory and chown was requested
|
||||||
return os.Chown(path, ownerUID, ownerGID)
|
return SafeChown(path, ownerUID, ownerGID)
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
// nothing to do; directory path fully exists already and chown was NOT requested
|
// nothing to do; directory path fully exists already and chown was NOT requested
|
||||||
return nil
|
return nil
|
||||||
@ -60,7 +60,7 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
|
|||||||
// even if it existed, we will chown the requested path + any subpaths that
|
// even if it existed, we will chown the requested path + any subpaths that
|
||||||
// didn't exist when we called MkdirAll
|
// didn't exist when we called MkdirAll
|
||||||
for _, pathComponent := range paths {
|
for _, pathComponent := range paths {
|
||||||
if err := os.Chown(pathComponent, ownerUID, ownerGID); err != nil {
|
if err := SafeChown(pathComponent, ownerUID, ownerGID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
vendor/github.com/containers/storage/store.go
generated
vendored
32
vendor/github.com/containers/storage/store.go
generated
vendored
@ -32,7 +32,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultStoreOptions is a reasonable default set of options.
|
// DefaultStoreOptions is a reasonable default set of options.
|
||||||
DefaultStoreOptions StoreOptions
|
defaultStoreOptions StoreOptions
|
||||||
stores []*store
|
stores []*store
|
||||||
storesLock sync.Mutex
|
storesLock sync.Mutex
|
||||||
)
|
)
|
||||||
@ -550,7 +550,7 @@ type store struct {
|
|||||||
// }
|
// }
|
||||||
func GetStore(options StoreOptions) (Store, error) {
|
func GetStore(options StoreOptions) (Store, error) {
|
||||||
if options.RunRoot == "" && options.GraphRoot == "" && options.GraphDriverName == "" && len(options.GraphDriverOptions) == 0 {
|
if options.RunRoot == "" && options.GraphRoot == "" && options.GraphDriverName == "" && len(options.GraphDriverOptions) == 0 {
|
||||||
options = DefaultStoreOptions
|
options = defaultStoreOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.GraphRoot != "" {
|
if options.GraphRoot != "" {
|
||||||
@ -3217,8 +3217,20 @@ func copyStringInterfaceMap(m map[string]interface{}) map[string]interface{} {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultConfigFile path to the system wide storage.conf file
|
// defaultConfigFile path to the system wide storage.conf file
|
||||||
const DefaultConfigFile = "/etc/containers/storage.conf"
|
const defaultConfigFile = "/etc/containers/storage.conf"
|
||||||
|
|
||||||
|
// DefaultConfigFile returns the path to the storage config file used
|
||||||
|
func DefaultConfigFile(rootless bool) (string, error) {
|
||||||
|
if rootless {
|
||||||
|
home, err := homeDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "cannot determine users homedir")
|
||||||
|
}
|
||||||
|
return filepath.Join(home, ".config/containers/storage.conf"), nil
|
||||||
|
}
|
||||||
|
return defaultConfigFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TOML-friendly explicit tables used for conversions.
|
// TOML-friendly explicit tables used for conversions.
|
||||||
type tomlConfig struct {
|
type tomlConfig struct {
|
||||||
@ -3358,19 +3370,19 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
DefaultStoreOptions.RunRoot = "/var/run/containers/storage"
|
defaultStoreOptions.RunRoot = "/var/run/containers/storage"
|
||||||
DefaultStoreOptions.GraphRoot = "/var/lib/containers/storage"
|
defaultStoreOptions.GraphRoot = "/var/lib/containers/storage"
|
||||||
DefaultStoreOptions.GraphDriverName = ""
|
defaultStoreOptions.GraphDriverName = ""
|
||||||
|
|
||||||
ReloadConfigurationFile(DefaultConfigFile, &DefaultStoreOptions)
|
ReloadConfigurationFile(defaultConfigFile, &defaultStoreOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDefaultMountOptions() ([]string, error) {
|
func GetDefaultMountOptions() ([]string, error) {
|
||||||
mountOpts := []string{
|
mountOpts := []string{
|
||||||
".mountopt",
|
".mountopt",
|
||||||
fmt.Sprintf("%s.mountopt", DefaultStoreOptions.GraphDriverName),
|
fmt.Sprintf("%s.mountopt", defaultStoreOptions.GraphDriverName),
|
||||||
}
|
}
|
||||||
for _, option := range DefaultStoreOptions.GraphDriverOptions {
|
for _, option := range defaultStoreOptions.GraphDriverOptions {
|
||||||
key, val, err := parsers.ParseKeyValueOpt(option)
|
key, val, err := parsers.ParseKeyValueOpt(option)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
234
vendor/github.com/containers/storage/utils.go
generated
vendored
Normal file
234
vendor/github.com/containers/storage/utils.go
generated
vendored
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
|
"github.com/containers/storage/pkg/idtools"
|
||||||
|
"github.com/containers/storage/pkg/system"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
|
||||||
|
func ParseIDMapping(UIDMapSlice, GIDMapSlice []string, subUIDMap, subGIDMap string) (*IDMappingOptions, error) {
|
||||||
|
options := IDMappingOptions{
|
||||||
|
HostUIDMapping: true,
|
||||||
|
HostGIDMapping: true,
|
||||||
|
}
|
||||||
|
if subGIDMap == "" && subUIDMap != "" {
|
||||||
|
subGIDMap = subUIDMap
|
||||||
|
}
|
||||||
|
if subUIDMap == "" && subGIDMap != "" {
|
||||||
|
subUIDMap = subGIDMap
|
||||||
|
}
|
||||||
|
if len(GIDMapSlice) == 0 && len(UIDMapSlice) != 0 {
|
||||||
|
GIDMapSlice = UIDMapSlice
|
||||||
|
}
|
||||||
|
if len(UIDMapSlice) == 0 && len(GIDMapSlice) != 0 {
|
||||||
|
UIDMapSlice = GIDMapSlice
|
||||||
|
}
|
||||||
|
if len(UIDMapSlice) == 0 && subUIDMap == "" && os.Getuid() != 0 {
|
||||||
|
UIDMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getuid())}
|
||||||
|
}
|
||||||
|
if len(GIDMapSlice) == 0 && subGIDMap == "" && os.Getuid() != 0 {
|
||||||
|
GIDMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getgid())}
|
||||||
|
}
|
||||||
|
|
||||||
|
if subUIDMap != "" && subGIDMap != "" {
|
||||||
|
mappings, err := idtools.NewIDMappings(subUIDMap, subGIDMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to create NewIDMappings for uidmap=%s gidmap=%s", subUIDMap, subGIDMap)
|
||||||
|
}
|
||||||
|
options.UIDMap = mappings.UIDs()
|
||||||
|
options.GIDMap = mappings.GIDs()
|
||||||
|
}
|
||||||
|
parsedUIDMap, err := idtools.ParseIDMap(UIDMapSlice, "UID")
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to create ParseUIDMap UID=%s", UIDMapSlice)
|
||||||
|
}
|
||||||
|
parsedGIDMap, err := idtools.ParseIDMap(GIDMapSlice, "GID")
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to create ParseGIDMap GID=%s", UIDMapSlice)
|
||||||
|
}
|
||||||
|
options.UIDMap = append(options.UIDMap, parsedUIDMap...)
|
||||||
|
options.GIDMap = append(options.GIDMap, parsedGIDMap...)
|
||||||
|
if len(options.UIDMap) > 0 {
|
||||||
|
options.HostUIDMapping = false
|
||||||
|
}
|
||||||
|
if len(options.GIDMap) > 0 {
|
||||||
|
options.HostGIDMapping = false
|
||||||
|
}
|
||||||
|
return &options, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRootlessRuntimeDir returns the runtime directory when running as non root
|
||||||
|
func GetRootlessRuntimeDir(rootlessUid int) (string, error) {
|
||||||
|
runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
|
||||||
|
if runtimeDir == "" {
|
||||||
|
tmpDir := fmt.Sprintf("/run/user/%d", rootlessUid)
|
||||||
|
st, err := system.Stat(tmpDir)
|
||||||
|
if err == nil && int(st.UID()) == os.Getuid() && st.Mode() == 0700 {
|
||||||
|
return tmpDir, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpDir := fmt.Sprintf("%s/%d", os.TempDir(), rootlessUid)
|
||||||
|
if err := os.MkdirAll(tmpDir, 0700); err != nil {
|
||||||
|
logrus.Errorf("failed to create %s: %v", tmpDir, err)
|
||||||
|
} else {
|
||||||
|
return tmpDir, nil
|
||||||
|
}
|
||||||
|
home, err := homeDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "neither XDG_RUNTIME_DIR nor HOME was set non-empty")
|
||||||
|
}
|
||||||
|
resolvedHome, err := filepath.EvalSymlinks(home)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "cannot resolve %s", home)
|
||||||
|
}
|
||||||
|
return filepath.Join(resolvedHome, "rundir"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRootlessDirInfo returns the parent path of where the storage for containers and
|
||||||
|
// volumes will be in rootless mode
|
||||||
|
func getRootlessDirInfo(rootlessUid int) (string, string, error) {
|
||||||
|
rootlessRuntime, err := GetRootlessRuntimeDir(rootlessUid)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataDir := os.Getenv("XDG_DATA_HOME")
|
||||||
|
if dataDir == "" {
|
||||||
|
home, err := homeDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", "", errors.Wrapf(err, "neither XDG_DATA_HOME nor HOME was set non-empty")
|
||||||
|
}
|
||||||
|
// runc doesn't like symlinks in the rootfs path, and at least
|
||||||
|
// on CoreOS /home is a symlink to /var/home, so resolve any symlink.
|
||||||
|
resolvedHome, err := filepath.EvalSymlinks(home)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", errors.Wrapf(err, "cannot resolve %s", home)
|
||||||
|
}
|
||||||
|
dataDir = filepath.Join(resolvedHome, ".local", "share")
|
||||||
|
}
|
||||||
|
return dataDir, rootlessRuntime, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRootlessStorageOpts returns the storage opts for containers running as non root
|
||||||
|
func getRootlessStorageOpts(rootlessUid int) (StoreOptions, error) {
|
||||||
|
var opts StoreOptions
|
||||||
|
|
||||||
|
dataDir, rootlessRuntime, err := getRootlessDirInfo(rootlessUid)
|
||||||
|
if err != nil {
|
||||||
|
return opts, err
|
||||||
|
}
|
||||||
|
opts.RunRoot = rootlessRuntime
|
||||||
|
opts.GraphRoot = filepath.Join(dataDir, "containers", "storage")
|
||||||
|
if path, err := exec.LookPath("fuse-overlayfs"); err == nil {
|
||||||
|
opts.GraphDriverName = "overlay"
|
||||||
|
opts.GraphDriverOptions = []string{fmt.Sprintf("overlay.mount_program=%s", path)}
|
||||||
|
} else {
|
||||||
|
opts.GraphDriverName = "vfs"
|
||||||
|
}
|
||||||
|
return opts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tomlOptionsConfig struct {
|
||||||
|
MountProgram string `toml:"mount_program"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTomlStorage(storeOptions *StoreOptions) *tomlConfig {
|
||||||
|
config := new(tomlConfig)
|
||||||
|
|
||||||
|
config.Storage.Driver = storeOptions.GraphDriverName
|
||||||
|
config.Storage.RunRoot = storeOptions.RunRoot
|
||||||
|
config.Storage.GraphRoot = storeOptions.GraphRoot
|
||||||
|
for _, i := range storeOptions.GraphDriverOptions {
|
||||||
|
s := strings.Split(i, "=")
|
||||||
|
if s[0] == "overlay.mount_program" {
|
||||||
|
config.Storage.Options.MountProgram = s[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultStoreOptions returns the default storage ops for containers
|
||||||
|
func DefaultStoreOptions(rootless bool, rootlessUid int) (StoreOptions, error) {
|
||||||
|
var (
|
||||||
|
defaultRootlessRunRoot string
|
||||||
|
defaultRootlessGraphRoot string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
storageOpts := defaultStoreOptions
|
||||||
|
if rootless {
|
||||||
|
storageOpts, err = getRootlessStorageOpts(rootlessUid)
|
||||||
|
if err != nil {
|
||||||
|
return storageOpts, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storageConf, err := DefaultConfigFile(rootless)
|
||||||
|
if err != nil {
|
||||||
|
return storageOpts, err
|
||||||
|
}
|
||||||
|
if _, err = os.Stat(storageConf); err == nil {
|
||||||
|
defaultRootlessRunRoot = storageOpts.RunRoot
|
||||||
|
defaultRootlessGraphRoot = storageOpts.GraphRoot
|
||||||
|
storageOpts = StoreOptions{}
|
||||||
|
ReloadConfigurationFile(storageConf, &storageOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return storageOpts, errors.Wrapf(err, "cannot stat %s", storageConf)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rootless {
|
||||||
|
if err == nil {
|
||||||
|
// If the file did not specify a graphroot or runroot,
|
||||||
|
// set sane defaults so we don't try and use root-owned
|
||||||
|
// directories
|
||||||
|
if storageOpts.RunRoot == "" {
|
||||||
|
storageOpts.RunRoot = defaultRootlessRunRoot
|
||||||
|
}
|
||||||
|
if storageOpts.GraphRoot == "" {
|
||||||
|
storageOpts.GraphRoot = defaultRootlessGraphRoot
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := os.MkdirAll(filepath.Dir(storageConf), 0755); err != nil {
|
||||||
|
return storageOpts, errors.Wrapf(err, "cannot make directory %s", filepath.Dir(storageConf))
|
||||||
|
}
|
||||||
|
file, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return storageOpts, errors.Wrapf(err, "cannot open %s", storageConf)
|
||||||
|
}
|
||||||
|
|
||||||
|
tomlConfiguration := getTomlStorage(&storageOpts)
|
||||||
|
defer file.Close()
|
||||||
|
enc := toml.NewEncoder(file)
|
||||||
|
if err := enc.Encode(tomlConfiguration); err != nil {
|
||||||
|
os.Remove(storageConf)
|
||||||
|
|
||||||
|
return storageOpts, errors.Wrapf(err, "failed to encode %s", storageConf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return storageOpts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func homeDir() (string, error) {
|
||||||
|
home := os.Getenv("HOME")
|
||||||
|
if home == "" {
|
||||||
|
usr, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "neither XDG_RUNTIME_DIR nor HOME was set non-empty")
|
||||||
|
}
|
||||||
|
home = usr.HomeDir
|
||||||
|
}
|
||||||
|
return home, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user