Merge pull request #539 from runcom/stop-signal-cc

server: store and use image's stop signal to stop containers
This commit is contained in:
Mrunal Patel 2017-05-27 08:43:36 -07:00 committed by GitHub
commit cf4e5ee903
58 changed files with 952 additions and 10087 deletions

View file

@ -217,7 +217,7 @@
args:
chdir: /root/src/github.com/kubernetes-incubator/cri-o/
- name: run integration tests RHEL
shell: 'CGROUP_MANAGER=systemd STORAGE_OPTS="--storage-driver=overlay2 --storage-opt overlay2.override_kernel_check=1" make localintegration > testout.txt'
shell: 'CGROUP_MANAGER=systemd STORAGE_OPTS="--storage-driver=overlay2 --storage-opt overlay2.override_kernel_check=1" make localintegration 2>&1 > testout.txt'
args:
chdir: /root/src/github.com/kubernetes-incubator/cri-o
async: 3600
@ -233,7 +233,7 @@
ignore_errors: yes
when: (ansible_distribution == 'RedHat' or ansible_distribution == 'CentOS') and xunit
- name: run integration tests Fedora
shell: 'CGROUP_MANAGER=systemd STORAGE_OPTS="--storage-driver=overlay2" make localintegration > testout.txt'
shell: 'CGROUP_MANAGER=systemd STORAGE_OPTS="--storage-driver=overlay2" make localintegration 2>&1 > testout.txt'
args:
chdir: /root/src/github.com/kubernetes-incubator/cri-o
async: 3600
@ -350,7 +350,7 @@
state: present
- name: run k8s tests
shell: |
make test-e2e-node PARALLELISM=1 RUNTIME=remote CONTAINER_RUNTIME_ENDPOINT=/var/run/crio.sock IMAGE_SERVICE_ENDPOINT=/var/run/crio.sock TEST_ARGS="--prepull-images=true" FOCUS="\[Conformance\]" >> node-e2e.log
make test-e2e-node PARALLELISM=1 RUNTIME=remote CONTAINER_RUNTIME_ENDPOINT=/var/run/crio.sock IMAGE_SERVICE_ENDPOINT=/var/run/crio.sock TEST_ARGS="--prepull-images=true" FOCUS="\[Conformance\]" 2>&1 > node-e2e.log
args:
chdir: /root/src/k8s.io/kubernetes
async: 7200

View file

