Merge pull request #284 from tiborvass/19.03-revert-remove-legacy-registry
[19.03] Keep but deprecate registry v2 schema1 logic and revert to libtrust-key-based engine ID
diff --git a/Dockerfile b/Dockerfile
index 1dad41e..d563fab 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -51,6 +51,11 @@
&& make PREFIX=/build/ install-criu
FROM base AS registry
+# Install two versions of the registry. The first is an older version that
+# only supports schema1 manifests. The second is a newer version that supports
+# both. This allows integration-cli tests to cover push/pull with both schema1
+# and schema2 manifests.
+ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
@@ -58,6 +63,13 @@
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
go build -buildmode=pie -o /build/registry-v2 github.com/docker/distribution/cmd/registry \
+ && case $(dpkg --print-architecture) in \
+ amd64|ppc64*|s390x) \
+ (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1"); \
+ GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH"; \
+ go build -buildmode=pie -o /build/registry-v2-schema1 github.com/docker/distribution/cmd/registry; \
+ ;; \
+ esac \
&& rm -rf "$GOPATH"
diff --git a/cmd/dockerd/config.go b/cmd/dockerd/config.go
index 3173154..b28ac1c 100644
--- a/cmd/dockerd/config.go
+++ b/cmd/dockerd/config.go
@@ -12,6 +12,8 @@
const (
// defaultShutdownTimeout is the default shutdown timeout for the daemon
defaultShutdownTimeout = 15
+ // defaultTrustKeyFile is the default filename for the trust key
+ defaultTrustKeyFile = "key.json"
)
// installCommonConfigFlags adds flags to the pflag.FlagSet to configure the daemon
diff --git a/cmd/dockerd/daemon.go b/cmd/dockerd/daemon.go
index 0359411..539015e 100644
--- a/cmd/dockerd/daemon.go
+++ b/cmd/dockerd/daemon.go
@@ -424,6 +424,14 @@
conf.CommonTLSOptions.KeyFile = opts.TLSOptions.KeyFile
}
+ if conf.TrustKeyPath == "" {
+ daemonConfDir, err := getDaemonConfDir(conf.Root)
+ if err != nil {
+ return nil, err
+ }
+ conf.TrustKeyPath = filepath.Join(daemonConfDir, defaultTrustKeyFile)
+ }
+
if flags.Changed("graph") && flags.Changed("data-root") {
return nil, errors.New(`cannot specify both "--graph" and "--data-root" option`)
}
diff --git a/cmd/dockerd/daemon_unix.go b/cmd/dockerd/daemon_unix.go
index a0f1b89..a6685bb 100644
--- a/cmd/dockerd/daemon_unix.go
+++ b/cmd/dockerd/daemon_unix.go
@@ -58,6 +58,10 @@
return nil
}
+func getDaemonConfDir(_ string) (string, error) {
+ return getDefaultDaemonConfigDir()
+}
+
func (cli *DaemonCli) getPlatformContainerdDaemonOpts() ([]supervisor.DaemonOpt, error) {
opts := []supervisor.DaemonOpt{
supervisor.WithOOMScore(cli.Config.OOMScoreAdjust),
diff --git a/cmd/dockerd/daemon_windows.go b/cmd/dockerd/daemon_windows.go
index bf7b9ef..4693276 100644
--- a/cmd/dockerd/daemon_windows.go
+++ b/cmd/dockerd/daemon_windows.go
@@ -5,6 +5,7 @@
"fmt"
"net"
"os"
+ "path/filepath"
"time"
"github.com/docker/docker/daemon/config"
@@ -23,6 +24,10 @@
return nil
}
+func getDaemonConfDir(root string) (string, error) {
+ return filepath.Join(root, `\config`), nil
+}
+
// preNotifySystem sends a message to the host when the API is active, but before the daemon is
func preNotifySystem() {
// start the service now to prevent timeouts waiting for daemon to start
diff --git a/daemon/config/config.go b/daemon/config/config.go
index 75430ed..80ecbbd 100644
--- a/daemon/config/config.go
+++ b/daemon/config/config.go
@@ -63,6 +63,8 @@
var skipValidateOptions = map[string]bool{
"features": true,
"builder": true,
+ // Corresponding flag has been removed because it was already unusable
+ "deprecated-key-path": true,
}
// skipDuplicates contains configuration keys that
@@ -134,6 +136,12 @@
SocketGroup string `json:"group,omitempty"`
CorsHeaders string `json:"api-cors-header,omitempty"`
+ // TrustKeyPath is used to generate the daemon ID and for signing schema 1 manifests
+ // when pushing to a registry which does not support schema 2. This field is marked as
+ // deprecated because schema 1 manifests are deprecated in favor of schema 2 and the
+ // daemon ID will use a dedicated identifier not shared with exported signatures.
+ TrustKeyPath string `json:"deprecated-key-path,omitempty"`
+
// LiveRestoreEnabled determines whether we should keep containers
// alive upon daemon shutdown/start
LiveRestoreEnabled bool `json:"live-restore,omitempty"`
@@ -239,7 +247,6 @@
config := Config{}
config.LogConfig.Config = make(map[string]string)
config.ClusterOpts = make(map[string]string)
-
return &config
}
diff --git a/daemon/daemon.go b/daemon/daemon.go
index e6a1fb1..f049b0d 100644
--- a/daemon/daemon.go
+++ b/daemon/daemon.go
@@ -960,7 +960,7 @@
return nil, err
}
- uuid, err := loadOrCreateUUID(filepath.Join(config.Root, "engine_uuid"))
+ trustKey, err := loadOrCreateTrustKey(config.TrustKeyPath)
if err != nil {
return nil, err
}
@@ -1005,7 +1005,7 @@
return nil, errors.New("Devices cgroup isn't mounted")
}
- d.ID = uuid
+ d.ID = trustKey.PublicKey().KeyID()
d.repository = daemonRepo
d.containers = container.NewMemoryStore()
if d.containersReplica, err = container.NewViewDB(); err != nil {
@@ -1036,6 +1036,7 @@
MaxConcurrentUploads: *config.MaxConcurrentUploads,
ReferenceStore: rs,
RegistryService: registryService,
+ TrustKey: trustKey,
})
go d.execCommandGC()
diff --git a/daemon/images/image_push.go b/daemon/images/image_push.go
index c397b1c..4c7be8d 100644
--- a/daemon/images/image_push.go
+++ b/daemon/images/image_push.go
@@ -54,6 +54,7 @@
},
ConfigMediaType: schema2.MediaTypeImageConfig,
LayerStores: distribution.NewLayerProvidersFromStores(i.layerStores),
+ TrustKey: i.trustKey,
UploadManager: i.uploadManager,
}
diff --git a/daemon/images/service.go b/daemon/images/service.go
index 9034e5f..e8df5cb 100644
--- a/daemon/images/service.go
+++ b/daemon/images/service.go
@@ -14,6 +14,7 @@
"github.com/docker/docker/layer"
dockerreference "github.com/docker/docker/reference"
"github.com/docker/docker/registry"
+ "github.com/docker/libtrust"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -39,6 +40,7 @@
MaxConcurrentUploads int
ReferenceStore dockerreference.Store
RegistryService registry.Service
+ TrustKey libtrust.PrivateKey
}
// NewImageService returns a new ImageService from a configuration
@@ -54,6 +56,7 @@
layerStores: config.LayerStores,
referenceStore: config.ReferenceStore,
registryService: config.RegistryService,
+ trustKey: config.TrustKey,
uploadManager: xfer.NewLayerUploadManager(config.MaxConcurrentUploads),
}
}
@@ -69,6 +72,7 @@
pruneRunning int32
referenceStore dockerreference.Store
registryService registry.Service
+ trustKey libtrust.PrivateKey
uploadManager *xfer.LayerUploadManager
}
diff --git a/daemon/trustkey.go b/daemon/trustkey.go
new file mode 100644
index 0000000..4d72c93
--- /dev/null
+++ b/daemon/trustkey.go
@@ -0,0 +1,57 @@
+package daemon // import "github.com/docker/docker/daemon"
+
+import (
+ "encoding/json"
+ "encoding/pem"
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/docker/docker/pkg/ioutils"
+ "github.com/docker/docker/pkg/system"
+ "github.com/docker/libtrust"
+)
+
+// LoadOrCreateTrustKey attempts to load the libtrust key at the given path,
+// otherwise generates a new one
+// TODO: this should use more of libtrust.LoadOrCreateTrustKey which may need
+// a refactor or this function to be moved into libtrust
+func loadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
+ err := system.MkdirAll(filepath.Dir(trustKeyPath), 0755, "")
+ if err != nil {
+ return nil, err
+ }
+ trustKey, err := libtrust.LoadKeyFile(trustKeyPath)
+ if err == libtrust.ErrKeyFileDoesNotExist {
+ trustKey, err = libtrust.GenerateECP256PrivateKey()
+ if err != nil {
+ return nil, fmt.Errorf("Error generating key: %s", err)
+ }
+ encodedKey, err := serializePrivateKey(trustKey, filepath.Ext(trustKeyPath))
+ if err != nil {
+ return nil, fmt.Errorf("Error serializing key: %s", err)
+ }
+ if err := ioutils.AtomicWriteFile(trustKeyPath, encodedKey, os.FileMode(0600)); err != nil {
+ return nil, fmt.Errorf("Error saving key file: %s", err)
+ }
+ } else if err != nil {
+ return nil, fmt.Errorf("Error loading key file %s: %s", trustKeyPath, err)
+ }
+ return trustKey, nil
+}
+
+func serializePrivateKey(key libtrust.PrivateKey, ext string) (encoded []byte, err error) {
+ if ext == ".json" || ext == ".jwk" {
+ encoded, err = json.Marshal(key)
+ if err != nil {
+ return nil, fmt.Errorf("unable to encode private key JWK: %s", err)
+ }
+ } else {
+ pemBlock, err := key.PEMBlock()
+ if err != nil {
+ return nil, fmt.Errorf("unable to encode private key PEM: %s", err)
+ }
+ encoded = pem.EncodeToMemory(pemBlock)
+ }
+ return
+}
diff --git a/daemon/trustkey_test.go b/daemon/trustkey_test.go
new file mode 100644
index 0000000..e49e76a
--- /dev/null
+++ b/daemon/trustkey_test.go
@@ -0,0 +1,71 @@
+package daemon // import "github.com/docker/docker/daemon"
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "gotest.tools/assert"
+ is "gotest.tools/assert/cmp"
+ "gotest.tools/fs"
+)
+
+// LoadOrCreateTrustKey
+func TestLoadOrCreateTrustKeyInvalidKeyFile(t *testing.T) {
+ tmpKeyFolderPath, err := ioutil.TempDir("", "api-trustkey-test")
+ assert.NilError(t, err)
+ defer os.RemoveAll(tmpKeyFolderPath)
+
+ tmpKeyFile, err := ioutil.TempFile(tmpKeyFolderPath, "keyfile")
+ assert.NilError(t, err)
+
+ _, err = loadOrCreateTrustKey(tmpKeyFile.Name())
+ assert.Check(t, is.ErrorContains(err, "Error loading key file"))
+}
+
+func TestLoadOrCreateTrustKeyCreateKeyWhenFileDoesNotExist(t *testing.T) {
+ tmpKeyFolderPath := fs.NewDir(t, "api-trustkey-test")
+ defer tmpKeyFolderPath.Remove()
+
+ // Without the need to create the folder hierarchy
+ tmpKeyFile := tmpKeyFolderPath.Join("keyfile")
+
+ key, err := loadOrCreateTrustKey(tmpKeyFile)
+ assert.NilError(t, err)
+ assert.Check(t, key != nil)
+
+ _, err = os.Stat(tmpKeyFile)
+ assert.NilError(t, err, "key file doesn't exist")
+}
+
+func TestLoadOrCreateTrustKeyCreateKeyWhenDirectoryDoesNotExist(t *testing.T) {
+ tmpKeyFolderPath := fs.NewDir(t, "api-trustkey-test")
+ defer tmpKeyFolderPath.Remove()
+ tmpKeyFile := tmpKeyFolderPath.Join("folder/hierarchy/keyfile")
+
+ key, err := loadOrCreateTrustKey(tmpKeyFile)
+ assert.NilError(t, err)
+ assert.Check(t, key != nil)
+
+ _, err = os.Stat(tmpKeyFile)
+ assert.NilError(t, err, "key file doesn't exist")
+}
+
+func TestLoadOrCreateTrustKeyCreateKeyNoPath(t *testing.T) {
+ defer os.Remove("keyfile")
+ key, err := loadOrCreateTrustKey("keyfile")
+ assert.NilError(t, err)
+ assert.Check(t, key != nil)
+
+ _, err = os.Stat("keyfile")
+ assert.NilError(t, err, "key file doesn't exist")
+}
+
+func TestLoadOrCreateTrustKeyLoadValidKey(t *testing.T) {
+ tmpKeyFile := filepath.Join("testdata", "keyfile")
+ key, err := loadOrCreateTrustKey(tmpKeyFile)
+ assert.NilError(t, err)
+ expected := "AWX2:I27X:WQFX:IOMK:CNAK:O7PW:VYNB:ZLKC:CVAE:YJP2:SI4A:XXAY"
+ assert.Check(t, is.Contains(key.String(), expected))
+}
diff --git a/daemon/uuid.go b/daemon/uuid.go
deleted file mode 100644
index 9640866..0000000
--- a/daemon/uuid.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package daemon // import "github.com/docker/docker/daemon"
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
-
- "github.com/docker/docker/pkg/ioutils"
- "github.com/google/uuid"
-)
-
-func loadOrCreateUUID(path string) (string, error) {
- err := os.MkdirAll(filepath.Dir(path), 0755)
- if err != nil {
- return "", err
- }
- var id string
- idb, err := ioutil.ReadFile(path)
- if os.IsNotExist(err) {
- id = uuid.New().String()
- if err := ioutils.AtomicWriteFile(path, []byte(id), os.FileMode(0600)); err != nil {
- return "", fmt.Errorf("Error saving uuid file: %s", err)
- }
- } else if err != nil {
- return "", fmt.Errorf("Error loading uuid file %s: %s", path, err)
- } else {
- idp, err := uuid.Parse(string(idb))
- if err != nil {
- return "", fmt.Errorf("Error parsing uuid in file %s: %s", path, err)
- }
- id = idp.String()
- }
- return id, nil
-}
diff --git a/distribution/config.go b/distribution/config.go
index e9631d1..438051c 100644
--- a/distribution/config.go
+++ b/distribution/config.go
@@ -18,6 +18,7 @@
"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"
)
@@ -72,6 +73,9 @@
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
}
diff --git a/distribution/pull.go b/distribution/pull.go
index 5de73ae..be366ce 100644
--- a/distribution/pull.go
+++ b/distribution/pull.go
@@ -39,12 +39,7 @@
repoInfo: repoInfo,
}, nil
case registry.APIVersion1:
- return &v1Puller{
- v1IDService: metadata.NewV1IDService(imagePullConfig.MetadataStore),
- endpoint: endpoint,
- config: imagePullConfig,
- repoInfo: repoInfo,
- }, nil
+ return nil, fmt.Errorf("protocol version %d no longer supported. Please contact admins of registry %s", endpoint.Version, endpoint.URL)
}
return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL)
}
diff --git a/distribution/pull_v1.go b/distribution/pull_v1.go
deleted file mode 100644
index b244bb9..0000000
--- a/distribution/pull_v1.go
+++ /dev/null
@@ -1,365 +0,0 @@
-package distribution // import "github.com/docker/docker/distribution"
-
-import (
- "context"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "net"
- "net/url"
- "os"
- "strings"
- "time"
-
- "github.com/docker/distribution/reference"
- "github.com/docker/distribution/registry/client/transport"
- "github.com/docker/docker/distribution/metadata"
- "github.com/docker/docker/distribution/xfer"
- "github.com/docker/docker/dockerversion"
- "github.com/docker/docker/image"
- "github.com/docker/docker/image/v1"
- "github.com/docker/docker/layer"
- "github.com/docker/docker/pkg/ioutils"
- "github.com/docker/docker/pkg/progress"
- "github.com/docker/docker/pkg/stringid"
- "github.com/docker/docker/registry"
- specs "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/sirupsen/logrus"
-)
-
-type v1Puller struct {
- v1IDService *metadata.V1IDService
- endpoint registry.APIEndpoint
- config *ImagePullConfig
- repoInfo *registry.RepositoryInfo
- session *registry.Session
-}
-
-func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, _ *specs.Platform) error {
- if _, isCanonical := ref.(reference.Canonical); isCanonical {
- // Allowing fallback, because HTTPS v1 is before HTTP v2
- return fallbackError{err: ErrNoSupport{Err: errors.New("Cannot pull by digest with v1 registry")}}
- }
-
- tlsConfig, err := p.config.RegistryService.TLSConfig(p.repoInfo.Index.Name)
- if err != nil {
- return err
- }
- // Adds Docker-specific headers as well as user-specified headers (metaHeaders)
- tr := transport.NewTransport(
- // TODO(tiborvass): was ReceiveTimeout
- registry.NewTransport(tlsConfig),
- registry.Headers(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders)...,
- )
- client := registry.HTTPClient(tr)
- v1Endpoint := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders)
- p.session, err = registry.NewSession(client, p.config.AuthConfig, v1Endpoint)
- if err != nil {
- // TODO(dmcgowan): Check if should fallback
- logrus.Debugf("Fallback from error: %s", err)
- return fallbackError{err: err}
- }
- if err := p.pullRepository(ctx, ref); err != nil {
- // TODO(dmcgowan): Check if should fallback
- return err
- }
- progress.Message(p.config.ProgressOutput, "", p.repoInfo.Name.Name()+": this image was pulled from a legacy registry. Important: This registry version will not be supported in future versions of docker.")
-
- return nil
-}
-
-func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) error {
- progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.Name.Name())
-
- tagged, isTagged := ref.(reference.NamedTagged)
-
- repoData, err := p.session.GetRepositoryData(p.repoInfo.Name)
- if err != nil {
- if strings.Contains(err.Error(), "HTTP code: 404") {
- if isTagged {
- return fmt.Errorf("Error: image %s:%s not found", reference.Path(p.repoInfo.Name), tagged.Tag())
- }
- return fmt.Errorf("Error: image %s not found", reference.Path(p.repoInfo.Name))
- }
- // Unexpected HTTP error
- return err
- }
-
- logrus.Debug("Retrieving the tag list")
- var tagsList map[string]string
- if !isTagged {
- tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.Name)
- } else {
- var tagID string
- tagsList = make(map[string]string)
- tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo.Name, tagged.Tag())
- if err == registry.ErrRepoNotFound {
- return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.Name.Name())
- }
- tagsList[tagged.Tag()] = tagID
- }
- if err != nil {
- logrus.Errorf("unable to get remote tags: %s", err)
- return err
- }
-
- for tag, id := range tagsList {
- repoData.ImgList[id] = ®istry.ImgData{
- ID: id,
- Tag: tag,
- Checksum: "",
- }
- }
-
- layersDownloaded := false
- for _, imgData := range repoData.ImgList {
- if isTagged && imgData.Tag != tagged.Tag() {
- continue
- }
-
- err := p.downloadImage(ctx, repoData, imgData, &layersDownloaded)
- if err != nil {
- return err
- }
- }
-
- writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded)
- return nil
-}
-
-func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.RepositoryData, img *registry.ImgData, layersDownloaded *bool) error {
- if img.Tag == "" {
- logrus.Debugf("Image (id: %s) present in this repository but untagged, skipping", img.ID)
- return nil
- }
-
- localNameRef, err := reference.WithTag(p.repoInfo.Name, img.Tag)
- if err != nil {
- retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag)
- logrus.Debug(retErr.Error())
- return retErr
- }
-
- if err := v1.ValidateID(img.ID); err != nil {
- return err
- }
-
- progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.Name.Name())
- success := false
- var lastErr error
- for _, ep := range p.repoInfo.Index.Mirrors {
- ep += "v1/"
- progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.Name.Name(), ep))
- if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
- // Don't report errors when pulling from mirrors.
- logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.Name.Name(), ep, err)
- continue
- }
- success = true
- break
- }
- if !success {
- for _, ep := range repoData.Endpoints {
- progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.Name.Name(), ep)
- if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
- // It's not ideal that only the last error is returned, it would be better to concatenate the errors.
- // As the error is also given to the output stream the user will see the error.
- lastErr = err
- progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, p.repoInfo.Name.Name(), ep, err)
- continue
- }
- success = true
- break
- }
- }
- if !success {
- err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.Name.Name(), lastErr)
- progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), err.Error())
- return err
- }
- return nil
-}
-
-func (p *v1Puller) pullImage(ctx context.Context, v1ID, endpoint string, localNameRef reference.Named, layersDownloaded *bool) (err error) {
- var history []string
- history, err = p.session.GetRemoteHistory(v1ID, endpoint)
- if err != nil {
- return err
- }
- if len(history) < 1 {
- return fmt.Errorf("empty history for image %s", v1ID)
- }
- progress.Update(p.config.ProgressOutput, stringid.TruncateID(v1ID), "Pulling dependent layers")
-
- var (
- descriptors []xfer.DownloadDescriptor
- newHistory []image.History
- imgJSON []byte
- imgSize int64
- )
-
- // Iterate over layers, in order from bottom-most to top-most. Download
- // config for all layers and create descriptors.
- for i := len(history) - 1; i >= 0; i-- {
- v1LayerID := history[i]
- imgJSON, imgSize, err = p.downloadLayerConfig(v1LayerID, endpoint)
- if err != nil {
- return err
- }
-
- // Create a new-style config from the legacy configs
- h, err := v1.HistoryFromConfig(imgJSON, false)
- if err != nil {
- return err
- }
- newHistory = append(newHistory, h)
-
- layerDescriptor := &v1LayerDescriptor{
- v1LayerID: v1LayerID,
- indexName: p.repoInfo.Index.Name,
- endpoint: endpoint,
- v1IDService: p.v1IDService,
- layersDownloaded: layersDownloaded,
- layerSize: imgSize,
- session: p.session,
- }
-
- descriptors = append(descriptors, layerDescriptor)
- }
-
- rootFS := image.NewRootFS()
- resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, "", descriptors, p.config.ProgressOutput)
- if err != nil {
- return err
- }
- defer release()
-
- config, err := v1.MakeConfigFromV1Config(imgJSON, &resultRootFS, newHistory)
- if err != nil {
- return err
- }
-
- imageID, err := p.config.ImageStore.Put(config)
- if err != nil {
- return err
- }
-
- if p.config.ReferenceStore != nil {
- if err := p.config.ReferenceStore.AddTag(localNameRef, imageID, true); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func (p *v1Puller) downloadLayerConfig(v1LayerID, endpoint string) (imgJSON []byte, imgSize int64, err error) {
- progress.Update(p.config.ProgressOutput, stringid.TruncateID(v1LayerID), "Pulling metadata")
-
- retries := 5
- for j := 1; j <= retries; j++ {
- imgJSON, imgSize, err := p.session.GetRemoteImageJSON(v1LayerID, endpoint)
- if err != nil && j == retries {
- progress.Update(p.config.ProgressOutput, stringid.TruncateID(v1LayerID), "Error pulling layer metadata")
- return nil, 0, err
- } else if err != nil {
- time.Sleep(time.Duration(j) * 500 * time.Millisecond)
- continue
- }
-
- return imgJSON, imgSize, nil
- }
-
- // not reached
- return nil, 0, nil
-}
-
-type v1LayerDescriptor struct {
- v1LayerID string
- indexName string
- endpoint string
- v1IDService *metadata.V1IDService
- layersDownloaded *bool
- layerSize int64
- session *registry.Session
- tmpFile *os.File
-}
-
-func (ld *v1LayerDescriptor) Key() string {
- return "v1:" + ld.v1LayerID
-}
-
-func (ld *v1LayerDescriptor) ID() string {
- return stringid.TruncateID(ld.v1LayerID)
-}
-
-func (ld *v1LayerDescriptor) DiffID() (layer.DiffID, error) {
- return ld.v1IDService.Get(ld.v1LayerID, ld.indexName)
-}
-
-func (ld *v1LayerDescriptor) Download(ctx context.Context, progressOutput progress.Output) (io.ReadCloser, int64, error) {
- progress.Update(progressOutput, ld.ID(), "Pulling fs layer")
- layerReader, err := ld.session.GetRemoteImageLayer(ld.v1LayerID, ld.endpoint, ld.layerSize)
- if err != nil {
- progress.Update(progressOutput, ld.ID(), "Error pulling dependent layers")
- if uerr, ok := err.(*url.Error); ok {
- err = uerr.Err
- }
- if terr, ok := err.(net.Error); ok && terr.Timeout() {
- return nil, 0, err
- }
- return nil, 0, xfer.DoNotRetry{Err: err}
- }
- *ld.layersDownloaded = true
-
- ld.tmpFile, err = ioutil.TempFile("", "GetImageBlob")
- if err != nil {
- layerReader.Close()
- return nil, 0, err
- }
-
- reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(ctx, layerReader), progressOutput, ld.layerSize, ld.ID(), "Downloading")
- defer reader.Close()
-
- _, err = io.Copy(ld.tmpFile, reader)
- if err != nil {
- ld.Close()
- return nil, 0, err
- }
-
- progress.Update(progressOutput, ld.ID(), "Download complete")
-
- logrus.Debugf("Downloaded %s to tempfile %s", ld.ID(), ld.tmpFile.Name())
-
- ld.tmpFile.Seek(0, 0)
-
- // hand off the temporary file to the download manager, so it will only
- // be closed once
- tmpFile := ld.tmpFile
- ld.tmpFile = nil
-
- return ioutils.NewReadCloserWrapper(tmpFile, func() error {
- tmpFile.Close()
- err := os.RemoveAll(tmpFile.Name())
- if err != nil {
- logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name())
- }
- return err
- }), ld.layerSize, nil
-}
-
-func (ld *v1LayerDescriptor) Close() {
- if ld.tmpFile != nil {
- ld.tmpFile.Close()
- if err := os.RemoveAll(ld.tmpFile.Name()); err != nil {
- logrus.Errorf("Failed to remove temp file: %s", ld.tmpFile.Name())
- }
- ld.tmpFile = nil
- }
-}
-
-func (ld *v1LayerDescriptor) Registered(diffID layer.DiffID) {
- // Cache mapping from this layer's DiffID to the blobsum
- ld.v1IDService.Set(ld.v1LayerID, ld.indexName, diffID)
-}
diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go
index 8a59895..dd91ff2 100644
--- a/distribution/pull_v2.go
+++ b/distribution/pull_v2.go
@@ -23,7 +23,7 @@
"github.com/docker/docker/distribution/metadata"
"github.com/docker/docker/distribution/xfer"
"github.com/docker/docker/image"
- "github.com/docker/docker/image/v1"
+ v1 "github.com/docker/docker/image/v1"
"github.com/docker/docker/layer"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/progress"
@@ -392,6 +392,10 @@
if p.config.RequireSchema2 {
return false, fmt.Errorf("invalid manifest: not schema2")
}
+ msg := schema1DeprecationMessage(ref)
+ logrus.Warn(msg)
+ progress.Message(p.config.ProgressOutput, "", msg)
+
id, manifestDigest, err = p.pullSchema1(ctx, ref, v, platform)
if err != nil {
return false, err
@@ -787,6 +791,10 @@
switch v := manifest.(type) {
case *schema1.SignedManifest:
+ msg := schema1DeprecationMessage(ref)
+ logrus.Warn(msg)
+ progress.Message(p.config.ProgressOutput, "", msg)
+
platform := toOCIPlatform(manifestMatches[0].Platform)
id, _, err = p.pullSchema1(ctx, manifestRef, v, &platform)
if err != nil {
diff --git a/distribution/push.go b/distribution/push.go
index eb3bc55..5617a4c 100644
--- a/distribution/push.go
+++ b/distribution/push.go
@@ -41,13 +41,7 @@
config: imagePushConfig,
}, nil
case registry.APIVersion1:
- return &v1Pusher{
- v1IDService: metadata.NewV1IDService(imagePushConfig.MetadataStore),
- ref: ref,
- endpoint: endpoint,
- repoInfo: repoInfo,
- config: imagePushConfig,
- }, nil
+ return nil, fmt.Errorf("protocol version %d no longer supported. Please contact admins of registry %s", endpoint.Version, endpoint.URL)
}
return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL)
}
diff --git a/distribution/push_v1.go b/distribution/push_v1.go
deleted file mode 100644
index 7bd75e9..0000000
--- a/distribution/push_v1.go
+++ /dev/null
@@ -1,457 +0,0 @@
-package distribution // import "github.com/docker/docker/distribution"
-
-import (
- "context"
- "fmt"
- "sync"
-
- "github.com/docker/distribution/reference"
- "github.com/docker/distribution/registry/client/transport"
- "github.com/docker/docker/distribution/metadata"
- "github.com/docker/docker/dockerversion"
- "github.com/docker/docker/image"
- "github.com/docker/docker/image/v1"
- "github.com/docker/docker/layer"
- "github.com/docker/docker/pkg/ioutils"
- "github.com/docker/docker/pkg/progress"
- "github.com/docker/docker/pkg/stringid"
- "github.com/docker/docker/pkg/system"
- "github.com/docker/docker/registry"
- "github.com/opencontainers/go-digest"
- "github.com/sirupsen/logrus"
-)
-
-type v1Pusher struct {
- v1IDService *metadata.V1IDService
- endpoint registry.APIEndpoint
- ref reference.Named
- repoInfo *registry.RepositoryInfo
- config *ImagePushConfig
- session *registry.Session
-}
-
-func (p *v1Pusher) Push(ctx context.Context) error {
- tlsConfig, err := p.config.RegistryService.TLSConfig(p.repoInfo.Index.Name)
- if err != nil {
- return err
- }
- // Adds Docker-specific headers as well as user-specified headers (metaHeaders)
- tr := transport.NewTransport(
- // TODO(tiborvass): was NoTimeout
- registry.NewTransport(tlsConfig),
- registry.Headers(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders)...,
- )
- client := registry.HTTPClient(tr)
- v1Endpoint := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders)
- p.session, err = registry.NewSession(client, p.config.AuthConfig, v1Endpoint)
- if err != nil {
- // TODO(dmcgowan): Check if should fallback
- return fallbackError{err: err}
- }
- if err := p.pushRepository(ctx); err != nil {
- // TODO(dmcgowan): Check if should fallback
- return err
- }
- return nil
-}
-
-// v1Image exposes the configuration, filesystem layer ID, and a v1 ID for an
-// image being pushed to a v1 registry.
-type v1Image interface {
- Config() []byte
- Layer() layer.Layer
- V1ID() string
-}
-
-type v1ImageCommon struct {
- layer layer.Layer
- config []byte
- v1ID string
-}
-
-func (common *v1ImageCommon) Config() []byte {
- return common.config
-}
-
-func (common *v1ImageCommon) V1ID() string {
- return common.v1ID
-}
-
-func (common *v1ImageCommon) Layer() layer.Layer {
- return common.layer
-}
-
-// v1TopImage defines a runnable (top layer) image being pushed to a v1
-// registry.
-type v1TopImage struct {
- v1ImageCommon
- imageID image.ID
-}
-
-func newV1TopImage(imageID image.ID, img *image.Image, l layer.Layer, parent *v1DependencyImage) (*v1TopImage, error) {
- v1ID := imageID.Digest().Hex()
- parentV1ID := ""
- if parent != nil {
- parentV1ID = parent.V1ID()
- }
-
- config, err := v1.MakeV1ConfigFromConfig(img, v1ID, parentV1ID, false)
- if err != nil {
- return nil, err
- }
-
- return &v1TopImage{
- v1ImageCommon: v1ImageCommon{
- v1ID: v1ID,
- config: config,
- layer: l,
- },
- imageID: imageID,
- }, nil
-}
-
-// v1DependencyImage defines a dependency layer being pushed to a v1 registry.
-type v1DependencyImage struct {
- v1ImageCommon
-}
-
-func newV1DependencyImage(l layer.Layer, parent *v1DependencyImage) *v1DependencyImage {
- v1ID := digest.Digest(l.ChainID()).Hex()
-
- var config string
- if parent != nil {
- config = fmt.Sprintf(`{"id":"%s","parent":"%s"}`, v1ID, parent.V1ID())
- } else {
- config = fmt.Sprintf(`{"id":"%s"}`, v1ID)
- }
- return &v1DependencyImage{
- v1ImageCommon: v1ImageCommon{
- v1ID: v1ID,
- config: []byte(config),
- layer: l,
- },
- }
-}
-
-// Retrieve the all the images to be uploaded in the correct order
-func (p *v1Pusher) getImageList() (imageList []v1Image, tagsByImage map[image.ID][]string, referencedLayers []PushLayer, err error) {
- tagsByImage = make(map[image.ID][]string)
-
- // Ignore digest references
- if _, isCanonical := p.ref.(reference.Canonical); isCanonical {
- return
- }
-
- tagged, isTagged := p.ref.(reference.NamedTagged)
- if isTagged {
- // Push a specific tag
- var imgID image.ID
- var dgst digest.Digest
- dgst, err = p.config.ReferenceStore.Get(p.ref)
- if err != nil {
- return
- }
- imgID = image.IDFromDigest(dgst)
-
- imageList, err = p.imageListForTag(imgID, nil, &referencedLayers)
- if err != nil {
- return
- }
-
- tagsByImage[imgID] = []string{tagged.Tag()}
-
- return
- }
-
- imagesSeen := make(map[digest.Digest]struct{})
- dependenciesSeen := make(map[layer.ChainID]*v1DependencyImage)
-
- associations := p.config.ReferenceStore.ReferencesByName(p.ref)
- for _, association := range associations {
- if tagged, isTagged = association.Ref.(reference.NamedTagged); !isTagged {
- // Ignore digest references.
- continue
- }
-
- imgID := image.IDFromDigest(association.ID)
- tagsByImage[imgID] = append(tagsByImage[imgID], tagged.Tag())
-
- if _, present := imagesSeen[association.ID]; present {
- // Skip generating image list for already-seen image
- continue
- }
- imagesSeen[association.ID] = struct{}{}
-
- imageListForThisTag, err := p.imageListForTag(imgID, dependenciesSeen, &referencedLayers)
- if err != nil {
- return nil, nil, nil, err
- }
-
- // append to main image list
- imageList = append(imageList, imageListForThisTag...)
- }
- if len(imageList) == 0 {
- return nil, nil, nil, fmt.Errorf("No images found for the requested repository / tag")
- }
- logrus.Debugf("Image list: %v", imageList)
- logrus.Debugf("Tags by image: %v", tagsByImage)
-
- return
-}
-
-func (p *v1Pusher) imageListForTag(imgID image.ID, dependenciesSeen map[layer.ChainID]*v1DependencyImage, referencedLayers *[]PushLayer) (imageListForThisTag []v1Image, err error) {
- ics, ok := p.config.ImageStore.(*imageConfigStore)
- if !ok {
- return nil, fmt.Errorf("only image store images supported for v1 push")
- }
- img, err := ics.Store.Get(imgID)
- if err != nil {
- return nil, err
- }
-
- topLayerID := img.RootFS.ChainID()
-
- if !system.IsOSSupported(img.OperatingSystem()) {
- return nil, system.ErrNotSupportedOperatingSystem
- }
- pl, err := p.config.LayerStores[img.OperatingSystem()].Get(topLayerID)
- *referencedLayers = append(*referencedLayers, pl)
- if err != nil {
- return nil, fmt.Errorf("failed to get top layer from image: %v", err)
- }
-
- // V1 push is deprecated, only support existing layerstore layers
- lsl, ok := pl.(*storeLayer)
- if !ok {
- return nil, fmt.Errorf("only layer store layers supported for v1 push")
- }
- l := lsl.Layer
-
- dependencyImages, parent := generateDependencyImages(l.Parent(), dependenciesSeen)
-
- topImage, err := newV1TopImage(imgID, img, l, parent)
- if err != nil {
- return nil, err
- }
-
- imageListForThisTag = append(dependencyImages, topImage)
-
- return
-}
-
-func generateDependencyImages(l layer.Layer, dependenciesSeen map[layer.ChainID]*v1DependencyImage) (imageListForThisTag []v1Image, parent *v1DependencyImage) {
- if l == nil {
- return nil, nil
- }
-
- imageListForThisTag, parent = generateDependencyImages(l.Parent(), dependenciesSeen)
-
- if dependenciesSeen != nil {
- if dependencyImage, present := dependenciesSeen[l.ChainID()]; present {
- // This layer is already on the list, we can ignore it
- // and all its parents.
- return imageListForThisTag, dependencyImage
- }
- }
-
- dependencyImage := newV1DependencyImage(l, parent)
- imageListForThisTag = append(imageListForThisTag, dependencyImage)
-
- if dependenciesSeen != nil {
- dependenciesSeen[l.ChainID()] = dependencyImage
- }
-
- return imageListForThisTag, dependencyImage
-}
-
-// createImageIndex returns an index of an image's layer IDs and tags.
-func createImageIndex(images []v1Image, tags map[image.ID][]string) []*registry.ImgData {
- var imageIndex []*registry.ImgData
- for _, img := range images {
- v1ID := img.V1ID()
-
- if topImage, isTopImage := img.(*v1TopImage); isTopImage {
- if tags, hasTags := tags[topImage.imageID]; hasTags {
- // If an image has tags you must add an entry in the image index
- // for each tag
- for _, tag := range tags {
- imageIndex = append(imageIndex, ®istry.ImgData{
- ID: v1ID,
- Tag: tag,
- })
- }
- continue
- }
- }
-
- // If the image does not have a tag it still needs to be sent to the
- // registry with an empty tag so that it is associated with the repository
- imageIndex = append(imageIndex, ®istry.ImgData{
- ID: v1ID,
- Tag: "",
- })
- }
- return imageIndex
-}
-
-// lookupImageOnEndpoint checks the specified endpoint to see if an image exists
-// and if it is absent then it sends the image id to the channel to be pushed.
-func (p *v1Pusher) lookupImageOnEndpoint(wg *sync.WaitGroup, endpoint string, images chan v1Image, imagesToPush chan string) {
- defer wg.Done()
- for image := range images {
- v1ID := image.V1ID()
- truncID := stringid.TruncateID(image.Layer().DiffID().String())
- if err := p.session.LookupRemoteImage(v1ID, endpoint); err != nil {
- logrus.Errorf("Error in LookupRemoteImage: %s", err)
- imagesToPush <- v1ID
- progress.Update(p.config.ProgressOutput, truncID, "Waiting")
- } else {
- progress.Update(p.config.ProgressOutput, truncID, "Already exists")
- }
- }
-}
-
-func (p *v1Pusher) pushImageToEndpoint(ctx context.Context, endpoint string, imageList []v1Image, tags map[image.ID][]string, repo *registry.RepositoryData) error {
- workerCount := len(imageList)
- // start a maximum of 5 workers to check if images exist on the specified endpoint.
- if workerCount > 5 {
- workerCount = 5
- }
- var (
- wg = &sync.WaitGroup{}
- imageData = make(chan v1Image, workerCount*2)
- imagesToPush = make(chan string, workerCount*2)
- pushes = make(chan map[string]struct{}, 1)
- )
- for i := 0; i < workerCount; i++ {
- wg.Add(1)
- go p.lookupImageOnEndpoint(wg, endpoint, imageData, imagesToPush)
- }
- // start a go routine that consumes the images to push
- go func() {
- shouldPush := make(map[string]struct{})
- for id := range imagesToPush {
- shouldPush[id] = struct{}{}
- }
- pushes <- shouldPush
- }()
- for _, v1Image := range imageList {
- imageData <- v1Image
- }
- // close the channel to notify the workers that there will be no more images to check.
- close(imageData)
- wg.Wait()
- close(imagesToPush)
- // wait for all the images that require pushes to be collected into a consumable map.
- shouldPush := <-pushes
- // finish by pushing any images and tags to the endpoint. The order that the images are pushed
- // is very important that is why we are still iterating over the ordered list of imageIDs.
- for _, img := range imageList {
- v1ID := img.V1ID()
- if _, push := shouldPush[v1ID]; push {
- if _, err := p.pushImage(ctx, img, endpoint); err != nil {
- // FIXME: Continue on error?
- return err
- }
- }
- if topImage, isTopImage := img.(*v1TopImage); isTopImage {
- for _, tag := range tags[topImage.imageID] {
- progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+reference.Path(p.repoInfo.Name)+"/tags/"+tag)
- if err := p.session.PushRegistryTag(p.repoInfo.Name, v1ID, tag, endpoint); err != nil {
- return err
- }
- }
- }
- }
- return nil
-}
-
-// pushRepository pushes layers that do not already exist on the registry.
-func (p *v1Pusher) pushRepository(ctx context.Context) error {
- imgList, tags, referencedLayers, err := p.getImageList()
- defer func() {
- for _, l := range referencedLayers {
- l.Release()
- }
- }()
- if err != nil {
- return err
- }
-
- imageIndex := createImageIndex(imgList, tags)
- for _, data := range imageIndex {
- logrus.Debugf("Pushing ID: %s with Tag: %s", data.ID, data.Tag)
- }
-
- // Register all the images in a repository with the registry
- // If an image is not in this list it will not be associated with the repository
- repoData, err := p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, false, nil)
- if err != nil {
- return err
- }
- // push the repository to each of the endpoints only if it does not exist.
- for _, endpoint := range repoData.Endpoints {
- if err := p.pushImageToEndpoint(ctx, endpoint, imgList, tags, repoData); err != nil {
- return err
- }
- }
- _, err = p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, true, repoData.Endpoints)
- return err
-}
-
-func (p *v1Pusher) pushImage(ctx context.Context, v1Image v1Image, ep string) (checksum string, err error) {
- l := v1Image.Layer()
- v1ID := v1Image.V1ID()
- truncID := stringid.TruncateID(l.DiffID().String())
-
- jsonRaw := v1Image.Config()
- progress.Update(p.config.ProgressOutput, truncID, "Pushing")
-
- // General rule is to use ID for graph accesses and compatibilityID for
- // calls to session.registry()
- imgData := ®istry.ImgData{
- ID: v1ID,
- }
-
- // Send the json
- if err := p.session.PushImageJSONRegistry(imgData, jsonRaw, ep); err != nil {
- if err == registry.ErrAlreadyExists {
- progress.Update(p.config.ProgressOutput, truncID, "Image already pushed, skipping")
- return "", nil
- }
- return "", err
- }
-
- arch, err := l.TarStream()
- if err != nil {
- return "", err
- }
- defer arch.Close()
-
- // don't care if this fails; best effort
- size, _ := l.DiffSize()
-
- // Send the layer
- logrus.Debugf("rendered layer for %s of [%d] size", v1ID, size)
-
- reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(ctx, arch), p.config.ProgressOutput, size, truncID, "Pushing")
- defer reader.Close()
-
- checksum, checksumPayload, err := p.session.PushImageLayerRegistry(v1ID, reader, ep, jsonRaw)
- if err != nil {
- return "", err
- }
- imgData.Checksum = checksum
- imgData.ChecksumPayload = checksumPayload
- // Send the checksum
- if err := p.session.PushImageChecksumRegistry(imgData, ep); err != nil {
- return "", err
- }
-
- if err := p.v1IDService.Set(v1ID, p.repoInfo.Index.Name, l.DiffID()); err != nil {
- logrus.Warnf("Could not set v1 ID mapping: %v", err)
- }
-
- progress.Update(p.config.ProgressOutput, truncID, "Image successfully pushed")
- return imgData.Checksum, nil
-}
diff --git a/distribution/push_v2.go b/distribution/push_v2.go
index 8bc0edd..e15384e 100644
--- a/distribution/push_v2.go
+++ b/distribution/push_v2.go
@@ -5,6 +5,7 @@
"errors"
"fmt"
"io"
+ "runtime"
"sort"
"strings"
"sync"
@@ -180,8 +181,30 @@
putOptions := []distribution.ManifestServiceOption{distribution.WithTag(ref.Tag())}
if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil {
- logrus.Warnf("failed to upload schema2 manifest: %v", err)
- return err
+ if runtime.GOOS == "windows" || p.config.TrustKey == nil || p.config.RequireSchema2 {
+ logrus.Warnf("failed to upload schema2 manifest: %v", err)
+ return err
+ }
+
+ logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err)
+
+ msg := schema1DeprecationMessage(ref)
+ logrus.Warn(msg)
+ progress.Message(p.config.ProgressOutput, "", msg)
+
+ manifestRef, err := reference.WithTag(p.repo.Named(), ref.Tag())
+ if err != nil {
+ return err
+ }
+ builder = schema1.NewConfigManifestBuilder(p.repo.Blobs(ctx), p.config.TrustKey, manifestRef, imgConfig)
+ manifest, err = manifestFromBuilder(ctx, builder, descriptors)
+ if err != nil {
+ return err
+ }
+
+ if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil {
+ return err
+ }
}
var canonicalManifest []byte
diff --git a/distribution/registry.go b/distribution/registry.go
index d81530b..c474d14 100644
--- a/distribution/registry.go
+++ b/distribution/registry.go
@@ -156,3 +156,7 @@
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token))
return nil
}
+
+func schema1DeprecationMessage(ref reference.Named) string {
+ return fmt.Sprintf("[DEPRECATION NOTICE] registry v2 schema1 support will be removed in an upcoming release. Please contact admins of the %s registry NOW to avoid future disruption.", reference.Domain(ref))
+}
diff --git a/hack/validate/default b/hack/validate/default
index 4ca7e91..15ed836 100755
--- a/hack/validate/default
+++ b/hack/validate/default
@@ -14,4 +14,4 @@
. ${SCRIPTDIR}/toml
. ${SCRIPTDIR}/changelog-well-formed
. ${SCRIPTDIR}/changelog-date-descending
-. ${SCRIPTDIR}/deprecate-integration-cli
+#. ${SCRIPTDIR}/deprecate-integration-cli
diff --git a/integration-cli/check_test.go b/integration-cli/check_test.go
index c344b63..2a0b1a2 100644
--- a/integration-cli/check_test.go
+++ b/integration-cli/check_test.go
@@ -142,6 +142,39 @@
}
func init() {
+ check.Suite(&DockerSchema1RegistrySuite{
+ ds: &DockerSuite{},
+ })
+}
+
+type DockerSchema1RegistrySuite struct {
+ ds *DockerSuite
+ reg *registry.V2
+ d *daemon.Daemon
+}
+
+func (s *DockerSchema1RegistrySuite) OnTimeout(c *check.C) {
+ s.d.DumpStackAndQuit()
+}
+
+func (s *DockerSchema1RegistrySuite) SetUpTest(c *check.C) {
+ testRequires(c, DaemonIsLinux, RegistryHosting, NotArm64, testEnv.IsLocalDaemon)
+ s.reg = registry.NewV2(c, registry.Schema1)
+ s.reg.WaitReady(c)
+ s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
+}
+
+func (s *DockerSchema1RegistrySuite) TearDownTest(c *check.C) {
+ if s.reg != nil {
+ s.reg.Close()
+ }
+ if s.d != nil {
+ s.d.Stop(c)
+ }
+ s.ds.TearDownTest(c)
+}
+
+func init() {
check.Suite(&DockerRegistryAuthHtpasswdSuite{
ds: &DockerSuite{},
})
diff --git a/integration-cli/docker_cli_by_digest_test.go b/integration-cli/docker_cli_by_digest_test.go
index dd5549c..c884d31 100644
--- a/integration-cli/docker_cli_by_digest_test.go
+++ b/integration-cli/docker_cli_by_digest_test.go
@@ -3,9 +3,12 @@
import (
"encoding/json"
"fmt"
+ "os"
+ "path/filepath"
"regexp"
"strings"
+ "github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2"
"github.com/docker/docker/api/types"
"github.com/docker/docker/integration-cli/checker"
@@ -77,6 +80,10 @@
testPullByTagDisplaysDigest(c)
}
+func (s *DockerSchema1RegistrySuite) TestPullByTagDisplaysDigest(c *check.C) {
+ testPullByTagDisplaysDigest(c)
+}
+
func testPullByDigest(c *check.C) {
testRequires(c, DaemonIsLinux)
pushDigest, err := setupImage(c)
@@ -99,6 +106,10 @@
testPullByDigest(c)
}
+func (s *DockerSchema1RegistrySuite) TestPullByDigest(c *check.C) {
+ testPullByDigest(c)
+}
+
func testPullByDigestNoFallback(c *check.C) {
testRequires(c, DaemonIsLinux)
// pull from the registry using the <name>@<digest> reference
@@ -112,6 +123,10 @@
testPullByDigestNoFallback(c)
}
+func (s *DockerSchema1RegistrySuite) TestPullByDigestNoFallback(c *check.C) {
+ testPullByDigestNoFallback(c)
+}
+
func (s *DockerRegistrySuite) TestCreateByDigest(c *check.C) {
pushDigest, err := setupImage(c)
assert.NilError(c, err, "error setting up image")
@@ -546,3 +561,131 @@
expectedErrorMsg := fmt.Sprintf("manifest verification failed for digest %s", manifestDigest)
assert.Assert(c, is.Contains(out, expectedErrorMsg))
}
+
+// TestPullFailsWithAlteredManifest tests that a `docker pull` fails when
+// we have modified a manifest blob and its digest cannot be verified.
+// This is the schema1 version of the test.
+func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredManifest(c *check.C) {
+ testRequires(c, DaemonIsLinux)
+ manifestDigest, err := setupImage(c)
+ c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+
+ // Load the target manifest blob.
+ manifestBlob := s.reg.ReadBlobContents(c, manifestDigest)
+
+ var imgManifest schema1.Manifest
+ err = json.Unmarshal(manifestBlob, &imgManifest)
+ c.Assert(err, checker.IsNil, check.Commentf("unable to decode image manifest from blob"))
+
+ // Change a layer in the manifest.
+ imgManifest.FSLayers[0] = schema1.FSLayer{
+ BlobSum: digest.Digest("sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
+ }
+
+ // Move the existing data file aside, so that we can replace it with a
+ // malicious blob of data. NOTE: we defer the returned undo func.
+ undo := s.reg.TempMoveBlobData(c, manifestDigest)
+ defer undo()
+
+ alteredManifestBlob, err := json.MarshalIndent(imgManifest, "", " ")
+ c.Assert(err, checker.IsNil, check.Commentf("unable to encode altered image manifest to JSON"))
+
+ s.reg.WriteBlobContents(c, manifestDigest, alteredManifestBlob)
+
+ // Now try pulling that image by digest. We should get an error about
+ // digest verification for the manifest digest.
+
+ // Pull from the registry using the <name>@<digest> reference.
+ imageReference := fmt.Sprintf("%s@%s", repoName, manifestDigest)
+ out, exitStatus, _ := dockerCmdWithError("pull", imageReference)
+ c.Assert(exitStatus, checker.Not(check.Equals), 0)
+
+ expectedErrorMsg := fmt.Sprintf("image verification failed for digest %s", manifestDigest)
+ c.Assert(out, checker.Contains, expectedErrorMsg)
+}
+
+// TestPullFailsWithAlteredLayer tests that a `docker pull` fails when
+// we have modified a layer blob and its digest cannot be verified.
+// This is the schema2 version of the test.
+func (s *DockerRegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
+ testRequires(c, DaemonIsLinux)
+ manifestDigest, err := setupImage(c)
+ c.Assert(err, checker.IsNil)
+
+ // Load the target manifest blob.
+ manifestBlob := s.reg.ReadBlobContents(c, manifestDigest)
+
+ var imgManifest schema2.Manifest
+ err = json.Unmarshal(manifestBlob, &imgManifest)
+ c.Assert(err, checker.IsNil)
+
+ // Next, get the digest of one of the layers from the manifest.
+ targetLayerDigest := imgManifest.Layers[0].Digest
+
+ // Move the existing data file aside, so that we can replace it with a
+ // malicious blob of data. NOTE: we defer the returned undo func.
+ undo := s.reg.TempMoveBlobData(c, targetLayerDigest)
+ defer undo()
+
+ // Now make a fake data blob in this directory.
+ s.reg.WriteBlobContents(c, targetLayerDigest, []byte("This is not the data you are looking for."))
+
+ // Now try pulling that image by digest. We should get an error about
+ // digest verification for the target layer digest.
+
+ // Remove distribution cache to force a re-pull of the blobs
+ if err := os.RemoveAll(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "image", s.d.StorageDriver(), "distribution")); err != nil {
+ c.Fatalf("error clearing distribution cache: %v", err)
+ }
+
+ // Pull from the registry using the <name>@<digest> reference.
+ imageReference := fmt.Sprintf("%s@%s", repoName, manifestDigest)
+ out, exitStatus, _ := dockerCmdWithError("pull", imageReference)
+ c.Assert(exitStatus, checker.Not(check.Equals), 0, check.Commentf("expected a non-zero exit status"))
+
+ expectedErrorMsg := fmt.Sprintf("filesystem layer verification failed for digest %s", targetLayerDigest)
+ c.Assert(out, checker.Contains, expectedErrorMsg, check.Commentf("expected error message in output: %s", out))
+}
+
+// TestPullFailsWithAlteredLayer tests that a `docker pull` fails when
+// we have modified a layer blob and its digest cannot be verified.
+// This is the schema1 version of the test.
+func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
+ testRequires(c, DaemonIsLinux)
+ manifestDigest, err := setupImage(c)
+ c.Assert(err, checker.IsNil)
+
+ // Load the target manifest blob.
+ manifestBlob := s.reg.ReadBlobContents(c, manifestDigest)
+
+ var imgManifest schema1.Manifest
+ err = json.Unmarshal(manifestBlob, &imgManifest)
+ c.Assert(err, checker.IsNil)
+
+ // Next, get the digest of one of the layers from the manifest.
+ targetLayerDigest := imgManifest.FSLayers[0].BlobSum
+
+ // Move the existing data file aside, so that we can replace it with a
+ // malicious blob of data. NOTE: we defer the returned undo func.
+ undo := s.reg.TempMoveBlobData(c, targetLayerDigest)
+ defer undo()
+
+ // Now make a fake data blob in this directory.
+ s.reg.WriteBlobContents(c, targetLayerDigest, []byte("This is not the data you are looking for."))
+
+ // Now try pulling that image by digest. We should get an error about
+ // digest verification for the target layer digest.
+
+ // Remove distribution cache to force a re-pull of the blobs
+ if err := os.RemoveAll(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "image", s.d.StorageDriver(), "distribution")); err != nil {
+ c.Fatalf("error clearing distribution cache: %v", err)
+ }
+
+ // Pull from the registry using the <name>@<digest> reference.
+ imageReference := fmt.Sprintf("%s@%s", repoName, manifestDigest)
+ out, exitStatus, _ := dockerCmdWithError("pull", imageReference)
+ c.Assert(exitStatus, checker.Not(check.Equals), 0, check.Commentf("expected a non-zero exit status"))
+
+ expectedErrorMsg := fmt.Sprintf("filesystem layer verification failed for digest %s", targetLayerDigest)
+ c.Assert(out, checker.Contains, expectedErrorMsg, check.Commentf("expected error message in output: %s", out))
+}
diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go
index c9e88fa..fd3384e 100644
--- a/integration-cli/docker_cli_daemon_test.go
+++ b/integration-cli/docker_cli_daemon_test.go
@@ -35,6 +35,7 @@
"github.com/docker/docker/pkg/mount"
"github.com/docker/go-units"
"github.com/docker/libnetwork/iptables"
+ "github.com/docker/libtrust"
"github.com/go-check/check"
"github.com/kr/pty"
"golang.org/x/sys/unix"
@@ -550,6 +551,23 @@
}
}
+func (s *DockerDaemonSuite) TestDaemonKeyGeneration(c *check.C) {
+ // TODO: skip or update for Windows daemon
+ os.Remove("/etc/docker/key.json")
+ s.d.Start(c)
+ s.d.Stop(c)
+
+ k, err := libtrust.LoadKeyFile("/etc/docker/key.json")
+ if err != nil {
+ c.Fatalf("Error opening key file")
+ }
+ kid := k.KeyID()
+ // Test Key ID is a valid fingerprint (e.g. QQXN:JY5W:TBXI:MK3X:GX6P:PD5D:F56N:NHCS:LVRZ:JA46:R24J:XEFF)
+ if len(kid) != 59 {
+ c.Fatalf("Bad key ID: %s", kid)
+ }
+}
+
// GH#11320 - verify that the daemon exits on failure properly
// Note that this explicitly tests the conflict of {-b,--bridge} and {--bip} options as the means
// to get a daemon init failure; no other tests for -b/--bip conflict are therefore required
@@ -1174,6 +1192,59 @@
}
}
+func (s *DockerDaemonSuite) TestDaemonWithWrongkey(c *check.C) {
+ type Config struct {
+ Crv string `json:"crv"`
+ D string `json:"d"`
+ Kid string `json:"kid"`
+ Kty string `json:"kty"`
+ X string `json:"x"`
+ Y string `json:"y"`
+ }
+
+ os.Remove("/etc/docker/key.json")
+ s.d.Start(c)
+ s.d.Stop(c)
+
+ config := &Config{}
+ bytes, err := ioutil.ReadFile("/etc/docker/key.json")
+ if err != nil {
+ c.Fatalf("Error reading key.json file: %s", err)
+ }
+
+ // byte[] to Data-Struct
+ if err := json.Unmarshal(bytes, &config); err != nil {
+ c.Fatalf("Error Unmarshal: %s", err)
+ }
+
+ //replace config.Kid with the fake value
+ config.Kid = "VSAJ:FUYR:X3H2:B2VZ:KZ6U:CJD5:K7BX:ZXHY:UZXT:P4FT:MJWG:HRJ4"
+
+ // NEW Data-Struct to byte[]
+ newBytes, err := json.Marshal(&config)
+ if err != nil {
+ c.Fatalf("Error Marshal: %s", err)
+ }
+
+ // write back
+ if err := ioutil.WriteFile("/etc/docker/key.json", newBytes, 0400); err != nil {
+ c.Fatalf("Error ioutil.WriteFile: %s", err)
+ }
+
+ defer os.Remove("/etc/docker/key.json")
+
+ if err := s.d.StartWithError(); err == nil {
+ c.Fatalf("It should not be successful to start daemon with wrong key: %v", err)
+ }
+
+ content, err := s.d.ReadLogFile()
+ c.Assert(err, checker.IsNil)
+
+ if !strings.Contains(string(content), "Public Key ID does not match") {
+ c.Fatalf("Missing KeyID message from daemon logs: %s", string(content))
+ }
+}
+
func (s *DockerDaemonSuite) TestDaemonRestartKillWait(c *check.C) {
s.d.StartWithBusybox(c)
diff --git a/integration-cli/docker_cli_pull_local_test.go b/integration-cli/docker_cli_pull_local_test.go
index 0ddf777..b6b774e 100644
--- a/integration-cli/docker_cli_pull_local_test.go
+++ b/integration-cli/docker_cli_pull_local_test.go
@@ -56,6 +56,10 @@
testPullImageWithAliases(c)
}
+func (s *DockerSchema1RegistrySuite) TestPullImageWithAliases(c *check.C) {
+ testPullImageWithAliases(c)
+}
+
// testConcurrentPullWholeRepo pulls the same repo concurrently.
func testConcurrentPullWholeRepo(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
@@ -108,6 +112,10 @@
testConcurrentPullWholeRepo(c)
}
+func (s *DockerSchema1RegistrySuite) testConcurrentPullWholeRepo(c *check.C) {
+ testConcurrentPullWholeRepo(c)
+}
+
// testConcurrentFailingPull tries a concurrent pull that doesn't succeed.
func testConcurrentFailingPull(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
@@ -135,6 +143,10 @@
testConcurrentFailingPull(c)
}
+func (s *DockerSchema1RegistrySuite) testConcurrentFailingPull(c *check.C) {
+ testConcurrentFailingPull(c)
+}
+
// testConcurrentPullMultipleTags pulls multiple tags from the same repo
// concurrently.
func testConcurrentPullMultipleTags(c *check.C) {
@@ -187,6 +199,10 @@
testConcurrentPullMultipleTags(c)
}
+func (s *DockerSchema1RegistrySuite) TestConcurrentPullMultipleTags(c *check.C) {
+ testConcurrentPullMultipleTags(c)
+}
+
// testPullIDStability verifies that pushing an image and pulling it back
// preserves the image ID.
func testPullIDStability(c *check.C) {
@@ -244,6 +260,10 @@
testPullIDStability(c)
}
+func (s *DockerSchema1RegistrySuite) TestPullIDStability(c *check.C) {
+ testPullIDStability(c)
+}
+
// #21213
func testPullNoLayers(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/scratch", privateRegistryURL)
@@ -260,6 +280,10 @@
testPullNoLayers(c)
}
+func (s *DockerSchema1RegistrySuite) TestPullNoLayers(c *check.C) {
+ testPullNoLayers(c)
+}
+
func (s *DockerRegistrySuite) TestPullManifestList(c *check.C) {
testRequires(c, NotArm)
pushDigest, err := setupImage(c)
diff --git a/integration-cli/docker_cli_push_test.go b/integration-cli/docker_cli_push_test.go
index 40fb2b8..60d5811 100644
--- a/integration-cli/docker_cli_push_test.go
+++ b/integration-cli/docker_cli_push_test.go
@@ -30,6 +30,10 @@
testPushBusyboxImage(c)
}
+func (s *DockerSchema1RegistrySuite) TestPushBusyboxImage(c *check.C) {
+ testPushBusyboxImage(c)
+}
+
// pushing an image without a prefix should throw an error
func (s *DockerSuite) TestPushUnprefixedRepo(c *check.C) {
out, _, err := dockerCmdWithError("push", "busybox")
@@ -49,6 +53,10 @@
testPushUntagged(c)
}
+func (s *DockerSchema1RegistrySuite) TestPushUntagged(c *check.C) {
+ testPushUntagged(c)
+}
+
func testPushBadTag(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/busybox:latest", privateRegistryURL)
expected := "does not exist"
@@ -62,6 +70,10 @@
testPushBadTag(c)
}
+func (s *DockerSchema1RegistrySuite) TestPushBadTag(c *check.C) {
+ testPushBadTag(c)
+}
+
func testPushMultipleTags(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
repoTag1 := fmt.Sprintf("%v/dockercli/busybox:t1", privateRegistryURL)
@@ -103,6 +115,10 @@
testPushMultipleTags(c)
}
+func (s *DockerSchema1RegistrySuite) TestPushMultipleTags(c *check.C) {
+ testPushMultipleTags(c)
+}
+
func testPushEmptyLayer(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL)
emptyTarball, err := ioutil.TempFile("", "empty_tarball")
@@ -130,6 +146,10 @@
testPushEmptyLayer(c)
}
+func (s *DockerSchema1RegistrySuite) TestPushEmptyLayer(c *check.C) {
+ testPushEmptyLayer(c)
+}
+
// testConcurrentPush pushes multiple tags to the same repo
// concurrently.
func testConcurrentPush(c *check.C) {
@@ -180,6 +200,10 @@
testConcurrentPush(c)
}
+func (s *DockerSchema1RegistrySuite) TestConcurrentPush(c *check.C) {
+ testConcurrentPush(c)
+}
+
func (s *DockerRegistrySuite) TestCrossRepositoryLayerPush(c *check.C) {
sourceRepoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
// tag the image to upload it to the private registry
@@ -222,6 +246,39 @@
assert.Equal(c, out4, "hello world")
}
+func (s *DockerSchema1RegistrySuite) TestCrossRepositoryLayerPushNotSupported(c *check.C) {
+ sourceRepoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
+ // tag the image to upload it to the private registry
+ dockerCmd(c, "tag", "busybox", sourceRepoName)
+ // push the image to the registry
+ out1, _, err := dockerCmdWithError("push", sourceRepoName)
+ assert.NilError(c, err, fmt.Sprintf("pushing the image to the private registry has failed: %s", out1))
+ // ensure that none of the layers were mounted from another repository during push
+ assert.Assert(c, !strings.Contains(out1, "Mounted from"))
+
+ digest1 := reference.DigestRegexp.FindString(out1)
+ assert.Assert(c, len(digest1) > 0, "no digest found for pushed manifest")
+
+ destRepoName := fmt.Sprintf("%v/dockercli/crossrepopush", privateRegistryURL)
+ // retag the image to upload the same layers to another repo in the same registry
+ dockerCmd(c, "tag", "busybox", destRepoName)
+ // push the image to the registry
+ out2, _, err := dockerCmdWithError("push", destRepoName)
+ assert.NilError(c, err, fmt.Sprintf("pushing the image to the private registry has failed: %s", out2))
+ // schema1 registry should not support cross-repo layer mounts, so ensure that this does not happen
+ assert.Assert(c, !strings.Contains(out2, "Mounted from"))
+
+ digest2 := reference.DigestRegexp.FindString(out2)
+ assert.Assert(c, len(digest2) > 0, "no digest found for pushed manifest")
+ assert.Assert(c, digest1 != digest2)
+
+ // ensure that we can pull and run the second pushed repository
+ dockerCmd(c, "rmi", destRepoName)
+ dockerCmd(c, "pull", destRepoName)
+ out3, _ := dockerCmd(c, "run", destRepoName, "echo", "-n", "hello world")
+ assert.Assert(c, out3 == "hello world")
+}
+
func (s *DockerRegistryAuthHtpasswdSuite) TestPushNoCredentialsNoRetry(c *check.C) {
repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
dockerCmd(c, "tag", "busybox", repoName)
diff --git a/integration/config/config_test.go b/integration/config/config_test.go
index 5cadf09..ec5b060 100644
--- a/integration/config/config_test.go
+++ b/integration/config/config_test.go
@@ -4,6 +4,8 @@
"bytes"
"context"
"encoding/json"
+ "io/ioutil"
+ "path/filepath"
"sort"
"testing"
"time"
@@ -13,6 +15,7 @@
swarmtypes "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
"github.com/docker/docker/integration/internal/swarm"
+ "github.com/docker/docker/internal/test/daemon"
"github.com/docker/docker/pkg/stdcopy"
"gotest.tools/assert"
is "gotest.tools/assert/cmp"
@@ -417,6 +420,26 @@
assert.Assert(t, is.Equal(0, len(entries)))
}
+func TestConfigDaemonLibtrustID(t *testing.T) {
+ skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+ defer setupTest(t)()
+
+ d := daemon.New(t)
+ defer d.Stop(t)
+
+ trustKey := filepath.Join(d.RootDir(), "key.json")
+ err := ioutil.WriteFile(trustKey, []byte(`{"crv":"P-256","d":"dm28PH4Z4EbyUN8L0bPonAciAQa1QJmmyYd876mnypY","kid":"WTJ3:YSIP:CE2E:G6KJ:PSBD:YX2Y:WEYD:M64G:NU2V:XPZV:H2CR:VLUB","kty":"EC","x":"Mh5-JINSjaa_EZdXDttri255Z5fbCEOTQIZjAcScFTk","y":"eUyuAjfxevb07hCCpvi4Zi334Dy4GDWQvEToGEX4exQ"}`), 0644)
+ assert.NilError(t, err)
+
+ config := filepath.Join(d.RootDir(), "daemon.json")
+ err = ioutil.WriteFile(config, []byte(`{"deprecated-key-path": "`+trustKey+`"}`), 0644)
+ assert.NilError(t, err)
+
+ d.Start(t, "--config-file", config)
+ info := d.Info(t)
+ assert.Equal(t, info.ID, "WTJ3:YSIP:CE2E:G6KJ:PSBD:YX2Y:WEYD:M64G:NU2V:XPZV:H2CR:VLUB")
+}
+
func configNamesFromList(entries []swarmtypes.Config) []string {
var values []string
for _, entry := range entries {
diff --git a/integration/system/uuid_test.go b/integration/system/uuid_test.go
deleted file mode 100644
index d6df8fd..0000000
--- a/integration/system/uuid_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package system
-
-import (
- "context"
- "testing"
-
- "github.com/docker/docker/api/types/versions"
- "github.com/google/uuid"
- "gotest.tools/assert"
- "gotest.tools/skip"
-)
-
-func TestUUIDGeneration(t *testing.T) {
- skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "ID format changed")
- defer setupTest(t)()
-
- c := testEnv.APIClient()
- info, err := c.Info(context.Background())
- assert.NilError(t, err)
-
- _, err = uuid.Parse(info.ID)
- assert.NilError(t, err, info.ID)
-}
diff --git a/registry/service_v1.go b/registry/service_v1.go
deleted file mode 100644
index d955ec5..0000000
--- a/registry/service_v1.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package registry // import "github.com/docker/docker/registry"
-
-import "net/url"
-
-func (s *DefaultService) lookupV1Endpoints(hostname string) (endpoints []APIEndpoint, err error) {
- if hostname == DefaultNamespace || hostname == DefaultV2Registry.Host || hostname == IndexHostname {
- return []APIEndpoint{}, nil
- }
-
- tlsConfig, err := s.tlsConfig(hostname)
- if err != nil {
- return nil, err
- }
-
- endpoints = []APIEndpoint{
- {
- URL: &url.URL{
- Scheme: "https",
- Host: hostname,
- },
- Version: APIVersion1,
- TrimHostname: true,
- TLSConfig: tlsConfig,
- },
- }
-
- if tlsConfig.InsecureSkipVerify {
- endpoints = append(endpoints, APIEndpoint{ // or this
- URL: &url.URL{
- Scheme: "http",
- Host: hostname,
- },
- Version: APIVersion1,
- TrimHostname: true,
- // used to check if supposed to be secure via InsecureSkipVerify
- TLSConfig: tlsConfig,
- })
- }
- return endpoints, nil
-}
diff --git a/registry/service_v1_test.go b/registry/service_v1_test.go
deleted file mode 100644
index 11861f7..0000000
--- a/registry/service_v1_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package registry // import "github.com/docker/docker/registry"
-
-import (
- "os"
- "testing"
-
- "gotest.tools/skip"
-)
-
-func TestLookupV1Endpoints(t *testing.T) {
- skip.If(t, os.Getuid() != 0, "skipping test that requires root")
- s, err := NewService(ServiceOptions{})
- if err != nil {
- t.Fatal(err)
- }
-
- cases := []struct {
- hostname string
- expectedLen int
- }{
- {"example.com", 1},
- {DefaultNamespace, 0},
- {DefaultV2Registry.Host, 0},
- {IndexHostname, 0},
- }
-
- for _, c := range cases {
- if ret, err := s.lookupV1Endpoints(c.hostname); err != nil || len(ret) != c.expectedLen {
- t.Errorf("lookupV1Endpoints(`"+c.hostname+"`) returned %+v and %+v", ret, err)
- }
- }
-}