Update Vendor

Includes updates from docker/docker and imagespec

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)
This commit is contained in:
Josh Hawn 2017-05-16 18:30:44 -07:00
parent 3d655c090a
commit 4f9ac4899f
37 changed files with 1266 additions and 773 deletions

View File

@ -6,7 +6,7 @@ github.com/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c
github.com/coreos/etcd 824277cb3a577a0e8c829ca9ec557b973fe06d20 github.com/coreos/etcd 824277cb3a577a0e8c829ca9ec557b973fe06d20
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621 github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621
github.com/docker/docker 7fd8a9382c0fd3f23002288e357b5612b869a974 github.com/docker/docker 77c9728847358a3ed3581d828fb0753017e1afd3
github.com/docker/docker-credential-helpers v0.5.0 github.com/docker/docker-credential-helpers v0.5.0
github.com/docker/go d30aec9fd63c35133f8f79c3412ad91a3b08be06 github.com/docker/go d30aec9fd63c35133f8f79c3412ad91a3b08be06
github.com/docker/go-connections e15c02316c12de00874640cd76311849de2aeed5 github.com/docker/go-connections e15c02316c12de00874640cd76311849de2aeed5
@ -26,6 +26,7 @@ github.com/mattn/go-shellwords v1.0.3
github.com/miekg/pkcs11 df8ae6ca730422dba20c768ff38ef7d79077a59f github.com/miekg/pkcs11 df8ae6ca730422dba20c768ff38ef7d79077a59f
github.com/mitchellh/mapstructure f3009df150dadf309fdee4a54ed65c124afad715 github.com/mitchellh/mapstructure f3009df150dadf309fdee4a54ed65c124afad715
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
github.com/opencontainers/image-spec f03dbe35d449c54915d235f1a3cf8f585a24babe
github.com/opencontainers/runc 9c2d8d184e5da67c95d601382adf14862e4f2228 https://github.com/docker/runc.git github.com/opencontainers/runc 9c2d8d184e5da67c95d601382adf14862e4f2228 https://github.com/docker/runc.git
github.com/opencontainers/selinux ba1aefe8057f1d0cfb8e88d0ec1dc85925ef987d github.com/opencontainers/selinux ba1aefe8057f1d0cfb8e88d0ec1dc85925ef987d
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9 github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9

View File

@ -1 +0,0 @@
package manifest

View File

@ -1,155 +0,0 @@
package manifestlist
import (
"encoding/json"
"errors"
"fmt"
"github.com/docker/distribution"
"github.com/docker/distribution/manifest"
"github.com/opencontainers/go-digest"
)
// MediaTypeManifestList specifies the mediaType for manifest lists.
const MediaTypeManifestList = "application/vnd.docker.distribution.manifest.list.v2+json"
// SchemaVersion provides a pre-initialized version structure for this
// packages version of the manifest.
var SchemaVersion = manifest.Versioned{
SchemaVersion: 2,
MediaType: MediaTypeManifestList,
}
func init() {
manifestListFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
m := new(DeserializedManifestList)
err := m.UnmarshalJSON(b)
if err != nil {
return nil, distribution.Descriptor{}, err
}
dgst := digest.FromBytes(b)
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: MediaTypeManifestList}, err
}
err := distribution.RegisterManifestSchema(MediaTypeManifestList, manifestListFunc)
if err != nil {
panic(fmt.Sprintf("Unable to register manifest: %s", err))
}
}
// PlatformSpec specifies a platform where a particular image manifest is
// applicable.
type PlatformSpec 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"`
}
// A ManifestDescriptor references a platform-specific manifest.
type ManifestDescriptor struct {
distribution.Descriptor
// Platform specifies which platform the manifest pointed to by the
// descriptor runs on.
Platform PlatformSpec `json:"platform"`
}
// ManifestList references manifests for various platforms.
type ManifestList struct {
manifest.Versioned
// Config references the image configuration as a blob.
Manifests []ManifestDescriptor `json:"manifests"`
}
// References returnes the distribution descriptors for the referenced image
// manifests.
func (m ManifestList) References() []distribution.Descriptor {
dependencies := make([]distribution.Descriptor, len(m.Manifests))
for i := range m.Manifests {
dependencies[i] = m.Manifests[i].Descriptor
}
return dependencies
}
// DeserializedManifestList wraps ManifestList with a copy of the original
// JSON.
type DeserializedManifestList struct {
ManifestList
// canonical is the canonical byte representation of the Manifest.
canonical []byte
}
// FromDescriptors takes a slice of descriptors, and returns a
// DeserializedManifestList which contains the resulting manifest list
// and its JSON representation.
func FromDescriptors(descriptors []ManifestDescriptor) (*DeserializedManifestList, error) {
m := ManifestList{
Versioned: SchemaVersion,
}
m.Manifests = make([]ManifestDescriptor, len(descriptors), len(descriptors))
copy(m.Manifests, descriptors)
deserialized := DeserializedManifestList{
ManifestList: m,
}
var err error
deserialized.canonical, err = json.MarshalIndent(&m, "", " ")
return &deserialized, err
}
// UnmarshalJSON populates a new ManifestList struct from JSON data.
func (m *DeserializedManifestList) UnmarshalJSON(b []byte) error {
m.canonical = make([]byte, len(b), len(b))
// store manifest list in canonical
copy(m.canonical, b)
// Unmarshal canonical JSON into ManifestList object
var manifestList ManifestList
if err := json.Unmarshal(m.canonical, &manifestList); err != nil {
return err
}
m.ManifestList = manifestList
return nil
}
// MarshalJSON returns the contents of canonical. If canonical is empty,
// marshals the inner contents.
func (m *DeserializedManifestList) MarshalJSON() ([]byte, error) {
if len(m.canonical) > 0 {
return m.canonical, nil
}
return nil, errors.New("JSON representation not initialized in DeserializedManifestList")
}
// Payload returns the raw content of the manifest list. The contents can be
// used to calculate the content identifier.
func (m DeserializedManifestList) Payload() (string, []byte, error) {
return m.MediaType, m.canonical, nil
}

View File

@ -1,12 +0,0 @@
package manifest
// Versioned provides a struct with the manifest schemaVersion and mediaType.
// Incoming content with unknown schema version can be decoded against this
// struct to check the version.
type Versioned struct {
// SchemaVersion is the image manifest schema that this image follows
SchemaVersion int `json:"schemaVersion"`
// MediaType is the media type of this schema.
MediaType string `json:"mediaType,omitempty"`
}

View File

@ -276,6 +276,12 @@ type ServiceCreateOptions struct {
// //
// This field follows the format of the X-Registry-Auth header. // This field follows the format of the X-Registry-Auth header.
EncodedRegistryAuth string EncodedRegistryAuth string
// QueryRegistry indicates whether the service update requires
// contacting a registry. A registry may be contacted to retrieve
// the image digest and manifest, which in turn can be used to update
// platform or other information about the service.
QueryRegistry bool
} }
// ServiceCreateResponse contains the information returned to a client // ServiceCreateResponse contains the information returned to a client
@ -315,6 +321,12 @@ type ServiceUpdateOptions struct {
// The valid values are "previous" and "none". An empty value is the // The valid values are "previous" and "none". An empty value is the
// same as "none". // same as "none".
Rollback string Rollback string
// QueryRegistry indicates whether the service update requires
// contacting a registry. A registry may be contacted to retrieve
// the image digest and manifest, which in turn can be used to update
// platform or other information about the service.
QueryRegistry bool
} }
// ServiceListOptions holds parameters to list services with. // ServiceListOptions holds parameters to list services with.

View File

@ -0,0 +1,22 @@
package container
// WaitCondition is a type used to specify a container state for which
// to wait.
type WaitCondition string
// Possible WaitCondition Values.
//
// WaitConditionNotRunning (default) is used to wait for any of the non-running
// states: "created", "exited", "dead", "removing", or "removed".
//
// WaitConditionNextExit is used to wait for the next time the state changes
// to a non-running state. If the state is currently "created" or "exited",
// this would cause Wait() to block until either the container runs and exits
// or is removed.
//
// WaitConditionRemoved is used to wait for the container to be removed.
const (
WaitConditionNotRunning WaitCondition = "not-running"
WaitConditionNextExit WaitCondition = "next-exit"
WaitConditionRemoved WaitCondition = "removed"
)

