2018-02-05 16:05:59 -05:00
|
|
|
package distribution // import "github.com/docker/docker/distribution"
|
2016-12-16 14:19:05 -05:00
|
|
|
|
|
|
|
import (
|
2018-04-19 18:30:59 -04:00
|
|
|
"context"
|
2016-12-16 14:19:05 -05:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"runtime"
|
|
|
|
|
|
|
|
"github.com/docker/distribution"
|
|
|
|
"github.com/docker/distribution/manifest/schema2"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
|
|
"github.com/docker/docker/distribution/metadata"
|
|
|
|
"github.com/docker/docker/distribution/xfer"
|
|
|
|
"github.com/docker/docker/image"
|
|
|
|
"github.com/docker/docker/layer"
|
|
|
|
"github.com/docker/docker/pkg/progress"
|
2017-04-24 13:28:21 -04:00
|
|
|
"github.com/docker/docker/pkg/system"
|
2017-01-25 19:54:18 -05:00
|
|
|
refstore "github.com/docker/docker/reference"
|
2016-12-16 14:19:05 -05:00
|
|
|
"github.com/docker/docker/registry"
|
|
|
|
"github.com/docker/libtrust"
|
2017-01-06 20:23:18 -05:00
|
|
|
"github.com/opencontainers/go-digest"
|
2018-02-15 16:17:27 -05:00
|
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
2016-12-16 14:19:05 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// Config stores configuration for communicating
|
|
|
|
// with a registry.
|
|
|
|
type Config struct {
|
|
|
|
// MetaHeaders stores HTTP headers with metadata about the image
|
|
|
|
MetaHeaders map[string][]string
|
|
|
|
// AuthConfig holds authentication credentials for authenticating with
|
|
|
|
// the registry.
|
|
|
|
AuthConfig *types.AuthConfig
|
|
|
|
// ProgressOutput is the interface for showing the status of the pull
|
|
|
|
// operation.
|
|
|
|
ProgressOutput progress.Output
|
|
|
|
// RegistryService is the registry service to use for TLS configuration
|
|
|
|
// and endpoint lookup.
|
|
|
|
RegistryService registry.Service
|
|
|
|
// ImageEventLogger notifies events for a given image
|
|
|
|
ImageEventLogger func(id, name, action string)
|
|
|
|
// MetadataStore is the storage backend for distribution-specific
|
|
|
|
// metadata.
|
|
|
|
MetadataStore metadata.Store
|
|
|
|
// ImageStore manages images.
|
|
|
|
ImageStore ImageConfigStore
|
|
|
|
// ReferenceStore manages tags. This value is optional, when excluded
|
|
|
|
// content will not be tagged.
|
2017-01-25 19:54:18 -05:00
|
|
|
ReferenceStore refstore.Store
|
2016-12-16 14:19:05 -05:00
|
|
|
// RequireSchema2 ensures that only schema2 manifests are used.
|
|
|
|
RequireSchema2 bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// ImagePullConfig stores pull configuration.
|
|
|
|
type ImagePullConfig struct {
|
|
|
|
Config
|
|
|
|
|
|
|
|
// DownloadManager manages concurrent pulls.
|
|
|
|
DownloadManager RootFSDownloadManager
|
|
|
|
// Schema2Types is the valid schema2 configuration types allowed
|
|
|
|
// by the pull operation.
|
|
|
|
Schema2Types []string
|
2018-06-26 17:49:33 -04:00
|
|
|
// Platform is the requested platform of the image being pulled
|
|
|
|
Platform *specs.Platform
|
2016-12-16 14:19:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// ImagePushConfig stores push configuration.
|
|
|
|
type ImagePushConfig struct {
|
|
|
|
Config
|
|
|
|
|
|
|
|
// ConfigMediaType is the configuration media type for
|
|
|
|
// schema2 manifests.
|
|
|
|
ConfigMediaType string
|
2017-09-19 15:14:46 -04:00
|
|
|
// LayerStores (indexed by operating system) manages layers.
|
|
|
|
LayerStores map[string]PushLayerProvider
|
2016-12-16 14:19:05 -05:00
|
|
|
// TrustKey is the private key for legacy signatures. This is typically
|
|
|
|
// an ephemeral key, since these signatures are no longer verified.
|
|
|
|
TrustKey libtrust.PrivateKey
|
|
|
|
// UploadManager dispatches uploads.
|
|
|
|
UploadManager *xfer.LayerUploadManager
|
|
|
|
}
|
|
|
|
|
|
|
|
// ImageConfigStore handles storing and getting image configurations
|
|
|
|
// by digest. Allows getting an image configurations rootfs from the
|
|
|
|
// configuration.
|
|
|
|
type ImageConfigStore interface {
|
|
|
|
Put([]byte) (digest.Digest, error)
|
|
|
|
Get(digest.Digest) ([]byte, error)
|
2018-02-15 16:17:27 -05:00
|
|
|
RootFSFromConfig([]byte) (*image.RootFS, error)
|
|
|
|
PlatformFromConfig([]byte) (*specs.Platform, error)
|
2016-12-16 14:19:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// PushLayerProvider provides layers to be pushed by ChainID.
|
|
|
|
type PushLayerProvider interface {
|
|
|
|
Get(layer.ChainID) (PushLayer, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PushLayer is a pushable layer with metadata about the layer
|
|
|
|
// and access to the content of the layer.
|
|
|
|
type PushLayer interface {
|
|
|
|
ChainID() layer.ChainID
|
|
|
|
DiffID() layer.DiffID
|
|
|
|
Parent() PushLayer
|
|
|
|
Open() (io.ReadCloser, error)
|
|
|
|
Size() (int64, error)
|
|
|
|
MediaType() string
|
|
|
|
Release()
|
|
|
|
}
|
|
|
|
|
|
|
|
// RootFSDownloadManager handles downloading of the rootfs
|
|
|
|
type RootFSDownloadManager interface {
|
|
|
|
// Download downloads the layers into the given initial rootfs and
|
|
|
|
// returns the final rootfs.
|
|
|
|
// Given progress output to track download progress
|
|
|
|
// Returns function to release download resources
|
2017-08-24 14:48:16 -04:00
|
|
|
Download(ctx context.Context, initialRootFS image.RootFS, os string, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error)
|
2016-12-16 14:19:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
type imageConfigStore struct {
|
|
|
|
image.Store
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewImageConfigStoreFromStore returns an ImageConfigStore backed
|
|
|
|
// by an image.Store for container images.
|
|
|
|
func NewImageConfigStoreFromStore(is image.Store) ImageConfigStore {
|
|
|
|
return &imageConfigStore{
|
|
|
|
Store: is,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *imageConfigStore) Put(c []byte) (digest.Digest, error) {
|
|
|
|
id, err := s.Store.Create(c)
|
|
|
|
return digest.Digest(id), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *imageConfigStore) Get(d digest.Digest) ([]byte, error) {
|
|
|
|
img, err := s.Store.Get(image.IDFromDigest(d))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return img.RawJSON(), nil
|
|
|
|
}
|
|
|
|
|
2018-02-15 16:17:27 -05:00
|
|
|
func (s *imageConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) {
|
2016-12-16 14:19:05 -05:00
|
|
|
var unmarshalledConfig image.Image
|
|
|
|
if err := json.Unmarshal(c, &unmarshalledConfig); err != nil {
|
2018-02-15 16:17:27 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return unmarshalledConfig.RootFS, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *imageConfigStore) PlatformFromConfig(c []byte) (*specs.Platform, error) {
|
|
|
|
var unmarshalledConfig image.Image
|
|
|
|
if err := json.Unmarshal(c, &unmarshalledConfig); err != nil {
|
|
|
|
return nil, err
|
2016-12-16 14:19:05 -05:00
|
|
|
}
|
|
|
|
|
2017-01-30 20:04:14 -05:00
|
|
|
// fail immediately on Windows when downloading a non-Windows image
|
2017-04-24 13:28:21 -04:00
|
|
|
// and vice versa. Exception on Windows if Linux Containers are enabled.
|
|
|
|
if runtime.GOOS == "windows" && unmarshalledConfig.OS == "linux" && !system.LCOWSupported() {
|
2018-02-15 16:17:27 -05:00
|
|
|
return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
|
2017-01-30 20:04:14 -05:00
|
|
|
} else if runtime.GOOS != "windows" && unmarshalledConfig.OS == "windows" {
|
2018-02-15 16:17:27 -05:00
|
|
|
return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
|
2016-12-16 14:19:05 -05:00
|
|
|
}
|
|
|
|
|
2017-08-24 14:48:16 -04:00
|
|
|
os := unmarshalledConfig.OS
|
|
|
|
if os == "" {
|
|
|
|
os = runtime.GOOS
|
2017-04-25 19:45:42 -04:00
|
|
|
}
|
2017-11-20 11:33:20 -05:00
|
|
|
if !system.IsOSSupported(os) {
|
2018-02-15 16:17:27 -05:00
|
|
|
return nil, system.ErrNotSupportedOperatingSystem
|
2017-11-20 11:33:20 -05:00
|
|
|
}
|
2018-06-26 17:49:33 -04:00
|
|
|
return &specs.Platform{OS: os, Architecture: unmarshalledConfig.Architecture, OSVersion: unmarshalledConfig.OSVersion}, nil
|
2016-12-16 14:19:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
type storeLayerProvider struct {
|
|
|
|
ls layer.Store
|
|
|
|
}
|
|
|
|
|
2017-09-19 15:14:46 -04:00
|
|
|
// NewLayerProvidersFromStores returns layer providers backed by
|
2016-12-16 14:19:05 -05:00
|
|
|
// an instance of LayerStore. Only getting layers as gzipped
|
|
|
|
// tars is supported.
|
2017-09-19 15:14:46 -04:00
|
|
|
func NewLayerProvidersFromStores(lss map[string]layer.Store) map[string]PushLayerProvider {
|
|
|
|
plps := make(map[string]PushLayerProvider)
|
|
|
|
for os, ls := range lss {
|
|
|
|
plps[os] = &storeLayerProvider{ls: ls}
|
2016-12-16 14:19:05 -05:00
|
|
|
}
|
2017-09-19 15:14:46 -04:00
|
|
|
return plps
|
2016-12-16 14:19:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *storeLayerProvider) Get(lid layer.ChainID) (PushLayer, error) {
|
|
|
|
if lid == "" {
|
|
|
|
return &storeLayer{
|
|
|
|
Layer: layer.EmptyLayer,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
l, err := p.ls.Get(lid)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
sl := storeLayer{
|
|
|
|
Layer: l,
|
|
|
|
ls: p.ls,
|
|
|
|
}
|
|
|
|
if d, ok := l.(distribution.Describable); ok {
|
|
|
|
return &describableStoreLayer{
|
|
|
|
storeLayer: sl,
|
|
|
|
describable: d,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return &sl, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type storeLayer struct {
|
|
|
|
layer.Layer
|
|
|
|
ls layer.Store
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *storeLayer) Parent() PushLayer {
|
|
|
|
p := l.Layer.Parent()
|
|
|
|
if p == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2017-01-11 22:01:13 -05:00
|
|
|
sl := storeLayer{
|
2016-12-16 14:19:05 -05:00
|
|
|
Layer: p,
|
|
|
|
ls: l.ls,
|
|
|
|
}
|
2017-01-11 22:01:13 -05:00
|
|
|
if d, ok := p.(distribution.Describable); ok {
|
|
|
|
return &describableStoreLayer{
|
|
|
|
storeLayer: sl,
|
|
|
|
describable: d,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &sl
|
2016-12-16 14:19:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *storeLayer) Open() (io.ReadCloser, error) {
|
|
|
|
return l.Layer.TarStream()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *storeLayer) Size() (int64, error) {
|
|
|
|
return l.Layer.DiffSize()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *storeLayer) MediaType() string {
|
|
|
|
// layer store always returns uncompressed tars
|
|
|
|
return schema2.MediaTypeUncompressedLayer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *storeLayer) Release() {
|
|
|
|
if l.ls != nil {
|
|
|
|
layer.ReleaseAndLog(l.ls, l.Layer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type describableStoreLayer struct {
|
|
|
|
storeLayer
|
|
|
|
describable distribution.Describable
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *describableStoreLayer) Descriptor() distribution.Descriptor {
|
|
|
|
return l.describable.Descriptor()
|
|
|
|
}
|