Skip to content

Commit

Permalink
image:tag@digest format support
Browse files Browse the repository at this point in the history
  • Loading branch information
crazy-max committed Sep 16, 2023
1 parent 54a61c4 commit 2cce4e0
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 20 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ jobs:
loglevel: info
- folder: dockerfile1
loglevel: debug
- folder: dockerfile2
loglevel: debug
steps:
-
name: Checkout
Expand Down
12 changes: 12 additions & 0 deletions pkg/registry/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ func TestParseImage(t *testing.T) {
Tag: "latest",
},
},
{
desc: "gcr busybox tag/digest",
parseOpts: ParseImageOptions{
Name: "gcr.io/google-containers/busybox:latest" + sha256digest,
},
expected: Image{
Domain: "gcr.io",
Path: "google-containers/busybox",
Tag: "latest",
Digest: sha256digest,
},
},
{
desc: "github ddns-route53",
parseOpts: ParseImageOptions{
Expand Down
2 changes: 1 addition & 1 deletion pkg/registry/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (c *Client) Manifest(image Image, dbManifest Manifest) (Manifest, bool, err
ctx, cancel := c.timeoutContext()
defer cancel()

rmRef, err := ParseReference(image.String())
rmRef, err := ImageReference(image.String())
if err != nil {
return Manifest{}, false, errors.Wrap(err, "cannot parse reference")
}
Expand Down
176 changes: 176 additions & 0 deletions pkg/registry/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,3 +284,179 @@ func TestManifestVariant(t *testing.T) {
assert.Equal(t, "linux/arm/v7", manifest.Platform)
assert.Empty(t, manifest.DockerVersion)
}

func TestManifestTaggedDigest(t *testing.T) {
rc, err := New(Options{
CompareDigest: true,
ImageOs: "linux",
ImageArch: "amd64",
})
if err != nil {
t.Error(err)
}

img, err := ParseImage(ParseImageOptions{
Name: "crazymax/diun:latest@sha256:3fca3dd86c2710586208b0f92d1ec4ce25382f4cad4ae76a2275db8e8bb24031",
})
if err != nil {
t.Error(err)
}

manifest, updated, err := rc.Manifest(img, Manifest{
Name: "docker.io/crazymax/diun",
Tag: "latest",
MIMEType: "application/vnd.oci.image.index.v1+json",
Digest: "sha256:3fca3dd86c2710586208b0f92d1ec4ce25382f4cad4ae76a2275db8e8bb24031",
Platform: "linux/amd64",
Raw: []byte(`{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:3fca3dd86c2710586208b0f92d1ec4ce25382f4cad4ae76a2275db8e8bb24031",
"size": 4661,
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:bf782d6b2030c2a4c6884abb603ec5c99b5394554f57d56972cea24fb5d545d5",
"size": 866,
"platform": {
"architecture": "386",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:f44444abd33ee7c088d7527af84e3321f08313d12d9c679327bb8ae228e35f6a",
"size": 866,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:df77b6ef88fbdb6175a2c60a9487a235aa1bdb39f60ee0a277d480d3cbc9f34a",
"size": 866,
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v6"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:73e210387511588b38d16046de4ade809404b746cf6d16cd51ca23a96c8264b7",
"size": 866,
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:1e070a6b2a3b5bf7c2c296fba6b01c8896514ae62aae6e48f4c28a775e5218dd",
"size": 866,
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:b7f984a85faf86839928fef6854f21da7afd2f2405b6043bf2aca562f1e1aa77",
"size": 866,
"platform": {
"architecture": "ppc64le",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:baa9a5e6de3f155526071eb0e55dcf14c12dca5c4301475e038df88fa5cb7c5a",
"size": 568,
"annotations": {
"vnd.docker.reference.digest": "sha256:bf782d6b2030c2a4c6884abb603ec5c99b5394554f57d56972cea24fb5d545d5",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:422bcf3cad62b4d8b21593387759889bcef02c28d7b0a3f6866b98b6502e8f01",
"size": 568,
"annotations": {
"vnd.docker.reference.digest": "sha256:f44444abd33ee7c088d7527af84e3321f08313d12d9c679327bb8ae228e35f6a",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:8ca5e335824bf17c10143c88f0e6955b5571dd69e06cd1a0ba46681169aa355d",
"size": 568,
"annotations": {
"vnd.docker.reference.digest": "sha256:df77b6ef88fbdb6175a2c60a9487a235aa1bdb39f60ee0a277d480d3cbc9f34a",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:01fdd0609476fe4da74af6bcb5a4fff97b0f9efbbea6b6ab142371ecc0738ffd",
"size": 568,
"annotations": {
"vnd.docker.reference.digest": "sha256:73e210387511588b38d16046de4ade809404b746cf6d16cd51ca23a96c8264b7",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:93178a24195f522195951a2cf16719bbae5358686b3789339c1096a85375117c",
"size": 568,
"annotations": {
"vnd.docker.reference.digest": "sha256:1e070a6b2a3b5bf7c2c296fba6b01c8896514ae62aae6e48f4c28a775e5218dd",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:1f5e5456e6f236c03684fea8070ca4095092a1d07a186acb03b15d160d100043",
"size": 568,
"annotations": {
"vnd.docker.reference.digest": "sha256:b7f984a85faf86839928fef6854f21da7afd2f2405b6043bf2aca562f1e1aa77",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
}
]
}`),
})

assert.NoError(t, err)
assert.Equal(t, false, updated)
assert.Equal(t, "docker.io/crazymax/diun", manifest.Name)
assert.Equal(t, "latest", manifest.Tag)
assert.Equal(t, "application/vnd.oci.image.index.v1+json", manifest.MIMEType)
assert.Equal(t, "sha256:3fca3dd86c2710586208b0f92d1ec4ce25382f4cad4ae76a2275db8e8bb24031", manifest.Digest.String())
assert.Equal(t, "linux/amd64", manifest.Platform)
}
63 changes: 59 additions & 4 deletions pkg/registry/ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,66 @@ import (

"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/types"
"github.com/docker/distribution/reference"
"github.com/pkg/errors"
)

func ParseReference(imageStr string) (types.ImageReference, error) {
if !strings.HasPrefix(imageStr, "//") {
imageStr = fmt.Sprintf("//%s", imageStr)
func ImageReference(name string) (types.ImageReference, error) {
ref, err := namedReference(name)
if err != nil {
return nil, errors.Wrap(err, "cannot parse reference")
}
return docker.ParseReference(imageStr)
refStr := ref.String()
if !strings.HasPrefix(refStr, "//") {
refStr = fmt.Sprintf("//%s", refStr)
}
return docker.ParseReference(refStr)
}

func namedReference(name string) (reference.Named, error) {
name = strings.TrimPrefix(name, "//")

ref, err := reference.ParseNormalizedNamed(name)
if err != nil {
return nil, errors.Wrapf(err, "parsing normalized named %q", name)
}

if _, ok := ref.(reference.Named); !ok {
return nil, fmt.Errorf("%q is not a named reference", name)
}

if _, hasTag := ref.(reference.NamedTagged); hasTag {
ref, err = normalizeTaggedDigestedNamed(ref)
if err != nil {
return nil, errors.Wrapf(err, "normalizing tagged digested name %q", name)
}
return ref, nil
}
if _, hasDigest := ref.(reference.Digested); hasDigest {
return ref, nil
}

return reference.TagNameOnly(ref), nil
}

// normalizeTaggedDigestedNamed strips the tag off the specified named
// reference if it is tagged and digested. Note that the tag is entirely
// ignored.
func normalizeTaggedDigestedNamed(named reference.Named) (reference.Named, error) {
_, isTagged := named.(reference.NamedTagged)
if !isTagged {
return named, nil
}
digested, isDigested := named.(reference.Digested)
if !isDigested {
return named, nil
}
// strip off the tag
newNamed := reference.TrimNamed(named)
// re-add the digest
newNamed, err := reference.WithDigest(newNamed, digested.Digest())
if err != nil {
return named, err
}
return newNamed, nil
}
27 changes: 13 additions & 14 deletions pkg/registry/ref_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const (
sha256digest = "@sha256:" + sha256digestHex
)

func TestParseReference(t *testing.T) {
func TestImageReference(t *testing.T) {
testCases := []struct {
input string
expected string
Expand All @@ -23,28 +23,27 @@ func TestParseReference(t *testing.T) {
expected: "docker.io/library/busybox:latest",
},
{
input: "//busybox:notlatest",
expected: "docker.io/library/busybox:notlatest",
input: "docker.io/library/busybox",
expected: "docker.io/library/busybox:latest",
},
{
input: "//busybox" + sha256digest,
expected: "docker.io/library/busybox" + sha256digest,
input: "docker.io/library/busybox:latest",
expected: "docker.io/library/busybox:latest",
},
{
input: "//busybox",
expected: "docker.io/library/busybox:latest",
input: "busybox:notlatest",
expected: "docker.io/library/busybox:notlatest",
},
{
input: "//busybox:latest" + sha256digest,
expected: "",
wantErr: true,
input: "busybox" + sha256digest,
expected: "docker.io/library/busybox" + sha256digest,
},
{
input: "//docker.io/library/busybox:latest",
expected: "docker.io/library/busybox:latest",
input: "busybox:latest" + sha256digest,
expected: "docker.io/library/busybox" + sha256digest,
},
{
input: "//UPPERCASEISINVALID",
input: "UPPERCASEISINVALID",
expected: "",
wantErr: true,
},
Expand All @@ -53,7 +52,7 @@ func TestParseReference(t *testing.T) {
for _, tt := range testCases {
tt := tt
t.Run(tt.input, func(t *testing.T) {
ref, err := ParseReference(tt.input)
ref, err := ImageReference(tt.input)
if tt.wantErr {
require.Error(t, err)
return
Expand Down
2 changes: 1 addition & 1 deletion pkg/registry/tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (c *Client) Tags(opts TagsOptions) (*Tags, error) {
ctx, cancel := c.timeoutContext()
defer cancel()

imgRef, err := ParseReference(opts.Image.String())
imgRef, err := ImageReference(opts.Image.String())
if err != nil {
return nil, errors.Wrap(err, "cannot parse reference")
}
Expand Down
21 changes: 21 additions & 0 deletions pkg/registry/tags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,27 @@ func TestTags(t *testing.T) {
assert.True(t, len(tags.List) > 0)
}

func TestTagsWithDigest(t *testing.T) {
assert.NotNil(t, rc)

image, err := ParseImage(ParseImageOptions{
Name: "crazymax/diun:latest@sha256:3fca3dd86c2710586208b0f92d1ec4ce25382f4cad4ae76a2275db8e8bb24031",
})
if err != nil {
t.Error(err)
}

tags, err := rc.Tags(TagsOptions{
Image: image,
})
if err != nil {
t.Error(err)
}

assert.True(t, tags.Total > 0)
assert.True(t, len(tags.List) > 0)
}

func TestTagsSort(t *testing.T) {
testCases := []struct {
name string
Expand Down
15 changes: 15 additions & 0 deletions test/dockerfile2/diun.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
watch:
workers: 20
schedule: "0 */6 * * *"
firstCheckNotif: true

notif:
script:
cmd: "sh"
args:
- "/mount/notif.sh"

providers:
dockerfile:
patterns:
- /mount/Dockerfile

0 comments on commit 2cce4e0

Please sign in to comment.