View File

@ -4,12 +4,13 @@ import (
"encoding/json" "encoding/json"
"net" "net"
"github.com/docker/distribution" "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/docker/distribution/manifest/manifestlist"
) )
// ServiceConfig stores daemon registry services configuration. // ServiceConfig stores daemon registry services configuration.
type ServiceConfig struct { type ServiceConfig struct {
AllowNondistributableArtifactsCIDRs []*NetIPNet
AllowNondistributableArtifactsHostnames []string
InsecureRegistryCIDRs []*NetIPNet `json:"InsecureRegistryCIDRs"` InsecureRegistryCIDRs []*NetIPNet `json:"InsecureRegistryCIDRs"`
IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"` IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"`
Mirrors []string Mirrors []string
@ -111,8 +112,8 @@ type SearchResults struct {
type DistributionInspect struct { type DistributionInspect struct {
// Descriptor contains information about the manifest, including // Descriptor contains information about the manifest, including
// the content addressable digest // the content addressable digest
Descriptor distribution.Descriptor Descriptor v1.Descriptor
// Platforms contains the list of platforms supported by the image, // Platforms contains the list of platforms supported by the image,
// obtained by parsing the manifest // obtained by parsing the manifest
Platforms []manifestlist.PlatformSpec Platforms []v1.Platform
} }

View File

@ -88,6 +88,11 @@ type ResourceRequirements struct {
type Placement struct { type Placement struct {
Constraints []string `json:",omitempty"` Constraints []string `json:",omitempty"`
Preferences []PlacementPreference `json:",omitempty"` Preferences []PlacementPreference `json:",omitempty"`
// Platforms stores all the platforms that the image can run on.
// This field is used in the platform filter for scheduling. If empty,
// then the platform filter is off, meaning there are no scheduling restrictions.
Platforms []Platform `json:",omitempty"`
} }
// PlacementPreference provides a way to make the scheduler aware of factors // PlacementPreference provides a way to make the scheduler aware of factors

View File

@ -2,25 +2,83 @@ package client
import ( import (
"encoding/json" "encoding/json"
"net/url"
"golang.org/x/net/context" "golang.org/x/net/context"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/versions"
) )
// ContainerWait pauses execution until a container exits. // ContainerWait waits until the specified continer is in a certain state
// It returns the API status code as response of its readiness. // indicated by the given condition, either "not-running" (default),
func (cli *Client) ContainerWait(ctx context.Context, containerID string) (int64, error) { // "next-exit", or "removed".
//
// If this client's API version is beforer 1.30, condition is ignored and
// ContainerWait will return immediately with the two channels, as the server
// will wait as if the condition were "not-running".
//
// If this client's API version is at least 1.30, ContainerWait blocks until
// the request has been acknowledged by the server (with a response header),
// then returns two channels on which the caller can wait for the exit status
// of the container or an error if there was a problem either beginning the
// wait request or in getting the response. This allows the caller to
// sychronize ContainerWait with other calls, such as specifying a
// "next-exit" condition before issuing a ContainerStart request.
func (cli *Client) ContainerWait(ctx context.Context, containerID string, condition container.WaitCondition) (<-chan container.ContainerWaitOKBody, <-chan error) {
if versions.LessThan(cli.ClientVersion(), "1.30") {
return cli.legacyContainerWait(ctx, containerID)
}
resultC := make(chan container.ContainerWaitOKBody)
errC := make(chan error)
query := url.Values{}
query.Set("condition", string(condition))
resp, err := cli.post(ctx, "/containers/"+containerID+"/wait", query, nil, nil)
if err != nil {
defer ensureReaderClosed(resp)
errC <- err
return resultC, errC
}
go func() {
defer ensureReaderClosed(resp)
var res container.ContainerWaitOKBody
if err := json.NewDecoder(resp.body).Decode(&res); err != nil {
errC <- err
return
}
resultC <- res
}()
return resultC, errC
}
// legacyContainerWait returns immediately and doesn't have an option to wait
// until the container is removed.
func (cli *Client) legacyContainerWait(ctx context.Context, containerID string) (<-chan container.ContainerWaitOKBody, <-chan error) {
resultC := make(chan container.ContainerWaitOKBody)
errC := make(chan error)
go func() {
resp, err := cli.post(ctx, "/containers/"+containerID+"/wait", nil, nil, nil) resp, err := cli.post(ctx, "/containers/"+containerID+"/wait", nil, nil, nil)
if err != nil { if err != nil {
return -1, err errC <- err
return
} }
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
var res container.ContainerWaitOKBody var res container.ContainerWaitOKBody
if err := json.NewDecoder(resp.body).Decode(&res); err != nil { if err := json.NewDecoder(resp.body).Decode(&res); err != nil {
return -1, err errC <- err
return
} }
return res.StatusCode, nil resultC <- res
}()
return resultC, errC
} }

View File

@ -0,0 +1,31 @@
package client
import (
"encoding/json"
"net/url"
registrytypes "github.com/docker/docker/api/types/registry"
"golang.org/x/net/context"
)
// DistributionInspect returns the image digest with full Manifest
func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registrytypes.DistributionInspect, error) {
var headers map[string][]string
if encodedRegistryAuth != "" {
headers = map[string][]string{
"X-Registry-Auth": {encodedRegistryAuth},
}
}
// Contact the registry to retrieve digest and platform information
var distributionInspect registrytypes.DistributionInspect
resp, err := cli.get(ctx, "/distribution/"+image+"/json", url.Values{}, headers)
if err != nil {
return distributionInspect, err
}
err = json.NewDecoder(resp.body).Decode(&distributionInspect)
ensureReaderClosed(resp)
return distributionInspect, err
}

View File

@ -20,6 +20,7 @@ import (
type CommonAPIClient interface { type CommonAPIClient interface {
ConfigAPIClient ConfigAPIClient
ContainerAPIClient ContainerAPIClient
DistributionAPIClient
ImageAPIClient ImageAPIClient
NodeAPIClient NodeAPIClient
NetworkAPIClient NetworkAPIClient
@ -63,12 +64,17 @@ type ContainerAPIClient interface {
ContainerTop(ctx context.Context, container string, arguments []string) (container.ContainerTopOKBody, error) ContainerTop(ctx context.Context, container string, arguments []string) (container.ContainerTopOKBody, error)
ContainerUnpause(ctx context.Context, container string) error ContainerUnpause(ctx context.Context, container string) error
ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error)
ContainerWait(ctx context.Context, container string) (int64, error) ContainerWait(ctx context.Context, container string, condition container.WaitCondition) (<-chan container.ContainerWaitOKBody, <-chan error)
CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error
ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error)
} }
// DistributionAPIClient defines API client methods for the registry
type DistributionAPIClient interface {
DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registry.DistributionInspect, error)
}
// ImageAPIClient defines API client methods for the images // ImageAPIClient defines API client methods for the images
type ImageAPIClient interface { type ImageAPIClient interface {
ImageBuild(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) ImageBuild(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error)

View File

@ -2,22 +2,39 @@ package client
import ( import (
"encoding/json" "encoding/json"
"fmt"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
"github.com/opencontainers/go-digest"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// ServiceCreate creates a new Service. // ServiceCreate creates a new Service.
func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options types.ServiceCreateOptions) (types.ServiceCreateResponse, error) { func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options types.ServiceCreateOptions) (types.ServiceCreateResponse, error) {
var headers map[string][]string var distErr error
headers := map[string][]string{
"version": {cli.version},
}
if options.EncodedRegistryAuth != "" { if options.EncodedRegistryAuth != "" {
headers = map[string][]string{ headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth}
"X-Registry-Auth": {options.EncodedRegistryAuth},
}
} }
// Contact the registry to retrieve digest and platform information
if options.QueryRegistry {
distributionInspect, err := cli.DistributionInspect(ctx, service.TaskTemplate.ContainerSpec.Image, options.EncodedRegistryAuth)
distErr = err
if err == nil {
// now pin by digest if the image doesn't already contain a digest
img := imageWithDigestString(service.TaskTemplate.ContainerSpec.Image, distributionInspect.Descriptor.Digest)
if img != "" {
service.TaskTemplate.ContainerSpec.Image = img
}
}
}
var response types.ServiceCreateResponse var response types.ServiceCreateResponse
resp, err := cli.post(ctx, "/services/create", nil, service, headers) resp, err := cli.post(ctx, "/services/create", nil, service, headers)
if err != nil { if err != nil {
@ -25,6 +42,38 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec,
} }
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.body).Decode(&response)
if distErr != nil {
response.Warnings = append(response.Warnings, digestWarning(service.TaskTemplate.ContainerSpec.Image))
}
ensureReaderClosed(resp) ensureReaderClosed(resp)
return response, err return response, err
} }
// imageWithDigestString takes an image string and a digest, and updates
// the image string if it didn't originally contain a digest. It assumes
// that the image string is not an image ID
func imageWithDigestString(image string, dgst digest.Digest) string {
isCanonical := false
ref, err := reference.ParseAnyReference(image)
if err == nil {
_, isCanonical = ref.(reference.Canonical)
if !isCanonical {
namedRef, _ := ref.(reference.Named)
img, err := reference.WithDigest(namedRef, dgst)
if err == nil {
return img.String()
}
}
}
return ""
}
// digestWarning constructs a formatted warning string using the
// image name that could not be pinned by digest. The formatting
// is hardcoded, but could me made smarter in the future
func digestWarning(image string) string {
return fmt.Sprintf("image %s could not be accessed on a registry to record\nits digest. Each node will access %s independently,\npossibly leading to different nodes running different\nversions of the image.\n", image, image)
}

View File

@ -13,14 +13,16 @@ import (
// ServiceUpdate updates a Service. // ServiceUpdate updates a Service.
func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) { func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) {
var ( var (
headers map[string][]string
query = url.Values{} query = url.Values{}
distErr error
) )
if options.EncodedRegistryAuth != "" { headers := map[string][]string{
headers = map[string][]string{ "version": {cli.version},
"X-Registry-Auth": {options.EncodedRegistryAuth},
} }
if options.EncodedRegistryAuth != "" {
headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth}
} }
if options.RegistryAuthFrom != "" { if options.RegistryAuthFrom != "" {
@ -33,6 +35,20 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
query.Set("version", strconv.FormatUint(version.Index, 10)) query.Set("version", strconv.FormatUint(version.Index, 10))
// Contact the registry to retrieve digest and platform information
// This happens only when the image has changed
if options.QueryRegistry {
distributionInspect, err := cli.DistributionInspect(ctx, service.TaskTemplate.ContainerSpec.Image, options.EncodedRegistryAuth)
distErr = err
if err == nil {
// now pin by digest if the image doesn't already contain a digest
img := imageWithDigestString(service.TaskTemplate.ContainerSpec.Image, distributionInspect.Descriptor.Digest)
if img != "" {
service.TaskTemplate.ContainerSpec.Image = img
}
}
}
var response types.ServiceUpdateResponse var response types.ServiceUpdateResponse
resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers) resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers)
if err != nil { if err != nil {
@ -40,6 +56,11 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
} }
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.body).Decode(&response)
if distErr != nil {
response.Warnings = append(response.Warnings, digestWarning(service.TaskTemplate.ContainerSpec.Image))
}
ensureReaderClosed(resp) ensureReaderClosed(resp)
return response, err return response, err
} }

View File

@ -1,9 +1,10 @@
package client package client
import ( import (
"github.com/docker/docker/api/types/filters"
"net/url" "net/url"
"regexp" "regexp"
"github.com/docker/docker/api/types/filters"
) )
var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`) var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`)

68
vendor/github.com/docker/docker/hack/README.md generated vendored Normal file
View File

@ -0,0 +1,68 @@
## About
This directory contains a collection of scripts used to build and manage this
repository. If there are any issues regarding the intention of a particular
script (or even part of a certain script), please reach out to us.
It may help us either refine our current scripts, or add on new ones
that are appropriate for a given use case.
## DinD (dind.sh)
DinD is a wrapper script which allows Docker to be run inside a Docker
container. DinD requires the container to
be run with privileged mode enabled.
## Generate Authors (generate-authors.sh)
Generates AUTHORS; a file with all the names and corresponding emails of
individual contributors. AUTHORS can be found in the home directory of
this repository.
## Install (install.sh)
Executable install script for installing Docker. If updates to this are
desired, please use hack/release.sh during a normal release. The following
one-liner may be used for script hotfixes:
- `aws s3 cp --acl public-read hack/install.sh s3://get.docker.com/index`
## Make
There are two make files, each with different extensions. Neither are supposed
to be called directly; only invoke `make`. Both scripts run inside a Docker
container.
### make.ps1
- The Windows native build script that uses PowerShell semantics; it is limited
unlike `hack\make.sh` since it does not provide support for the full set of
operations provided by the Linux counterpart, `make.sh`. However, `make.ps1`
does provide support for local Windows development and Windows to Windows CI.
More information is found within `make.ps1` by the author, @jhowardmsft
### make.sh
- Referenced via `make test` when running tests on a local machine,
or directly referenced when running tests inside a Docker development container.
- When running on a local machine, `make test` to run all tests found in
`test`, `test-unit`, `test-integration-cli`, and `test-docker-py` on
your local machine. The default timeout is set in `make.sh` to 60 minutes
(`${TIMEOUT:=60m}`), since it currently takes up to an hour to run
all of the tests.
- When running inside a Docker development container, `hack/make.sh` does
not have a single target that runs all the tests. You need to provide a
single command line with multiple targets that performs the same thing.
An example referenced from [Run targets inside a development container](https://docs.docker.com/opensource/project/test-and-docs/#run-targets-inside-a-development-container): `root@5f8630b873fe:/go/src/github.com/moby/moby# hack/make.sh dynbinary binary cross test-unit test-integration-cli test-docker-py`
- For more information related to testing outside the scope of this README,
refer to
[Run tests and test documentation](https://docs.docker.com/opensource/project/test-and-docs/)
## Release (release.sh)
Releases any bundles built by `make` on a public AWS S3 bucket.
For information regarding configuration, please view `release.sh`.
## Vendor (vendor.sh)
A shell script that is a wrapper around Vndr. For information on how to use
this, please refer to [vndr's README](https://github.com/LK4D4/vndr/blob/master/README.md)

View File

@ -38,6 +38,8 @@ Following environment variables are known to work in this step:
- `BUILDFLAGS` - `BUILDFLAGS`
- `DOCKER_INCREMENTAL_BINARY` - `DOCKER_INCREMENTAL_BINARY`
Note: during the transition into Moby Project, you might need to create a symbolic link `$GOPATH/src/github.com/docker/docker` to `$GOPATH/src/github.com/moby/moby`.
### Step 2: Execute tests ### Step 2: Execute tests
$ ./hack/integration-cli-on-swarm/integration-cli-on-swarm -replicas 40 -push-worker-image YOUR_REGISTRY.EXAMPLE.COM/integration-cli-worker:latest $ ./hack/integration-cli-on-swarm/integration-cli-on-swarm -replicas 40 -push-worker-image YOUR_REGISTRY.EXAMPLE.COM/integration-cli-worker:latest

View File

@ -1,173 +0,0 @@
package opts
import (
"encoding/csv"
"fmt"
"os"
"strconv"
"strings"
mounttypes "github.com/docker/docker/api/types/mount"
"github.com/docker/go-units"
)
// MountOpt is a Value type for parsing mounts
type MountOpt struct {
values []mounttypes.Mount
}
// Set a new mount value
func (m *MountOpt) Set(value string) error {
csvReader := csv.NewReader(strings.NewReader(value))
fields, err := csvReader.Read()
if err != nil {
return err
}
mount := mounttypes.Mount{}
volumeOptions := func() *mounttypes.VolumeOptions {
if mount.VolumeOptions == nil {
mount.VolumeOptions = &mounttypes.VolumeOptions{
Labels: make(map[string]string),
}
}
if mount.VolumeOptions.DriverConfig == nil {
mount.VolumeOptions.DriverConfig = &mounttypes.Driver{}
}
return mount.VolumeOptions
}
bindOptions := func() *mounttypes.BindOptions {
if mount.BindOptions == nil {
mount.BindOptions = new(mounttypes.BindOptions)
}
return mount.BindOptions
}
tmpfsOptions := func() *mounttypes.TmpfsOptions {
if mount.TmpfsOptions == nil {
mount.TmpfsOptions = new(mounttypes.TmpfsOptions)
}
return mount.TmpfsOptions
}
setValueOnMap := func(target map[string]string, value string) {
parts := strings.SplitN(value, "=", 2)
if len(parts) == 1 {
target[value] = ""
} else {
target[parts[0]] = parts[1]
}
}
mount.Type = mounttypes.TypeVolume // default to volume mounts
// Set writable as the default
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
key := strings.ToLower(parts[0])
if len(parts) == 1 {
switch key {
case "readonly", "ro":
mount.ReadOnly = true
continue
case "volume-nocopy":
volumeOptions().NoCopy = true
continue
}
}
if len(parts) != 2 {
return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
}
value := parts[1]
switch key {
case "type":
mount.Type = mounttypes.Type(strings.ToLower(value))
case "source", "src":
mount.Source = value
case "target", "dst", "destination":
mount.Target = value
case "readonly", "ro":
mount.ReadOnly, err = strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("invalid value for %s: %s", key, value)
}
case "consistency":
mount.Consistency = mounttypes.Consistency(strings.ToLower(value))
case "bind-propagation":
bindOptions().Propagation = mounttypes.Propagation(strings.ToLower(value))
case "volume-nocopy":
volumeOptions().NoCopy, err = strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("invalid value for volume-nocopy: %s", value)
}
case "volume-label":
setValueOnMap(volumeOptions().Labels, value)
case "volume-driver":
volumeOptions().DriverConfig.Name = value
case "volume-opt":
if volumeOptions().DriverConfig.Options == nil {
volumeOptions().DriverConfig.Options = make(map[string]string)
}
setValueOnMap(volumeOptions().DriverConfig.Options, value)
case "tmpfs-size":
sizeBytes, err := units.RAMInBytes(value)
if err != nil {
return fmt.Errorf("invalid value for %s: %s", key, value)
}
tmpfsOptions().SizeBytes = sizeBytes
case "tmpfs-mode":
ui64, err := strconv.ParseUint(value, 8, 32)
if err != nil {
return fmt.Errorf("invalid value for %s: %s", key, value)
}
tmpfsOptions().Mode = os.FileMode(ui64)
default:
return fmt.Errorf("unexpected key '%s' in '%s'", key, field)
}
}
if mount.Type == "" {
return fmt.Errorf("type is required")
}
if mount.Target == "" {
return fmt.Errorf("target is required")
}
if mount.VolumeOptions != nil && mount.Type != mounttypes.TypeVolume {
return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mount.Type)
}
if mount.BindOptions != nil && mount.Type != mounttypes.TypeBind {
return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mount.Type)
}
if mount.TmpfsOptions != nil && mount.Type != mounttypes.TypeTmpfs {
return fmt.Errorf("cannot mix 'tmpfs-*' options with mount type '%s'", mount.Type)
}
m.values = append(m.values, mount)
return nil
}
// Type returns the type of this option
func (m *MountOpt) Type() string {
return "mount"
}
// String returns a string repr of this option
func (m *MountOpt) String() string {
mounts := []string{}
for _, mount := range m.values {
repr := fmt.Sprintf("%s %s %s", mount.Type, mount.Source, mount.Target)
mounts = append(mounts, repr)
}
return strings.Join(mounts, ", ")
}
// Value returns the mounts
func (m *MountOpt) Value() []mounttypes.Mount {
return m.values
}

