Browse code

Remove the rest of v1 manifest support

As people are using the UUID in `docker info` that was based on the v1 manifest signing key, replace
with a UUID instead.

Remove deprecated `--disable-legacy-registry` option that was scheduled to be removed in 18.03.

Signed-off-by: Justin Cormack <justin.cormack@docker.com>

Justin Cormack authored on 2018/09/19 00:14:04
Showing 17 changed files
... ...
@@ -12,8 +12,6 @@ import (
12 12
 const (
13 13
 	// defaultShutdownTimeout is the default shutdown timeout for the daemon
14 14
 	defaultShutdownTimeout = 15
15
-	// defaultTrustKeyFile is the default filename for the trust key
16
-	defaultTrustKeyFile = "key.json"
17 15
 )
18 16
 
19 17
 // installCommonConfigFlags adds flags to the pflag.FlagSet to configure the daemon
... ...
@@ -83,13 +81,6 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) error {
83 83
 
84 84
 	flags.IntVar(&conf.NetworkControlPlaneMTU, "network-control-plane-mtu", config.DefaultNetworkMtu, "Network Control plane MTU")
85 85
 
86
-	// "--deprecated-key-path" is to allow configuration of the key used
87
-	// for the daemon ID and the deprecated image signing. It was never
88
-	// exposed as a command line option but is added here to allow
89
-	// overriding the default path in configuration.
90
-	flags.Var(opts.NewQuotedString(&conf.TrustKeyPath), "deprecated-key-path", "Path to key file for ID and image signing")
91
-	flags.MarkHidden("deprecated-key-path")
92
-
93 86
 	conf.MaxConcurrentDownloads = &maxConcurrentDownloads
94 87
 	conf.MaxConcurrentUploads = &maxConcurrentUploads
95 88
 	return nil
... ...
@@ -103,10 +94,4 @@ func installRegistryServiceFlags(options *registry.ServiceOptions, flags *pflag.
103 103
 	flags.Var(ana, "allow-nondistributable-artifacts", "Allow push of nondistributable artifacts to registry")
104 104
 	flags.Var(mirrors, "registry-mirror", "Preferred Docker registry mirror")
105 105
 	flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication")
106
-
107
-	if runtime.GOOS != "windows" {
108
-		// TODO: Remove this flag after 3 release cycles (18.03)
109
-		flags.BoolVar(&options.V2Only, "disable-legacy-registry", true, "Disable contacting legacy registries")
110
-		flags.MarkHidden("disable-legacy-registry")
111
-	}
112 106
 }
... ...
@@ -432,14 +432,6 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
432 432
 		conf.CommonTLSOptions.KeyFile = opts.TLSOptions.KeyFile
433 433
 	}
434 434
 
435
-	if conf.TrustKeyPath == "" {
436
-		daemonConfDir, err := getDaemonConfDir(conf.Root)
437
-		if err != nil {
438
-			return nil, err
439
-		}
440
-		conf.TrustKeyPath = filepath.Join(daemonConfDir, defaultTrustKeyFile)
441
-	}
442
-
443 435
 	if flags.Changed("graph") && flags.Changed("data-root") {
444 436
 		return nil, errors.New(`cannot specify both "--graph" and "--data-root" option`)
445 437
 	}
... ...
@@ -462,17 +454,6 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
462 462
 		return nil, err
463 463
 	}
464 464
 
465
-	if runtime.GOOS != "windows" {
466
-		if flags.Changed("disable-legacy-registry") {
467
-			// TODO: Remove this error after 3 release cycles (18.03)
468
-			return nil, errors.New("ERROR: The '--disable-legacy-registry' flag has been removed. Interacting with legacy (v1) registries is no longer supported")
469
-		}
470
-		if !conf.V2Only {
471
-			// TODO: Remove this error after 3 release cycles (18.03)
472
-			return nil, errors.New("ERROR: The 'disable-legacy-registry' configuration option has been removed. Interacting with legacy (v1) registries is no longer supported")
473
-		}
474
-	}
475
-
476 465
 	if flags.Changed("graph") {
477 466
 		logrus.Warnf(`The "-g / --graph" flag is deprecated. Please use "--data-root" instead`)
478 467
 	}
