diff --git a/schema/defs-config.json b/schema/defs-config.json index 2faf3c656..ba86cd1df 100644 --- a/schema/defs-config.json +++ b/schema/defs-config.json @@ -38,7 +38,10 @@ } }, "Volumes": { - "$ref": "defs.json#/definitions/mapStringObject" + "oneOf": [ + {"$ref": "defs.json#/definitions/mapStringObject"}, + {"type": "null"} + ] }, "WorkingDir": { "type": "string" diff --git a/schema/fs.go b/schema/fs.go index e49730a95..d7c4d000f 100644 --- a/schema/fs.go +++ b/schema/fs.go @@ -203,7 +203,7 @@ var _escData = map[string]*_escFile{ "/config-schema.json": { local: "config-schema.json", size: 707, - modtime: 1464139547, + modtime: 1464121931, compressed: ` H4sIAAAJbogA/5SRPW7DMAyF5/oUhpOxjjp0ytoDdOgJVJmKGcCiQDJDUPju1U/c2kvhLobx+L73JOqr adtuAHGMUZFCd2679wjhjYJaDMBt+vN4aT8iOPTobHE9Z+woboTJZmRUjWdjrkKhr+qJ+GIGtl77l1dT @@ -216,7 +216,7 @@ T4aE9IoTdGU2V0tnbzoS/xG1dbMbUdPhbgx7GZK9zscuVu4jgy+HBy99HZ/yKxxMUjBgfi1ZdrjJYiL1 "/content-descriptor.json": { local: "content-descriptor.json", size: 616, - modtime: 1464914956, + modtime: 1466030773, compressed: ` H4sIAAAJbogA/4yRMVPDMAyF9/wKXdqR1gxMXWFngI1jcG0lVe9iG1kMhet/x4oTSGGgW/L8vvck+7MB aD1mx5SEYmh30D4mDPcxiKWADPqFQeBhMkWGp4SOOnJ2JG40Yp3dAQer+EEk7Yw55hg2Vd1G7o1n28nm @@ -228,23 +228,24 @@ AmOHXKZBD4uOEV+XM+U8dnlDg+1xq8uuyj8F0tRsfnpH6lzhNtPHf5OoBSjA/iSYr5hmvgkqz9QjX/Z5 "/defs-config.json": { local: "defs-config.json", - size: 1755, - modtime: 1464139547, + size: 1822, + modtime: 1466096871, compressed: ` -H4sIAAAJbogA/7xUzc7TMBA8N09hBY6BXhAHri1HVKQIOFZusm63xF5rvQEi1HfHSauS/qRKKV8PVeOx -Z2bXY/t3olRaQigYvSC59INK52DQYTsKymsWLOpKsxJSCw9uRk40OmAVvwyuVe6hQIOF7vjZXvCoEAVb -jwgW3fLjOCLSeGgNabWFQjpqh3smD9EXQm91xL8E4BOkpxGE0a3T49Qu+8v7BJa4OWe+ZjAtMxYb3m4D -uVfTXt1TdPL+3S29/Kf2/09z5ut8o/ms5YckP/7yFKD8TCz3qlrt825DF/toruu7H0NpaGbdpFl/CgXs -eRk38otWA6bCjafY9vO9Z7Z8vulXqmp797EYFeA34u9xyRzH36qk/3/QSplITHjkZpdozBLLiy5ffnsr -3QAP+o7rf4NBTh+YuzegYNACg8frUEeWTCYRNcRWS5d+JL0RtHA9YF3Lhv7pyTzUs1xdPJuj2GQtDN/Q -W1SwXppll8oQfUVUgXaDqSTtb5f8CQAA//8Cok052wYAAA== +H4sIAAAJbogA/7xUzY7TMBA+b57CChwDe0EcuHY5oiJFwAGhyk3G21lijzWeAFGVd8dJS0nTpmop20PV +eOzvx/7sWSdKpSWEgtELkkvfqfQBDDrsRkF5zYJFXWlWQmruwc3IiUYHrOKXwUeVeyjQYKF7fLYh3DFE +wk4jFot++W4cK9J46ARp+QSF9NC+7pk8RF0Ig9Wx/ikA71UGHEEY3WO6m2qzv7gPYImbMfIlg+mQ0Wx4 +/RTIvbgf+L5HJ2/fnOLLf2r//zhnvs5Xmkdbvory/S9PAcqPxHIpq9U+7w90vonmOL/7MZWGZtZNmg2n +UMCObZzIL0pNiAo3nuK2b689s+XtRT9TVdvDa0EO5l2CX/fY15cG22Yj/B97rq6qtB1Mfjvq7gvx98j2 +gOc/zGT4v+VKmUhMuKY5lGjMAsuDg3r+hCrdAE/qnrf/FQbZ71EXH0DBoAUmb+jWR5bc3cWqIbZa+osS +Qa8ELRx/5LqWFf1T1936WSwPOu9ZaLIWph/5KShYL82iT2UKviSqQLvJVJLu1ya/AwAA//9M4B9rHgcA +AA== `, }, "/defs-image.json": { local: "defs-image.json", size: 2528, - modtime: 1464914958, + modtime: 1466030773, compressed: ` H4sIAAAJbogA/7RWy27bMBC8+ysIJUAOfqjXGkGAoEGBnlIgPTVQgY20kphKpLqkgzqB/r2k3k+nrt0b ueQOZ4Zjym8LxpwAlU8801wKZ8ucOwy54HamWAakub9LgJiW7D5D8UkKDVwgsS8pRMgeMvR5yH0o2lcl @@ -263,7 +264,7 @@ Wb7IF38CAAD//wKthPngCQAA "/defs.json": { local: "defs.json", size: 3193, - modtime: 1464139547, + modtime: 1466096269, compressed: ` H4sIAAAJbogA/7RWTXPaMBC98ys8tEfa2PIX9NYp/cghAzOZnjo9uGYBtSCpstxpmuG/VzLGWPZiMKWH JPau9r23T6tYzwPHGS4gSyUVinI2fOMMp7CkjJq3zMkzWDhqLXm+WvNc6UdwZgLYO85UQhlI51FASpc0 @@ -282,7 +283,7 @@ MrVJbn8cB+ZnN/gbAAD//0JyEpx5DAAA "/image-manifest-schema.json": { local: "image-manifest-schema.json", size: 1032, - modtime: 1464914959, + modtime: 1466030773, compressed: ` H4sIAAAJbogA/6RSPU/zMBDe8ytOacc39TswdWViQAxULIjBJOfkqsYOPoNUVf3v+KMujsoAdMyTez7u 8R0qgLpDbi1Njoyu11A/TKhvjXaSNFq4G2WPcC81KWQHjxO2pKiVcfpfoC+5HXCUgTo4N62F2LLRTUJX @@ -297,7 +298,7 @@ X3p8DwgEAAA= "/manifest-list-schema.json": { local: "manifest-list-schema.json", size: 1010, - modtime: 1464139547, + modtime: 1461966159, compressed: ` H4sIAAAJbogA/6ySMU/7MBDF93yKU9rxn/o/MHWFBQnEQMWCGExyaa5q7OAzSFXV747tS0qiMIDoUqkv fu9+7+xjBpBXyKWjzpM1+Rryhw7NtTVek0EHt63eItxrQzWyhzsKP48dllRTqZPlX8xYctlgq6O/8b5b diff --git a/schema/manifest_backwards_compatibility_test.go b/schema/manifest_backwards_compatibility_test.go new file mode 100644 index 000000000..9a96f8e5a --- /dev/null +++ b/schema/manifest_backwards_compatibility_test.go @@ -0,0 +1,139 @@ +// Copyright 2016 The Linux Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema_test + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "strings" + "testing" + + "github.com/opencontainers/image-spec/schema" +) + +var compatMap = map[string]string{ + "application/vnd.docker.distribution.manifest.list.v2+json": "application/vnd.oci.image.manifest.list.v1+json", + "application/vnd.docker.distribution.manifest.v2+json": "application/vnd.oci.image.manifest.v1+json", + "application/vnd.docker.image.rootfs.diff.tar.gzip": "application/vnd.oci.image.rootfs.tar.gzip", + "application/vnd.docker.container.image.v1+json": "application/vnd.oci.image.serialization.config.v1+json", +} + +// convertFormats converts Docker v2.2 image format JSON documents to OCI +// format by simply replacing instances of the strings found in the compatMap +// found in the input string. +func convertFormats(input string) string { + out := input + for k, v := range compatMap { + out = strings.Replace(out, v, k, -1) + } + return out +} + +func TestBackwardsCompatibilityManifest(t *testing.T) { + for i, tt := range []struct { + manifest string + digest string + fail bool + }{ + // manifest pulled from docker hub using hash value + // + // curl -L -H "Authorization: Bearer ..." -H \ + // "Accept: application/vnd.docker.distribution.manifest.v2+json" \ + // https://registry-1.docker.io/v2/library/docker/manifests/sha256:888206c77cd2811ec47e752ba291e5b7734e3ef137dfd222daadaca39a9f17bc + { + digest: "sha256:888206c77cd2811ec47e752ba291e5b7734e3ef137dfd222daadaca39a9f17bc", + manifest: `{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/octet-stream", + "size": 3210, + "digest": "sha256:5359a4f250650c20227055957e353e8f8a74152f35fe36f00b6b1f9fc19c8861" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 2310272, + "digest": "sha256:fae91920dcd4542f97c9350b3157139a5d901362c2abec284de5ebd1b45b4957" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 913022, + "digest": "sha256:f384f6ab36adad485192f09379c0b58dc612a3cde82c551e082a7c29a87c95da" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 9861668, + "digest": "sha256:ed0d2dd5e1a0e5e650a330a864c8a122e9aa91fa6ba9ac6f0bd1882e59df55e7" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 465, + "digest": "sha256:ec4d00b58417c45f7ddcfde7bcad8c9d62a7d6d5d17cdc1f7d79bcb2e22c1491" + } + ] +}`, + fail: false, + }, + } { + sum := sha256.Sum256([]byte(tt.manifest)) + got := fmt.Sprintf("sha256:%s", hex.EncodeToString(sum[:])) + if tt.digest != got { + t.Errorf("test %d: expected digest %s but got %s", i, tt.digest, got) + } + + manifest := convertFormats(tt.manifest) + r := strings.NewReader(manifest) + err := schema.MediaTypeManifest.Validate(r) + + if got := err != nil; tt.fail != got { + t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err) + } + } +} + +func TestBackwardsCompatibilityConfig(t *testing.T) { + for i, tt := range []struct { + manifest string + digest string + fail bool + }{ + // manifest pulled from docker hub blob store + // + // curl -L -H "Authorization: Bearer ..." -H \ + // -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \ + // https://registry-1.docker.io/v2/library/docker/blobs/sha256:5359a4f250650c20227055957e353e8f8a74152f35fe36f00b6b1f9fc19c8861 + { + digest: "sha256:5359a4f250650c20227055957e353e8f8a74152f35fe36f00b6b1f9fc19c8861", + manifest: `{"architecture":"amd64","config":{"Hostname":"e5e5b3910a57","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","DOCKER_BUCKET=get.docker.com","DOCKER_VERSION=1.10.3","DOCKER_SHA256=d0df512afa109006a450f41873634951e19ddabf8c7bd419caeb5a526032d86d"],"Cmd":["sh"],"ArgsEscaped":true,"Image":"sha256:bda352ba7ab5823b7dc74b380c5ad1699edee278a6d2ebbe451129b108778742","Volumes":null,"WorkingDir":"","Entrypoint":["docker-entrypoint.sh"],"OnBuild":[],"Labels":{}},"container":"881be788b4387039b52fa195da9fe26f264385aa497ce650cfdcf3806c2d2021","container_config":{"Hostname":"e5e5b3910a57","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","DOCKER_BUCKET=get.docker.com","DOCKER_VERSION=1.10.3","DOCKER_SHA256=d0df512afa109006a450f41873634951e19ddabf8c7bd419caeb5a526032d86d"],"Cmd":["/bin/sh","-c","#(nop) CMD [\"sh\"]"],"ArgsEscaped":true,"Image":"sha256:bda352ba7ab5823b7dc74b380c5ad1699edee278a6d2ebbe451129b108778742","Volumes":null,"WorkingDir":"","Entrypoint":["docker-entrypoint.sh"],"OnBuild":[],"Labels":{}},"created":"2016-06-08T00:52:29.30472774Z","docker_version":"1.10.3","history":[{"created":"2016-06-08T00:48:01.932532048Z","created_by":"/bin/sh -c #(nop) ADD file:bca92e550bd2ce926584aef2032464b6ebf543ce69133b6602c781866165d703 in /"},{"created":"2016-06-08T00:52:10.503417531Z","created_by":"/bin/sh -c apk add --no-cache \t\tca-certificates \t\tcurl \t\topenssl"},{"created":"2016-06-08T00:52:10.700704697Z","created_by":"/bin/sh -c #(nop) ENV DOCKER_BUCKET=get.docker.com","empty_layer":true},{"created":"2016-06-08T00:52:25.746175479Z","created_by":"/bin/sh -c #(nop) ENV DOCKER_VERSION=1.10.3","empty_layer":true},{"created":"2016-06-08T00:52:25.954613633Z","created_by":"/bin/sh -c #(nop) ENV DOCKER_SHA256=d0df512afa109006a450f41873634951e19ddabf8c7bd419caeb5a526032d86d","empty_layer":true},{"created":"2016-06-08T00:52:28.173693898Z","created_by":"/bin/sh -c curl -fSL \"https://${DOCKER_BUCKET}/builds/Linux/x86_64/docker-$DOCKER_VERSION\" -o /usr/local/bin/docker \t\u0026\u0026 echo \"${DOCKER_SHA256} /usr/local/bin/docker\" | sha256sum -c - \t\u0026\u0026 chmod +x /usr/local/bin/docker"},{"created":"2016-06-08T00:52:28.924486515Z","created_by":"/bin/sh -c #(nop) COPY file:50006c902e7677711aeffe4ab7b7042d649618b96dec760f322a8566dd83ab25 in /usr/local/bin/"},{"created":"2016-06-08T00:52:29.121963047Z","created_by":"/bin/sh -c #(nop) ENTRYPOINT \u0026{[\"docker-entrypoint.sh\"]}","empty_layer":true},{"created":"2016-06-08T00:52:29.30472774Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:77f08abee8bf9334407f52d104e1891283018450b3c196118ddfe31505126b87","sha256:707d16737060172b977d5f7eaaddfcfaae1008472193d7e8e5a01111a5f8dd5c","sha256:44da042e7b2458ee0b3877f2321cdf4fd45a49b9b51e00492c2ba68055573eff","sha256:1bc2be83dce13b9bac9476c9c1d2ca6e0db3e07b443f7298fc5a1af75b2cb4ef"]}}`, + fail: false, + }, + } { + sum := sha256.Sum256([]byte(tt.manifest)) + got := fmt.Sprintf("sha256:%s", hex.EncodeToString(sum[:])) + if tt.digest != got { + t.Errorf("test %d: expected digest %s but got %s", i, tt.digest, got) + } + + manifest := convertFormats(tt.manifest) + r := strings.NewReader(manifest) + err := schema.MediaTypeImageSerializationConfig.Validate(r) + + if got := err != nil; tt.fail != got { + t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err) + } + } +} diff --git a/serialization.md b/serialization.md index b06a66a8e..0f24bdf69 100644 --- a/serialization.md +++ b/serialization.md @@ -297,7 +297,7 @@ Note: whitespace has been added to this example for clarity. Whitespace is OPTIO Volumes struct
- A set of directories which should be created as data volumes in a container running this image. + A set of directories which should be created as data volumes in a container running this image. This field MAY be "null".

If a file or folder exists within the image with the same path as a data volume, that file or folder is replaced with the data volume and is never merged.