View File

@ -1,162 +0,0 @@
package opts
import (
"encoding/csv"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/go-connections/nat"
)
const (
portOptTargetPort = "target"
portOptPublishedPort = "published"
portOptProtocol = "protocol"
portOptMode = "mode"
)
// PortOpt represents a port config in swarm mode.
type PortOpt struct {
ports []swarm.PortConfig
}
// Set a new port value
func (p *PortOpt) Set(value string) error {
longSyntax, err := regexp.MatchString(`\w+=\w+(,\w+=\w+)*`, value)
if err != nil {
return err
}
if longSyntax {
csvReader := csv.NewReader(strings.NewReader(value))
fields, err := csvReader.Read()
if err != nil {
return err
}
pConfig := swarm.PortConfig{}
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
if len(parts) != 2 {
return fmt.Errorf("invalid field %s", field)
}
key := strings.ToLower(parts[0])
value := strings.ToLower(parts[1])
switch key {
case portOptProtocol:
if value != string(swarm.PortConfigProtocolTCP) && value != string(swarm.PortConfigProtocolUDP) {
return fmt.Errorf("invalid protocol value %s", value)
}
pConfig.Protocol = swarm.PortConfigProtocol(value)
case portOptMode:
if value != string(swarm.PortConfigPublishModeIngress) && value != string(swarm.PortConfigPublishModeHost) {
return fmt.Errorf("invalid publish mode value %s", value)
}
pConfig.PublishMode = swarm.PortConfigPublishMode(value)
case portOptTargetPort:
tPort, err := strconv.ParseUint(value, 10, 16)
if err != nil {
return err
}
pConfig.TargetPort = uint32(tPort)
case portOptPublishedPort:
pPort, err := strconv.ParseUint(value, 10, 16)
if err != nil {
return err
}
pConfig.PublishedPort = uint32(pPort)
default:
return fmt.Errorf("invalid field key %s", key)
}
}
if pConfig.TargetPort == 0 {
return fmt.Errorf("missing mandatory field %q", portOptTargetPort)
}
if pConfig.PublishMode == "" {
pConfig.PublishMode = swarm.PortConfigPublishModeIngress
}
if pConfig.Protocol == "" {
pConfig.Protocol = swarm.PortConfigProtocolTCP
}
p.ports = append(p.ports, pConfig)
} else {
// short syntax
portConfigs := []swarm.PortConfig{}
ports, portBindingMap, err := nat.ParsePortSpecs([]string{value})
if err != nil {
return err
}
for _, portBindings := range portBindingMap {
for _, portBinding := range portBindings {
if portBinding.HostIP != "" {
return fmt.Errorf("HostIP is not supported.")
}
}
}
for port := range ports {
portConfig, err := ConvertPortToPortConfig(port, portBindingMap)
if err != nil {
return err
}
portConfigs = append(portConfigs, portConfig...)
}
p.ports = append(p.ports, portConfigs...)
}
return nil
}
// Type returns the type of this option
func (p *PortOpt) Type() string {
return "port"
}
// String returns a string repr of this option
func (p *PortOpt) String() string {
ports := []string{}
for _, port := range p.ports {
repr := fmt.Sprintf("%v:%v/%s/%s", port.PublishedPort, port.TargetPort, port.Protocol, port.PublishMode)
ports = append(ports, repr)
}
return strings.Join(ports, ", ")
}
// Value returns the ports
func (p *PortOpt) Value() []swarm.PortConfig {
return p.ports
}
// ConvertPortToPortConfig converts ports to the swarm type
func ConvertPortToPortConfig(
port nat.Port,
portBindings map[nat.Port][]nat.PortBinding,
) ([]swarm.PortConfig, error) {
ports := []swarm.PortConfig{}
for _, binding := range portBindings[port] {
hostPort, err := strconv.ParseUint(binding.HostPort, 10, 16)
if err != nil && binding.HostPort != "" {
return nil, fmt.Errorf("invalid hostport binding (%s) for port (%s)", binding.HostPort, port.Port())
}
ports = append(ports, swarm.PortConfig{
//TODO Name: ?
Protocol: swarm.PortConfigProtocol(strings.ToLower(port.Proto())),
TargetPort: uint32(port.Int()),
PublishedPort: uint32(hostPort),
PublishMode: swarm.PortConfigPublishModeIngress,
})
}
return ports, nil
}