... ...
@@ -55,10 +55,6 @@ func setDefaultUmask() error {
55 55
 	return nil
56 56
 }
57 57
 
58
-func getDaemonConfDir(_ string) (string, error) {
59
-	return getDefaultDaemonConfigDir()
60
-}
61
-
62 58
 func (cli *DaemonCli) getPlatformContainerdDaemonOpts() ([]supervisor.DaemonOpt, error) {
63 59
 	opts := []supervisor.DaemonOpt{
64 60
 		supervisor.WithOOMScore(cli.Config.OOMScoreAdjust),
... ...
@@ -4,7 +4,6 @@ import (
4 4
 	"fmt"
5 5
 	"net"
6 6
 	"os"
7
-	"path/filepath"
8 7
 
9 8
 	"github.com/docker/docker/daemon/config"
10 9
 	"github.com/docker/docker/libcontainerd/supervisor"
... ...
@@ -21,10 +20,6 @@ func setDefaultUmask() error {
21 21
 	return nil
22 22
 }
23 23
 
24
-func getDaemonConfDir(root string) (string, error) {
25
-	return filepath.Join(root, `\config`), nil
26
-}
27
-
28 24
 // preNotifySystem sends a message to the host when the API is active, but before the daemon is
29 25
 func preNotifySystem() {
30 26
 	// start the service now to prevent timeouts waiting for daemon to start
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"io/ioutil"
9 9
 	"os"
10 10
 	"reflect"
11
-	"runtime"
12 11
 	"strings"
13 12
 	"sync"
14 13
 
... ...
@@ -135,12 +134,6 @@ type CommonConfig struct {
135 135
 	SocketGroup           string                    `json:"group,omitempty"`
136 136
 	CorsHeaders           string                    `json:"api-cors-header,omitempty"`
137 137
 
138
-	// TrustKeyPath is used to generate the daemon ID and for signing schema 1 manifests
139
-	// when pushing to a registry which does not support schema 2. This field is marked as
140
-	// deprecated because schema 1 manifests are deprecated in favor of schema 2 and the
141
-	// daemon ID will use a dedicated identifier not shared with exported signatures.
142
-	TrustKeyPath string `json:"deprecated-key-path,omitempty"`
143
-
144 138
 	// LiveRestoreEnabled determines whether we should keep containers
145 139
 	// alive upon daemon shutdown/start
146 140
 	LiveRestoreEnabled bool `json:"live-restore,omitempty"`
... ...
@@ -247,9 +240,6 @@ func New() *Config {
247 247
 	config.LogConfig.Config = make(map[string]string)
248 248
 	config.ClusterOpts = make(map[string]string)
249 249
 
250
-	if runtime.GOOS != "linux" {
251
-		config.V2Only = true
252
-	}
253 250
 	return &config
254 251
 }
255 252
 
... ...
@@ -953,7 +953,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
953 953
 		return nil, err
954 954
 	}
955 955
 
956
-	trustKey, err := loadOrCreateTrustKey(config.TrustKeyPath)
956
+	uuid, err := loadOrCreateUUID(filepath.Join(config.Root, "engine_uuid"))
957 957
 	if err != nil {
958 958
 		return nil, err
959 959
 	}
... ...
@@ -998,7 +998,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
998 998
 		return nil, errors.New("Devices cgroup isn't mounted")
999 999
 	}
1000 1000
 
1001
-	d.ID = trustKey.PublicKey().KeyID()
1001
+	d.ID = uuid
1002 1002
 	d.repository = daemonRepo
1003 1003
 	d.containers = container.NewMemoryStore()
1004 1004
 	if d.containersReplica, err = container.NewViewDB(); err != nil {
... ...
@@ -1029,7 +1029,6 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
1029 1029
 		MaxConcurrentUploads:      *config.MaxConcurrentUploads,
1030 1030
 		ReferenceStore:            rs,
1031 1031
 		RegistryService:           registryService,
1032
-		TrustKey:                  trustKey,
1033 1032
 	})
1034 1033
 
1035 1034
 	go d.execCommandGC()
... ...
@@ -54,7 +54,6 @@ func (i *ImageService) PushImage(ctx context.Context, image, tag string, metaHea
54 54
 		},
55 55
 		ConfigMediaType: schema2.MediaTypeImageConfig,
56 56
 		LayerStores:     distribution.NewLayerProvidersFromStores(i.layerStores),
57
-		TrustKey:        i.trustKey,
58 57
 		UploadManager:   i.uploadManager,
59 58
 	}
60 59
 
... ...
@@ -14,7 +14,6 @@ import (
14 14
 	"github.com/docker/docker/layer"
15 15
 	dockerreference "github.com/docker/docker/reference"
16 16
 	"github.com/docker/docker/registry"
17
-	"github.com/docker/libtrust"
18 17
 	"github.com/opencontainers/go-digest"
19 18
 	"github.com/pkg/errors"
20 19
 	"github.com/sirupsen/logrus"
... ...
@@ -40,7 +39,6 @@ type ImageServiceConfig struct {
40 40
 	MaxConcurrentUploads      int
41 41
 	ReferenceStore            dockerreference.Store
42 42
 	RegistryService           registry.Service
43
-	TrustKey                  libtrust.PrivateKey
44 43
 }
45 44
 
46 45
 // NewImageService returns a new ImageService from a configuration
... ...
@@ -56,7 +54,6 @@ func NewImageService(config ImageServiceConfig) *ImageService {
56 56
 		layerStores:               config.LayerStores,
57 57
 		referenceStore:            config.ReferenceStore,
58 58
 		registryService:           config.RegistryService,
59
-		trustKey:                  config.TrustKey,
60 59
 		uploadManager:             xfer.NewLayerUploadManager(config.MaxConcurrentUploads),
61 60
 	}
62 61
 }
... ...
@@ -72,7 +69,6 @@ type ImageService struct {
72 72
 	pruneRunning              int32
73 73
 	referenceStore            dockerreference.Store
74 74
 	registryService           registry.Service
75
-	trustKey                  libtrust.PrivateKey
76 75
 	uploadManager             *xfer.LayerUploadManager
77 76
 }
78 77
 
79 78
deleted file mode 100644
... ...
@@ -1,57 +0,0 @@
1
-package daemon // import "github.com/docker/docker/daemon"
2
-
3
-import (
4
-	"encoding/json"
5
-	"encoding/pem"
6
-	"fmt"
7
-	"os"
8
-	"path/filepath"
9
-
10
-	"github.com/docker/docker/pkg/ioutils"
11
-	"github.com/docker/docker/pkg/system"
12
-	"github.com/docker/libtrust"
13
-)
14
-
15
-// LoadOrCreateTrustKey attempts to load the libtrust key at the given path,
16
-// otherwise generates a new one
17
-// TODO: this should use more of libtrust.LoadOrCreateTrustKey which may need
18
-// a refactor or this function to be moved into libtrust
19
-func loadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
20
-	err := system.MkdirAll(filepath.Dir(trustKeyPath), 0755, "")
21
-	if err != nil {
22
-		return nil, err
23
-	}
24
-	trustKey, err := libtrust.LoadKeyFile(trustKeyPath)
25
-	if err == libtrust.ErrKeyFileDoesNotExist {
26
-		trustKey, err = libtrust.GenerateECP256PrivateKey()
27
-		if err != nil {
28
-			return nil, fmt.Errorf("Error generating key: %s", err)
29
-		}
30
-		encodedKey, err := serializePrivateKey(trustKey, filepath.Ext(trustKeyPath))
31
-		if err != nil {
32
-			return nil, fmt.Errorf("Error serializing key: %s", err)
33
-		}
34
-		if err := ioutils.AtomicWriteFile(trustKeyPath, encodedKey, os.FileMode(0600)); err != nil {
35
-			return nil, fmt.Errorf("Error saving key file: %s", err)
36
-		}
37
-	} else if err != nil {
38
-		return nil, fmt.Errorf("Error loading key file %s: %s", trustKeyPath, err)
39
-	}
40
-	return trustKey, nil
41
-}
42
-
43
-func serializePrivateKey(key libtrust.PrivateKey, ext string) (encoded []byte, err error) {
44
-	if ext == ".json" || ext == ".jwk" {
45
-		encoded, err = json.Marshal(key)
46
-		if err != nil {
47
-			return nil, fmt.Errorf("unable to encode private key JWK: %s", err)
48
-		}
49
-	} else {
50
-		pemBlock, err := key.PEMBlock()
51
-		if err != nil {
52
-			return nil, fmt.Errorf("unable to encode private key PEM: %s", err)
53
-		}
54
-		encoded = pem.EncodeToMemory(pemBlock)
55
-	}
56
-	return
57
-}
58 1
deleted file mode 100644
... ...
@@ -1,71 +0,0 @@
1
-package daemon // import "github.com/docker/docker/daemon"
2
-
3
-import (
4
-	"io/ioutil"
5
-	"os"
6
-	"path/filepath"
7
-	"testing"
8
-
9
-	"gotest.tools/assert"
10
-	is "gotest.tools/assert/cmp"
11
-	"gotest.tools/fs"
12
-)
13
-
14
-// LoadOrCreateTrustKey
15
-func TestLoadOrCreateTrustKeyInvalidKeyFile(t *testing.T) {
16
-	tmpKeyFolderPath, err := ioutil.TempDir("", "api-trustkey-test")
17
-	assert.NilError(t, err)
18
-	defer os.RemoveAll(tmpKeyFolderPath)
19
-
20
-	tmpKeyFile, err := ioutil.TempFile(tmpKeyFolderPath, "keyfile")
21
-	assert.NilError(t, err)
22
-
23
-	_, err = loadOrCreateTrustKey(tmpKeyFile.Name())
24
-	assert.Check(t, is.ErrorContains(err, "Error loading key file"))
25
-}
26
-
27
-func TestLoadOrCreateTrustKeyCreateKeyWhenFileDoesNotExist(t *testing.T) {
28
-	tmpKeyFolderPath := fs.NewDir(t, "api-trustkey-test")
29
-	defer tmpKeyFolderPath.Remove()
30
-
31
-	// Without the need to create the folder hierarchy
32
-	tmpKeyFile := tmpKeyFolderPath.Join("keyfile")
33
-
34
-	key, err := loadOrCreateTrustKey(tmpKeyFile)
35
-	assert.NilError(t, err)
36
-	assert.Check(t, key != nil)
37
-
38
-	_, err = os.Stat(tmpKeyFile)
39
-	assert.NilError(t, err, "key file doesn't exist")
40
-}
41
-
42
-func TestLoadOrCreateTrustKeyCreateKeyWhenDirectoryDoesNotExist(t *testing.T) {
43
-	tmpKeyFolderPath := fs.NewDir(t, "api-trustkey-test")
44
-	defer tmpKeyFolderPath.Remove()
45
-	tmpKeyFile := tmpKeyFolderPath.Join("folder/hierarchy/keyfile")
46
-
47
-	key, err := loadOrCreateTrustKey(tmpKeyFile)
48
-	assert.NilError(t, err)
49
-	assert.Check(t, key != nil)
50
-
51
-	_, err = os.Stat(tmpKeyFile)
52
-	assert.NilError(t, err, "key file doesn't exist")
53
-}
54
-
55
-func TestLoadOrCreateTrustKeyCreateKeyNoPath(t *testing.T) {
56
-	defer os.Remove("keyfile")
57
-	key, err := loadOrCreateTrustKey("keyfile")
58
-	assert.NilError(t, err)
59
-	assert.Check(t, key != nil)
60
-
61
-	_, err = os.Stat("keyfile")
62
-	assert.NilError(t, err, "key file doesn't exist")
63
-}
64
-
65
-func TestLoadOrCreateTrustKeyLoadValidKey(t *testing.T) {
66
-	tmpKeyFile := filepath.Join("testdata", "keyfile")
67
-	key, err := loadOrCreateTrustKey(tmpKeyFile)
68
-	assert.NilError(t, err)
69
-	expected := "AWX2:I27X:WQFX:IOMK:CNAK:O7PW:VYNB:ZLKC:CVAE:YJP2:SI4A:XXAY"
70
-	assert.Check(t, is.Contains(key.String(), expected))
71
-}
72 1
new file mode 100644
... ...
@@ -0,0 +1,28 @@
0
+package daemon // import "github.com/docker/docker/daemon"
1
+
2
+import (
3
+	"fmt"
4
+	"io/ioutil"
5
+	"os"
6
+	"path/filepath"
7
+
8
+	"github.com/docker/docker/pkg/ioutils"
9
+	"github.com/pborman/uuid"
10
+)
11
+
12
+func loadOrCreateUUID(path string) (string, error) {
13
+	err := os.MkdirAll(filepath.Dir(path), 0755)
14
+	if err != nil {
15
+		return "", err
16
+	}
17
+	id, err := ioutil.ReadFile(path)
18
+	if os.IsNotExist(err) {
19
+		id = []byte(uuid.New())
20
+		if err := ioutils.AtomicWriteFile(path, id, os.FileMode(0600)); err != nil {
21
+			return "", fmt.Errorf("Error saving uuid file: %s", err)
22
+		}
23
+	} else if err != nil {
24
+		return "", fmt.Errorf("Error loading uuid file %s: %s", path, err)
25
+	}
26
+	return string(id), nil
27
+}
... ...
@@ -18,7 +18,6 @@ import (
18 18
 	"github.com/docker/docker/pkg/system"
19 19
 	refstore "github.com/docker/docker/reference"
20 20
 	"github.com/docker/docker/registry"
21
-	"github.com/docker/libtrust"
22 21
 	"github.com/opencontainers/go-digest"
23 22
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
24 23
 )
