99cd23cefd
This reverts commit98fc09128b
in order to keep registry v2 schema1 handling and libtrust-key-based engine ID. Because registry v2 schema1 was not officially deprecated and registries are still relying on it, this patch puts its logic back. However, registry v1 relics are not added back since v1 logic has been removed a while ago. This also fixes an engine upgrade issue in a swarm cluster. It was relying on the Engine ID to be the same upon upgrade, but the mentioned commit modified the logic to use UUID and from a different file. Since the libtrust key is always needed to support v2 schema1 pushes, that the old engine ID is based on the libtrust key, and that the engine ID needs to be conserved across upgrades, adding a UUID-based engine ID logic seems to add more complexity than it solves the problems. Hence reverting the engine ID changes as well. Signed-off-by: Tibor Vass <tibor@docker.com> (cherry picked from commitf695e98cb7
) Signed-off-by: Tibor Vass <tibor@docker.com>
266 lines
7.4 KiB
Go
266 lines
7.4 KiB
Go
package distribution // import "github.com/docker/docker/distribution"
|
|
|
|
import (
|
|
"context"
|
|
"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"
|
|
"github.com/docker/docker/pkg/system"
|
|
refstore "github.com/docker/docker/reference"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/docker/libtrust"
|
|
"github.com/opencontainers/go-digest"
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
)
|
|
|
|
// 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.
|
|
ReferenceStore refstore.Store
|
|
// 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
|
|
// Platform is the requested platform of the image being pulled
|
|
Platform *specs.Platform
|
|
}
|
|
|
|
// ImagePushConfig stores push configuration.
|
|
type ImagePushConfig struct {
|
|
Config
|
|
|
|
// ConfigMediaType is the configuration media type for
|
|
// schema2 manifests.
|
|
ConfigMediaType string
|
|
// LayerStores (indexed by operating system) manages layers.
|
|
LayerStores map[string]PushLayerProvider
|
|
// 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)
|
|
RootFSFromConfig([]byte) (*image.RootFS, error)
|
|
PlatformFromConfig([]byte) (*specs.Platform, error)
|
|
}
|
|
|
|
// 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
|
|
Download(ctx context.Context, initialRootFS image.RootFS, os string, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func (s *imageConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) {
|
|
var unmarshalledConfig image.Image
|
|
if err := json.Unmarshal(c, &unmarshalledConfig); err != nil {
|
|
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
|
|
}
|
|
|
|
// fail immediately on Windows when downloading a non-Windows image
|
|
// and vice versa. Exception on Windows if Linux Containers are enabled.
|
|
if runtime.GOOS == "windows" && unmarshalledConfig.OS == "linux" && !system.LCOWSupported() {
|
|
return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
|
|
} else if runtime.GOOS != "windows" && unmarshalledConfig.OS == "windows" {
|
|
return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
|
|
}
|
|
|
|
os := unmarshalledConfig.OS
|
|
if os == "" {
|
|
os = runtime.GOOS
|
|
}
|
|
if !system.IsOSSupported(os) {
|
|
return nil, system.ErrNotSupportedOperatingSystem
|
|
}
|
|
return &specs.Platform{OS: os, Architecture: unmarshalledConfig.Architecture, OSVersion: unmarshalledConfig.OSVersion}, nil
|
|
}
|
|
|
|
type storeLayerProvider struct {
|
|
ls layer.Store
|
|
}
|
|
|
|
// NewLayerProvidersFromStores returns layer providers backed by
|
|
// an instance of LayerStore. Only getting layers as gzipped
|
|
// tars is supported.
|
|
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}
|
|
}
|
|
return plps
|
|
}
|
|
|
|
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
|
|
}
|
|
sl := storeLayer{
|
|
Layer: p,
|
|
ls: l.ls,
|
|
}
|
|
if d, ok := p.(distribution.Describable); ok {
|
|
return &describableStoreLayer{
|
|
storeLayer: sl,
|
|
describable: d,
|
|
}
|
|
}
|
|
|
|
return &sl
|
|
}
|
|
|
|
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()
|
|
}
|