@ -1,5 +1,5 @@
{
"memo": "2005de643a6067047c2105429c375537db3ff6899467af5f8204d70a2c745fc4",
"memo": "93ab0fa7baac600756f69c227eb4ffb24afbc057bd81a3c504ee64e5aec41a81",
"projects": [
{
"name": "cloud.google.com/go",
@ -92,7 +92,7 @@
{
"name": "github.com/containers/image",
"branch": "master",
"revision": "11dfba3e17bbb9c1ef50ee3687e5525e56dbd151",
"revision": "84d3787ee366601bdb0a31ca26261c20a0a60e64",
"packages": [
"copy",
"directory",
@ -176,7 +176,8 @@
"revision": "48702e0da86bd25e76cfef347e2adeb434a0d0a6",
"packages": [
"daemon",
"dbus"
"dbus",
"util"
]
},
{
@ -184,6 +185,7 @@
"version": "v3",
"revision": "3ac0863d7acf3bc44daf49afef8919af12f704ef",
"packages": [
"dlopen",
"health",
"httputil",
"timeutil"
@ -236,6 +238,7 @@
"api/types/versions",
"api/types/volume",
"client",
"pkg/ioutils",
"pkg/longpath",
"pkg/random",
"pkg/registrar",
@ -510,8 +513,8 @@
},
{
"name": "github.com/opencontainers/image-spec",
"version": "v1.0.0-rc5",
"revision": "5faaada8762b465d5ce8a8da27b92d577a1aa576",
"version": "v1.0.0-rc6",
"revision": "1a6593ab6c3ab5902072b4694a22ff19425396ae",
"packages": [
"specs-go",
"specs-go/v1"
@ -523,10 +526,14 @@
"revision": "b263a43430ac6996a4302b891688544225197294",
"packages": [
"libcontainer/apparmor",
"libcontainer/cgroups",
"libcontainer/cgroups/fs",
"libcontainer/cgroups/systemd",
"libcontainer/configs",
"libcontainer/devices",
"libcontainer/system",
"libcontainer/user"
"libcontainer/user",
"libcontainer/utils"
]
},
{

View file

@ -7,7 +7,7 @@
"branch": "master"
},
"github.com/opencontainers/image-spec": {
"version": "v1.0.0-rc5"
"version": "v1.0.0-rc6"
},
"github.com/containers/storage": {
"branch": "master"

View file

@ -5,15 +5,21 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/containernetworking/cni/pkg/ns"
"github.com/docker/docker/pkg/signal"
specs "github.com/opencontainers/runtime-spec/specs-go"
"k8s.io/apimachinery/pkg/fields"
pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)
const (
defaultStopSignal = "TERM"
)
// Container represents a runtime container.
type Container struct {
id string
@ -32,7 +38,8 @@ type Container struct {
// this is the /var/run/storage/... directory, erased on reboot
bundlePath string
// this is the /var/lib/storage/... directory
dir string
dir string
stopSignal string
}
// ContainerState represents the status of a container.
@ -46,7 +53,7 @@ type ContainerState struct {
}
// NewContainer creates a container object.
func NewContainer(id string, name string, bundlePath string, logPath string, netns ns.NetNS, labels map[string]string, annotations map[string]string, image *pb.ImageSpec, metadata *pb.ContainerMetadata, sandbox string, terminal bool, privileged bool, dir string, created time.Time) (*Container, error) {
func NewContainer(id string, name string, bundlePath string, logPath string, netns ns.NetNS, labels map[string]string, annotations map[string]string, image *pb.ImageSpec, metadata *pb.ContainerMetadata, sandbox string, terminal bool, privileged bool, dir string, created time.Time, stopSignal string) (*Container, error) {
state := &ContainerState{}
state.Created = created
c := &Container{
@ -64,10 +71,25 @@ func NewContainer(id string, name string, bundlePath string, logPath string, net
image: image,
dir: dir,
state: state,
stopSignal: stopSignal,
}
return c, nil
}
// GetStopSignal returns the container's own stop signal configured from the
// image configuration or the default one.
func (c *Container) GetStopSignal() string {
if c.stopSignal == "" {
return defaultStopSignal
}
cleanSignal := strings.TrimPrefix(strings.ToUpper(c.stopSignal), "SIG")
_, ok := signal.SignalMap[cleanSignal]
if !ok {
return defaultStopSignal
}
return cleanSignal
}
// FromDisk restores container's state from disk
func (c *Container) FromDisk() error {
jsonSource, err := os.Open(c.StatePath())

View file

@ -503,7 +503,7 @@ func (r *Runtime) ExecSync(c *Container, command []string, timeout int64) (resp
func (r *Runtime) StopContainer(c *Container, timeout int64) error {
c.opLock.Lock()
defer c.opLock.Unlock()
if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "kill", c.name, "TERM"); err != nil {
if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "kill", c.name, c.GetStopSignal()); err != nil {
return err
}
if timeout == -1 {

View file

@ -589,6 +589,11 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
containerImageConfig := containerInfo.Config
if containerImageConfig.Config.StopSignal != "" {
// this key is defined in image-spec conversion document at https://github.com/opencontainers/image-spec/pull/492/files#diff-8aafbe2c3690162540381b8cdb157112R57
specgen.AddAnnotation("org.opencontainers.image.stopSignal", containerImageConfig.Config.StopSignal)
}
// TODO: volume handling in CRI-O
// right now, we do just mount tmpfs in order to have images like
// gcr.io/k8s-testimages/redis:e2e to work with CRI-O
@ -662,7 +667,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
return nil, err
}
container, err := oci.NewContainer(containerID, containerName, containerInfo.RunDir, logPath, sb.netNs(), labels, annotations, imageSpec, metadata, sb.id, containerConfig.Tty, sb.privileged, containerInfo.Dir, created)
container, err := oci.NewContainer(containerID, containerName, containerInfo.RunDir, logPath, sb.netNs(), labels, annotations, imageSpec, metadata, sb.id, containerConfig.Tty, sb.privileged, containerInfo.Dir, created, containerImageConfig.Config.StopSignal)
if err != nil {
return nil, err
}

View file

@ -269,6 +269,10 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest
g.AddAnnotation("crio/resolv_path", resolvPath)
g.AddAnnotation("crio/hostname", hostname)
g.AddAnnotation("crio/kube_name", kubeName)
if podContainer.Config.Config.StopSignal != "" {
// this key is defined in image-spec conversion document at https://github.com/opencontainers/image-spec/pull/492/files#diff-8aafbe2c3690162540381b8cdb157112R57
g.AddAnnotation("org.opencontainers.image.stopSignal", podContainer.Config.Config.StopSignal)
}
created := time.Now()
g.AddAnnotation("crio/created", created.Format(time.RFC3339Nano))
@ -407,7 +411,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest
return nil, fmt.Errorf("failed to write runtime configuration for pod sandbox %s(%s): %v", sb.name, id, err)
}
container, err := oci.NewContainer(id, containerName, podContainer.RunDir, logPath, sb.netNs(), labels, annotations, nil, nil, id, false, sb.privileged, podContainer.Dir, created)
container, err := oci.NewContainer(id, containerName, podContainer.RunDir, logPath, sb.netNs(), labels, annotations, nil, nil, id, false, sb.privileged, podContainer.Dir, created, podContainer.Config.Config.StopSignal)
if err != nil {
return nil, err
}

View file

@ -145,7 +145,7 @@ func (s *Server) loadContainer(id string) error {
return err
}
ctr, err := oci.NewContainer(id, name, containerPath, m.Annotations["crio/log_path"], sb.netNs(), labels, annotations, img, &metadata, sb.id, tty, sb.privileged, containerDir, created)
ctr, err := oci.NewContainer(id, name, containerPath, m.Annotations["crio/log_path"], sb.netNs(), labels, annotations, img, &metadata, sb.id, tty, sb.privileged, containerDir, created, m.Annotations["org.opencontainers.image.stopSignal"])
if err != nil {
return err
}
@ -301,7 +301,7 @@ func (s *Server) loadSandbox(id string) error {
return err
}
scontainer, err := oci.NewContainer(m.Annotations["crio/container_id"], cname, sandboxPath, m.Annotations["crio/log_path"], sb.netNs(), labels, annotations, nil, nil, id, false, privileged, sandboxDir, created)
scontainer, err := oci.NewContainer(m.Annotations["crio/container_id"], cname, sandboxPath, m.Annotations["crio/log_path"], sb.netNs(), labels, annotations, nil, nil, id, false, privileged, sandboxDir, created, m.Annotations["org.opencontainers.image.stopSignal"])
if err != nil {
return err
}

View file

@ -168,8 +168,8 @@ func main() {
},
RootFS: v1.RootFS{
Type: "layers",
DiffIDs: []string{
layer.Digest.String(),
DiffIDs: []digest.Digest{
layer.Digest,
},
},
}

View file

@ -19,12 +19,12 @@ import (
type ociImageDestination struct {
ref ociReference
index imgspecv1.ImageIndex
index imgspecv1.Index
}
// newImageDestination returns an ImageDestination for writing to an existing directory.
func newImageDestination(ref ociReference) types.ImageDestination {
index := imgspecv1.ImageIndex{
index := imgspecv1.Index{
Versioned: imgspec.Versioned{
SchemaVersion: 2,
},
@ -173,15 +173,13 @@ func (d *ociImageDestination) PutManifest(m []byte) error {
}
annotations := make(map[string]string)
annotations["org.opencontainers.ref.name"] = d.ref.tag
annotations["org.opencontainers.image.ref.name"] = d.ref.tag
desc.Annotations = annotations
d.index.Manifests = append(d.index.Manifests, imgspecv1.ManifestDescriptor{
Descriptor: desc,
Platform: imgspecv1.Platform{
Architecture: runtime.GOARCH,
OS: runtime.GOOS,
},
})
desc.Platform = &imgspecv1.Platform{
Architecture: runtime.GOARCH,
OS: runtime.GOOS,
}
d.index.Manifests = append(d.index.Manifests, desc)
return nil
}

View file

@ -12,7 +12,7 @@ import (
type ociImageSource struct {
ref ociReference
descriptor imgspecv1.ManifestDescriptor
descriptor imgspecv1.Descriptor
}
// newImageSource returns an ImageSource for reading from an existing directory.

View file

@ -186,22 +186,22 @@ func (ref ociReference) NewImage(ctx *types.SystemContext) (types.Image, error)
return image.FromSource(src)
}
func (ref ociReference) getManifestDescriptor() (imgspecv1.ManifestDescriptor, error) {
func (ref ociReference) getManifestDescriptor() (imgspecv1.Descriptor, error) {
indexJSON, err := os.Open(ref.indexPath())
if err != nil {
return imgspecv1.ManifestDescriptor{}, err
return imgspecv1.Descriptor{}, err
}
defer indexJSON.Close()
index := imgspecv1.ImageIndex{}
index := imgspecv1.Index{}
if err := json.NewDecoder(indexJSON).Decode(&index); err != nil {
return imgspecv1.ManifestDescriptor{}, err
return imgspecv1.Descriptor{}, err
}
var d *imgspecv1.ManifestDescriptor
var d *imgspecv1.Descriptor
for _, md := range index.Manifests {
if md.MediaType != imgspecv1.MediaTypeImageManifest {
continue
}
refName, ok := md.Annotations["org.opencontainers.ref.name"]
refName, ok := md.Annotations["org.opencontainers.image.ref.name"]
if !ok {
continue
}
@ -211,7 +211,7 @@ func (ref ociReference) getManifestDescriptor() (imgspecv1.ManifestDescriptor, e
}
}
if d == nil {
return imgspecv1.ManifestDescriptor{}, fmt.Errorf("no descriptor found for reference %q", ref.tag)
return imgspecv1.Descriptor{}, fmt.Errorf("no descriptor found for reference %q", ref.tag)
}
return *d, nil
}

View file

@ -127,7 +127,7 @@ func refToTempOCI(t *testing.T) (ref types.ImageReference, tmpDir string) {
"os": "linux"
},
"annotations": {
"org.opencontainers.ref.name": "tagValue"
"org.opencontainers.image.ref.name": "tagValue"
}
}
]

View file

@ -1,5 +1,5 @@
github.com/Sirupsen/logrus 7f4b1adc791766938c29457bed0703fb9134421a
github.com/containers/storage master
github.com/containers/storage 29d2c86eadb88a0cbfbbedec8762126a3987d4c3
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
github.com/docker/distribution df5327f76fb6468b84a87771e361762b8be23fdb
github.com/docker/docker 75843d36aa5c3eaade50da005f9e0ff2602f3d5e
@ -15,7 +15,7 @@ github.com/mattn/go-shellwords 005a0944d84452842197c2108bd9168ced206f78
github.com/mistifyio/go-zfs c0224de804d438efd11ea6e52ada8014537d6062
github.com/mtrmac/gpgme b2432428689ca58c2b8e8dea9449d3295cf96fc9
github.com/opencontainers/go-digest aa2ec055abd10d26d539eb630a92241b781ce4bc
github.com/opencontainers/image-spec v1.0.0-rc5
github.com/opencontainers/image-spec v1.0.0-rc6
github.com/opencontainers/runc 6b1d0e76f239ffb435445e5ae316d2676c07c6e3
github.com/pborman/uuid 1b00554d822231195d1babd97ff4a781231955c9
github.com/pkg/errors 248dadf4e9068a0b3e79f02ed0a610d935de5302
@ -33,3 +33,4 @@ github.com/xeipuuv/gojsonreference master
github.com/xeipuuv/gojsonpointer master
github.com/tchap/go-patricia v2.2.6
github.com/opencontainers/selinux ba1aefe8057f1d0cfb8e88d0ec1dc85925ef987d
github.com/BurntSushi/toml b26d9c308763d68093482582cea63d69be07a0f0

View file

@ -1,2 +1,3 @@
/oci-validate-examples
output
header.html

View file

@ -1,12 +1,27 @@
approve_by_comment: true
approve_regex: '^(Approved|lgtm|LGTM|:shipit:|:star:|:\+1:|:ship:)'
reject_regex: ^Rejected
reset_on_push: true
author_approval: ignored
signed_off_by:
required: true
reviewers:
teams:
- image-spec-maintainers
name: default
version: 2
requirements:
signed_off_by:
required: true
group_defaults:
required: 2
approve_by_comment:
enabled: true
approve_regex: '^(Approved|lgtm|LGTM|:shipit:|:star:|:\+1:|:ship:)'
reject_regex: ^Rejected
reset_on_push:
enabled: true
author_approval:
ignored: true
always_pending:
title_regex: ^WIP
explanation: 'Work in progress...'
conditions:
branches:
- master
groups:
image-spec:
teams:
- image-spec-maintainers

View file

@ -0,0 +1,54 @@
// Copyright 2017 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 main
import (
"bytes"
"fmt"
"os"
"os/exec"
"strings"
"text/template"
specs "github.com/opencontainers/image-spec/specs-go"
)
var headerTemplate = template.Must(template.New("gen").Parse(`<title>image-spec {{.Version}}</title>
<base href="https://raw.githubusercontent.com/opencontainers/image-spec/{{.Branch}}/">`))
type Obj struct {
Version string
Branch string
}
func main() {
obj := Obj{
Version: specs.Version,
Branch: specs.Version,
}
if strings.HasSuffix(specs.Version, "-dev") {
cmd := exec.Command("git", "log", "-1", `--pretty=%H`, "HEAD")
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
obj.Branch = strings.Trim(out.String(), " \n\r")
}
headerTemplate.Execute(os.Stdout, obj)
}

View file

@ -20,5 +20,5 @@ for d in $(find . -type d -not -iwholename '*.git*' -a -not -iname '.tool' -a -n
--disable=gas \
--cyclo-over=35 \
--tests \
--deadline=10s "${d}"
--deadline=60s "${d}"
done

View file

@ -14,7 +14,7 @@ A maintainer SHOULD propose a motion on the dev@opencontainers.org mailing list
Voting on a proposed motion SHOULD happen on the dev@opencontainers.org mailing list (except [security issues](#security-issues)) with maintainers posting LGTM or REJECT.
Maintainers MAY also explicitly not vote by posting ABSTAIN (which is useful to revert a previous vote).
Maintainers MAY post multiple times (e.g. as they revise their position based on feeback), but only their final post counts in the tally.
Maintainers MAY post multiple times (e.g. as they revise their position based on feedback), but only their final post counts in the tally.
A proposed motion is adopted if two-thirds of votes cast, a quorum having voted, are in favor of the release.
Voting SHOULD remain open for a week to collect feedback from the wider community and allow the maintainers to digest the proposed motion.

View file

@ -36,9 +36,9 @@ $ make validate-examples
### Virtual schema http/FileSystem
The `schema` validator uses a virtual [http/FileSystem](https://golang.org/pkg/net/http/#FileSystem) to load the JSON schema files for validating OCI images and/or manifests.
The virtual file system is generated using the `esc` tool and compiled into consumers of the `schema` package so the JSON schema files don't have to be distributed along with and consumer binaries.
The virtual filesystem is generated using the `esc` tool and compiled into consumers of the `schema` package so the JSON schema files don't have to be distributed along with and consumer binaries.
Whenever changes are being done in any of the `schema/*.json` files, one must refresh the generated virtual file system.
Whenever changes are being done in any of the `schema/*.json` files, one must refresh the generated virtual filesystem.
Otherwise schema changes will not be visible inside `schema` consumers.
Prerequisites:

View file

@ -13,7 +13,7 @@ ifeq "$(strip $(PANDOC))" ''
-v $(shell pwd)/$(OUTPUT_DIRNAME)/:/$(OUTPUT_DIRNAME)/ \
-u $(shell id -u) \
--workdir /input \
docker.io/vbatts/pandoc:1.16.0.2-1.fc24
docker.io/vbatts/pandoc:1.17.0.3-2.fc25.x86_64
PANDOC_SRC := /input/
PANDOC_DST := /
endif
@ -30,7 +30,8 @@ DOC_FILES := \
layer.md \
config.md \
annotations.md \
considerations.md
considerations.md \
implementations.md
FIGURE_FILES := \
img/media-types.png
@ -40,6 +41,8 @@ DOC_FILENAME ?= oci-image-spec
EPOCH_TEST_COMMIT ?= v0.2.0
TOOLS := esc gitvalidation glide glide-vc
default: check-license lint test
help:
@ -65,16 +68,19 @@ $(OUTPUT_DIRNAME)/$(DOC_FILENAME).pdf: $(DOC_FILES) $(FIGURE_FILES)
else
$(OUTPUT_DIRNAME)/$(DOC_FILENAME).pdf: $(DOC_FILES) $(FIGURE_FILES)
@mkdir -p $(OUTPUT_DIRNAME)/ && \
$(PANDOC) -f markdown_github -t latex -o $(PANDOC_DST)$@ $(patsubst %,$(PANDOC_SRC)%,$(DOC_FILES))
$(PANDOC) -f markdown_github -t latex --latex-engine=xelatex -o $(PANDOC_DST)$@ $(patsubst %,$(PANDOC_SRC)%,$(DOC_FILES))
ls -sh $(shell readlink -f $@)
$(OUTPUT_DIRNAME)/$(DOC_FILENAME).html: $(DOC_FILES) $(FIGURE_FILES)
$(OUTPUT_DIRNAME)/$(DOC_FILENAME).html: header.html $(DOC_FILES) $(FIGURE_FILES)
@mkdir -p $(OUTPUT_DIRNAME)/ && \
cp -ap img/ $(shell pwd)/$(OUTPUT_DIRNAME)/&& \
$(PANDOC) -f markdown_github -t html5 -o $(PANDOC_DST)$@ $(patsubst %,$(PANDOC_SRC)%,$(DOC_FILES))
$(PANDOC) -f markdown_github -t html5 -H $(PANDOC_SRC)header.html --standalone -o $(PANDOC_DST)$@ $(patsubst %,$(PANDOC_SRC)%,$(DOC_FILES))
ls -sh $(shell readlink -f $@)
endif
header.html: .tool/genheader.go specs-go/version.go
go run .tool/genheader.go > $@
validate-examples: schema/fs.go
go test -run TestValidate ./schema
@ -98,7 +104,6 @@ test: schema/fs.go
img/%.png: img/%.dot
dot -Tpng $^ > $@
.PHONY: .gitvalidation
# When this is running in travis, it will only check the travis commit range
.gitvalidation:
@ -109,9 +114,10 @@ else
git-validation -v -run DCO,short-subject,dangling-whitespace -range $(EPOCH_TEST_COMMIT)..HEAD
endif
.PHONY: install.tools
install.tools: $(TOOLS:%=.install.%)
install.tools: .install.gitvalidation .install.glide .install.glide-vc
.install.esc:
go get -u github.com/mjibson/esc
.install.gitvalidation:
go get -u github.com/vbatts/git-validation
@ -123,13 +129,17 @@ install.tools: .install.gitvalidation .install.glide .install.glide-vc
go get -u github.com/sgotti/glide-vc
clean:
rm -rf *~ $(OUTPUT_DIRNAME)
rm -rf *~ $(OUTPUT_DIRNAME) header.html
.PHONY: \
$(TOOLS:%=.install.%) \
validate-examples \
check-license \
clean \
lint \
install.tools \
docs \
test \
.gitvalidation \
schema/fs.go \
schema-fs

View file

@ -7,7 +7,7 @@
The OCI Image Format project creates and maintains the software shipping container image format spec (OCI Image Format).
The specification can be found [here](spec.md).
**[The specification can be found here](spec.md).**
This repository also provides [Go types](specs-go), [intra-blob validation tooling, and JSON Schema](schema).
The Go types and validation should be compatible with the current Go release; earlier Go releases are not supported.
@ -42,22 +42,13 @@ To support this UX the OCI Image Format contains sufficient information to launc
A: Distribution, for example using HTTP as both Docker v2.2 and AppC do today, is currently out of scope on the [OCI Scope Table](https://www.opencontainers.org/about/oci-scope-table).
There has been [some discussion on the TOB mailing list](https://groups.google.com/a/opencontainers.org/d/msg/tob/A3JnmI-D-6Y/tLuptPDHAgAJ) to make distribution an optional layer, but this topic is a work in progress.
**Q: Why a new project?**
A: The [first OCI spec](https://github.com/opencontainers/runtime-spec) centered around defining the run side of a container.
This is generally seen to be an orthogonal concern to the shipping container component.
As practical examples of this separation you see many organizations separating these concerns into different teams and organizations: the Docker Distribution project and the Docker containerd project; Amazon ECS and Amazon EC2 Container Registry, etc.
**Q: Why work on this?**
A: We are seeing many independent implementations of container image handling including build systems, registries, and image analysis tools.
As an organization we would like to encourage this growth and bring people together to ensure a technically correct and open specification continues to evolve reflecting the OCI values.
**Q: What happens to AppC or Docker Image Formats?**
A: Existing formats can continue to be a proving ground for technologies, as needed.
The OCI Image Format project strives to provide a dependable open specification that can be shared between different tools and be evolved for years or decades of compatibility; as the deb and rpm format have.
Find more [FAQ on the OCI site](https://www.opencontainers.org/faq).
## Roadmap
The [GitHub milestones](https://github.com/opencontainers/image-spec/milestones) lay out the path to the OCI v1.0.0 release in late 2016.
@ -85,7 +76,7 @@ When in doubt, start on the [mailing-list](#mailing-list).
The contributors and maintainers of all OCI projects have a weekly meeting Wednesdays at 2:00 PM (USA Pacific).
Everyone is welcome to participate via [UberConference web][UberConference] or audio-only: +1-415-968-0849 (no PIN needed).
An initial agenda will be posted to the [mailing list](#mailing-list) earlier in the week, and everyone is welcome to propose additional topics or suggest other agenda alterations there.
Minutes are posted to the [mailing list](#mailing-list) and minutes from past calls are archived to the [wiki](https://github.com/opencontainers/runtime-spec/wiki) for those who are unable to join the call.
Minutes are posted to the [mailing list](#mailing-list) and minutes from past calls are archived [here][minutes].
## Mailing List
@ -173,3 +164,4 @@ Read more on [How to Write a Git Commit Message](http://chris.beams.io/posts/git
[UberConference]: https://www.uberconference.com/opencontainers
[irc-logs]: http://ircbot.wl.linuxfoundation.org/eavesdrop/%23opencontainers/
[minutes]: http://ircbot.wl.linuxfoundation.org/meetings/opencontainers/

View file

@ -5,20 +5,54 @@ This property contains arbitrary metadata.
## Rules
Annotations MUST be a key-value map where both the key and value MUST be strings.
While the value MUST be present, it MAY be an empty string.
Keys MUST be unique within this map, and best practice is to namespace the keys.
Keys SHOULD be named using a reverse domain notation - e.g. `com.example.myKey`.
Keys using the `org.opencontainers` namespace are reserved and MUST NOT be used by other specifications and extensions.
If there are no annotations then this property MUST either be absent or be an empty map.
Consumers MUST NOT generate an error if they encounter an unknown annotation key.
* Annotations MUST be a key-value map where both the key and value MUST be strings.
* While the value MUST be present, it MAY be an empty string.
* Keys MUST be unique within this map, and best practice is to namespace the keys.
* Keys SHOULD be named using a reverse domain notation - e.g. `com.example.myKey`.
* The prefix `org.opencontainers` is reserved for keys defined in Open Container Initiative (OCI) specifications and MUST NOT be used by other specifications and extensions.
* Keys using the `org.opencontainers.image` namespace are reserved for use in the OCI Image Specification and MUST NOT be used by other specifications and extensions, including other OCI specifications.
* If there are no annotations then this property MUST either be absent or be an empty map.
* Consumers MUST NOT generate an error if they encounter an unknown annotation key.
## Pre-Defined Annotation Keys
This specification defines the following annotation keys, intended for but not limited to [image index](image-index.md) and image [manifest](manifest.md) authors:
* **org.opencontainers.created** date on which the image was built (string, date-time as defined by [RFC 3339](https://tools.ietf.org/html/rfc3339#section-5.6)).
* **org.opencontainers.authors** contact details of the people or organization responsible for the image (freeform string)
* **org.opencontainers.homepage** URL to find more information on the image (string, a URL with scheme HTTP or HTTPS)
* **org.opencontainers.documentation** URL to get documentation on the image (string, a URL with scheme HTTP or HTTPS)
* **org.opencontainers.source** URL to get source code for the binary files in the image (string, a URL with scheme HTTP or HTTPS)
* **org.opencontainers.ref.name** Name of the reference (string)
* **org.opencontainers.image.created** date and time on which the image was built (string, date-time as defined by [RFC 3339](https://tools.ietf.org/html/rfc3339#section-5.6)).
* **org.opencontainers.image.authors** contact details of the people or organization responsible for the image (freeform string)
* **org.opencontainers.image.url** URL to find more information on the image (string)
* **org.opencontainers.image.documentation** URL to get documentation on the image (string)
* **org.opencontainers.image.source** URL to get source code for building the image (string)
* **org.opencontainers.image.version** version of the packaged software
* The version MAY match a label or tag in the source code repository
* version MAY be [Semantic versioning-compatible](http://semver.org/)
* **org.opencontainers.image.revision** Source control revision identifier for the packaged software.
* **org.opencontainers.image.vendor** Name of the distributing entity, organization or individual.
* **org.opencontainers.image.licenses** License(s) under which contained software is distributed as a [SPDX License Expression][spdx-license-expression].
* **org.opencontainers.image.ref.name** Name of the reference for a target (string). SHOULD only be considered valid when on descriptors on `index.json` within [image layout](image-layout.md).
* **org.opencontainers.image.title** Human-readable title of the image (string)
* **org.opencontainers.image.description** Human-readable description of the software packaged in the image (string)
## Back-compatibility with Label Schema
[Label Schema](https://label-schema.org) defined a number of conventional labels for container images, and these are now superceded by annotations with keys starting **org.opencontainers.image**.
While users are encouraged to use the **org.opencontainers.image** keys, tools MAY choose to support compatible annotations using the **org.label-schema** prefix as follows.
| `org.opencontainers.image` prefix | `org.label-schema prefix` | Compatibility notes |
|---------------------------|-------------------------|---------------------|
| `created` | `build-date` | Compatible |
| `url` | `url` | Compatible |
| `source` | `vcs-url` | Compatible |
| `version` | `version` | Compatible |
| `revision` | `vcs-ref` | Compatible |
| `vendor` | `vendor` | Compatible |
| `title` | `name` | Compatible |
| `description` | `description` | Compatible |
| `documentation` | `usage` | Value is compatible if the documentation is located by a URL |
| `authors` | | No equivalent in Label Schema |
| `licenses` | | No equivalent in Label Schema |
| `ref.name` | | No equivalent in Label Schema |
| | `schema-version`| No equivalent in the OCI Image Spec |
| | `docker.*`, `rkt.*` | No equivalent in the OCI Image Spec |
[spdx-license-expression]: https://spdx.org/spdx-specification-21-web-version#h.jxpfx0ykyb60

View file

@ -11,39 +11,82 @@ This specification uses the following terms:
### [Layer](layer.md)
Image filesystems are composed of *layers*.
Each layer represents a set of filesystem changes in a tar-based [layer format](layer.md), recording files to be added, changed, or deleted relative to its parent layer.
Layers do not have configuration metadata such as environment variables or default arguments - these are properties of the image as a whole rather than any particular layer.
Using a layer-based or union filesystem such as AUFS, or by computing the diff from filesystem snapshots, the filesystem changeset can be used to present a series of image layers as if they were one cohesive filesystem.
* Image filesystems are composed of *layers*.
* Each layer represents a set of filesystem changes in a tar-based [layer format](layer.md), recording files to be added, changed, or deleted relative to its parent layer.
* Layers do not have configuration metadata such as environment variables or default arguments - these are properties of the image as a whole rather than any particular layer.
* Using a layer-based or union filesystem such as AUFS, or by computing the diff from filesystem snapshots, the filesystem changeset can be used to present a series of image layers as if they were one cohesive filesystem.
### Image JSON
Each image has an associated JSON structure which describes some basic information about the image such as date created, author, as well as execution/runtime configuration like its entrypoint, default arguments, networking, and volumes.
The JSON structure also references a cryptographic hash of each layer used by the image, and provides history information for those layers.
This JSON is considered to be immutable, because changing it would change the computed [ImageID](#imageid).
Changing it means creating a new derived image, instead of changing the existing image.
* Each image has an associated JSON structure which describes some basic information about the image such as date created, author, as well as execution/runtime configuration like its entrypoint, default arguments, networking, and volumes.
* The JSON structure also references a cryptographic hash of each layer used by the image, and provides history information for those layers.
* This JSON is considered to be immutable, because changing it would change the computed [ImageID](#imageid).
* Changing it means creating a new derived image, instead of changing the existing image.
### Layer DiffID
A layer DiffID is a SHA256 digest over the layer's uncompressed tar archive and serialized in the descriptor digest format, e.g., `sha256:a9561eb1b190625c9adb5a9513e72c4dedafc1cb2d4c5236c9a6957ec7dfd5a9`.
Layers must be packed and unpacked reproducibly to avoid changing the layer DiffID, for example by using tar-split to save the tar headers.
A layer DiffID is the digest over the layer's uncompressed tar archive and serialized in the descriptor digest format, e.g., `sha256:a9561eb1b190625c9adb5a9513e72c4dedafc1cb2d4c5236c9a6957ec7dfd5a9`.
Layers SHOULD be packed and unpacked reproducibly to avoid changing the layer DiffID, for example by using [tar-split][] to save the tar headers.
NOTE: Do not confuse DiffIDs with [layer digests](manifest.md#image-manifest-property-descriptions), often referenced in the manifest, which are digests over compressed or uncompressed content.
### Layer ChainID
For convenience, it is sometimes useful to refer to a stack of layers with a single identifier.
This is called a `ChainID`.
For a single layer (or the layer at the bottom of a stack), the
`ChainID` is equal to the layer's `DiffID`.
Otherwise the `ChainID` is given by the formula:
`ChainID(layerN) = SHA256hex(ChainID(layerN-1) + " " + DiffID(layerN))`.
While a layer's `DiffID` identifies a single changeset, the `ChainID` identifies the subsequent application of those changesets.
This ensures that we have handles referring to both the layer itself, as well as the result of the application of a series of changesets.
Use in combination with `rootfs.diff_ids` while applying layers to a root filesystem to uniquely and safely identify the result.
#### Definition
The `ChainID` of an applied set of layers is defined with the following recursion:
```
ChainID(L₀) = DiffID(L₀)
ChainID(L₀|...|Lₙ₋₁|Lₙ) = Digest(ChainID(L₀|...|Lₙ₋₁) + " " + DiffID(Lₙ))
```
For this, we define the binary `|` operation to be the result of applying the right operand to the left operand.
For example, given base layer `A` and a changeset `B`, we refer to the result of applying `B` to `A` as `A|B`.
Above, we define the `ChainID` for a single layer (`L₀`) as equivalent to the `DiffID` for that layer.
Otherwise, the `ChainID` for a set of applied layers (`L₀|...|Lₙ₋₁|Lₙ`) is defined as the recursion `Digest(ChainID(L₀|...|Lₙ₋₁) + " " + DiffID(Lₙ))`.
#### Explanation
Let's say we have layers A, B, C, ordered from bottom to top, where A is the base and C is the top.
Defining `|` as a binary application operator, the root filesystem may be `A|B|C`.
While it is implied that `C` is only useful when applied to `A|B`, the identifier `C` is insufficient to identify this result, as we'd have the equality `C = A|B|C`, which isn't true.
The main issue is when we have two definitions of `C`, `C = C` and `C = A|B|C`.
If this is true (with some handwaving), `C = x|C` where `x = any application`.
This means that if an attacker can define `x`, relying on `C` provides no guarantee that the layers were applied in any order.
The `ChainID` addresses this problem by being defined as a compound hash.
__We differentiate the changeset `C`, from the order-dependent application `A|B|C` by saying that the resulting rootfs is identified by ChainID(A|B|C), which can be calculated by `ImageConfig.rootfs`.__
Let's expand the definition of `ChainID(A|B|C)` to explore its internal structure:
```
ChainID(A) = DiffID(A)
ChainID(A|B) = Digest(ChainID(A) + " " + DiffID(B))
ChainID(A|B|C) = Digest(ChainID(A|B) + " " + DiffID(C))
```
We can replace each definition and reduce to a single equality:
```
ChainID(A|B|C) = Digest(Digest(DiffID(A) + " " + DiffID(B)) + " " + DiffID(C))
```
Hopefully, the above is illustrative of the _actual_ contents of the `ChainID`.
Most importantly, we can easily see that `ChainID(C) != ChainID(A|B|C)`, otherwise, `ChainID(C) = DiffID(C)`, which is the base case, could not be true.
### ImageID
Each image's ID is given by the SHA256 hash of its [configuration JSON](#image-json).
It is represented as a hexadecimal encoding of 256 bits, e.g., `sha256:a9561eb1b190625c9adb5a9513e72c4dedafc1cb2d4c5236c9a6957ec7dfd5a9`.
Since the [configuration JSON](#image-json) that gets hashed references hashes of each layer in the image, this formulation of the ImageID makes images content-addresable.
Since the [configuration JSON](#image-json) that gets hashed references hashes of each layer in the image, this formulation of the ImageID makes images content-addressable.
## Properties
@ -69,7 +112,7 @@ Note: Any OPTIONAL field MAY also be set to null, which is equivalent to being a
- **config** *object*, OPTIONAL
The execution parameters which should be used as a base when running a container using the image.
The execution parameters which SHOULD be used as a base when running a container using the image.
This field can be `null`, in which case any execution parameters should be specified at creation of the container.
- **User** *string*, OPTIONAL
@ -89,40 +132,39 @@ Note: Any OPTIONAL field MAY also be set to null, which is equivalent to being a
- **Env** *array of strings*, OPTIONAL
Entries are in the format of `VARNAME="var value"`.
Entries are in the format of `VARNAME=VARVALUE`.
These values act as defaults and are merged with any specified when creating a container.
- **Entrypoint** *array of strings*, OPTIONAL
A list of arguments to use as the command to execute when the container starts.
This value acts as a default and is replaced by an entrypoint specified when creating a container.
These values act as defaults and may be replaced by an entrypoint specified when creating a container.
- **Cmd** *array of strings*, OPTIONAL
Default arguments to the entrypoint of the container.
These values act as defaults and are replaced with any specified when creating a container.
If an `Entrypoint` value is not specified, then the first entry of the `Cmd` array should be interpreted as the executable to run.
These values act as defaults and may be replaced by any specified when creating a container.
If an `Entrypoint` value is not specified, then the first entry of the `Cmd` array SHOULD be interpreted as the executable to run.
- **Volumes** *object*, OPTIONAL
A set of directories which should be created as data volumes in a container running this image.
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.
A set of directories which SHOULD be created as data volumes in a container running this image.
If a file or folder exists within the image with the same path as a data volume, that file or folder will be replaced by the data volume and never be merged.
**NOTE:** This JSON structure value is unusual because it is a direct JSON serialization of the Go type `map[string]struct{}` and is represented in JSON as an object mapping its keys to an empty object.
- **WorkingDir** *string*, OPTIONAL
Sets the current working directory of the entrypoint process in the container.
This value acts as a default and is replaced by a working directory specified when creating a container.
This value acts as a default and may be replaced by a working directory specified when creating a container.
- **Labels** *object*, OPTIONAL
The field contains arbitrary metadata for the container.
Labels MUST be a key-value map where both the key and value MUST be strings.
Keys MUST be unique within this map, and best practice is to namespace the keys.
Keys SHOULD be named using a reverse domain notation - e.g. `com.example.myKey`.
Keys using the `org.opencontainers` namespace are reserved and MUST NOT be used by subsequent specifications.
If there are no labels then this property MAY either be absent or an empty map.
Implementations that are reading/processing this configuration file MUST NOT generate an error if they encounter an unknown labels key.
This property MUST use the [annotation rules](annotations.md#rules).
- **StopSignal** *string*, OPTIONAL
The field contains the system call signal that will be sent to the container to exit. The signal can be a signal name in the format `SIGNAME`, for instance `SIGKILL` or `SIGRTMIN+3`.
- **rootfs** *object*, REQUIRED
@ -165,7 +207,7 @@ Note: Any OPTIONAL field MAY also be set to null, which is equivalent to being a
This field is used to mark if the history item created a filesystem diff.
It is set to true if this history item doesn't correspond to an actual layer in the rootfs section (for example, Dockerfile's [ENV](https://docs.docker.com/engine/reference/builder/#/env) command results in no change to the filesystem).
Any extra fields in the Image JSON struct are considered implementation specific and should be ignored by any implementations which are unable to interpret them.
Any extra fields in the Image JSON struct are considered implementation specific and MUST be ignored by any implementations which are unable to interpret them.
Whitespace is OPTIONAL and implementations MAY have compact JSON with no whitespace.
@ -230,3 +272,4 @@ Here is an example image configuration JSON document:
[rfc3339-s5.6]: https://tools.ietf.org/html/rfc3339#section-5.6
[runtime-platform]: https://github.com/opencontainers/runtime-spec/blob/v1.0.0-rc3/config.md#platform
[tar-split]: https://github.com/vbatts/tar-split

View file

@ -5,12 +5,12 @@ Instead they MUST ignore unknown properties.
# Canonicalization
OCI Images [are](descriptor.md) [content-addressable](image-layout.md).
One benefit of content-addressable storage is easy deduplication.
Many images might depend on a particular [layer](layer.md), but there will only be one blob in the [store](image-layout.md).
With a different serialization, that same semantic layer would have a different hash, and if both versions of the layer are referenced there will be two blobs with the same semantic content.
To allow efficient storage, implementations serializing content for blobs SHOULD use a canonical serialization.
This increases the chance that different implementations can push the same semantic content to the store without creating redundant blobs.
* OCI Images are [content-addressable](https://en.wikipedia.org/wiki/Content-addressable_storage). See [descriptors](descriptor.md) for more.
* One benefit of content-addressable storage is easy deduplication.
* Many images might depend on a particular [layer](layer.md), but there will only be one blob in the [store](image-layout.md).
* With a different serialization, that same semantic layer would have a different hash, and if both versions of the layer are referenced there will be two blobs with the same semantic content.
* To allow efficient storage, implementations serializing content for blobs SHOULD use a canonical serialization.
* This increases the chance that different implementations can push the same semantic content to the store without creating redundant blobs.
## JSON

View file

@ -1,13 +1,11 @@
# OCI Content Descriptors
An OCI image consists of several different components, arranged in a [Merkle Directed Acyclic Graph (DAG)](https://en.wikipedia.org/wiki/Merkle_tree).
References between components in the graph are expressed through _Content Descriptors_.
A Content Descriptor (or simply _Descriptor_) describes the disposition of the targeted content.
A Content Descriptor includes the type of the content, a content identifier (_digest_), and the byte-size of the raw content.
Descriptors SHOULD be embedded in other formats to securely reference external content.
Other formats SHOULD use descriptors to securely reference external content.
* An OCI image consists of several different components, arranged in a [Merkle Directed Acyclic Graph (DAG)](https://en.wikipedia.org/wiki/Merkle_tree).
* References between components in the graph are expressed through _Content Descriptors_.
* A Content Descriptor (or simply _Descriptor_) describes the disposition of the targeted content.
* A Content Descriptor includes the type of the content, a content identifier (_digest_), and the byte-size of the raw content.
* Descriptors SHOULD be embedded in other formats to securely reference external content.
* Other formats SHOULD use descriptors to securely reference external content.
This section defines the `application/vnd.oci.descriptor.v1+json` [media type](media-types.md).
@ -46,6 +44,8 @@ The following fields contain the primary properties that constitute a Descriptor
This OPTIONAL property contains arbitrary metadata for this descriptor.
This OPTIONAL property MUST use the [annotation rules](annotations.md#rules).
Descriptors pointing to [`application/vnd.oci.image.manifest.v1+json`](manifest.md) SHOULD include the extended field `platform`, see [Image Index Property Descriptions](image-index.md#image-index-property-descriptions) for details.
### Reserved
The following field keys are reserved and MUST NOT be used by other specifications.
@ -57,63 +57,100 @@ The following field keys are reserved and MUST NOT be used by other specificatio
All other fields may be included in other OCI specifications.
Extended _Descriptor_ field additions proposed in other OCI specifications SHOULD first be considered for addition into this specification.
## Digests and Verification
## Digests
The _digest_ property of a Descriptor acts as a content identifier, enabling [content addressability](http://en.wikipedia.org/wiki/Content-addressable_storage).
It uniquely identifies content by taking a [collision-resistant hash](https://en.wikipedia.org/wiki/Cryptographic_hash_function) of the bytes.
If the identifier can be communicated in a secure manner, one can retrieve the content from an insecure source, calculate the digest independently, and be certain that the correct content was obtained.
If the _digest_ can be communicated in a secure manner, one can verify content from an insecure source by recalculating the digest independently, ensuring the content has not been modified.
The value of the digest property, the _digest string_, is a serialized hash result, consisting of an _algorithm_ portion and a _hex_ portion.
The algorithm identifies the methodology used to calculate the digest; the hex portion is the lowercase hex-encoded result of the hash.
The value of the `digest` property is a string consisting of an _algorithm_ portion and an _encoded_ portion.
The _algorithm_ specifies the cryptographic hash function and encoding used for the digest; the _encoded_ portion contains the encoded result of the hash function.
The digest string MUST match the following grammar:
A digest string MUST match the following grammar:
```
digest := algorithm ":" hex
algorithm := /[a-z0-9_+.-]+/
hex := /[a-f0-9]+/
digest := algorithm ":" encoded
algorithm := algorithm-component [algorithm-separator algorithm-component]*
algorithm-component := /[a-z0-9]+/
algorithm-separator := /[+._-]/
encoded := /[a-zA-Z0-9=_-]+/
```
Note that _algorithm_ MAY impose algorithm-specific restriction on the grammar of the _encoded_ portion.
See also [Registered Algorithms](#registered-algorithms).
Some example digest strings include the following:
digest | description |
----------------------------------------------------------------------------------|------------------------------------------------
sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b | Common sha256 based digest |
digest | algorithm | Registered |
--------------------------------------------------------------------------|---------------------|------------|
`sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b` | [SHA-256](#sha-256) | Yes |
`sha512:401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b372742...` | [SHA-512](#sha-512) | Yes |
`multihash+base58:QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8` | Multihash | No |
`sha256+b64u:LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564` | SHA-256 with urlsafe base64 | No |
Before consuming content targeted by a descriptor from untrusted sources, the byte content SHOULD be verified against the digest.
Please see [Registered Algorithms](#registered-algorithms) for a list of registered algorithms.
Implementations SHOULD allow digests with unrecognized algorithms to pass validation if they comply with the above grammar.
While `sha256` will only use hex encoded digests, separators in _algorithm_ and alphanumerics in _encoded_ are included to allow for extensions.
As an example, we can parameterize the encoding and algorithm as `multihash+base58:QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8`, which would be considered valid but unregistered by this specification.
### Verification
Before consuming content targeted by a descriptor from untrusted sources, the byte content SHOULD be verified against the digest string.
Before calculating the digest, the size of the content SHOULD be verified to reduce hash collision space.
Heavy processing before calculating a hash SHOULD be avoided.
Implementations MAY employ some canonicalization of the underlying content to ensure stable content identifiers.
Implementations MAY employ [canonicalization](canonicalization.md#canonicalization) of the underlying content to ensure stable content identifiers.
### Algorithms
### Digest calculations
While the _algorithm_ component of the digest does allow one to utilize a wide variety of algorithms, compliant implementations SHOULD use [SHA-256](#sha-256).
Let's use a simple example in pseudo-code to demonstrate a digest calculation:
A _digest_ is calculated by the following pseudo-code, where `H` is the selected hash algorithm, identified by string `<alg>`:
```
let ID(C) = Descriptor.digest
let C = <bytes>
let D = '<alg>:' + EncodeHex(H(C))
let D = '<alg>:' + Encode(H(C))
let verified = ID(C) == D
```
Above, we define the content identifier as `ID(C)`, extracted from the `Descriptor.digest` field.
Content `C` is a string of bytes.
Function `H` returns the hash of `C` in bytes and is passed to function `EncodeHex` to obtain the _digest_.
Function `H` returns the hash of `C` in bytes and is passed to function `Encode` and prefixed with the algorithm to obtain the digest.
The result `verified` is true if `ID(C)` is equal to `D`, confirming that `C` is the content identified by `D`.
After verification, the following is true:
```
D == ID(C) == '<alg>:' + EncodeHex(H(C))
D == ID(C) == '<alg>:' + Encode(H(C))
```
The _digest_ is confirmed as the content identifier by independently calculating the _digest_.
### Registered algorithms
While the _algorithm_ component of the digest string allows the use of a variety of cryptographic algorithms, compliant implementations SHOULD use [SHA-256](#sha-256).
The following algorithm identifiers are currently defined by this specification:
| algorithm identifier | algorithm |
|----------------------|---------------------|
| `sha256` | [SHA-256](#sha-256) |
| `sha512` | [SHA-512](#sha-512) |
If a useful algorithm is not included in the above table, it SHOULD be submitted to this specification for registration.
#### SHA-256
[SHA-256](https://tools.ietf.org/html/rfc4634#page-7) is a collision-resistant hash function, chosen for ubiquity, reasonable size and secure characteristics.
[SHA-256][rfc4634-s4.1] is a collision-resistant hash function, chosen for ubiquity, reasonable size and secure characteristics.
Implementations MUST implement SHA-256 digest verification for use in descriptors.
When the _algorithm identifier_ is `sha256`, the _encoded_ portion MUST match `/[a-f0-9]{64}/`.
Note that `[A-F]` MUST NOT be used here.
#### SHA-512
[SHA-512][rfc4634-s4.2] is a collision-resistant hash function which [may be more perfomant][sha256-vs-sha512] than [SHA-256](#sha-256) on some CPUs.
Implementations MAY implement SHA-512 digest verification for use in descriptors.
When the _algorithm identifier_ is `sha512`, the _encoded_ portion MUST match `/[a-f0-9]{128}/`.
Note that `[A-F]` MUST NOT be used here.
## Examples
The following example describes a [_Manifest_](manifest.md#image-manifest) with a content identifier of "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270" and a size of 7682 bytes:
@ -140,6 +177,9 @@ In the following example, the descriptor indicates that the referenced manifest
```
[rfc3986]: https://tools.ietf.org/html/rfc3986
[rfc4634-s4.1]: https://tools.ietf.org/html/rfc4634#section-4.1
[rfc4634-s4.2]: https://tools.ietf.org/html/rfc4634#section-4.2
[rfc6838]: https://tools.ietf.org/html/rfc6838
[rfc6838-s4.2]: https://tools.ietf.org/html/rfc6838#section-4.2
[rfc7230-s2.7]: https://tools.ietf.org/html/rfc7230#section-2.7
[sha256-vs-sha512]: https://groups.google.com/a/opencontainers.org/forum/#!topic/dev/hsMw7cAwrZE

View file

@ -25,7 +25,7 @@ For the media type(s) that this document is compatible with, see the [matrix][ma
This REQUIRED property contains a list of [manifests](manifest.md) for specific platforms.
While this property MUST be present, the size of the array MAY be zero.
Each object in `manifests` has the base properties of [descriptor](descriptor.md) with the following additional properties and restrictions:
Each object in `manifests` includes a set of [descriptor properties](descriptor.md#properties) with the following additional properties and restrictions:
- **`mediaType`** *string*
@ -40,7 +40,7 @@ For the media type(s) that this document is compatible with, see the [matrix][ma
- **`platform`** *object*
This OPTIONAL property describes the platform which the image in the manifest runs on.
This OPTIONAL property describes the minimum runtime requirements of the image.
This property SHOULD be present if its target is platform-specific.
- **`architecture`** *string*
@ -55,7 +55,9 @@ For the media type(s) that this document is compatible with, see the [matrix][ma
- **`os.version`** *string*
This OPTIONAL property specifies the operating system version, for example `10.0.10586`.
This OPTIONAL property specifies the version of the operating system targeted by the referenced blob.
Implementations MAY refuse to use manifests where `os.version` is not known to work with the host OS version.
Valid values are implementation-defined. e.g. `10.0.14393.1066` on `windows`.
- **`os.features`** *array of strings*
@ -63,11 +65,20 @@ For the media type(s) that this document is compatible with, see the [matrix][ma
- **`variant`** *string*
This OPTIONAL property specifies the variant of the CPU, for example `armv6l` to specify a particular CPU variant of the ARM CPU.
This OPTIONAL property specifies the variant of the CPU.
Image indexes SHOULD use, and implementations SHOULD understand, values listed in the following table.
When the variant of the CPU is not listed in the table, values are implementation-defined and SHOULD be submitted to this specification for standardization.
| ISA/ABI | `architecture` | `variant` |
|-----------------|----------------|-------------|
| ARM 32-bit, v6 | `arm` | `v6` |
| ARM 32-bit, v7 | `arm` | `v7` |
| ARM 32-bit, v8 | `arm` | `v8` |
| ARM 64-bit, v8 | `arm64` | `v8` |
- **`features`** *array of strings*
This OPTIONAL property specifies an array of strings, each specifying a mandatory CPU feature (for example `sse4` or `aes`).
This property is RESERVED for future versions of the specification.
- **`annotations`** *string-string map*
@ -98,10 +109,7 @@ For the media type(s) that this document is compatible with, see the [matrix][ma
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
"platform": {
"architecture": "amd64",
"os": "linux",
"os.features": [
"sse4"
]
"os": "linux"
}
}
],

View file

@ -1,7 +1,7 @@
## OCI Image Layout Specification
The OCI Image Layout is a slash separated layout of OCI content-addressable blobs and [location-addressable](https://en.wikipedia.org/wiki/Content-addressable_storage#Content-addressed_vs._location-addressed) references (refs).
This layout MAY be used in a variety of different transport mechanisms: archive formats (e.g. tar, zip), shared filesystem environments (e.g. nfs), or networked file fetching (e.g. http, ftp, rsync).
* The OCI Image Layout is a slash separated layout of OCI content-addressable blobs and [location-addressable](https://en.wikipedia.org/wiki/Content-addressable_storage#Content-addressed_vs._location-addressed) references (refs).
* This layout MAY be used in a variety of different transport mechanisms: archive formats (e.g. tar, zip), shared filesystem environments (e.g. nfs), or networked file fetching (e.g. http, ftp, rsync).
Given an image layout and a ref, a tool can create an [OCI Runtime Specification bundle](https://github.com/opencontainers/runtime-spec/blob/v1.0.0-rc3/bundle.md) by:
@ -15,7 +15,7 @@ The image layout is as follows:
- `blobs` directory
- Contains content-addressable blobs
- A blob has no schema and should be considered opaque
- A blob has no schema and SHOULD be considered opaque
- Directory MUST exist and MAY be empty
- See [blobs](#blobs) section
- `oci-layout` file
@ -26,8 +26,7 @@ The image layout is as follows:
- It MAY include additional fields
- `index.json` file
- It MUST exist
- It MUST be a JSON object
- It MUST have the base properties of an [image index](image-index.md).
- It MUST be an [image index](image-index.md) JSON object.
- See [index.json](#indexjson-file) section
## Example Layout
@ -53,14 +52,11 @@ afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51 ./blobs/sha256/
## Blobs
Object names in the `blobs` subdirectories are composed of a directory for each hash algorithm, the children of which will contain the actual content.
A blob, referenced with digest `<alg>:<hex>` (per [descriptor](descriptor.md#digests-and-verification)), MUST have its content stored in a file under `blobs/<alg>/<hex>`.
The character set of the entry name for `<hex>` and `<alg>` MUST match the respective grammar elements described in [descriptor](descriptor.md#digests-and-verification).
For example `sha256:5b` will map to the layout `blobs/sha256/5b`.
The blobs directory MAY contain blobs which are not referenced by any of the [refs](#indexjson-file).
The blobs directory MAY be missing referenced blobs, in which case the missing blobs SHOULD be fulfilled by an external blob store.
* Object names in the `blobs` subdirectories are composed of a directory for each hash algorithm, the children of which will contain the actual content.
* The content of `blobs/<alg>/<encoded>` MUST match the digest `<alg>:<encoded>` (referenced per [descriptor](descriptor.md#digests-and-verification)). For example, the content of `blobs/sha256/da39a3ee5e6b4b0d3255bfef95601890afd80709` MUST match the digest `sha256:da39a3ee5e6b4b0d3255bfef95601890afd80709`.
* The character set of the entry name for `<alg>` and `<encoded>` MUST match the respective grammar elements described in [descriptor](descriptor.md#digests-and-verification).
* The blobs directory MAY contain blobs which are not referenced by any of the [refs](#indexjson-file).
* The blobs directory MAY be missing referenced blobs, in which case the missing blobs SHOULD be fulfilled by an external blob store.
### Example Blobs
@ -131,10 +127,11 @@ $ cat ./blobs/sha256/e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7f
This JSON object serves as a marker for the base of an Open Container Image Layout and to provide the version of the image-layout in use.
The `imageLayoutVersion` value will align with the OCI Image Specification version at the time changes to the layout are made, and will pin a given version until changes to the image layout are required.
This section defines the `application/vnd.oci.layout.header.v1+json` [media type](media-types.md).
### oci-layout Example
```json
```json,title=OCI%20Layout&mediatype=application/vnd.oci.layout.header.v1%2Bjson
{
"imageLayoutVersion": "1.0.0"
}
@ -147,17 +144,17 @@ The [image index](image-index.md) is a multi-descriptor entry point.
This index provides an established path (`/index.json`) to have an entry point for an image-layout and to discover auxiliary descriptors.
No semantic restriction is given for the "org.opencontainers.ref.name" annotation of descriptors.
In general the `mediaType` of each [descriptor][descriptors] object in the `manifests` field will be either `application/vnd.oci.image.index.v1+json` or `application/vnd.oci.image.manifest.v1+json`.
Future versions of the spec MAY use a different mediatype (i.e. a new versioned format).
An encountered `mediaType` that is unknown SHOULD be safely ignored.
* No semantic restriction is given for the "org.opencontainers.image.ref.name" annotation of descriptors.
* In general the `mediaType` of each [descriptor][descriptors] object in the `manifests` field will be either `application/vnd.oci.image.index.v1+json` or `application/vnd.oci.image.manifest.v1+json`.
* Future versions of the spec MAY use a different mediatype (i.e. a new versioned format).
* An encountered `mediaType` that is unknown SHOULD be safely ignored.
**Implementor's Note:**
A common use case of descriptors with a "org.opencontainers.ref.name" annotation is representing a "tag" for a container image.
A common use case of descriptors with a "org.opencontainers.image.ref.name" annotation is representing a "tag" for a container image.
For example, an image may have a tag for different versions or builds of the software.
In the wild you often see "tags" like "v1.0.0-vendor.0", "2.0.0-debug", etc.
Those tags will often be represented in an image-layout repository with matching "org.opencontainers.ref.name" annotations like "v1.0.0-vendor.0", "2.0.0-debug", etc.
Those tags will often be represented in an image-layout repository with matching "org.opencontainers.image.ref.name" annotations like "v1.0.0-vendor.0", "2.0.0-debug", etc.
### Index Example
@ -171,7 +168,7 @@ Those tags will often be represented in an image-layout repository with matching
"size": 7143,
"digest": "sha256:0228f90e926ba6b96e4f39cf294b2586d38fbb5a1e385c05cd1ee40ea54fe7fd",
"annotations": {
"org.opencontainers.ref.name": "stable-release"
"org.opencontainers.image.ref.name": "stable-release"
}
},
{
@ -183,7 +180,7 @@ Those tags will often be represented in an image-layout repository with matching
"os": "linux"
},
"annotations": {
"org.opencontainers.ref.name": "v1.0"
"org.opencontainers.image.ref.name": "v1.0"
}
},
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View file

@ -0,0 +1,21 @@
# OCI Image Implementations
Projects or Companies currently adopting the OCI Image Specification
* [projectatomic/skopeo](https://github.com/projectatomic/skopeo)
* [Amazon Elastic Container Registry (ECR)](https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-manifest-formats.html) ([announcement](https://aws.amazon.com/about-aws/whats-new/2017/01/amazon-ecr-supports-docker-image-manifest-v2-schema-2/))
* [openSUSE/umoci](https://github.com/openSUSE/umoci)
* [cloudfoundry/grootfs](https://github.com/cloudfoundry/grootfs) ([source](https://github.com/cloudfoundry/grootfs/blob/c3da26e1e463b51be1add289032f3dca6698b335/fetcher/remote/docker_src.go))
* [Mesos plans](https://issues.apache.org/jira/browse/MESOS-5011) ([design doc](https://docs.google.com/document/d/1Pus7D-inIBoLSIPyu3rl_apxvUhtp3rp0_b0Ttr2Xww/edit#heading=h.hrvk2wboog4p))
* [Docker](https://github.com/docker)
- [docker/containerd](https://github.com/docker/containerd)
- [docker/docker (`docker save/load` WIP)](https://github.com/docker/docker/pull/26369)
- [docker/distribution (registry PR)](https://github.com/docker/distribution/pull/2076)
* [Containers](https://github.com/containers/)
- [containers/build](https://github.com/containers/build)
- [containers/image](https://github.com/containers/image)
* [coreos/rkt](https://github.com/coreos/rkt)
* [box-builder/box](https://github.com/box-builder/box)
_(to add your project please open a [pull-request](https://github.com/opencontainers/image-spec/pulls))_

View file

@ -8,13 +8,13 @@ This section defines the `application/vnd.oci.image.layer.v1.tar`, `application/
## `+gzip` Media Types
The media type `application/vnd.oci.image.layer.v1.tar+gzip` represents an `application/vnd.oci.image.layer.v1.tar` payload which has been compressed with [gzip][rfc1952].
The media type `application/vnd.oci.image.layer.nondistributable.v1.tar+gzip` represents an `application/vnd.oci.image.layer.nondistributable.v1.tar` payload which has been compressed with [gzip][rfc1952].
* The media type `application/vnd.oci.image.layer.v1.tar+gzip` represents an `application/vnd.oci.image.layer.v1.tar` payload which has been compressed with [gzip][rfc1952_2].
* The media type `application/vnd.oci.image.layer.nondistributable.v1.tar+gzip` represents an `application/vnd.oci.image.layer.nondistributable.v1.tar` payload which has been compressed with [gzip][rfc1952_2].
## Distributable Format
Layer Changesets for the [media type](media-types.md) `application/vnd.oci.image.layer.v1.tar` MUST be packaged in [tar archive][tar-archive].
Layer Changesets for the [media type](media-types.md) `application/vnd.oci.image.layer.v1.tar` MUST NOT include duplicate entries for file paths in the resulting [tar archive][tar-archive].
* Layer Changesets for the [media type](media-types.md) `application/vnd.oci.image.layer.v1.tar` MUST be packaged in [tar archive][tar-archive].
* Layer Changesets for the [media type](media-types.md) `application/vnd.oci.image.layer.v1.tar` MUST NOT include duplicate entries for file paths in the resulting [tar archive][tar-archive].
## Change Types
@ -58,17 +58,14 @@ Where supported, MUST include file attributes for Additions and Modifications in
#### Hardlinks
Hardlinks are a [POSIX concept](http://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html) for having one or more directory entries for the same file on the same device.
Not all filesystems support hardlinks (e.g. [FAT](https://en.wikipedia.org/wiki/File_Allocation_Table)).
Hardlinks are possible with all [file types](#file-types) except `directories`.
Non-directory files are considered "hardlinked" when their link count is greater than 1.
Hardlinked files are on a same device (i.e. comparing Major:Minor pair) and have the same inode.
The corresponding files that share the link with the > 1 linkcount may be outside the directory that the changeset is being produced from, in which case the `linkname` is not recorded in the changeset.
Hardlinks are stored in a tar archive with type of a `1` char, per the [GNU Basic Tar Format][gnu-tar-standard] and [libarchive tar(5)][libarchive-tar].
While approaches to deriving new or changed hardlinks may vary, a possible approach is:
* Hardlinks are a [POSIX concept](http://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html) for having one or more directory entries for the same file on the same device.
* Not all filesystems support hardlinks (e.g. [FAT](https://en.wikipedia.org/wiki/File_Allocation_Table)).
* Hardlinks are possible with all [file types](#file-types) except `directories`.
* Non-directory files are considered "hardlinked" when their link count is greater than 1.
* Hardlinked files are on a same device (i.e. comparing Major:Minor pair) and have the same inode.
* The corresponding files that share the link with the > 1 linkcount may be outside the directory that the changeset is being produced from, in which case the `linkname` is not recorded in the changeset.
* Hardlinks are stored in a tar archive with type of a `1` char, per the [GNU Basic Tar Format][gnu-tar-standard] and [libarchive tar(5)][libarchive-tar].
* While approaches to deriving new or changed hardlinks may vary, a possible approach is:
```
SET LinkMap to map[< Major:Minor String >]map[< inode integer >]< path string >
@ -213,11 +210,9 @@ To signify that the resource `./etc/my-app-config` MUST be removed when the chan
## Applying Changesets
Layer Changesets of [media type](media-types.md) `application/vnd.oci.image.layer.v1.tar` are _applied_, rather than simply extracted as tar archives.
Applying a layer changeset requires special consideration for the [whiteout](#whiteouts) files.
In the absence of any [whiteout](#whiteouts) files in a layer changeset, the archive is extracted like a regular tar archive.
* Layer Changesets of [media type](media-types.md) `application/vnd.oci.image.layer.v1.tar` are _applied_, rather than simply extracted as tar archives.
* Applying a layer changeset requires special consideration for the [whiteout](#whiteouts) files.
* In the absence of any [whiteout](#whiteouts) files in a layer changeset, the archive is extracted like a regular tar archive.
### Changeset over existing files
@ -230,13 +225,13 @@ In all other cases, the implementation MUST do the semantic equivalent of the fo
## Whiteouts
A whiteout file is an empty file with a special filename that signifies a path should be deleted.
A whiteout filename consists of the prefix `.wh.` plus the basename of the path to be deleted.
As files prefixed with `.wh.` are special whiteout markers, it is not possible to create a filesystem which has a file or directory with a name beginning with `.wh.`.
* A whiteout file is an empty file with a special filename that signifies a path should be deleted.
* A whiteout filename consists of the prefix `.wh.` plus the basename of the path to be deleted.
* As files prefixed with `.wh.` are special whiteout markers, it is not possible to create a filesystem which has a file or directory with a name beginning with `.wh.`.
* Once a whiteout is applied, the whiteout itself MUST also be hidden.
* Whiteout files MUST only apply to resources in lower/parent layers.
* Files that are present in the same layer as a whiteout file can only be hidden by whiteout files in subsequent layers.
Once a whiteout is applied, the whiteout itself MUST also be hidden.
Whiteout files MUST only apply to resources in lower/parent layers.
Files that are present in the same layer as a whiteout file can only be hidden by whiteout files in subsequent layers.
The following is a base layer with several resources:
```
@ -271,8 +266,9 @@ Implementations SHOULD generate layers such that the whiteout files appear befor
### Opaque Whiteout
In addition to expressing that a single entry should be removed from a lower layer, layers may remove all of the children using an opaque whiteout entry.
An opaque whiteout entry is a file with the name `.wh..wh..opq` indicating that all siblings are hidden in the lower layer.
* In addition to expressing that a single entry should be removed from a lower layer, layers may remove all of the children using an opaque whiteout entry.
* An opaque whiteout entry is a file with the name `.wh..wh..opq` indicating that all siblings are hidden in the lower layer.
Let's take the following base layer as an example:
```
@ -323,5 +319,5 @@ Implementations SHOULD NOT upload layers tagged with this media type; however, s
[libarchive-tar]: https://github.com/libarchive/libarchive/wiki/ManPageTar5#POSIX_ustar_Archives
[gnu-tar-standard]: http://www.gnu.org/software/tar/manual/html_node/Standard.html
[rfc1952]: https://tools.ietf.org/html/rfc1952
[rfc1952_2]: https://tools.ietf.org/html/rfc1952
[tar-archive]: https://en.wikipedia.org/wiki/Tar_(computing)

View file

@ -22,7 +22,7 @@ Unlike the [image index](image-index.md), which contains information about a set
- **`mediaType`** *string*
This property is *reserved* for use, to [maintain compatibility][matrix].
This property is *reserved* for use, to [maintain compatibility](media-types.md#compatibility-matrix).
When used, this field contains the media type of this document, which differs from the [descriptor](descriptor.md#properties) use of `mediaType`.
- **`config`** *[descriptor](descriptor.md)*
@ -44,7 +44,7 @@ Unlike the [image index](image-index.md), which contains information about a set
Each item in the array MUST be a [descriptor](descriptor.md).
The array MUST have the base layer at index 0.
Subsequent layers MUST then follow in stack order (i.e. from `layers[0]` to `layers[len(layers)-1]`).
The final filesystem layout MUST match the result of [applying](layer.md#applying) the layers to an empty directory.
The final filesystem layout MUST match the result of [applying](layer.md#applying-changesets) the layers to an empty directory.
The [ownership, mode, and other attributes](layer.md#file-attributes) of the initial empty directory are unspecified.
Beyond the [descriptor requirements](descriptor.md#properties), the value has the following additional restrictions:

View file

@ -3,6 +3,7 @@
The following media types identify the formats described here and their referenced resources:
- `application/vnd.oci.descriptor.v1+json`: [Content Descriptor](descriptor.md)
- `application/vnd.oci.layout.header.v1+json`: [OCI Layout](image-layout.md#oci-layout-file)
- `application/vnd.oci.image.index.v1+json`: [Image Index](image-index.md)
- `application/vnd.oci.image.manifest.v1+json`: [Image manifest](manifest.md#image-manifest)
- `application/vnd.oci.image.config.v1+json`: [Image config](config.md)
@ -62,6 +63,6 @@ The following figure shows how the above media types reference each other:
![](img/media-types.png)
[Descriptors](descriptor.md) are used for all references.
The image-index being a "fat manifest" references one or more image manifests per target platform. An image manifest references exactly one target configuration and possibly many layers.
The image-index being a "fat manifest" references a list of image manifests per target platform. An image manifest references exactly one target configuration and possibly many layers.
[rfc1952]: https://tools.ietf.org/html/rfc1952

View file

@ -27,7 +27,7 @@ import (
var compatMap = map[string]string{
"application/vnd.docker.distribution.manifest.list.v2+json": v1.MediaTypeImageIndex,
"application/vnd.docker.distribution.manifest.v2+json": v1.MediaTypeImageManifest,
"application/vnd.docker.image.rootfs.diff.tar.gzip": v1.MediaTypeImageLayer,
"application/vnd.docker.image.rootfs.diff.tar.gzip": v1.MediaTypeImageLayerGzip,
"application/vnd.docker.container.image.v1+json": v1.MediaTypeImageConfig,
}
@ -49,7 +49,7 @@ func TestBackwardsCompatibilityImageIndex(t *testing.T) {
fail bool
}{
{
digest: "sha256:219f4b61132fe9d09b0ec5c15517be2ca712e4744b0e0cc3be71295b35b2a467",
digest: "sha256:4ffd0883f25635999f04ea543240a27c9a4341979ff7d46a9774f71512eebb1f",
imageIndex: `{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
@ -69,10 +69,7 @@ func TestBackwardsCompatibilityImageIndex(t *testing.T) {
"digest": "sha256:ae1b0e06e8ade3a11267564a26e750585ba2259c0ecab59ab165ad1af41d1bdd",
"platform": {
"architecture": "amd64",
"os": "linux",
"features": [
"sse"
]
"os": "linux"
}
},
{
@ -91,7 +88,7 @@ func TestBackwardsCompatibilityImageIndex(t *testing.T) {
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "armv7"
"variant": "v7"
}
},
{
@ -101,7 +98,7 @@ func TestBackwardsCompatibilityImageIndex(t *testing.T) {
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "armv8"
"variant": "v8"
}
}
]

View file

@ -80,6 +80,9 @@
"type": "null"
}
]
},
"StopSignal": {
"type": "string"
}
}
},

View file

@ -164,6 +164,7 @@ func TestConfig(t *testing.T) {
"/var/job-result-data": {},
"/var/log/my-app-logs": {}
},
"StopSignal": "SIGKILL",
"WorkingDir": "/home/alice",
"Labels": {
"com.example.project.git.url": "https://example.com/project.git",

View file

@ -6,27 +6,23 @@
"properties": {
"mediaType": {
"description": "the mediatype of the referenced object",
"$ref": "defs-image.json#/definitions/mediaType"
"$ref": "defs-descriptor.json#/definitions/mediaType"
},
"size": {
"description": "the size in bytes of the referenced object",
"$ref": "defs.json#/definitions/int64"
},
"digest": {
"description": "the cryptographic checksum digest of the object, in the pattern '<hash>:<hexadecimal digest>'",
"$ref": "defs-image.json#/definitions/digest"
"description": "the cryptographic checksum digest of the object, in the pattern '<algorithm>:<encoded>'",
"$ref": "defs-descriptor.json#/definitions/digest"
},
"urls": {
"description": "a list of urls from which this object may be downloaded",
"type": "array",
"items": {
"type": "string",
"format": "uri"
}
"$ref": "defs-descriptor.json#/definitions/urls"
},
"annotations": {
"id": "https://opencontainers.org/schema/image/descriptor/annotations",
"$ref": "defs-image.json#/definitions/annotations"
"$ref": "defs-descriptor.json#/definitions/annotations"
}
},
"required": [

View file

@ -0,0 +1,27 @@
{
"description": "Definitions particular to OpenContainer Descriptor Specification",
"definitions": {
"mediaType": {
"id": "https://opencontainers.org/schema/image/descriptor/mediaType",
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9!#$&-^_.+]{0,126}/[A-Za-z0-9][A-Za-z0-9!#$&-^_.+]{0,126}$"
},
"digest": {
"description": "the cryptographic checksum digest of the object, in the pattern '<algorithm>:<encoded>'",
"type": "string",
"pattern": "^[a-z0-9]+(?:[+._-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$"
},
"urls": {
"description": "a list of urls from which this object may be downloaded",
"type": "array",
"items": {
"type": "string",
"format": "uri"
}
},
"annotations": {
"id": "https://opencontainers.org/schema/image/descriptor/annotations",
"$ref": "defs.json#/definitions/mapStringString"
}
}
}

View file

@ -1,99 +0,0 @@
{
"description": "Definitions particular to OpenContainer Image Specification",
"definitions": {
"mediaType": {
"id": "https://opencontainers.org/schema/image/mediaType",
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9!#$&-^_.+]{0,126}/[A-Za-z0-9][A-Za-z0-9!#$&-^_.+]{0,126}$"
},
"digest": {
"description": "the cryptographic checksum digest of the object, in the pattern '<hash>:<hexadecimal digest>'",
"type": "string",
"pattern": "^[a-z0-9_+.-]+:[a-f0-9]+$"
},
"manifestDescriptor": {
"id": "https://opencontainers.org/schema/image/manifestDescriptor",
"type": "object",
"required": [
"mediaType",
"size",
"digest"
],
"properties": {
"mediaType": {
"description": "the mediatype of the referenced object",
"$ref": "#/definitions/mediaType"
},
"size": {
"description": "the size in bytes of the referenced object",
"$ref": "defs.json#/definitions/int64"
},
"digest": {
"description": "the cryptographic checksum digest of the object, in the pattern '<hash>:<hexadecimal digest>'",
"$ref": "#/definitions/digest"
},
"urls": {
"description": "a list of urls from which this object may be downloaded",
"type": "array",
"items": {
"type": "string",
"format": "uri"
}
},
"platform": {
"id": "https://opencontainers.org/schema/image/platform",
"type": "object",
"required": [
"architecture",
"os"
],
"properties": {
"architecture": {
"id": "https://opencontainers.org/schema/image/platform/architecture",
"type": "string"
},
"os": {
"id": "https://opencontainers.org/schema/image/platform/os",
"type": "string"
},
"os.version": {
"id": "https://opencontainers.org/schema/image/platform/os.version",
"type": "string"
},
"os.features": {
"id": "https://opencontainers.org/schema/image/platform/os.features",
"type": "array",
"items": {
"type": "string"
}
},
"variant": {
"type": "string"
},
"features": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"annotations": {
"id": "https://opencontainers.org/schema/image/descriptor/annotations",
"$ref": "#/definitions/annotations"
}
}
},
"annotations": {
"id": "https://opencontainers.org/schema/image/annotations",
"oneOf": [
{
"$ref": "defs.json#/definitions/mapStringString"
},
{
"type": "null"
}
]
}
}
}

View file

@ -86,83 +86,6 @@
"type": "object"
}
}
},
"UID": {
"$ref": "#/definitions/uint32"
},
"GID": {
"$ref": "#/definitions/uint32"
},
"ArrayOfGIDs": {
"type": "array",
"items": {
"$ref": "#/definitions/GID"
}
},
"ArrayOfStrings": {
"type": "array",
"items": {
"type": "string"
}
},
"FilePath": {
"type": "string"
},
"Env": {
"$ref": "#/definitions/ArrayOfStrings"
},
"Hook": {
"properties": {
"path": {
"$ref": "#/definitions/FilePath"
},
"args": {
"$ref": "#/definitions/ArrayOfStrings"
},
"env": {
"$ref": "#/definitions/Env"
}
}
},
"ArrayOfHooks": {
"type": "array",
"items": {
"$ref": "#/definitions/Hook"
}
},
"IDMapping": {
"properties": {
"hostID": {
"$ref": "#/definitions/uint32"
},
"containerID": {
"$ref": "#/definitions/uint32"
},
"size": {
"$ref": "#/definitions/uint32"
}
}
},
"Mount": {
"properties": {
"source": {
"$ref": "#/definitions/FilePath"
},
"destination": {
"$ref": "#/definitions/FilePath"
},
"options": {
"$ref": "#/definitions/ArrayOfStrings"
},
"type": {
"type": "string"
}
},
"required": [
"destination",
"source",
"type"
]
}
}
}

View file

@ -191,7 +191,7 @@ func TestDescriptor(t *testing.T) {
fail: true,
},
// expected failure: digest does not match pattern (invalid hash characters)
// expected failure: digest does not match pattern (characters needs to be lower for sha256)
{
descriptor: `
{
@ -202,6 +202,96 @@ func TestDescriptor(t *testing.T) {
`,
fail: true,
},
// expected success: valid URL entry
{
descriptor: `
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 7682,
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
"urls": [
"https://example.com/foo"
]
}
`,
fail: false,
},
// expected failure: urls does not match format (invalide url characters)
{
descriptor: `
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 7682,
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
"urls": [
"value"
]
}
`,
fail: true,
},
{
descriptor: `{
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "sha256+b64:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
}`,
},
{
descriptor: `{
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "sha256+b64:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
}`,
},
{
descriptor: `{
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "sha256+foo-bar:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
}`,
},
{
descriptor: `
{
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "sha256.foo-bar:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
}`,
},
{
descriptor: `{
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "multihash+base58:QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8"
}`,
},
{
// fail: repeated separators in algorithm
descriptor: `{
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "sha256+foo+-b:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
}`,
fail: true,
},
{
descriptor: `{
"digest": "sha256+b64u:LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564",
"size": 1000000,
"mediaType": "application/vnd.oci.image.config.v1+json"
}`,
},
{
// test for those who cannot use modulo arithmetic to recover padding.
descriptor: `{
"digest": "sha256+b64u.unknownlength:LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564=",
"size": 1000000,
"mediaType": "application/vnd.oci.image.config.v1+json"
}`,
},
} {
r := strings.NewReader(tt.descriptor)
err := schema.ValidatorMediaTypeDescriptor.Validate(r)

File diff suppressed because it is too large Load diff

View file

@ -18,4 +18,4 @@ package schema
// using esc (https://github.com/mjibson/esc).
// This should generally be invoked with `make schema-fs`
//go:generate esc -private -pkg=schema -ignore=.*go -ignore=.*swp .
//go:generate esc -private -pkg=schema -include=.*\.json$ .

View file

@ -14,18 +14,73 @@
"manifests": {
"type": "array",
"items": {
"$ref": "defs-image.json#/definitions/manifestDescriptor"
}
},
"elements": {
"type": "array",
"items": {
"$ref": "content-descriptor.json"
"id": "https://opencontainers.org/schema/image/manifestDescriptor",
"type": "object",
"required": [
"mediaType",
"size",
"digest"
],
"properties": {
"mediaType": {
"description": "the mediatype of the referenced object",
"$ref": "defs-descriptor.json#/definitions/mediaType"
},
"size": {
"description": "the size in bytes of the referenced object",
"$ref": "defs.json#/definitions/int64"
},
"digest": {
"description": "the cryptographic checksum digest of the object, in the pattern '<algorithm>:<encoded>'",
"$ref": "defs-descriptor.json#/definitions/digest"
},
"urls": {
"description": "a list of urls from which this object may be downloaded",
"$ref": "defs-descriptor.json#/definitions/urls"
},
"platform": {
"id": "https://opencontainers.org/schema/image/platform",
"type": "object",
"required": [
"architecture",
"os"
],
"properties": {
"architecture": {
"id": "https://opencontainers.org/schema/image/platform/architecture",
"type": "string"
},
"os": {
"id": "https://opencontainers.org/schema/image/platform/os",
"type": "string"
},
"os.version": {
"id": "https://opencontainers.org/schema/image/platform/os.version",
"type": "string"
},
"os.features": {
"id": "https://opencontainers.org/schema/image/platform/os.features",
"type": "array",
"items": {
"type": "string"
}
},
"variant": {
"type": "string"
}
}
}
},
"annotations": {
"id": "https://opencontainers.org/schema/image/descriptor/annotations",
"$ref": "defs-descriptor.json#/definitions/annotations"
}
}
}
},
"annotations": {
"id": "https://opencontainers.org/schema/image/index/annotations",
"$ref": "defs-image.json#/definitions/annotations"
"$ref": "defs-descriptor.json#/definitions/annotations"
}
},
"required": [

View file

@ -1,11 +1,11 @@
{
"description": "OpenContainer Image Layout Schema",
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://opencontainers.org/schema/image-layout",
"id": "https://opencontainers.org/schema/image/layout",
"type": "object",
"properties": {
"imageLayoutVersion": {
"description": "version of the OCI image-layout",
"description": "version of the OCI Image Layout (in the oci-layout file)",
"type": "string",
"enum": [
"1.0.0"

View file

@ -23,7 +23,7 @@
},
"annotations": {
"id": "https://opencontainers.org/schema/image/manifest/annotations",
"$ref": "defs-image.json#/definitions/annotations"
"$ref": "defs-descriptor.json#/definitions/annotations"
}
},
"required": [

View file

@ -59,11 +59,8 @@ func TestImageIndex(t *testing.T) {
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
"platform": {
"architecture": "amd64",
"os": "linux",
"features": [
"sse4"
]
}
"os": "linux"
}
}
]
}
@ -82,10 +79,7 @@ func TestImageIndex(t *testing.T) {
"size": 7682,
"platform": {
"architecture": "amd64",
"os": "linux",
"features": [
"sse4"
]
"os": "linux"
}
}
]
@ -94,7 +88,7 @@ func TestImageIndex(t *testing.T) {
fail: true,
},
// expected pass: manifest.platform is optional
// expected failure: in the optional field platform platform.architecture is missing, expected required
{
imageIndex: `
{
@ -103,12 +97,15 @@ func TestImageIndex(t *testing.T) {
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 7682,
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
"platform": {
"os": "linux",
}
}
]
}
`,
fail: false,
fail: true,
},
// expected failure: invalid referenced manifest media type
@ -123,10 +120,7 @@ func TestImageIndex(t *testing.T) {
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
"platform": {
"architecture": "amd64",
"os": "linux",
"features": [
"sse4"
]
"os": "linux"
}
}
]
@ -147,10 +141,7 @@ func TestImageIndex(t *testing.T) {
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
"platform": {
"architecture": "amd64",
"os": "linux",
"features": [
"sse4"
]
"os": "linux"
}
}
]
@ -180,10 +171,7 @@ func TestImageIndex(t *testing.T) {
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
"platform": {
"architecture": "amd64",
"os": "linux",
"features": [
"sse4"
]
"os": "linux"
}
}
],
@ -205,11 +193,7 @@ func TestImageIndex(t *testing.T) {
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 7143,
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
"platform": {
"architecture": "ppc64le",
"os": "linux"
}
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
}
]
}

View file

@ -92,22 +92,6 @@ func TestManifest(t *testing.T) {
fail: true,
},
// expected failure: config.digest is not hex hashed format
{
manifest: `
{
"schemaVersion": 2,
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "sha256:c86f7763873b6c0aaehhhhhhhhhhhhhhhhmmmmmmmmmmmmmmmm22d9h3bab59b4f"
},
"layers": []
}
`,
fail: true,
},
// valid manifest with optional fields
{
manifest: `
@ -188,6 +172,59 @@ func TestManifest(t *testing.T) {
},
"layers": []
}
`,
fail: true,
},
// expected pass: test bounds of algorithm field in digest.
{
manifest: `
{
"schemaVersion": 2,
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "sha256+b64:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
},
"layers": [
{
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "sha256+foo-bar:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
},
{
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "sha256.foo-bar:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
},
{
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "multihash+base58:QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8"
}
]
}
`,
},
// expected failure: push bounds of algorithm field in digest too far.
{
manifest: `
{
"schemaVersion": 2,
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "sha256+b64:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
},
"layers": [
{
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1470,
"digest": "sha256+foo+-b:c86f7763873b6c0aae22d963bab59b4f5debbed6685761b5951584f6efb0633b"
}
]
}
`,
fail: true,
},

View file

@ -22,12 +22,12 @@ import (
// Media types for the OCI image formats
const (
ValidatorMediaTypeDescriptor Validator = v1.MediaTypeDescriptor
ValidatorMediaTypeManifest Validator = v1.MediaTypeImageManifest
ValidatorMediaTypeImageIndex Validator = v1.MediaTypeImageIndex
ValidatorMediaTypeImageConfig Validator = v1.MediaTypeImageConfig
ValidatorTypeImageLayout Validator = v1.ImageLayoutFile
ValidatorMediaTypeImageLayer unimplemented = v1.MediaTypeImageLayer
ValidatorMediaTypeDescriptor Validator = v1.MediaTypeDescriptor
ValidatorMediaTypeLayoutHeader Validator = v1.MediaTypeLayoutHeader
ValidatorMediaTypeManifest Validator = v1.MediaTypeImageManifest
ValidatorMediaTypeImageIndex Validator = v1.MediaTypeImageIndex
ValidatorMediaTypeImageConfig Validator = v1.MediaTypeImageConfig
ValidatorMediaTypeImageLayer unimplemented = v1.MediaTypeImageLayer
)
var (
@ -37,15 +37,15 @@ var (
// specs maps OCI schema media types to schema files.
specs = map[Validator]string{
ValidatorMediaTypeDescriptor: "content-descriptor.json",
ValidatorMediaTypeManifest: "image-manifest-schema.json",
ValidatorMediaTypeImageIndex: "image-index-schema.json",
ValidatorMediaTypeImageConfig: "config-schema.json",
ValidatorTypeImageLayout: "image-layout-schema.json",
ValidatorMediaTypeDescriptor: "content-descriptor.json",
ValidatorMediaTypeLayoutHeader: "image-layout-schema.json",
ValidatorMediaTypeManifest: "image-manifest-schema.json",
ValidatorMediaTypeImageIndex: "image-index-schema.json",
ValidatorMediaTypeImageConfig: "config-schema.json",
}
)
// FileSystem returns an in-memory file system including the schema files.
// FileSystem returns an in-memory filesystem including the schema files.
// The schema files are located at the root directory.
func FileSystem() http.FileSystem {
return fs

View file

@ -20,7 +20,9 @@ import (
"fmt"
"io"
"io/ioutil"
"regexp"
digest "github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/xeipuuv/gojsonschema"
@ -33,7 +35,8 @@ type Validator string
type validateDescendantsFunc func(r io.Reader) error
var mapValidateDescendants = map[Validator]validateDescendantsFunc{
ValidatorMediaTypeManifest: validateManifestDescendants,
ValidatorMediaTypeManifest: validateManifestDescendants,
ValidatorMediaTypeDescriptor: validateDescriptorDescendants,
}
// ValidationError contains all the errors that happened during validation.
@ -111,9 +114,47 @@ func validateManifestDescendants(r io.Reader) error {
for _, layer := range header.Layers {
if layer.MediaType != string(v1.MediaTypeImageLayer) &&
layer.MediaType != string(v1.MediaTypeImageLayerNonDistributable) {
layer.MediaType != string(v1.MediaTypeImageLayerGzip) &&
layer.MediaType != string(v1.MediaTypeImageLayerNonDistributable) &&
layer.MediaType != string(v1.MediaTypeImageLayerNonDistributableGzip) {
fmt.Printf("warning: layer %s has an unknown media type: %s\n", layer.Digest, layer.MediaType)
}
}
return nil
}
var (
sha256EncodedRegexp = regexp.MustCompile(`^[a-f0-9]{64}$`)
sha512EncodedRegexp = regexp.MustCompile(`^[a-f0-9]{128}$`)
)
func validateDescriptorDescendants(r io.Reader) error {
header := v1.Descriptor{}
buf, err := ioutil.ReadAll(r)
if err != nil {
return errors.Wrapf(err, "error reading the io stream")
}
err = json.Unmarshal(buf, &header)
if err != nil {
return errors.Wrap(err, "descriptor format mismatch")
}
if header.Digest.Validate() != nil {
// we ignore unsupported algorithms
fmt.Printf("warning: unsupported digest: %q: %v\n", header.Digest, err)
return nil
}
switch header.Digest.Algorithm() {
case digest.SHA256:
if !sha256EncodedRegexp.MatchString(header.Digest.Hex()) {
return errors.Errorf("unexpected sha256 digest: %q", header.Digest)
}
case digest.SHA512:
if !sha512EncodedRegexp.MatchString(header.Digest.Hex()) {
return errors.Errorf("unexpected sha512 digest: %q", header.Digest)
}
}
return nil
}

View file

@ -25,18 +25,18 @@ The goal of this specification is to enable the creation of interoperable tools
## Notational Conventions
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as described in [RFC 2119](http://tools.ietf.org/html/rfc2119) (Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997).
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as described in [RFC 2119](http://tools.ietf.org/html/rfc2119) (Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997).
The key words "unspecified", "undefined", and "implementation-defined" are to be interpreted as described in the [rationale for the C99 standard][c99-unspecified].
An implementation is not compliant if it fails to satisfy one or more of the MUST, REQUIRED, or SHALL requirements for the protocols it implements.
An implementation is compliant if it satisfies all the MUST, REQUIRED, and SHALL requirements for the protocols it implements.
An implementation is not compliant if it fails to satisfy one or more of the MUST, MUST NOT, REQUIRED, SHALL, or SHALL NOT requirements for the protocols it implements.
An implementation is compliant if it satisfies all the MUST, MUST NOT, REQUIRED, SHALL, and SHALL NOT requirements for the protocols it implements.
## Overview
At a high level the image manifest contains metadata about the contents and dependencies of the image including the content-addressable identity of one or more [filesystem layer changeset](layer.md) archives that will be unpacked to make up the final runnable filesystem.
The image configuration includes information such as application arguments, environments, etc.
The image index is a higher-level manifest which points to one or more manifests and descriptors.
The image index is a higher-level manifest which points to a list of manifests and descriptors.
Typically, these manifests may provide different implementations of the image, possibly varying by platform or other attributes.
![](img/build-diagram.png)
@ -51,10 +51,17 @@ The [OCI Image Media Types](media-types.md) document is a starting point to unde
The high-level components of the spec include:
* An archival format for container images, consisting of an [image manifest](manifest.md), an [image index](image-index.md) (optional), an [image layout](image-layout.md), a set of [filesystem layers](layer.md), and [image configuration](config.md) (base OCI layer)
* A [process of referencing container images by a cryptographic hash of their content](descriptor.md) (base OCI layer)
* A format for [storing CAS blobs and references to them](image-layout.md) (optional OCI layer)
* Signatures that are based on signing image content address (optional OCI layer)
* Naming that is federated based on DNS and can be delegated (optional OCI layer)
* [Image Manifest](manifest.md) - a document describing the components that make up a container image
* [Image Index](image-index.md) - an annotated index of image manifests
* [Image Layout](image-layout.md) - a filesystem layout representing the contents of an image
* [Filesystem Layer](layer.md) - a changeset that describes a container's filesystem
* [Image Configuration](config.md) - a document determining layer ordering and configuration of the image suitable for translation into a [runtime bundle][runtime-spec]
* [Descriptor](descriptor.md) - a reference that describes the type, metadata and content address of referenced content
Future versions of this specification may include the following OPTIONAL features:
* Signatures that are based on signing image content address
* Naming that is federated based on DNS and can be delegated
[c99-unspecified]: http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf#page=18
[runtime-spec]: https://github.com/opencontainers/runtime-spec

View file

@ -14,7 +14,11 @@
package v1
import "time"
import (
"time"
digest "github.com/opencontainers/go-digest"
)
// ImageConfig defines the execution parameters which should be used as a base when running a container using an image.
type ImageConfig struct {
@ -40,7 +44,10 @@ type ImageConfig struct {
WorkingDir string `json:"WorkingDir,omitempty"`
// Labels contains arbitrary metadata for the container.
Labels map[string]string `json:"labels,omitempty"`
Labels map[string]string `json:"Labels,omitempty"`
// StopSignal contains the system call signal that will be sent to the container to exit.
StopSignal string `json:"StopSignal,omitempty"`
}
// RootFS describes a layer content addresses
@ -49,13 +56,13 @@ type RootFS struct {
Type string `json:"type"`
// DiffIDs is an array of layer content hashes (DiffIDs), in order from bottom-most to top-most.
DiffIDs []string `json:"diff_ids"`
DiffIDs []digest.Digest `json:"diff_ids"`
}
// History describes the history of a layer.
type History struct {
// Created is the combined date and time at which the layer was created, formatted as defined by RFC 3339, section 5.6.
Created time.Time `json:"created,omitempty"`
Created *time.Time `json:"created,omitempty"`
// CreatedBy is the command which created the layer.
CreatedBy string `json:"created_by,omitempty"`
@ -74,7 +81,7 @@ type History struct {
// This provides the `application/vnd.oci.image.config.v1+json` mediatype when marshalled to JSON.
type Image struct {
// Created is the combined date and time at which the image was created, formatted as defined by RFC 3339, section 5.6.
Created time.Time `json:"created,omitempty"`
Created *time.Time `json:"created,omitempty"`
// Author defines the name and/or email address of the person or entity which created and is responsible for maintaining the image.
Author string `json:"author,omitempty"`

View file

@ -17,7 +17,8 @@ package v1
import digest "github.com/opencontainers/go-digest"
// Descriptor describes the disposition of targeted content.
// This structure provides `application/vnd.oci.descriptor.v1+json` mediatype when marshalled to JSON
// This structure provides `application/vnd.oci.descriptor.v1+json` mediatype
// when marshalled to JSON.
type Descriptor struct {
// MediaType is the media type of the object this schema refers to.
MediaType string `json:"mediaType,omitempty"`
@ -33,4 +34,31 @@ type Descriptor struct {
// Annotations contains arbitrary metadata relating to the targeted content.
Annotations map[string]string `json:"annotations,omitempty"`
// Platform describes the platform which the image in the manifest runs on.
//
// This should only be used when referring to a manifest.
Platform *Platform `json:"platform,omitempty"`
}
// Platform describes the platform which the image in the manifest runs on.
type Platform struct {
// Architecture field specifies the CPU architecture, for example
// `amd64` or `ppc64`.
Architecture string `json:"architecture"`
// OS specifies the operating system, for example `linux` or `windows`.
OS string `json:"os"`
// OSVersion is an optional field specifying the operating system
// version, for example on Windows `10.0.14393.1066`.
OSVersion string `json:"os.version,omitempty"`
// OSFeatures is an optional field specifying an array of strings,
// each listing a required OS feature (for example on Windows `win32k`).
OSFeatures []string `json:"os.features,omitempty"`
// Variant is an optional field specifying a variant of the CPU, for
// example `v7` to specify ARMv7 when architecture is `arm`.
Variant string `json:"variant,omitempty"`
}

View file

@ -1,63 +0,0 @@
// 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 v1
import "github.com/opencontainers/image-spec/specs-go"
// Platform describes the platform which the image in the manifest runs on.
type Platform struct {
// Architecture field specifies the CPU architecture, for example
// `amd64` or `ppc64`.
Architecture string `json:"architecture"`
// OS specifies the operating system, for example `linux` or `windows`.
OS string `json:"os"`
// OSVersion is an optional field specifying the operating system
// version, for example `10.0.10586`.
OSVersion string `json:"os.version,omitempty"`
// OSFeatures is an optional field specifying an array of strings,
// each listing a required OS feature (for example on Windows `win32k`).
OSFeatures []string `json:"os.features,omitempty"`
// Variant is an optional field specifying a variant of the CPU, for
// example `ppc64le` to specify a little-endian version of a PowerPC CPU.
Variant string `json:"variant,omitempty"`
// Features is an optional field specifying an array of strings, each
// listing a required CPU feature (for example `sse4` or `aes`).
Features []string `json:"features,omitempty"`
}
// ManifestDescriptor describes a platform specific manifest.
type ManifestDescriptor struct {
Descriptor
// Platform describes the platform which the image in the manifest runs on.
Platform Platform `json:"platform"`
}
// ImageIndex references manifests for various platforms.
// This structure provides `application/vnd.oci.image.index.v1+json` mediatype when marshalled to JSON.
type ImageIndex struct {
specs.Versioned
// Manifests references platform specific manifests.
Manifests []ManifestDescriptor `json:"manifests"`
// Annotations contains arbitrary metadata for the image index.
Annotations map[string]string `json:"annotations,omitempty"`
}

View file

@ -0,0 +1,29 @@
// 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 v1
import "github.com/opencontainers/image-spec/specs-go"
// Index references manifests for various platforms.
// This structure provides `application/vnd.oci.image.index.v1+json` mediatype when marshalled to JSON.
type Index struct {
specs.Versioned
// Manifests references platform specific manifests.
Manifests []Descriptor `json:"manifests"`
// Annotations contains arbitrary metadata for the image index.
Annotations map[string]string `json:"annotations,omitempty"`
}

View file

@ -27,6 +27,6 @@ type Manifest struct {
// Layers is an indexed list of layers referenced by the manifest.
Layers []Descriptor `json:"layers"`
// Annotations contains arbitrary metadata for the manifest.
// Annotations contains arbitrary metadata for the image manifest.
Annotations map[string]string `json:"annotations,omitempty"`
}

View file

@ -18,6 +18,9 @@ const (
// MediaTypeDescriptor specifies the media type for a content descriptor.
MediaTypeDescriptor = "application/vnd.oci.descriptor.v1+json"
// MediaTypeLayoutHeader specifies the media type for the oci-layout.
MediaTypeLayoutHeader = "application/vnd.oci.layout.header.v1+json"
// MediaTypeImageManifest specifies the media type for an image manifest.
MediaTypeImageManifest = "application/vnd.oci.image.manifest.v1+json"

View file

@ -25,7 +25,7 @@ const (
VersionPatch = 0
// VersionDev indicates development branch. Releases will be empty string.
VersionDev = "-rc5"
VersionDev = "-rc6-dev"
)
// Version is the specification version that the package types support.