View File

@ -1,98 +0,0 @@
package opts
import (
"encoding/csv"
"fmt"
"os"
"strconv"
"strings"
swarmtypes "github.com/docker/docker/api/types/swarm"
)
// SecretOpt is a Value type for parsing secrets
type SecretOpt struct {
values []*swarmtypes.SecretReference
}
// Set a new secret value
func (o *SecretOpt) Set(value string) error {
csvReader := csv.NewReader(strings.NewReader(value))
fields, err := csvReader.Read()
if err != nil {
return err
}
options := &swarmtypes.SecretReference{
File: &swarmtypes.SecretReferenceFileTarget{
UID: "0",
GID: "0",
Mode: 0444,
},
}
// support a simple syntax of --secret foo
if len(fields) == 1 {
options.File.Name = fields[0]
options.SecretName = fields[0]
o.values = append(o.values, options)
return nil
}
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
key := strings.ToLower(parts[0])
if len(parts) != 2 {
return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
}
value := parts[1]
switch key {
case "source", "src":
options.SecretName = value
case "target":
options.File.Name = value
case "uid":
options.File.UID = value
case "gid":
options.File.GID = value
case "mode":
m, err := strconv.ParseUint(value, 0, 32)
if err != nil {
return fmt.Errorf("invalid mode specified: %v", err)
}
options.File.Mode = os.FileMode(m)
default:
return fmt.Errorf("invalid field in secret request: %s", key)
}
}
if options.SecretName == "" {
return fmt.Errorf("source is required")
}
o.values = append(o.values, options)
return nil
}
// Type returns the type of this option
func (o *SecretOpt) Type() string {
return "secret"
}
// String returns a string repr of this option
func (o *SecretOpt) String() string {
secrets := []string{}
for _, secret := range o.values {
repr := fmt.Sprintf("%s -> %s", secret.SecretName, secret.File.Name)
secrets = append(secrets, repr)
}
return strings.Join(secrets, ", ")
}
// Value returns the secret requests
func (o *SecretOpt) Value() []*swarmtypes.SecretReference {
return o.values
}

