Skip to content

Commit 8997f24

Browse files
committed
Introduce --index and --oci options
This let user tune the media type to be used to get manifest, without breaking compatibility. Signed-off-by: Nicolas De Loof <[email protected]>
1 parent e8c40f0 commit 8997f24

File tree

6 files changed

+62
-14
lines changed

6 files changed

+62
-14
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@ $ reg manifest r.j3ss.co/htop
139139
}
140140
```
141141

142+
For multi-architecture docker images, `--index` allow to get the Manifest index (a.k.a Manifest List) with
143+
all supported platforms/architecture listed.
144+
145+
OCI compatibility can be checked using `--oci` option, which will force use of [OCI media types](https://github.com/opencontainers/image-spec/blob/master/media-types.md).
146+
147+
142148
### Get the Digest
143149
```console
144150
$ reg digest r.j3ss.co/htop

digest.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,15 @@ func (cmd *digestCommand) ShortHelp() string { return digestHelp }
1616
func (cmd *digestCommand) LongHelp() string { return digestHelp }
1717
func (cmd *digestCommand) Hidden() bool { return false }
1818

19-
func (cmd *digestCommand) Register(fs *flag.FlagSet) {}
19+
func (cmd *digestCommand) Register(fs *flag.FlagSet) {
20+
fs.BoolVar(&cmd.index, "index", false, "get manifest index (multi-architecture images, docker apps)")
21+
fs.BoolVar(&cmd.oci, "oci", false, "use OCI media type only")
22+
}
2023

21-
type digestCommand struct{}
24+
type digestCommand struct {
25+
index bool
26+
oci bool
27+
}
2228