... ...
@@ -73,9 +72,6 @@ type ImagePushConfig struct {
73 73
 	ConfigMediaType string
74 74
 	// LayerStores (indexed by operating system) manages layers.
75 75
 	LayerStores map[string]PushLayerProvider
76
-	// TrustKey is the private key for legacy signatures. This is typically
77
-	// an ephemeral key, since these signatures are no longer verified.
78
-	TrustKey libtrust.PrivateKey
79 76
 	// UploadManager dispatches uploads.
80 77
 	UploadManager *xfer.LayerUploadManager
81 78
 }
... ...
@@ -5,7 +5,6 @@ import (
5 5
 	"errors"
6 6
 	"fmt"
7 7
 	"io"
8
-	"runtime"
9 8
 	"sort"
10 9
 	"strings"
11 10
 	"sync"
... ...
@@ -181,26 +180,8 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id
181 181
 
182 182
 	putOptions := []distribution.ManifestServiceOption{distribution.WithTag(ref.Tag())}
183 183
 	if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil {
184
-		if runtime.GOOS == "windows" || p.config.TrustKey == nil || p.config.RequireSchema2 {
185
-			logrus.Warnf("failed to upload schema2 manifest: %v", err)
186
-			return err
187
-		}
188
-
189
-		logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err)
190
-
191
-		manifestRef, err := reference.WithTag(p.repo.Named(), ref.Tag())
192
-		if err != nil {
193
-			return err
194
-		}
195
-		builder = schema1.NewConfigManifestBuilder(p.repo.Blobs(ctx), p.config.TrustKey, manifestRef, imgConfig)
196
-		manifest, err = manifestFromBuilder(ctx, builder, descriptors)
197
-		if err != nil {
198
-			return err
199
-		}
200
-
201
-		if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil {
202
-			return err
203
-		}
184
+		logrus.Warnf("failed to upload schema2 manifest: %v", err)
185
+		return err
204 186
 	}
