imagetools: support oci-layout refs

Add oci-layout:// source and target support to imagetools create and
inspect while keeping merge, filter, and referrer logic shared.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2026-03-12 22:45:01 -07:00
parent 71edf28d8e
commit 9894189361
13 changed files with 869 additions and 192 deletions
+2 -3
View File
@@ -805,11 +805,10 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
itpull := imagetools.New(imageopt)
ref, err := reference.ParseNormalizedNamed(names[0])
ref, err := imagetools.ParseLocation(names[0])
if err != nil {
return err
}
ref = reference.TagNameOnly(ref)
srcs := make([]*imagetools.Source, len(descs))
for i, desc := range descs {
@@ -832,7 +831,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
itpush := imagetools.New(imageopt)
for _, n := range names {
nn, err := reference.ParseNormalizedNamed(n)
nn, err := imagetools.ParseLocation(n)
if err != nil {
return err
}
+6 -33
View File
@@ -27,6 +27,7 @@ import (
"github.com/docker/buildx/util/buildflags"
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/dockerutil"
"github.com/docker/buildx/util/ocilayout"
"github.com/docker/buildx/util/osutil"
"github.com/docker/buildx/util/progress"
"github.com/docker/buildx/util/sourcemeta"
@@ -913,7 +914,11 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro
// handle OCI layout
if localPath, ok := strings.CutPrefix(v.Path, "oci-layout://"); ok {
localPath, dig, tag := parseOCILayoutPath(localPath)
ref, _, err := ocilayout.Parse("oci-layout://" + localPath)
if err != nil {
return nil, err
}
localPath, dig, tag := ref.Path, ref.Digest.String(), ref.Tag
if dig == "" {
dig, err = resolveDigest(localPath, tag)
if err != nil {
@@ -1402,38 +1407,6 @@ func isActive(ce *client.CacheOptionsEntry) bool {
return ce.Attrs["token"] != "" && (ce.Attrs["url"] != "" || ce.Attrs["url_v2"] != "")
}
// parseOCILayoutPath handles the oci-layout url accepted by buildx.
func parseOCILayoutPath(s string) (localPath, dgst, tag string) {
localPath = s
// Look for the digest reference. There might be multiple @ symbols
// in the path and the @ symbol may be part of the path or part of
// the digest. If we find the @ symbol, verify that it's a valid
// digest reference instead of just assuming it is because it
// might be part of the file path.
if i := strings.LastIndex(localPath, "@"); i >= 0 {
after := localPath[i+1:]
if reference.DigestRegexp.MatchString(after) {
localPath, dgst = localPath[:i], after
}
}
// Do the same with the tag. This isn't as necessary since colons
// aren't valid as file paths on Linux/Unix systems, but they are valid
// on Windows systems so we might as well just be safe.
if i := strings.LastIndex(localPath, ":"); i >= 0 {
after := localPath[i+1:]
if reference.TagRegexp.MatchString(after) {
localPath, tag = localPath[:i], after
}
}
if tag == "" {
tag = "latest"
}
return
}
func defaultPlatform(bopts gateway.BuildOpts) *ocispecs.Platform {
pl := bopts.Workers[0].Platforms
if len(pl) == 0 {
-42
View File
@@ -40,48 +40,6 @@ func TestCacheOptions_DerivedVars(t *testing.T) {
}, CreateCaches(cacheFrom))
}
func TestParseOCILayoutPath(t *testing.T) {
for _, tt := range []struct {
s string
path string
dgst string
tag string
}{
{
s: "/path/to/oci/layout",
path: "/path/to/oci/layout",
tag: "latest",
},
{
s: "/path/to/oci/layout:1.3",
path: "/path/to/oci/layout",
tag: "1.3",
},
{
s: "/path/to/oci/layout@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
path: "/path/to/oci/layout",
dgst: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
tag: "latest",
},
{
s: "/path/to/oci/@/layout@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
path: "/path/to/oci/@/layout",
dgst: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
tag: "latest",
},
{
s: "/path/to/oci/@/layout",
path: "/path/to/oci/@/layout",
tag: "latest",
},
} {
path, dgst, tag := parseOCILayoutPath(tt.s)
assert.Equal(t, tt.path, path, "comparing path: %s", tt.s)
assert.Equal(t, tt.dgst, dgst, "comparing digest: %s", tt.s)
assert.Equal(t, tt.tag, tag, "comparing tag: %s", tt.s)
}
}
func TestCreateExports_RegistryUnpack(t *testing.T) {
tests := []struct {
name string