2329
func (cmd *digestCommand) Run(ctx context.Context, args []string) error {
2430
if len(args) < 1 {
@@ -37,7 +43,7 @@ func (cmd *digestCommand) Run(ctx context.Context, args []string) error {
3743
}
3844

3945
// Get the digest.
40-
digest, err := r.Digest(ctx, image)
46+
digest, err := r.Digest(ctx, image, mediatypes(cmd.index, cmd.oci)...)
4147
if err != nil {
4248
return err
4349
}

manifest.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import (
55
"encoding/json"
66
"flag"
77
"fmt"
8-
8+
"github.com/docker/distribution/manifest/manifestlist"
9+
"github.com/docker/distribution/manifest/schema2"
910
"github.com/genuinetools/reg/registry"
11+
v1 "github.com/opencontainers/image-spec/specs-go/v1"
1012
)
1113

1214
const manifestHelp = `Get the json manifest for a repository.`
@@ -19,10 +21,14 @@ func (cmd *manifestCommand) Hidden() bool { return false }
1921

2022
func (cmd *manifestCommand) Register(fs *flag.FlagSet) {
2123
fs.BoolVar(&cmd.v1, "v1", false, "force the version of the manifest retrieved to v1")
24+
fs.BoolVar(&cmd.index, "index", false, "get manifest index (multi-architecture images, docker apps)")
25+
fs.BoolVar(&cmd.oci, "oci", false, "use OCI media type only")
2226
}
2327

2428
type manifestCommand struct {
25-
v1 bool
29+
v1 bool
30+
index bool
31+
oci bool
2632
}
2733

2834
func (cmd *manifestCommand) Run(ctx context.Context, args []string) error {
@@ -50,7 +56,7 @@ func (cmd *manifestCommand) Run(ctx context.Context, args []string) error {
5056
}
5157
} else {
5258
// Get the v2 manifest.
53-
manifest, err = r.Manifest(ctx, image.Path, image.Reference())
59+
manifest, err = r.Manifest(ctx, image.Path, image.Reference(), mediatypes(cmd.index, cmd.oci)...)
5460
if err != nil {
5561
return err
5662
}
@@ -64,3 +70,20 @@ func (cmd *manifestCommand) Run(ctx context.Context, args []string) error {
6470
fmt.Println(string(b))
6571
return nil
6672
}
73+
74+
func mediatypes(index, oci bool) []string {
75+
mediatypes := []string{}
76+
if oci {
77+
mediatypes = append(mediatypes, v1.MediaTypeImageManifest)
78+
} else {
79+
mediatypes = append(mediatypes, schema2.MediaTypeManifest)
80+
}
81+
if index {
82+
if oci {
83+
mediatypes = append(mediatypes, v1.MediaTypeImageIndex)
84+
} else {
85+
mediatypes = append(mediatypes, manifestlist.MediaTypeManifestList)
86+
}
87+
}
88+
return mediatypes
89+
}

manifest_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ func TestManifestV1(t *testing.T) {
3131
}
3232

3333
func TestManifestList(t *testing.T) {
34-
out, err := run("manifest", "docker.io/ndeloof/hello-app")
34+
out, err := run("manifest", "--index", "docker.io/library/busybox")
3535
if err != nil {
3636
t.Fatalf("output: %s, error: %v", out, err)
3737
}
3838

39-
expected := `"schemaVersion": 2,`
39+
expected := `"mediaType": "application\/vnd.docker.distribution.manifest.list.v2+json"`
4040
if !strings.Contains(out, expected) {
4141
t.Fatalf("expected: %s\ngot: %s", expected, out)
4242
}

registry/digest.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ package registry
33
import (
44
"context"
55
"fmt"
6+
"github.com/docker/distribution/manifest/schema2"
7+
"github.com/sirupsen/logrus"
68
"net/http"
9+
"strings"
710

8-
"github.com/docker/distribution/manifest/schema2"
9-
digest "github.com/opencontainers/go-digest"
11+
"github.com/opencontainers/go-digest"
1012
)
1113

1214
// Digest returns the digest for an image.
13-
func (r *Registry) Digest(ctx context.Context, image Image) (digest.Digest, error) {
15+
func (r *Registry) Digest(ctx context.Context, image Image, mediatypes ...string) (digest.Digest, error) {
1416
if len(image.Digest) > 1 {
1517
// return early if we already have an image digest.
1618
return image.Digest, nil
@@ -25,7 +27,12 @@ func (r *Registry) Digest(ctx context.Context, image Image) (digest.Digest, erro
2527
return "", err
2628
}
2729

28-
req.Header.Add("Accept", schema2.MediaTypeManifest)
30+
if mediatypes == nil {
31+
mediatypes = []string{schema2.MediaTypeManifest}
32+
}
33+
logrus.Debugf("Using media types %s", mediatypes)
34+
req.Header.Add("Accept", strings.Join(mediatypes, ", "))
35+
2936
resp, err := r.Client.Do(req.WithContext(ctx))
3037
if err != nil {
3138
return "", err

registry/manifest.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import (
55
"context"
66
"encoding/json"
77
"errors"
8+
"github.com/sirupsen/logrus"
89
"io/ioutil"
910
"net/http"
11+
"strings"
1012

1113
"github.com/docker/distribution"
1214
"github.com/docker/distribution/manifest/manifestlist"
@@ -20,7 +22,7 @@ var (
2022
)
2123

2224
// Manifest returns the manifest for a specific repository:tag.
23-
func (r *Registry) Manifest(ctx context.Context, repository, ref string) (distribution.Manifest, error) {
25+
func (r *Registry) Manifest(ctx context.Context, repository, ref string, mediatypes ...string) (distribution.Manifest, error) {
2426
uri := r.url("/v2/%s/manifests/%s", repository, ref)
2527
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
2628

@@ -29,7 +31,11 @@ func (r *Registry) Manifest(ctx context.Context, repository, ref string) (distri
2931
return nil, err
3032
}
3133

32-
req.Header.Add("Accept", schema2.MediaTypeManifest+", "+manifestlist.MediaTypeManifestList)
34+
if mediatypes == nil {
35+
mediatypes = []string{schema2.MediaTypeManifest}
36+
}
37+
logrus.Debugf("Using media types %s", mediatypes)
38+
req.Header.Add("Accept", strings.Join(mediatypes, ", "))
3339

3440
resp, err := r.Client.Do(req.WithContext(ctx))
3541
if err != nil {

0 commit comments

Comments
 (0)