View File

@ -1,89 +0,0 @@
package opts
import (
"fmt"
"strconv"
"strings"
"github.com/docker/docker/api/types/blkiodev"
)
// ValidatorWeightFctType defines a validator function that returns a validated struct and/or an error.
type ValidatorWeightFctType func(val string) (*blkiodev.WeightDevice, error)
// ValidateWeightDevice validates that the specified string has a valid device-weight format.
func ValidateWeightDevice(val string) (*blkiodev.WeightDevice, error) {
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
weight, err := strconv.ParseUint(split[1], 10, 0)
if err != nil {
return nil, fmt.Errorf("invalid weight for device: %s", val)
}
if weight > 0 && (weight < 10 || weight > 1000) {
return nil, fmt.Errorf("invalid weight for device: %s", val)
}
return &blkiodev.WeightDevice{
Path: split[0],
Weight: uint16(weight),
}, nil
}
// WeightdeviceOpt defines a map of WeightDevices
type WeightdeviceOpt struct {
values []*blkiodev.WeightDevice
validator ValidatorWeightFctType
}
// NewWeightdeviceOpt creates a new WeightdeviceOpt
func NewWeightdeviceOpt(validator ValidatorWeightFctType) WeightdeviceOpt {
values := []*blkiodev.WeightDevice{}
return WeightdeviceOpt{
values: values,
validator: validator,
}
}
// Set validates a WeightDevice and sets its name as a key in WeightdeviceOpt
func (opt *WeightdeviceOpt) Set(val string) error {
var value *blkiodev.WeightDevice
if opt.validator != nil {
v, err := opt.validator(val)
if err != nil {
return err
}
value = v
}
(opt.values) = append((opt.values), value)
return nil
}
// String returns WeightdeviceOpt values as a string.
func (opt *WeightdeviceOpt) String() string {
var out []string
for _, v := range opt.values {
out = append(out, v.String())
}
return fmt.Sprintf("%v", out)
}
// GetList returns a slice of pointers to WeightDevices.
func (opt *WeightdeviceOpt) GetList() []*blkiodev.WeightDevice {
var weightdevice []*blkiodev.WeightDevice
for _, v := range opt.values {
weightdevice = append(weightdevice, v)
}
return weightdevice
}
// Type returns the option type
func (opt *WeightdeviceOpt) Type() string {
return "list"
}

View File

