build: refactor reference parsing for image layouts

We allow any valid image reference format for the oci-layout, not just
limiting to name@digest, we additionally allow images of the form
name:tag@digest now.

The name of the reference is used to find the local directory to lookup
the store in, while the tag and digest are attached to a random identity
to generate the dummy reference sent to the oci-layout context.

This separation of the target to replace and the value to replace it
with ensures that any tag or digest set in the client is properly sent
across to the server. The tag is used when a digest was not specified,
and it is resolved in the context of the local directory before being
sent, using the same helpers as we use for the local cache expoter.

Signed-off-by: Justin Chadwell <me@jedevc.com>
pull/1496/head
Justin Chadwell 2 years ago committed by Tonis Tiigi
parent 4735a71fbd
commit 8b960ededd
No known key found for this signature in database
GPG Key ID: AFA9DE5F8AB7AF39

@ -11,6 +11,7 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"strconv"
"strings"
@ -36,8 +37,10 @@ import (
"github.com/docker/docker/pkg/jsonmessage"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/client/ociindex"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
gateway "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/upload/uploadprovider"
"github.com/moby/buildkit/solver/errdefs"
@ -1497,26 +1500,59 @@ func LoadInputs(ctx context.Context, d driver.Driver, inp Inputs, pw progress.Wr
// handle OCI layout
if strings.HasPrefix(v.Path, "oci-layout://") {
pathAlone := strings.TrimPrefix(v.Path, "oci-layout://")
parts := strings.SplitN(pathAlone, "@", 2)
if len(parts) != 2 {
return nil, errors.Errorf("invalid oci-layout context %s, must be oci-layout:///path/to/layout@sha256:hash", v.Path)
localPath := pathAlone
localPath, dig, hasDigest := strings.Cut(localPath, "@")
localPath, tag, hasTag := strings.Cut(localPath, ":")
if !hasDigest {
indexPath := path.Join(localPath, "index.json")
index, err := ociindex.ReadIndexJSONFileLocked(indexPath)
if err != nil {
return nil, errors.Wrapf(err, "failed to read oci-layout index at %s", indexPath)
}
if len(index.Manifests) == 1 {
dig = string(index.Manifests[0].Digest)
hasDigest = true
}
if !hasTag {
tag = "latest"
}
for _, m := range index.Manifests {
if m.Annotations[specs.AnnotationRefName] == tag {
dig = string(m.Digest)
hasDigest = true
break
}
}
}
if !hasDigest {
return nil, errors.Errorf("oci-layout reference %q could not be resolved", v.Path)
}
localPath := parts[0]
dgst, err := digest.Parse(parts[1])
_, err := digest.Parse(dig)
if err != nil {
return nil, errors.Wrapf(err, "invalid oci-layout context %s, does not have proper hash, must be oci-layout:///path/to/layout@sha256:hash", v.Path)
return nil, errors.Wrapf(err, "invalid oci-layout digest %s", dig)
}
store, err := local.NewStore(localPath)
if err != nil {
return nil, errors.Wrapf(err, "invalid store at %s", localPath)
}
// now we can add it
storeName := identity.NewID()
if target.OCIStores == nil {
target.OCIStores = map[string]content.Store{}
}
target.OCIStores[k] = store
target.OCIStores[storeName] = store
layout := "oci-layout://" + storeName
if hasTag {
layout += ":" + tag
}
if hasDigest {
layout += "@" + dig
}
target.FrontendAttrs["context:"+k] = fmt.Sprintf("oci-layout:%s@%s", k, dgst.String())
target.FrontendAttrs["context:"+k] = layout
continue
}
st, err := os.Stat(v.Path)

Loading…
Cancel
Save