forked from TrueCloudLab/distribution
addressing comments from stevvooe
Signed-off-by: Mike Brown <brownwm@us.ibm.com>
This commit is contained in:
parent
c1532332ad
commit
ec2aa05cdf
6 changed files with 86 additions and 144 deletions
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO (not assigned): consider making mockBlobService common for ocischema, schema2, and schema1
|
|
||||||
type mockBlobService struct {
|
type mockBlobService struct {
|
||||||
descriptors map[digest.Digest]distribution.Descriptor
|
descriptors map[digest.Digest]distribution.Descriptor
|
||||||
}
|
}
|
||||||
|
@ -50,64 +49,45 @@ func (bs *mockBlobService) Resume(ctx context.Context, id string) (distribution.
|
||||||
|
|
||||||
func TestBuilder(t *testing.T) {
|
func TestBuilder(t *testing.T) {
|
||||||
imgJSON := []byte(`{
|
imgJSON := []byte(`{
|
||||||
|
"created": "2015-10-31T22:22:56.015925234Z",
|
||||||
|
"author": "Alyssa P. Hacker <alyspdev@example.com>",
|
||||||
"architecture": "amd64",
|
"architecture": "amd64",
|
||||||
|
"os": "linux",
|
||||||
"config": {
|
"config": {
|
||||||
"AttachStderr": false,
|
"User": "alice",
|
||||||
"AttachStdin": false,
|
"ExposedPorts": {
|
||||||
"AttachStdout": false,
|
"8080/tcp": {}
|
||||||
"Cmd": [
|
},
|
||||||
"/bin/sh",
|
|
||||||
"-c",
|
|
||||||
"echo hi"
|
|
||||||
],
|
|
||||||
"Domainname": "",
|
|
||||||
"Entrypoint": null,
|
|
||||||
"Env": [
|
"Env": [
|
||||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
"derived=true",
|
"FOO=oci_is_a",
|
||||||
"asdf=true"
|
"BAR=well_written_spec"
|
||||||
|
],
|
||||||
|
"Entrypoint": [
|
||||||
|
"/bin/my-app-binary"
|
||||||
],
|
],
|
||||||
"Hostname": "23304fc829f9",
|
|
||||||
"Image": "sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246",
|
|
||||||
"Labels": {},
|
|
||||||
"OnBuild": [],
|
|
||||||
"OpenStdin": false,
|
|
||||||
"StdinOnce": false,
|
|
||||||
"Tty": false,
|
|
||||||
"User": "",
|
|
||||||
"Volumes": null,
|
|
||||||
"WorkingDir": ""
|
|
||||||
},
|
|
||||||
"container": "e91032eb0403a61bfe085ff5a5a48e3659e5a6deae9f4d678daa2ae399d5a001",
|
|
||||||
"container_config": {
|
|
||||||
"AttachStderr": false,
|
|
||||||
"AttachStdin": false,
|
|
||||||
"AttachStdout": false,
|
|
||||||
"Cmd": [
|
"Cmd": [
|
||||||
"/bin/sh",
|
"--foreground",
|
||||||
"-c",
|
"--config",
|
||||||
"#(nop) CMD [\"/bin/sh\" \"-c\" \"echo hi\"]"
|
"/etc/my-app.d/default.cfg"
|
||||||
],
|
],
|
||||||
"Domainname": "",
|
"Volumes": {
|
||||||
"Entrypoint": null,
|
"/var/job-result-data": {},
|
||||||
"Env": [
|
"/var/log/my-app-logs": {}
|
||||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
},
|
||||||
"derived=true",
|
"WorkingDir": "/home/alice",
|
||||||
"asdf=true"
|
"Labels": {
|
||||||
],
|
"com.example.project.git.url": "https://example.com/project.git",
|
||||||
"Hostname": "23304fc829f9",
|
"com.example.project.git.commit": "45a939b2999782a3f005621a8d0f29aa387e1d6b"
|
||||||
"Image": "sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246",
|
}
|
||||||
"Labels": {},
|
},
|
||||||
"OnBuild": [],
|
"rootfs": {
|
||||||
"OpenStdin": false,
|
"diff_ids": [
|
||||||
"StdinOnce": false,
|
"sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
|
||||||
"Tty": false,
|
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||||
"User": "",
|
],
|
||||||
"Volumes": null,
|
"type": "layers"
|
||||||
"WorkingDir": ""
|
|
||||||
},
|
},
|
||||||
"created": "2015-11-04T23:06:32.365666163Z",
|
|
||||||
"docker_version": "1.9.0-dev",
|
|
||||||
"history": [
|
"history": [
|
||||||
{
|
{
|
||||||
"created": "2015-10-31T22:22:54.690851953Z",
|
"created": "2015-10-31T22:22:54.690851953Z",
|
||||||
|
@ -115,37 +95,10 @@ func TestBuilder(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"created": "2015-10-31T22:22:55.613815829Z",
|
"created": "2015-10-31T22:22:55.613815829Z",
|
||||||
"created_by": "/bin/sh -c #(nop) CMD [\"sh\"]"
|
"created_by": "/bin/sh -c #(nop) CMD [\"sh\"]",
|
||||||
},
|
|
||||||
{
|
|
||||||
"created": "2015-11-04T23:06:30.934316144Z",
|
|
||||||
"created_by": "/bin/sh -c #(nop) ENV derived=true",
|
|
||||||
"empty_layer": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"created": "2015-11-04T23:06:31.192097572Z",
|
|
||||||
"created_by": "/bin/sh -c #(nop) ENV asdf=true",
|
|
||||||
"empty_layer": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"created": "2015-11-04T23:06:32.083868454Z",
|
|
||||||
"created_by": "/bin/sh -c dd if=/dev/zero of=/file bs=1024 count=1024"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"created": "2015-11-04T23:06:32.365666163Z",
|
|
||||||
"created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\" \"-c\" \"echo hi\"]",
|
|
||||||
"empty_layer": true
|
"empty_layer": true
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"os": "linux",
|
|
||||||
"rootfs": {
|
|
||||||
"diff_ids": [
|
|
||||||
"sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
|
|
||||||
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
|
|
||||||
"sha256:13f53e08df5a220ab6d13c58b2bf83a59cbdc2e04d0a3f041ddf4b0ba4112d49"
|
|
||||||
],
|
|
||||||
"type": "layers"
|
|
||||||
}
|
|
||||||
}`)
|
}`)
|
||||||
configDigest := digest.FromBytes(imgJSON)
|
configDigest := digest.FromBytes(imgJSON)
|
||||||
|
|
||||||
|
@ -154,6 +107,7 @@ func TestBuilder(t *testing.T) {
|
||||||
Digest: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
|
Digest: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
|
||||||
Size: 5312,
|
Size: 5312,
|
||||||
MediaType: v1.MediaTypeImageLayerGzip,
|
MediaType: v1.MediaTypeImageLayerGzip,
|
||||||
|
Annotations: map[string]string{"apple": "orange", "lettuce": "wrap"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Digest: digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"),
|
Digest: digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"),
|
||||||
|
@ -200,7 +154,7 @@ func TestBuilder(t *testing.T) {
|
||||||
if target.MediaType != v1.MediaTypeImageConfig {
|
if target.MediaType != v1.MediaTypeImageConfig {
|
||||||
t.Fatalf("unexpected media type in target: %s", target.MediaType)
|
t.Fatalf("unexpected media type in target: %s", target.MediaType)
|
||||||
}
|
}
|
||||||
if target.Size != 3153 {
|
if target.Size != 1582 {
|
||||||
t.Fatalf("unexpected size in target: %d", target.Size)
|
t.Fatalf("unexpected size in target: %d", target.Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ var (
|
||||||
// SchemaVersion provides a pre-initialized version structure for this
|
// SchemaVersion provides a pre-initialized version structure for this
|
||||||
// packages version of the manifest.
|
// packages version of the manifest.
|
||||||
SchemaVersion = manifest.Versioned{
|
SchemaVersion = manifest.Versioned{
|
||||||
SchemaVersion: 2, // TODO: (mikebrow/stevvooe) this could be confusing cause oci version 1 is closer to docker 2 than 1
|
SchemaVersion: 2, // historical value here.. does not pertain to OCI or docker version
|
||||||
MediaType: v1.MediaTypeImageManifest,
|
MediaType: v1.MediaTypeImageManifest,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -17,13 +17,19 @@ var expectedManifestSerialization = []byte(`{
|
||||||
"config": {
|
"config": {
|
||||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||||
"size": 985,
|
"size": 985,
|
||||||
"digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b"
|
"digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
||||||
|
"annotations": {
|
||||||
|
"apple": "orange"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"layers": [
|
"layers": [
|
||||||
{
|
{
|
||||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||||
"size": 153263,
|
"size": 153263,
|
||||||
"digest": "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b"
|
"digest": "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b",
|
||||||
|
"annotations": {
|
||||||
|
"lettuce": "wrap"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`)
|
}`)
|
||||||
|
@ -35,12 +41,14 @@ func TestManifest(t *testing.T) {
|
||||||
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
||||||
Size: 985,
|
Size: 985,
|
||||||
MediaType: v1.MediaTypeImageConfig,
|
MediaType: v1.MediaTypeImageConfig,
|
||||||
|
Annotations: map[string]string{"apple": "orange"},
|
||||||
},
|
},
|
||||||
Layers: []distribution.Descriptor{
|
Layers: []distribution.Descriptor{
|
||||||
{
|
{
|
||||||
Digest: "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b",
|
Digest: "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b",
|
||||||
Size: 153263,
|
Size: 153263,
|
||||||
MediaType: v1.MediaTypeImageLayerGzip,
|
MediaType: v1.MediaTypeImageLayerGzip,
|
||||||
|
Annotations: map[string]string{"lettuce": "wrap"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -90,6 +98,9 @@ func TestManifest(t *testing.T) {
|
||||||
if target.Size != 985 {
|
if target.Size != 985 {
|
||||||
t.Fatalf("unexpected size in target: %d", target.Size)
|
t.Fatalf("unexpected size in target: %d", target.Size)
|
||||||
}
|
}
|
||||||
|
if target.Annotations["apple"] != "orange" {
|
||||||
|
t.Fatalf("unexpected annotation in target: %s", target.Annotations["apple"])
|
||||||
|
}
|
||||||
|
|
||||||
references := deserialized.References()
|
references := deserialized.References()
|
||||||
if len(references) != 2 {
|
if len(references) != 2 {
|
||||||
|
@ -110,4 +121,7 @@ func TestManifest(t *testing.T) {
|
||||||
if references[1].Size != 153263 {
|
if references[1].Size != 153263 {
|
||||||
t.Fatalf("unexpected size in reference: %d", references[0].Size)
|
t.Fatalf("unexpected size in reference: %d", references[0].Size)
|
||||||
}
|
}
|
||||||
|
if references[1].Annotations["lettuce"] != "wrap" {
|
||||||
|
t.Fatalf("unexpected annotation in reference: %s", references[1].Annotations["lettuce"])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,8 +122,7 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request)
|
||||||
|
|
||||||
if imh.Tag != "" {
|
if imh.Tag != "" {
|
||||||
tags := imh.Repository.Tags(imh)
|
tags := imh.Repository.Tags(imh)
|
||||||
var desc distribution.Descriptor
|
desc, err := tags.Get(imh, imh.Tag)
|
||||||
desc, err = tags.Get(imh, imh.Tag)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(distribution.ErrTagUnknown); ok {
|
if _, ok := err.(distribution.ErrTagUnknown); ok {
|
||||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
|
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
|
||||||
|
@ -144,8 +143,7 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request)
|
||||||
if imh.Tag != "" {
|
if imh.Tag != "" {
|
||||||
options = append(options, distribution.WithTag(imh.Tag))
|
options = append(options, distribution.WithTag(imh.Tag))
|
||||||
}
|
}
|
||||||
var manifest distribution.Manifest
|
manifest, err := manifests.Get(imh, imh.Digest, options...)
|
||||||
manifest, err = manifests.Get(imh, imh.Digest, options...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(distribution.ErrManifestUnknownRevision); ok {
|
if _, ok := err.(distribution.ErrManifestUnknownRevision); ok {
|
||||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
|
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
|
||||||
|
@ -266,7 +264,7 @@ func (imh *manifestHandler) convertSchema2Manifest(schema2Manifest *schema2.Dese
|
||||||
|
|
||||||
builder := schema1.NewConfigManifestBuilder(imh.Repository.Blobs(imh), imh.Context.App.trustKey, ref, configJSON)
|
builder := schema1.NewConfigManifestBuilder(imh.Repository.Blobs(imh), imh.Context.App.trustKey, ref, configJSON)
|
||||||
for _, d := range schema2Manifest.Layers {
|
for _, d := range schema2Manifest.Layers {
|
||||||
if err = builder.AppendReference(d); err != nil {
|
if err := builder.AppendReference(d); err != nil {
|
||||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err))
|
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -339,7 +337,7 @@ func (imh *manifestHandler) PutManifest(w http.ResponseWriter, r *http.Request)
|
||||||
options = append(options, distribution.WithTag(imh.Tag))
|
options = append(options, distribution.WithTag(imh.Tag))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = imh.applyResourcePolicy(manifest); err != nil {
|
if err := imh.applyResourcePolicy(manifest); err != nil {
|
||||||
imh.Errors = append(imh.Errors, err)
|
imh.Errors = append(imh.Errors, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package storage
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
"github.com/docker/distribution/context"
|
"github.com/docker/distribution/context"
|
||||||
|
@ -80,23 +79,6 @@ func (ms *ocischemaManifestHandler) verifyManifest(ctx context.Context, mnfst oc
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
switch descriptor.MediaType {
|
switch descriptor.MediaType {
|
||||||
// TODO: mikebrow/steveoe verify we should treat oci nondistributable like foreign layers?
|
|
||||||
case v1.MediaTypeImageLayerNonDistributable, v1.MediaTypeImageLayerNonDistributableGzip:
|
|
||||||
// Clients download this layer from an external URL, so do not check for
|
|
||||||
// its presence.
|
|
||||||
if len(descriptor.URLs) == 0 {
|
|
||||||
err = errMissingURL
|
|
||||||
}
|
|
||||||
allow := ms.manifestURLs.allow
|
|
||||||
deny := ms.manifestURLs.deny
|
|
||||||
for _, u := range descriptor.URLs {
|
|
||||||
var pu *url.URL
|
|
||||||
pu, err = url.Parse(u)
|
|
||||||
if err != nil || (pu.Scheme != "http" && pu.Scheme != "https") || pu.Fragment != "" || (allow != nil && !allow.MatchString(u)) || (deny != nil && deny.MatchString(u)) {
|
|
||||||
err = errInvalidURL
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case v1.MediaTypeImageManifest:
|
case v1.MediaTypeImageManifest:
|
||||||
var exists bool
|
var exists bool
|
||||||
exists, err = manifestService.Exists(ctx, descriptor.Digest)
|
exists, err = manifestService.Exists(ctx, descriptor.Digest)
|
||||||
|
|
|
@ -53,12 +53,6 @@ func TestVerifyOCIManifestNonDistributableLayer(t *testing.T) {
|
||||||
|
|
||||||
cases := []testcase{
|
cases := []testcase{
|
||||||
{
|
{
|
||||||
nonDistributableLayer,
|
|
||||||
nil,
|
|
||||||
errMissingURL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// regular layers may have foreign urls (non-Distributable Layers)
|
|
||||||
layer,
|
layer,
|
||||||
[]string{"http://foo/bar"},
|
[]string{"http://foo/bar"},
|
||||||
nil,
|
nil,
|
||||||
|
@ -66,37 +60,37 @@ func TestVerifyOCIManifestNonDistributableLayer(t *testing.T) {
|
||||||
{
|
{
|
||||||
nonDistributableLayer,
|
nonDistributableLayer,
|
||||||
[]string{"file:///local/file"},
|
[]string{"file:///local/file"},
|
||||||
errInvalidURL,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
nonDistributableLayer,
|
nonDistributableLayer,
|
||||||
[]string{"http://foo/bar#baz"},
|
[]string{"http://foo/bar#baz"},
|
||||||
errInvalidURL,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
nonDistributableLayer,
|
nonDistributableLayer,
|
||||||
[]string{""},
|
[]string{""},
|
||||||
errInvalidURL,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
nonDistributableLayer,
|
nonDistributableLayer,
|
||||||
[]string{"https://foo/bar", ""},
|
[]string{"https://foo/bar", ""},
|
||||||
errInvalidURL,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
nonDistributableLayer,
|
nonDistributableLayer,
|
||||||
[]string{"", "https://foo/bar"},
|
[]string{"", "https://foo/bar"},
|
||||||
errInvalidURL,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
nonDistributableLayer,
|
nonDistributableLayer,
|
||||||
[]string{"http://nope/bar"},
|
[]string{"http://nope/bar"},
|
||||||
errInvalidURL,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
nonDistributableLayer,
|
nonDistributableLayer,
|
||||||
[]string{"http://foo/nope"},
|
[]string{"http://foo/nope"},
|
||||||
errInvalidURL,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
nonDistributableLayer,
|
nonDistributableLayer,
|
||||||
|
|
Loading…
Reference in a new issue