@ -42,11 +42,14 @@ func CanonicalTarNameForPath(p string) (string, error) {
// chmodTarEntry is used to adjust the file permissions used in tar header based // chmodTarEntry is used to adjust the file permissions used in tar header based
// on the platform the archival is done. // on the platform the archival is done.
func chmodTarEntry(perm os.FileMode) os.FileMode { func chmodTarEntry(perm os.FileMode) os.FileMode {
perm &= 0755 //perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.)
permPart := perm & os.ModePerm
noPermPart := perm &^ os.ModePerm
// Add the x bit: make everything +x from windows // Add the x bit: make everything +x from windows
perm |= 0111 permPart |= 0111
permPart &= 0755
return perm return noPermPart | permPart
} }
func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (err error) { func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (err error) {

View File

@ -12,6 +12,7 @@ import (
"github.com/docker/docker/pkg/symlink" "github.com/docker/docker/pkg/symlink"
"github.com/docker/docker/pkg/urlutil" "github.com/docker/docker/pkg/urlutil"
"github.com/pkg/errors"
) )
// Clone clones a repository into a newly created directory which // Clone clones a repository into a newly created directory which
@ -30,21 +31,45 @@ func Clone(remoteURL string) (string, error) {
return "", err return "", err
} }
fragment := u.Fragment if out, err := gitWithinDir(root, "init"); err != nil {
clone := cloneArgs(u, root) return "", errors.Wrapf(err, "failed to init repo at %s: %s", root, out)
if output, err := git(clone...); err != nil {
return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output)
} }
return checkoutGit(fragment, root) ref, subdir := getRefAndSubdir(u.Fragment)
fetch := fetchArgs(u, ref)
u.Fragment = ""
// Add origin remote for compatibility with previous implementation that
// used "git clone" and also to make sure local refs are created for branches
if out, err := gitWithinDir(root, "remote", "add", "origin", u.String()); err != nil {
return "", errors.Wrapf(err, "failed add origin repo at %s: %s", u.String(), out)
} }
func cloneArgs(remoteURL *url.URL, root string) []string { if output, err := gitWithinDir(root, fetch...); err != nil {
args := []string{"clone", "--recursive"} return "", errors.Wrapf(err, "error fetching: %s", output)
shallow := len(remoteURL.Fragment) == 0 }
if shallow && strings.HasPrefix(remoteURL.Scheme, "http") { return checkoutGit(root, ref, subdir)
}
func getRefAndSubdir(fragment string) (ref string, subdir string) {
refAndDir := strings.SplitN(fragment, ":", 2)
ref = "master"
if len(refAndDir[0]) != 0 {
ref = refAndDir[0]
}
if len(refAndDir) > 1 && len(refAndDir[1]) != 0 {
subdir = refAndDir[1]
}
return
}
func fetchArgs(remoteURL *url.URL, ref string) []string {
args := []string{"fetch", "--recurse-submodules=yes"}
shallow := true
if strings.HasPrefix(remoteURL.Scheme, "http") {
res, err := http.Head(fmt.Sprintf("%s/info/refs?service=git-upload-pack", remoteURL)) res, err := http.Head(fmt.Sprintf("%s/info/refs?service=git-upload-pack", remoteURL))
if err != nil || res.Header.Get("Content-Type") != "application/x-git-upload-pack-advertisement" { if err != nil || res.Header.Get("Content-Type") != "application/x-git-upload-pack-advertisement" {
shallow = false shallow = false
@ -55,26 +80,23 @@ func cloneArgs(remoteURL *url.URL, root string) []string {
args = append(args, "--depth", "1") args = append(args, "--depth", "1")
} }
if remoteURL.Fragment != "" { return append(args, "origin", ref)
remoteURL.Fragment = ""
} }
return append(args, remoteURL.String(), root) func checkoutGit(root, ref, subdir string) (string, error) {
} // Try checking out by ref name first. This will work on branches and sets
// .git/HEAD to the current branch name
func checkoutGit(fragment, root string) (string, error) { if output, err := gitWithinDir(root, "checkout", ref); err != nil {
refAndDir := strings.SplitN(fragment, ":", 2) // If checking out by branch name fails check out the last fetched ref
if _, err2 := gitWithinDir(root, "checkout", "FETCH_HEAD"); err2 != nil {
if len(refAndDir[0]) != 0 { return "", errors.Wrapf(err, "error checking out %s: %s", ref, output)
if output, err := gitWithinDir(root, "checkout", refAndDir[0]); err != nil {
return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output)
} }
} }
if len(refAndDir) > 1 && len(refAndDir[1]) != 0 { if subdir != "" {
newCtx, err := symlink.FollowSymlinkInScope(filepath.Join(root, refAndDir[1]), root) newCtx, err := symlink.FollowSymlinkInScope(filepath.Join(root, subdir), root)
if err != nil { if err != nil {
return "", fmt.Errorf("Error setting git context, %q not within git root: %s", refAndDir[1], err) return "", errors.Wrapf(err, "error setting git context, %q not within git root", subdir)
} }
fi, err := os.Stat(newCtx) fi, err := os.Stat(newCtx)
@ -82,7 +104,7 @@ func checkoutGit(fragment, root string) (string, error) {
return "", err return "", err
} }
if !fi.IsDir() { if !fi.IsDir() {
return "", fmt.Errorf("Error setting git context, not a directory: %s", newCtx) return "", errors.Errorf("error setting git context, not a directory: %s", newCtx)
} }
root = newCtx root = newCtx
} }

74
vendor/github.com/docker/docker/pkg/term/proxy.go generated vendored Normal file
View File

@ -0,0 +1,74 @@
package term
import (
"io"
)
// EscapeError is special error which returned by a TTY proxy reader's Read()
// method in case its detach escape sequence is read.
type EscapeError struct{}
func (EscapeError) Error() string {
return "read escape sequence"
}
// escapeProxy is used only for attaches with a TTY. It is used to proxy
// stdin keypresses from the underlying reader and look for the passed in
// escape key sequence to signal a detach.
type escapeProxy struct {
escapeKeys []byte
escapeKeyPos int
r io.Reader
}
// NewEscapeProxy returns a new TTY proxy reader which wraps the given reader
// and detects when the specified escape keys are read, in which case the Read
// method will return an error of type EscapeError.
func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader {
return &escapeProxy{
escapeKeys: escapeKeys,
r: r,
}
}
func (r *escapeProxy) Read(buf []byte) (int, error) {
nr, err := r.r.Read(buf)
preserve := func() {
// this preserves the original key presses in the passed in buffer
nr += r.escapeKeyPos
preserve := make([]byte, 0, r.escapeKeyPos+len(buf))
preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...)
preserve = append(preserve, buf...)
r.escapeKeyPos = 0
copy(buf[0:nr], preserve)
}
if nr != 1 || err != nil {
if r.escapeKeyPos > 0 {
preserve()
}
return nr, err
}
if buf[0] != r.escapeKeys[r.escapeKeyPos] {
if r.escapeKeyPos > 0 {
preserve()
}
return nr, nil
}
if r.escapeKeyPos == len(r.escapeKeys)-1 {
return 0, EscapeError{}
}
// Looks like we've got an escape key, but we need to match again on the next
// read.
// Store the current escape key we found so we can look for the next one on
// the next read.
// Since this is an escape key, make sure we don't let the caller read it
// If later on we find that this is not the escape sequence, we'll add the
// keys back
r.escapeKeyPos++
return nr - r.escapeKeyPos, nil
}

View File

@ -18,6 +18,7 @@ import (
// ServiceOptions holds command line options. // ServiceOptions holds command line options.
type ServiceOptions struct { type ServiceOptions struct {
AllowNondistributableArtifacts []string `json:"allow-nondistributable-artifacts,omitempty"`
Mirrors []string `json:"registry-mirrors,omitempty"` Mirrors []string `json:"registry-mirrors,omitempty"`
InsecureRegistries []string `json:"insecure-registries,omitempty"` InsecureRegistries []string `json:"insecure-registries,omitempty"`
@ -74,9 +75,11 @@ var lookupIP = net.LookupIP
// InstallCliFlags adds command-line options to the top-level flag parser for // InstallCliFlags adds command-line options to the top-level flag parser for
// the current process. // the current process.
func (options *ServiceOptions) InstallCliFlags(flags *pflag.FlagSet) { func (options *ServiceOptions) InstallCliFlags(flags *pflag.FlagSet) {
ana := opts.NewNamedListOptsRef("allow-nondistributable-artifacts", &options.AllowNondistributableArtifacts, ValidateIndexName)
mirrors := opts.NewNamedListOptsRef("registry-mirrors", &options.Mirrors, ValidateMirror) mirrors := opts.NewNamedListOptsRef("registry-mirrors", &options.Mirrors, ValidateMirror)
insecureRegistries := opts.NewNamedListOptsRef("insecure-registries", &options.InsecureRegistries, ValidateIndexName) insecureRegistries := opts.NewNamedListOptsRef("insecure-registries", &options.InsecureRegistries, ValidateIndexName)
flags.Var(ana, "allow-nondistributable-artifacts", "Allow push of nondistributable artifacts to registry")
flags.Var(mirrors, "registry-mirror", "Preferred Docker registry mirror") flags.Var(mirrors, "registry-mirror", "Preferred Docker registry mirror")
flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication") flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication")
@ -95,12 +98,50 @@ func newServiceConfig(options ServiceOptions) *serviceConfig {
V2Only: options.V2Only, V2Only: options.V2Only,
} }
config.LoadAllowNondistributableArtifacts(options.AllowNondistributableArtifacts)
config.LoadMirrors(options.Mirrors) config.LoadMirrors(options.Mirrors)
config.LoadInsecureRegistries(options.InsecureRegistries) config.LoadInsecureRegistries(options.InsecureRegistries)
return config return config
} }
// LoadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries into config.
func (config *serviceConfig) LoadAllowNondistributableArtifacts(registries []string) error {
cidrs := map[string]*registrytypes.NetIPNet{}
hostnames := map[string]bool{}
for _, r := range registries {
if _, err := ValidateIndexName(r); err != nil {
return err
}
if validateNoScheme(r) != nil {
return fmt.Errorf("allow-nondistributable-artifacts registry %s should not contain '://'", r)
}
if _, ipnet, err := net.ParseCIDR(r); err == nil {
// Valid CIDR.
cidrs[ipnet.String()] = (*registrytypes.NetIPNet)(ipnet)
} else if err := validateHostPort(r); err == nil {
// Must be `host:port` if not CIDR.
hostnames[r] = true
} else {
return fmt.Errorf("allow-nondistributable-artifacts registry %s is not valid: %v", r, err)
}
}
config.AllowNondistributableArtifactsCIDRs = make([]*(registrytypes.NetIPNet), 0)
for _, c := range cidrs {
config.AllowNondistributableArtifactsCIDRs = append(config.AllowNondistributableArtifactsCIDRs, c)
}
config.AllowNondistributableArtifactsHostnames = make([]string, 0)
for h := range hostnames {
config.AllowNondistributableArtifactsHostnames = append(config.AllowNondistributableArtifactsHostnames, h)
}
return nil
}
// LoadMirrors loads mirrors to config, after removing duplicates. // LoadMirrors loads mirrors to config, after removing duplicates.
// Returns an error if mirrors contains an invalid mirror. // Returns an error if mirrors contains an invalid mirror.
func (config *serviceConfig) LoadMirrors(mirrors []string) error { func (config *serviceConfig) LoadMirrors(mirrors []string) error {
@ -211,6 +252,25 @@ skip:
return nil return nil
} }
// allowNondistributableArtifacts returns true if the provided hostname is part of the list of regsitries
// that allow push of nondistributable artifacts.
//
// The list can contain elements with CIDR notation to specify a whole subnet. If the subnet contains an IP
// of the registry specified by hostname, true is returned.
//
// hostname should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name
// or an IP address. If it is a domain name, then it will be resolved to IP addresses for matching. If
// resolution fails, CIDR matching is not performed.
func allowNondistributableArtifacts(config *serviceConfig, hostname string) bool {
for _, h := range config.AllowNondistributableArtifactsHostnames {
if h == hostname {
return true
}
}
return isCIDRMatch(config.AllowNondistributableArtifactsCIDRs, hostname)
}
// isSecureIndex returns false if the provided indexName is part of the list of insecure registries // isSecureIndex returns false if the provided indexName is part of the list of insecure registries
// Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs. // Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs.
// //
@ -229,10 +289,17 @@ func isSecureIndex(config *serviceConfig, indexName string) bool {
return index.Secure return index.Secure
} }
host, _, err := net.SplitHostPort(indexName) return !isCIDRMatch(config.InsecureRegistryCIDRs, indexName)
}
// isCIDRMatch returns true if URLHost matches an element of cidrs. URLHost is a URL.Host (`host:port` or `host`)
// where the `host` part can be either a domain name or an IP address. If it is a domain name, then it will be
// resolved to IP addresses for matching. If resolution fails, false is returned.
func isCIDRMatch(cidrs []*registrytypes.NetIPNet, URLHost string) bool {
host, _, err := net.SplitHostPort(URLHost)
if err != nil { if err != nil {
// assume indexName is of the form `host` without the port and go on. // Assume URLHost is of the form `host` without the port and go on.
host = indexName host = URLHost
} }
addrs, err := lookupIP(host) addrs, err := lookupIP(host)
@ -249,15 +316,15 @@ func isSecureIndex(config *serviceConfig, indexName string) bool {
// Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined. // Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined.
for _, addr := range addrs { for _, addr := range addrs {
for _, ipnet := range config.InsecureRegistryCIDRs { for _, ipnet := range cidrs {
// check if the addr falls in the subnet // check if the addr falls in the subnet
if (*net.IPNet)(ipnet).Contains(addr) { if (*net.IPNet)(ipnet).Contains(addr) {
return false return true
} }
} }
} }
return true return false
} }
// ValidateMirror validates an HTTP(S) registry mirror // ValidateMirror validates an HTTP(S) registry mirror