205 187
 
206 188
 	var canonicalManifest []byte
... ...
@@ -18,6 +18,7 @@ import (
18 18
 	"path"
19 19
 	"path/filepath"
20 20
 	"regexp"
21
+	"runtime"
21 22
 	"strconv"
22 23
 	"strings"
23 24
 	"sync"
... ...
@@ -35,7 +36,6 @@ import (
35 35
 	"github.com/docker/docker/pkg/mount"
36 36
 	"github.com/docker/go-units"
37 37
 	"github.com/docker/libnetwork/iptables"
38
-	"github.com/docker/libtrust"
39 38
 	"github.com/go-check/check"
40 39
 	"github.com/kr/pty"
41 40
 	"golang.org/x/sys/unix"
... ...
@@ -551,20 +551,23 @@ func (s *DockerDaemonSuite) TestDaemonAllocatesListeningPort(c *check.C) {
551 551
 	}
552 552
 }
553 553
 
554
-func (s *DockerDaemonSuite) TestDaemonKeyGeneration(c *check.C) {
555
-	// TODO: skip or update for Windows daemon
556
-	os.Remove("/etc/docker/key.json")
554
+func (s *DockerDaemonSuite) TestDaemonUUIDGeneration(c *check.C) {
555
+	dir := "/var/lib/docker"
556
+	if runtime.GOOS == "windows" {
557
+		dir = filepath.Join(os.Getenv("programdata"), "docker")
558
+	}
559
+	file := filepath.Join(dir, "engine_uuid")
560
+	os.Remove(file)
557 561
 	s.d.Start(c)
558 562
 	s.d.Stop(c)
559 563
 
560
-	k, err := libtrust.LoadKeyFile("/etc/docker/key.json")
564
+	fi, err := os.Stat(file)
561 565
 	if err != nil {
562
-		c.Fatalf("Error opening key file")
566
+		c.Fatalf("Error opening uuid file")
563 567
 	}
564
-	kid := k.KeyID()
565
-	// Test Key ID is a valid fingerprint (e.g. QQXN:JY5W:TBXI:MK3X:GX6P:PD5D:F56N:NHCS:LVRZ:JA46:R24J:XEFF)
566
-	if len(kid) != 59 {
567
-		c.Fatalf("Bad key ID: %s", kid)
568
+	// Test for uuid length
569
+	if fi.Size() != 36 {
570
+		c.Fatalf("Bad UUID size %d", fi.Size())
568 571
 	}
569 572
 }
570 573
 
... ...
@@ -92,7 +92,7 @@ func CreateInRegistry(ctx context.Context, repo string, auth *types.AuthConfig,
92 92
 		return nil, nil
93 93
 	}
94 94
 
95
-	regService, err := registry.NewService(registry.ServiceOptions{V2Only: true})
95
+	regService, err := registry.NewService(registry.ServiceOptions{})
96 96
 	if err != nil {
97 97
 		return err
98 98
 	}
... ...
@@ -19,16 +19,11 @@ type ServiceOptions struct {
19 19
 	AllowNondistributableArtifacts []string `json:"allow-nondistributable-artifacts,omitempty"`
20 20
 	Mirrors                        []string `json:"registry-mirrors,omitempty"`
21 21
 	InsecureRegistries             []string `json:"insecure-registries,omitempty"`
22
-
23
-	// V2Only controls access to legacy registries.  If it is set to true via the
24
-	// command line flag the daemon will not attempt to contact v1 legacy registries
25
-	V2Only bool `json:"disable-legacy-registry,omitempty"`
26 22
 }
27 23
 
28 24
 // serviceConfig holds daemon configuration for the registry service.
29 25
 type serviceConfig struct {
30 26
 	registrytypes.ServiceConfig
31
-	V2Only bool
32 27
 }
33 28
 
34 29
 var (
... ...
@@ -76,7 +71,6 @@ func newServiceConfig(options ServiceOptions) (*serviceConfig, error) {
76 76
 			// Hack: Bypass setting the mirrors to IndexConfigs since they are going away
77 77
 			// and Mirrors are only for the official registry anyways.
78 78
 		},
79
-		V2Only: options.V2Only,
80 79
 	}
81 80
 	if err := config.LoadAllowNondistributableArtifacts(options.AllowNondistributableArtifacts); err != nil {
82 81
 		return nil, err
... ...
@@ -309,20 +309,5 @@ func (s *DefaultService) LookupPushEndpoints(hostname string) (endpoints []APIEn
309 309
 }
310 310
 
311 311
 func (s *DefaultService) lookupEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
312
-	endpoints, err = s.lookupV2Endpoints(hostname)
313
-	if err != nil {
314
-		return nil, err
315
-	}
316
-
317
-	if s.config.V2Only {
318
-		return endpoints, nil
319
-	}
320
-
321
-	legacyEndpoints, err := s.lookupV1Endpoints(hostname)
322
-	if err != nil {
323
-		return nil, err
324
-	}
325
-	endpoints = append(endpoints, legacyEndpoints...)
326
-
327
-	return endpoints, nil
312
+	return s.lookupV2Endpoints(hostname)
328 313
 }