diff --git a/v2/cmd/manifest-tool/inspect.go b/v2/cmd/manifest-tool/inspect.go index 00e6fd23..b18b459f 100644 --- a/v2/cmd/manifest-tool/inspect.go +++ b/v2/cmd/manifest-tool/inspect.go @@ -45,10 +45,13 @@ var inspectCmd = &cli.Command{ logrus.Fatal("the --expand-config flag is only valid when used with --raw") } memoryStore := store.NewMemoryStore() - resolver := util.NewResolver(c.String("username"), c.String("password"), c.Bool("insecure"), - c.Bool("plain-http"), c.String("docker-cfg")) + err = util.CreateRegistryHost(imageRef, c.String("username"), c.String("password"), c.Bool("insecure"), + c.Bool("plain-http"), c.String("docker-cfg"), false) + if err != nil { + return fmt.Errorf("error creating registry host configuration: %v", err) + } - descriptor, err := registry.FetchDescriptor(resolver, memoryStore, imageRef) + descriptor, err := registry.FetchDescriptor(util.GetResolver(), memoryStore, imageRef) if err != nil { logrus.Error(err) } diff --git a/v2/pkg/registry/push.go b/v2/pkg/registry/push.go index a091fa8f..8bf04b56 100644 --- a/v2/pkg/registry/push.go +++ b/v2/pkg/registry/push.go @@ -21,12 +21,14 @@ func PushManifestList(username, password string, input types.YAMLInput, ignoreMi return hash, length, fmt.Errorf("error parsing name for manifest list (%s): %v", input.Image, err) } - resolver := util.NewResolver(username, password, insecure, plainHttp, configDir) - + err = util.CreateRegistryHost(targetRef, username, password, insecure, plainHttp, configDir, true) + if err != nil { + return hash, length, fmt.Errorf("error creating registry host configuration: %v", err) + } manifestList := types.ManifestList{ Name: input.Image, Reference: targetRef, - Resolver: resolver, + Resolver: util.GetResolver(), Type: manifestType, } // create an in-memory store for OCI descriptors and content used during the push operation @@ -48,7 +50,7 @@ func PushManifestList(username, password string, input types.YAMLInput, ignoreMi if reference.Domain(targetRef) != reference.Domain(ref) { return hash, length, fmt.Errorf("source image (%s) registry does not match target image (%s) registry", ref, targetRef) } - descriptor, err := FetchDescriptor(resolver, memoryStore, ref) + descriptor, err := FetchDescriptor(util.GetResolver(), memoryStore, ref) if err != nil { if ignoreMissing { logrus.Warnf("Couldn't access image '%q'. Skipping due to 'ignore missing' configuration.", img.Image) diff --git a/v2/pkg/util/resolver.go b/v2/pkg/util/resolver.go index 705e61a4..b9788605 100644 --- a/v2/pkg/util/resolver.go +++ b/v2/pkg/util/resolver.go @@ -2,6 +2,7 @@ package util import ( "crypto/tls" + "fmt" "net/http" "os" "strings" @@ -11,15 +12,30 @@ import ( "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/config/credentials" + "github.com/docker/distribution/reference" "github.com/sirupsen/logrus" ) -func NewResolver(username, password string, insecure, plainHTTP bool, dockerConfigPath string) remotes.Resolver { +var registryHost docker.RegistryHost - opts := docker.ResolverOptions{ - PlainHTTP: plainHTTP, +func CreateRegistryHost(imageRef reference.Named, username, password string, insecure, plainHTTP bool, dockerConfigPath string, pushOp bool) error { + + hostname, _ := splitHostname(imageRef.String()) + if hostname == "docker.io" { + hostname = "registry-1.docker.io" + } + registryHost = docker.RegistryHost{ + Host: hostname, + Scheme: "https", + Path: "/v2", + Capabilities: docker.HostCapabilityPull | docker.HostCapabilityResolve, } + if pushOp { + registryHost.Capabilities |= docker.HostCapabilityPush + } + client := http.DefaultClient + if insecure { client.Transport = &http.Transport{ TLSClientConfig: &tls.Config{ @@ -27,48 +43,43 @@ func NewResolver(username, password string, insecure, plainHTTP bool, dockerConf }, } } - opts.Client = client + registryHost.Client = client - if username != "" || password != "" { - opts.Credentials = func(hostName string) (string, string, error) { - return username, password, nil - } - return docker.NewResolver(opts) + if plainHTTP { + registryHost.Scheme = "http" } - var ( - err error - cfg *configfile.ConfigFile - ) - if dockerConfigPath == "" || dockerConfigPath == config.Dir() { - cfg, err = config.Load(config.Dir()) - if err != nil { - // handle error - logrus.Errorf("unable to load default Docker auth config: %v", err) + + credFunc := func(hostName string) (string, string, error) { + if username != "" || password != "" { + return username, password, nil } - } else { - cfg = configfile.New(dockerConfigPath) - if _, err := os.Stat(dockerConfigPath); err == nil { - file, err := os.Open(dockerConfigPath) + var ( + err error + cfg *configfile.ConfigFile + ) + if dockerConfigPath == "" || dockerConfigPath == config.Dir() { + cfg, err = config.Load(config.Dir()) if err != nil { - logrus.Errorf("Can't load docker config file %s: %v", dockerConfigPath, err) - // fall back to resolver with no config - return docker.NewResolver(opts) + logrus.Warnf("unable to load default Docker auth config: %v", err) } - defer file.Close() - if err := cfg.LoadFromReader(file); err != nil { - logrus.Errorf("Can't read and parse docker config file %s: %v", dockerConfigPath, err) - return docker.NewResolver(opts) + } else { + cfg = configfile.New(dockerConfigPath) + if _, err := os.Stat(dockerConfigPath); err == nil { + file, err := os.Open(dockerConfigPath) + if err != nil { + return "", "", fmt.Errorf("can't load docker config file %s: %w", dockerConfigPath, err) + } + defer file.Close() + if err := cfg.LoadFromReader(file); err != nil { + return "", "", fmt.Errorf("can't read and parse docker config file %s: %v", dockerConfigPath, err) + } + } else if !os.IsNotExist(err) { + return "", "", fmt.Errorf("unable to open docker config file %s: %v", dockerConfigPath, err) } - } else if !os.IsNotExist(err) { - logrus.Errorf("Unable to open docker config file %s: %v", dockerConfigPath, err) - return docker.NewResolver(opts) } - } - if !cfg.ContainsAuth() { - cfg.CredentialsStore = credentials.DetectDefaultStore(cfg.CredentialsStore) - } - // support cred helpers - opts.Credentials = func(hostName string) (string, string, error) { + if !cfg.ContainsAuth() { + cfg.CredentialsStore = credentials.DetectDefaultStore(cfg.CredentialsStore) + } hostname := resolveHostname(hostName) auth, err := cfg.GetAuthConfig(hostname) if err != nil { @@ -78,10 +89,25 @@ func NewResolver(username, password string, insecure, plainHTTP bool, dockerConf return "", auth.IdentityToken, nil } return auth.Username, auth.Password, nil + + } + registryHost.Authorizer = docker.NewDockerAuthorizer(docker.WithAuthCreds(credFunc)) + + return nil +} + +func GetResolver() remotes.Resolver { + + opts := docker.ResolverOptions{ + Hosts: getHosts, } return docker.NewResolver(opts) } +func getHosts(name string) ([]docker.RegistryHost, error) { + return []docker.RegistryHost{registryHost}, nil +} + // resolveHostname resolves Docker specific hostnames func resolveHostname(hostname string) string { if strings.HasSuffix(hostname, "docker.io") {