mirror of
https://github.com/moby/buildkit.git
synced 2026-06-30 19:57:39 +00:00
source: add support for oci-layout+blob schema
Matching the docker-image+blob implementation. Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
@@ -58,6 +58,7 @@ import (
|
||||
"github.com/moby/buildkit/session/secrets/secretsprovider"
|
||||
"github.com/moby/buildkit/session/sshforward/sshprovider"
|
||||
"github.com/moby/buildkit/solver/errdefs"
|
||||
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/solver/result"
|
||||
"github.com/moby/buildkit/sourcepolicy"
|
||||
@@ -237,6 +238,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){
|
||||
testRegistryEmptyCacheExport,
|
||||
testSnapshotWithMultipleBlobs,
|
||||
testImageBlobSource,
|
||||
testOCILayoutBlobSource,
|
||||
testExportLocalNoPlatformSplit,
|
||||
testExportLocalNoPlatformSplitOverwrite,
|
||||
testExportLocalForcePlatformSplit,
|
||||
@@ -758,6 +760,9 @@ func testExportBusyboxLocal(t *testing.T, sb integration.Sandbox) {
|
||||
destDir := t.TempDir()
|
||||
|
||||
_, err = c.Solve(sb.Context(), def, SolveOpt{
|
||||
FrontendAttrs: map[string]string{
|
||||
"attest:provenance": "",
|
||||
},
|
||||
Exports: []ExportEntry{
|
||||
{
|
||||
Type: ExporterLocal,
|
||||
@@ -11597,12 +11602,8 @@ func testImageBlobSource(t *testing.T, sb integration.Sandbox) {
|
||||
require.NoError(t, err)
|
||||
|
||||
var stmt struct {
|
||||
Predicate struct {
|
||||
Materials []struct {
|
||||
URI string `json:"uri"`
|
||||
Digest map[string]string `json:"digest"`
|
||||
} `json:"materials"`
|
||||
} `json:"predicate"`
|
||||
intoto.StatementHeader
|
||||
Predicate provenancetypes.ProvenancePredicateSLSA02 `json:"predicate"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(provDt, &stmt))
|
||||
|
||||
@@ -11624,6 +11625,109 @@ func testImageBlobSource(t *testing.T, sb integration.Sandbox) {
|
||||
require.True(t, found, "expected to find %q in %+v", expectedName, stmt.Predicate.Materials)
|
||||
}
|
||||
|
||||
func testOCILayoutBlobSource(t *testing.T, sb integration.Sandbox) {
|
||||
workers.CheckFeatureCompat(t, sb, workers.FeatureOCIExporter, workers.FeatureOCILayout)
|
||||
requiresLinux(t)
|
||||
c, err := New(sb.Context(), sb.Address())
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
st := llb.Image("alpine")
|
||||
def, err := st.Marshal(sb.Context())
|
||||
require.NoError(t, err)
|
||||
|
||||
ociDir := t.TempDir()
|
||||
_, err = c.Solve(sb.Context(), def, SolveOpt{
|
||||
Exports: []ExportEntry{
|
||||
{
|
||||
Type: ExporterOCI,
|
||||
Attrs: map[string]string{
|
||||
"tar": "false",
|
||||
},
|
||||
OutputDir: ociDir,
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
indexDt, err := os.ReadFile(filepath.Join(ociDir, ocispecs.ImageIndexFile))
|
||||
require.NoError(t, err)
|
||||
|
||||
var index ocispecs.Index
|
||||
err = json.Unmarshal(indexDt, &index)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(index.Manifests))
|
||||
|
||||
var mfst ocispecs.Manifest
|
||||
mfstDt, err := os.ReadFile(filepath.Join(ociDir, "blobs/sha256", index.Manifests[0].Digest.Hex()))
|
||||
require.NoError(t, err)
|
||||
err = json.Unmarshal(mfstDt, &mfst)
|
||||
require.NoError(t, err)
|
||||
require.GreaterOrEqual(t, len(mfst.Layers), 1)
|
||||
layer := mfst.Layers[0]
|
||||
|
||||
store, err := local.NewStore(ociDir)
|
||||
require.NoError(t, err)
|
||||
csID := "my-blob-content-store"
|
||||
|
||||
blob := llb.OCILayoutBlob("not/real@"+layer.Digest.String(), llb.ImageBlobOCIStore("", csID), llb.Filename("layer.tar.gz"), llb.Chown(123, 456))
|
||||
st = llb.Image("alpine").Run(llb.Shlex(`sh -c 'sha256sum /layers/layer.tar.gz | cut -d" " -f0 > /out/checksum && stat -c "%u-%g-%s" /layers/layer.tar.gz > /out/stat'`), llb.AddMount("/layers", blob, llb.Readonly)).AddMount("/out", llb.Scratch())
|
||||
|
||||
def, err = st.Marshal(sb.Context())
|
||||
require.NoError(t, err)
|
||||
|
||||
destDir := t.TempDir()
|
||||
_, err = c.Solve(sb.Context(), def, SolveOpt{
|
||||
FrontendAttrs: map[string]string{
|
||||
"attest:provenance": "",
|
||||
},
|
||||
Exports: []ExportEntry{
|
||||
{
|
||||
Type: ExporterLocal,
|
||||
OutputDir: destDir,
|
||||
},
|
||||
},
|
||||
OCIStores: map[string]content.Store{
|
||||
csID: store,
|
||||
},
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err := os.ReadFile(filepath.Join(destDir, "stat"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "123-456-"+strconv.FormatInt(layer.Size, 10), strings.TrimSpace(string(dt)))
|
||||
|
||||
dt, err = os.ReadFile(filepath.Join(destDir, "checksum"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, layer.Digest.Hex(), strings.TrimSpace(string(dt)))
|
||||
|
||||
provDt, err := os.ReadFile(filepath.Join(destDir, "provenance.json"))
|
||||
require.NoError(t, err)
|
||||
|
||||
var stmt struct {
|
||||
intoto.StatementHeader
|
||||
Predicate provenancetypes.ProvenancePredicateSLSA02 `json:"predicate"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(provDt, &stmt))
|
||||
|
||||
expectedName, err := purl.RefToPURL(packageurl.TypeOCI, "not/real@"+layer.Digest.String(), nil)
|
||||
require.NoError(t, err)
|
||||
purlObj, err := packageurl.FromString(expectedName)
|
||||
require.NoError(t, err)
|
||||
purlObj.Qualifiers = append(purlObj.Qualifiers, packageurl.Qualifier{Key: "ref_type", Value: "blob"})
|
||||
expectedName = purlObj.ToString()
|
||||
|
||||
found := false
|
||||
for _, m := range stmt.Predicate.Materials {
|
||||
if m.URI == expectedName {
|
||||
found = true
|
||||
require.Equal(t, layer.Digest.Hex(), m.Digest["sha256"])
|
||||
break
|
||||
}
|
||||
}
|
||||
require.True(t, found, "expected to find %q in %+v", expectedName, stmt.Predicate.Materials)
|
||||
}
|
||||
|
||||
func testFrontendVerifyPlatforms(t *testing.T, sb integration.Sandbox) {
|
||||
c, err := New(sb.Context(), sb.Address())
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -100,6 +100,8 @@ func (s *SourceOp) Inputs() []Output {
|
||||
type ImageBlobInfo struct {
|
||||
constraintsWrapper
|
||||
fileinfoWrapper
|
||||
sessionID string
|
||||
storeID string
|
||||
}
|
||||
|
||||
type ImageBlobOption interface {
|
||||
@@ -158,6 +160,60 @@ func ImageBlob(ref string, opts ...ImageBlobOption) State {
|
||||
return NewState(source.Output())
|
||||
}
|
||||
|
||||
// OCILayoutBlob returns a state that represents a single digest-addressed blob from an OCI layout store.
|
||||
func OCILayoutBlob(ref string, opts ...ImageBlobOption) State {
|
||||
bi := &ImageBlobInfo{}
|
||||
for _, o := range opts {
|
||||
o.SetImageBlobOption(bi)
|
||||
}
|
||||
attrs := map[string]string{}
|
||||
|
||||
if bi.Filename != "" {
|
||||
attrs[pb.AttrHTTPFilename] = bi.Filename
|
||||
}
|
||||
if bi.Perm != 0 {
|
||||
attrs[pb.AttrHTTPPerm] = "0" + strconv.FormatInt(int64(bi.Perm), 8)
|
||||
}
|
||||
if bi.UID != 0 {
|
||||
attrs[pb.AttrHTTPUID] = strconv.Itoa(bi.UID)
|
||||
}
|
||||
if bi.GID != 0 {
|
||||
attrs[pb.AttrHTTPGID] = strconv.Itoa(bi.GID)
|
||||
}
|
||||
if bi.sessionID != "" {
|
||||
attrs[pb.AttrOCILayoutSessionID] = bi.sessionID
|
||||
}
|
||||
if bi.storeID != "" {
|
||||
attrs[pb.AttrOCILayoutStoreID] = bi.storeID
|
||||
}
|
||||
|
||||
addCap(&bi.Constraints, pb.CapSourceImageBlob)
|
||||
|
||||
var digested reference.Digested
|
||||
|
||||
r, err := reference.ParseNormalizedNamed(ref)
|
||||
if err == nil {
|
||||
if _, tagged := r.(reference.Tagged); tagged {
|
||||
err = errors.Errorf("tagged image reference not allowed for blob reference")
|
||||
} else if ref, ok := r.(reference.Digested); !ok {
|
||||
err = errors.Errorf("checksum required in blob reference")
|
||||
} else {
|
||||
digested = ref
|
||||
}
|
||||
}
|
||||
|
||||
repoName := "invalid"
|
||||
if digested != nil {
|
||||
repoName = digested.String()
|
||||
}
|
||||
|
||||
source := NewSource("oci-layout+blob://"+repoName, attrs, bi.Constraints)
|
||||
if err != nil {
|
||||
source.err = err
|
||||
}
|
||||
return NewState(source.Output())
|
||||
}
|
||||
|
||||
// Image returns a state that represents a docker image in a registry.
|
||||
// Example:
|
||||
//
|
||||
@@ -259,6 +315,12 @@ func (fn imageOptionFunc) SetImageOption(ii *ImageInfo) {
|
||||
fn(ii)
|
||||
}
|
||||
|
||||
type imageBlobOptionFunc func(*ImageBlobInfo)
|
||||
|
||||
func (fn imageBlobOptionFunc) SetImageBlobOption(ib *ImageBlobInfo) {
|
||||
fn(ib)
|
||||
}
|
||||
|
||||
var MarkImageInternal = imageOptionFunc(func(ii *ImageInfo) {
|
||||
ii.RecordType = "internal"
|
||||
})
|
||||
@@ -685,6 +747,14 @@ func OCIStore(sessionID string, storeID string) OCILayoutOption {
|
||||
})
|
||||
}
|
||||
|
||||
// ImageBlobOCIStore returns an [ImageBlobOption] that configures the OCI layout session/store used by [OCILayoutBlob].
|
||||
func ImageBlobOCIStore(sessionID string, storeID string) ImageBlobOption {
|
||||
return imageBlobOptionFunc(func(ib *ImageBlobInfo) {
|
||||
ib.sessionID = sessionID
|
||||
ib.storeID = storeID
|
||||
})
|
||||
}
|
||||
|
||||
func OCILayerLimit(limit int) OCILayoutOption {
|
||||
return ociLayoutOptionFunc(func(oi *OCILayoutInfo) {
|
||||
oi.layerLimit = &limit
|
||||
|
||||
@@ -104,6 +104,35 @@ func TestImageBlobSource(t *testing.T) {
|
||||
require.Equal(t, "docker-image+blob://docker.io/myuser/myrepo@"+string(blobDgst), src.Source.Identifier)
|
||||
}
|
||||
|
||||
func TestOCILayoutBlobSource(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.TODO()
|
||||
|
||||
blobDgst := digest.FromBytes([]byte("foo"))
|
||||
|
||||
s := OCILayoutBlob("myrepo/blob@"+string(blobDgst), ImageBlobOCIStore("sid", "store0"))
|
||||
def, err := s.Marshal(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
m, arr := parseDef(t, def.Def)
|
||||
_ = m
|
||||
require.Equal(t, 2, len(arr))
|
||||
|
||||
dgst, idx := last(t, arr)
|
||||
require.Equal(t, 0, idx)
|
||||
|
||||
vtx, ok := m[dgst]
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
src, ok := vtx.Op.(*pb.Op_Source)
|
||||
require.Equal(t, true, ok)
|
||||
require.Nil(t, vtx.Platform)
|
||||
|
||||
require.Equal(t, "oci-layout+blob://docker.io/myrepo/blob@"+string(blobDgst), src.Source.Identifier)
|
||||
require.Equal(t, "sid", src.Source.Attrs[pb.AttrOCILayoutSessionID])
|
||||
require.Equal(t, "store0", src.Source.Attrs[pb.AttrOCILayoutStoreID])
|
||||
}
|
||||
|
||||
func TestStateSourceMapMarshal(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ func (c *Capture) AddImage(i provenancetypes.ImageSource) {
|
||||
|
||||
func (c *Capture) AddImageBlob(i provenancetypes.ImageBlobSource) {
|
||||
for _, v := range c.Sources.ImageBlobs {
|
||||
if v.Ref == i.Ref {
|
||||
if v.Ref == i.Ref && v.Local == i.Local {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,11 @@ func slsaMaterials(srcs provenancetypes.Sources) ([]slsa.ProvenanceMaterial, err
|
||||
}
|
||||
|
||||
for _, s := range srcs.ImageBlobs {
|
||||
uri, err := purl.RefToPURL(packageurl.TypeDocker, s.Ref, nil)
|
||||
purlType := packageurl.TypeDocker
|
||||
if s.Local {
|
||||
purlType = packageurl.TypeOCI
|
||||
}
|
||||
uri, err := purl.RefToPURL(purlType, s.Ref, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -37,3 +37,28 @@ func TestSLSAMaterialsImageBlobPURL(t *testing.T) {
|
||||
|
||||
require.Equal(t, dgst.Hex(), ms[0].Digest[dgst.Algorithm().String()])
|
||||
}
|
||||
|
||||
func TestSLSAMaterialsOCILayoutBlobPURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dgst := digest.FromString("blobdata")
|
||||
ms, err := slsaMaterials(provenancetypes.Sources{
|
||||
ImageBlobs: []provenancetypes.ImageBlobSource{
|
||||
{
|
||||
Ref: "example.com/ns/repo@" + dgst.String(),
|
||||
Digest: dgst,
|
||||
Local: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, ms, 1)
|
||||
|
||||
p, err := packageurl.FromString(ms[0].URI)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, packageurl.TypeOCI, p.Type)
|
||||
|
||||
q := p.Qualifiers.Map()
|
||||
require.Equal(t, "blob", q["ref_type"])
|
||||
require.Equal(t, dgst.String(), q["digest"])
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ type ImageSource struct {
|
||||
type ImageBlobSource struct {
|
||||
Ref string
|
||||
Digest digest.Digest
|
||||
Local bool
|
||||
}
|
||||
|
||||
type GitSource struct {
|
||||
|
||||
@@ -15,6 +15,9 @@ import (
|
||||
|
||||
type ImageBlobIdentifier struct {
|
||||
Reference reference.Spec
|
||||
SchemeName string
|
||||
SessionID string
|
||||
StoreID string
|
||||
RecordType client.UsageRecordType
|
||||
Filename string
|
||||
Perm int
|
||||
@@ -22,7 +25,7 @@ type ImageBlobIdentifier struct {
|
||||
GID int
|
||||
}
|
||||
|
||||
func NewImageBlobIdentifier(str string) (*ImageBlobIdentifier, error) {
|
||||
func NewImageBlobIdentifier(str string, scheme string) (*ImageBlobIdentifier, error) {
|
||||
ref, err := reference.Parse(str)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
@@ -31,13 +34,19 @@ func NewImageBlobIdentifier(str string) (*ImageBlobIdentifier, error) {
|
||||
if ref.Object == "" {
|
||||
return nil, errors.WithStack(reference.ErrObjectRequired)
|
||||
}
|
||||
return &ImageBlobIdentifier{Reference: ref}, nil
|
||||
return &ImageBlobIdentifier{
|
||||
Reference: ref,
|
||||
SchemeName: scheme,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ source.Identifier = (*ImageBlobIdentifier)(nil)
|
||||
|
||||
func (*ImageBlobIdentifier) Scheme() string {
|
||||
return srctypes.DockerImageBlobScheme
|
||||
func (id *ImageBlobIdentifier) Scheme() string {
|
||||
if id.SchemeName == "" {
|
||||
return srctypes.DockerImageBlobScheme
|
||||
}
|
||||
return id.SchemeName
|
||||
}
|
||||
|
||||
func (id *ImageBlobIdentifier) Capture(c *provenance.Capture, pin string) error {
|
||||
@@ -62,6 +71,7 @@ func (id *ImageBlobIdentifier) Capture(c *provenance.Capture, pin string) error
|
||||
c.AddImageBlob(provenancetypes.ImageBlobSource{
|
||||
Ref: id.Reference.String(),
|
||||
Digest: dgst,
|
||||
Local: id.Scheme() == srctypes.OCIBlobScheme,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,11 +14,15 @@ import (
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
"github.com/moby/buildkit/cache"
|
||||
"github.com/moby/buildkit/session"
|
||||
sessioncontent "github.com/moby/buildkit/session/content"
|
||||
"github.com/moby/buildkit/snapshot"
|
||||
"github.com/moby/buildkit/solver"
|
||||
srctypes "github.com/moby/buildkit/source/types"
|
||||
"github.com/moby/buildkit/util/contentutil"
|
||||
"github.com/moby/buildkit/util/iohelper"
|
||||
"github.com/moby/buildkit/util/resolver"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -64,20 +68,31 @@ func (p *puller) ensureResolver(ctx context.Context, g session.Group) error {
|
||||
return errors.Wrap(err, "invalid reference digest")
|
||||
}
|
||||
|
||||
r := resolver.DefaultPool.GetResolver(p.src.RegistryHosts, p.id.Reference.String(), resolver.ScopeType{}, p.SessionManager, g)
|
||||
f, err := r.Fetcher(ctx, p.id.Reference.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
rc io.ReadCloser
|
||||
err error
|
||||
)
|
||||
if p.id.Scheme() == srctypes.OCIBlobScheme {
|
||||
rc, err = p.fetchFromOCILayoutStore(ctx, g, dgst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
r := resolver.DefaultPool.GetResolver(p.src.RegistryHosts, p.id.Reference.String(), resolver.ScopeType{}, p.SessionManager, g)
|
||||
f, err := r.Fetcher(ctx, p.id.Reference.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fd, ok := f.(remotes.FetcherByDigest)
|
||||
if !ok {
|
||||
return errors.Errorf("invalid blob fetcher: %T", f)
|
||||
}
|
||||
fd, ok := f.(remotes.FetcherByDigest)
|
||||
if !ok {
|
||||
return errors.Errorf("invalid blob fetcher: %T", f)
|
||||
}
|
||||
|
||||
rc, _, err := fd.FetchByDigest(ctx, dgst)
|
||||
if err != nil {
|
||||
return err
|
||||
rc, _, err = fd.FetchByDigest(ctx, dgst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
p.rc = rc
|
||||
@@ -85,6 +100,53 @@ func (p *puller) ensureResolver(ctx context.Context, g session.Group) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *puller) fetchFromOCILayoutStore(ctx context.Context, g session.Group, dgst digest.Digest) (io.ReadCloser, error) {
|
||||
if p.id.StoreID == "" {
|
||||
return nil, errors.Errorf("oci-layout blob source requires store id")
|
||||
}
|
||||
|
||||
var rc io.ReadCloser
|
||||
err := p.withOCICaller(ctx, g, func(ctx context.Context, caller session.Caller) error {
|
||||
store := sessioncontent.NewCallerStore(caller, "oci:"+p.id.StoreID)
|
||||
info, err := store.Info(ctx, dgst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
readerAt, err := store.ReaderAt(ctx, ocispecs.Descriptor{
|
||||
Digest: info.Digest,
|
||||
Size: info.Size,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rc = iohelper.ReadCloser(readerAt)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rc, nil
|
||||
}
|
||||
|
||||
func (p *puller) withOCICaller(ctx context.Context, g session.Group, f func(context.Context, session.Caller) error) error {
|
||||
if p.id.SessionID != "" {
|
||||
timeoutCtx, cancel := context.WithCancelCause(ctx)
|
||||
timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
|
||||
defer func() { cancel(errors.WithStack(context.Canceled)) }()
|
||||
|
||||
caller, err := p.SessionManager.Get(timeoutCtx, p.id.SessionID, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(ctx, caller)
|
||||
}
|
||||
|
||||
return p.SessionManager.Any(ctx, g, func(ctx context.Context, _ string, caller session.Caller) error {
|
||||
return f(ctx, caller)
|
||||
})
|
||||
}
|
||||
|
||||
func (p *puller) CacheKey(ctx context.Context, jobCtx solver.JobContext, index int) (cacheKey string, imgDigest string, cacheOpts solver.CacheOpts, cacheDone bool, err error) {
|
||||
dgst := p.id.Reference.Digest()
|
||||
if err := dgst.Validate(); err != nil {
|
||||
|
||||
@@ -33,11 +33,18 @@ func NewSource(opt SourceOpt) (*Source, error) {
|
||||
}
|
||||
|
||||
func (is *Source) Schemes() []string {
|
||||
return []string{srctypes.DockerImageBlobScheme}
|
||||
return []string{srctypes.DockerImageBlobScheme, srctypes.OCIBlobScheme}
|
||||
}
|
||||
|
||||
func (is *Source) Identifier(scheme, ref string, attrs map[string]string, platform *pb.Platform) (source.Identifier, error) {
|
||||
return is.registryIdentifier(ref, attrs, platform)
|
||||
switch scheme {
|
||||
case srctypes.DockerImageBlobScheme:
|
||||
return is.registryIdentifier(ref, attrs, platform)
|
||||
case srctypes.OCIBlobScheme:
|
||||
return is.ociLayoutIdentifier(ref, attrs, platform)
|
||||
default:
|
||||
return nil, errors.Errorf("invalid image blob scheme %s", scheme)
|
||||
}
|
||||
}
|
||||
|
||||
func (is *Source) Resolve(ctx context.Context, id source.Identifier, sm *session.Manager, vtx solver.Vertex) (source.SourceInstance, error) {
|
||||
@@ -52,12 +59,30 @@ func (is *Source) Resolve(ctx context.Context, id source.Identifier, sm *session
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (is *Source) registryIdentifier(ref string, attrs map[string]string, platform *pb.Platform) (source.Identifier, error) {
|
||||
id, err := NewImageBlobIdentifier(ref)
|
||||
func (is *Source) registryIdentifier(ref string, attrs map[string]string, _ *pb.Platform) (source.Identifier, error) {
|
||||
id, err := NewImageBlobIdentifier(ref, srctypes.DockerImageBlobScheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return is.parseIdentifierAttrs(id, attrs, false)
|
||||
}
|
||||
|
||||
func (is *Source) ociLayoutIdentifier(ref string, attrs map[string]string, _ *pb.Platform) (source.Identifier, error) {
|
||||
id, err := NewImageBlobIdentifier(ref, srctypes.OCIBlobScheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsed, err := is.parseIdentifierAttrs(id, attrs, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if id.StoreID == "" {
|
||||
return nil, errors.Errorf("oci-layout blob source requires store id")
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func (is *Source) parseIdentifierAttrs(id *ImageBlobIdentifier, attrs map[string]string, allowOCIStore bool) (source.Identifier, error) {
|
||||
for k, v := range attrs {
|
||||
switch k {
|
||||
case pb.AttrHTTPFilename:
|
||||
@@ -86,6 +111,14 @@ func (is *Source) registryIdentifier(ref string, attrs map[string]string, platfo
|
||||
return nil, err
|
||||
}
|
||||
id.RecordType = rt
|
||||
case pb.AttrOCILayoutSessionID:
|
||||
if allowOCIStore {
|
||||
id.SessionID = v
|
||||
}
|
||||
case pb.AttrOCILayoutStoreID:
|
||||
if allowOCIStore {
|
||||
id.StoreID = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user