View File

@ -31,6 +31,7 @@ type Service interface {
Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error) Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error)
ServiceConfig() *registrytypes.ServiceConfig ServiceConfig() *registrytypes.ServiceConfig
TLSConfig(hostname string) (*tls.Config, error) TLSConfig(hostname string) (*tls.Config, error)
LoadAllowNondistributableArtifacts([]string) error
LoadMirrors([]string) error LoadMirrors([]string) error
LoadInsecureRegistries([]string) error LoadInsecureRegistries([]string) error
} }
@ -56,6 +57,8 @@ func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig {
defer s.mu.Unlock() defer s.mu.Unlock()
servConfig := registrytypes.ServiceConfig{ servConfig := registrytypes.ServiceConfig{
AllowNondistributableArtifactsCIDRs: make([]*(registrytypes.NetIPNet), 0),
AllowNondistributableArtifactsHostnames: make([]string, 0),
InsecureRegistryCIDRs: make([]*(registrytypes.NetIPNet), 0), InsecureRegistryCIDRs: make([]*(registrytypes.NetIPNet), 0),
IndexConfigs: make(map[string]*(registrytypes.IndexInfo)), IndexConfigs: make(map[string]*(registrytypes.IndexInfo)),
Mirrors: make([]string, 0), Mirrors: make([]string, 0),
@ -63,6 +66,8 @@ func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig {
// construct a new ServiceConfig which will not retrieve s.Config directly, // construct a new ServiceConfig which will not retrieve s.Config directly,
// and look up items in s.config with mu locked // and look up items in s.config with mu locked
servConfig.AllowNondistributableArtifactsCIDRs = append(servConfig.AllowNondistributableArtifactsCIDRs, s.config.ServiceConfig.AllowNondistributableArtifactsCIDRs...)
servConfig.AllowNondistributableArtifactsHostnames = append(servConfig.AllowNondistributableArtifactsHostnames, s.config.ServiceConfig.AllowNondistributableArtifactsHostnames...)
servConfig.InsecureRegistryCIDRs = append(servConfig.InsecureRegistryCIDRs, s.config.ServiceConfig.InsecureRegistryCIDRs...) servConfig.InsecureRegistryCIDRs = append(servConfig.InsecureRegistryCIDRs, s.config.ServiceConfig.InsecureRegistryCIDRs...)
for key, value := range s.config.ServiceConfig.IndexConfigs { for key, value := range s.config.ServiceConfig.IndexConfigs {
@ -74,6 +79,14 @@ func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig {
return &servConfig return &servConfig
} }
// LoadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries for Service.
func (s *DefaultService) LoadAllowNondistributableArtifacts(registries []string) error {
s.mu.Lock()
defer s.mu.Unlock()
return s.config.LoadAllowNondistributableArtifacts(registries)
}
// LoadMirrors loads registry mirrors for Service // LoadMirrors loads registry mirrors for Service
func (s *DefaultService) LoadMirrors(mirrors []string) error { func (s *DefaultService) LoadMirrors(mirrors []string) error {
s.mu.Lock() s.mu.Lock()
@ -238,6 +251,7 @@ type APIEndpoint struct {
Mirror bool Mirror bool
URL *url.URL URL *url.URL
Version APIVersion Version APIVersion
AllowNondistributableArtifacts bool
Official bool Official bool
TrimHostname bool TrimHostname bool
TLSConfig *tls.Config TLSConfig *tls.Config

View File

@ -44,6 +44,8 @@ func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndp
return endpoints, nil return endpoints, nil
} }
ana := allowNondistributableArtifacts(s.config, hostname)
tlsConfig, err = s.tlsConfig(hostname) tlsConfig, err = s.tlsConfig(hostname)
if err != nil { if err != nil {
return nil, err return nil, err
@ -56,6 +58,7 @@ func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndp
Host: hostname, Host: hostname,
}, },
Version: APIVersion2, Version: APIVersion2,
AllowNondistributableArtifacts: ana,
TrimHostname: true, TrimHostname: true,
TLSConfig: tlsConfig, TLSConfig: tlsConfig,
}, },
@ -68,6 +71,7 @@ func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndp
Host: hostname, Host: hostname,
}, },
Version: APIVersion2, Version: APIVersion2,
AllowNondistributableArtifacts: ana,
TrimHostname: true, TrimHostname: true,
// used to check if supposed to be secure via InsecureSkipVerify // used to check if supposed to be secure via InsecureSkipVerify
TLSConfig: tlsConfig, TLSConfig: tlsConfig,

View File

@ -62,6 +62,7 @@ github.com/miekg/pkcs11 df8ae6ca730422dba20c768ff38ef7d79077a59f
# When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly # When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly
github.com/opencontainers/runc b6b70e53451794e8333e9b602cc096b47a20bd0f github.com/opencontainers/runc b6b70e53451794e8333e9b602cc096b47a20bd0f
github.com/opencontainers/runtime-spec v1.0.0-rc5 # specs github.com/opencontainers/runtime-spec v1.0.0-rc5 # specs
github.com/opencontainers/image-spec f03dbe35d449c54915d235f1a3cf8f585a24babe
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
@ -105,7 +106,7 @@ github.com/docker/containerd 8ef7df579710405c4bb6e0812495671002ce08e0
github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4 github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4
# cluster # cluster
github.com/docker/swarmkit f420c4b9e1535170fc229db97ee8ac32374020b1 github.com/docker/swarmkit ae29cf24355ef2106b63884d2f9b0a6406e5a144
github.com/gogo/protobuf 8d70fb3182befc465c4a1eac8ad4d38ff49778e2 github.com/gogo/protobuf 8d70fb3182befc465c4a1eac8ad4d38ff49778e2
github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e
@ -132,6 +133,6 @@ github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty
# metrics # metrics
github.com/docker/go-metrics 8fd5772bf1584597834c6f7961a530f06cbfbb87 github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18
github.com/opencontainers/selinux v1.0.0-rc1 github.com/opencontainers/selinux v1.0.0-rc1

191
vendor/github.com/opencontainers/image-spec/LICENSE generated vendored Normal file
View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
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.

167
vendor/github.com/opencontainers/image-spec/README.md generated vendored Normal file
View File

@ -0,0 +1,167 @@
# OCI Image Format Specification
<div>
<a href="https://travis-ci.org/opencontainers/image-spec">
<img src="https://travis-ci.org/opencontainers/image-spec.svg?branch=master"></img>
</a>
</div>
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).**
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.
Additional documentation about how this group operates:
- [Code of Conduct](https://github.com/opencontainers/tob/blob/d2f9d68c1332870e40693fe077d311e0742bc73d/code-of-conduct.md)
- [Roadmap](#roadmap)
- [Releases](RELEASES.md)
- [Project Documentation](project.md)
The _optional_ and _base_ layers of all OCI projects are tracked in the [OCI Scope Table](https://www.opencontainers.org/about/oci-scope-table).
## Running an OCI Image
The OCI Image Format partner project is the [OCI Runtime Spec project](https://github.com/opencontainers/runtime-spec).
The Runtime Specification outlines how to run a "[filesystem bundle](https://github.com/opencontainers/runtime-spec/blob/master/bundle.md)" that is unpacked on disk.
At a high-level an OCI implementation would download an OCI Image then unpack that image into an OCI Runtime filesystem bundle.
At this point the OCI Runtime Bundle would be run by an OCI Runtime.
This entire workflow supports the UX that users have come to expect from container engines like Docker and rkt: primarily, the ability to run an image with no additional arguments:
* docker run example.com/org/app:v1.0.0
* rkt run example.com/org/app,version=v1.0.0
To support this UX the OCI Image Format contains sufficient information to launch the application on the target platform (e.g. command, arguments, environment variables, etc).
## FAQ
**Q: Why doesn't this project mention distribution?**
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: 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.
# Contributing
Development happens on GitHub for the spec.
Issues are used for bugs and actionable items and longer discussions can happen on the [mailing list](#mailing-list).
The specification and code is licensed under the Apache 2.0 license found in the `LICENSE` file of this repository.
## Discuss your design
The project welcomes submissions, but please let everyone know what you are working on.
Before undertaking a nontrivial change to this specification, send mail to the [mailing list](#mailing-list) to discuss what you plan to do.
This gives everyone a chance to validate the design, helps prevent duplication of effort, and ensures that the idea fits.
It also guarantees that the design is sound before code is written; a GitHub pull-request is not the place for high-level discussions.
Typos and grammatical errors can go straight to a pull-request.
When in doubt, start on the [mailing-list](#mailing-list).
## Weekly Call
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 [here][minutes].
## Mailing List
You can subscribe and join the mailing list on [Google Groups](https://groups.google.com/a/opencontainers.org/forum/#!forum/dev).
## IRC
OCI discussion happens on #opencontainers on Freenode ([logs][irc-logs]).
## Markdown style
To keep consistency throughout the Markdown files in the Open Container spec all files should be formatted one sentence per line.
This fixes two things: it makes diffing easier with git and it resolves fights about line wrapping length.
For example, this paragraph will span three lines in the Markdown source.
## Git commit
### Sign your work
The sign-off is a simple line at the end of the explanation for the patch, which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch.
The rules are pretty simple: if you can certify the below (from [developercertificate.org](http://developercertificate.org/)):
```
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```
then you just add a line to every git commit message:
Signed-off-by: Joe Smith <joe@gmail.com>
using your real name (sorry, no pseudonyms or anonymous contributions.)
You can add the sign off when creating the git commit via `git commit -s`.
### Commit Style
Simple house-keeping for clean git history.
Read more on [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/) or the Discussion section of [`git-commit(1)`](http://git-scm.com/docs/git-commit).
1. Separate the subject from body with a blank line
2. Limit the subject line to 50 characters
3. Capitalize the subject line
4. Do not end the subject line with a period
5. Use the imperative mood in the subject line
6. Wrap the body at 72 characters
7. Use the body to explain what and why vs. how
* If there was important/useful/essential conversation or information, copy or include a reference
8. When possible, one keyword to scope the change in the subject (i.e. "README: ...", "runtime: ...")
[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

@ -0,0 +1,103 @@
// 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 (
"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 {
// User defines the username or UID which the process in the container should run as.
User string `json:"User,omitempty"`
// ExposedPorts a set of ports to expose from a container running this image.
ExposedPorts map[string]struct{} `json:"ExposedPorts,omitempty"`
// Env is a list of environment variables to be used in a container.
Env []string `json:"Env,omitempty"`
// Entrypoint defines a list of arguments to use as the command to execute when the container starts.
Entrypoint []string `json:"Entrypoint,omitempty"`
// Cmd defines the default arguments to the entrypoint of the container.
Cmd []string `json:"Cmd,omitempty"`
// Volumes is a set of directories which should be created as data volumes in a container running this image.
Volumes map[string]struct{} `json:"Volumes,omitempty"`
// WorkingDir sets the current working directory of the entrypoint process in the container.
WorkingDir string `json:"WorkingDir,omitempty"`
// Labels contains arbitrary metadata for the container.
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
type RootFS struct {
// Type is the type of the rootfs.
Type string `json:"type"`
// DiffIDs is an array of layer content hashes (DiffIDs), in order from bottom-most to top-most.
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"`
// CreatedBy is the command which created the layer.
CreatedBy string `json:"created_by,omitempty"`
// Author is the author of the build point.
Author string `json:"author,omitempty"`
// Comment is a custom message set when creating the layer.
Comment string `json:"comment,omitempty"`
// EmptyLayer is used to mark if the history item created a filesystem diff.
EmptyLayer bool `json:"empty_layer,omitempty"`
}
// Image is the JSON structure which describes some basic information about the image.
// 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"`
// 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"`
// Architecture is the CPU architecture which the binaries in this image are built to run on.
Architecture string `json:"architecture"`
// OS is the name of the operating system which the image is built to run on.
OS string `json:"os"`
// Config defines the execution parameters which should be used as a base when running a container using the image.
Config ImageConfig `json:"config,omitempty"`
// RootFS references the layer content addresses used by the image.
RootFS RootFS `json:"rootfs"`
// History describes the history of each layer.
History []History `json:"history,omitempty"`
}

View File

@ -0,0 +1,68 @@
// 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 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.
type Descriptor struct {
// MediaType is the media type of the object this schema refers to.
MediaType string `json:"mediaType,omitempty"`
// Digest is the digest of the targeted content.
Digest digest.Digest `json:"digest"`
// Size specifies the size in bytes of the blob.
Size int64 `json:"size"`
// URLs specifies a list of URLs from which this object MAY be downloaded
URLs []string `json:"urls,omitempty"`
// 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 `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"`
}

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

@ -0,0 +1,28 @@
// 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
const (
// ImageLayoutFile is the file name of oci image layout file
ImageLayoutFile = "oci-layout"
// ImageLayoutVersion is the version of ImageLayout
ImageLayoutVersion = "1.0.0"
)
// ImageLayout is the structure in the "oci-layout" file, found in the root
// of an OCI Image-layout directory.
type ImageLayout struct {
Version string `json:"imageLayoutVersion"`
}

View File

@ -0,0 +1,32 @@
// 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"
// Manifest provides `application/vnd.oci.image.manifest.v1+json` mediatype structure when marshalled to JSON.
type Manifest struct {
specs.Versioned
// Config references a configuration object for a container, by digest.
// The referenced configuration object is a JSON blob that the runtime uses to set up the container.
Config Descriptor `json:"config"`
// Layers is an indexed list of layers referenced by the manifest.
Layers []Descriptor `json:"layers"`
// Annotations contains arbitrary metadata for the image manifest.
Annotations map[string]string `json:"annotations,omitempty"`
}

View File

@ -0,0 +1,48 @@
// 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
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"
// MediaTypeImageIndex specifies the media type for an image index.
MediaTypeImageIndex = "application/vnd.oci.image.index.v1+json"
// MediaTypeImageLayer is the media type used for layers referenced by the manifest.
MediaTypeImageLayer = "application/vnd.oci.image.layer.v1.tar"
// MediaTypeImageLayerGzip is the media type used for gzipped layers
// referenced by the manifest.
MediaTypeImageLayerGzip = "application/vnd.oci.image.layer.v1.tar+gzip"
// MediaTypeImageLayerNonDistributable is the media type for layers referenced by
// the manifest but with distribution restrictions.
MediaTypeImageLayerNonDistributable = "application/vnd.oci.image.layer.nondistributable.v1.tar"
// MediaTypeImageLayerNonDistributableGzip is the media type for
// gzipped layers referenced by the manifest but with distribution
// restrictions.
MediaTypeImageLayerNonDistributableGzip = "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip"
// MediaTypeImageConfig specifies the media type for the image configuration.
MediaTypeImageConfig = "application/vnd.oci.image.config.v1+json"
)

View File

@ -0,0 +1,32 @@
// 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 specs
import "fmt"
const (
// VersionMajor is for an API incompatible changes
VersionMajor = 1
// VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 0
// VersionPatch is for backwards-compatible bug fixes
VersionPatch = 0
// VersionDev indicates development branch. Releases will be empty string.
VersionDev = "-rc5-dev"
)
// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)

View File

@ -0,0 +1,23 @@
// 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 specs
// Versioned provides a struct with the manifest schemaVersion and mediaType.
// Incoming content with unknown schema version can be decoded against this
// struct to check the version.
type Versioned struct {
// SchemaVersion is the image manifest schema that this image follows
SchemaVersion int `json:"schemaVersion"`
}