cmd: Add support for private repositories and registries

This commit adds support for authentication for image pulls for
'linuxkit build'. For each image reference we look up credentials
via the docker CLI configuration and use it if defined for
a given registry server. The code caches credentials to avoid
lookups for every image.

Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
This commit is contained in:
Rolf Neugebauer 2018-07-20 11:09:40 +01:00
parent 1679668121
commit 615b122767

View File

@ -14,13 +14,25 @@ import (
"strings" "strings"
"github.com/containerd/containerd/reference" "github.com/containerd/containerd/reference"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/configfile"
distref "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/docker/registry"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
var (
// configFile is a cached copy of the client config file
configFile *configfile.ConfigFile
// authCache maps a registry URL to encoded authentication
authCache map[string]string
)
func dockerRun(input io.Reader, output io.Writer, trust bool, img string, args ...string) error { func dockerRun(input io.Reader, output io.Writer, trust bool, img string, args ...string) error {
log.Debugf("docker run %s (trust=%t) (input): %s", img, trust, strings.Join(args, " ")) log.Debugf("docker run %s (trust=%t) (input): %s", img, trust, strings.Join(args, " "))
docker, err := exec.LookPath("docker") docker, err := exec.LookPath("docker")
@ -146,7 +158,8 @@ func dockerPull(ref *reference.Spec, forcePull, trustedPull bool) error {
} }
log.Infof("Pull image: %s", ref) log.Infof("Pull image: %s", ref)
r, err := cli.ImagePull(context.Background(), ref.String(), types.ImagePullOptions{}) pullOptions := types.ImagePullOptions{RegistryAuth: getAuth(cli, ref.String())}
r, err := cli.ImagePull(context.Background(), ref.String(), pullOptions)
if err != nil { if err != nil {
return err return err
} }
@ -191,3 +204,48 @@ func dockerInspectImage(cli *client.Client, ref *reference.Spec, trustedPull boo
return inspect, nil return inspect, nil
} }
// getAuth attempts to get an encoded authentication spec for a reference
// This function does not error. It simply returns a empty string if something fails.
func getAuth(cli *client.Client, refStr string) string {
// Initialise state on first use
if authCache == nil {
authCache = make(map[string]string)
}
if configFile == nil {
configFile = config.LoadDefaultConfigFile(ioutil.Discard)
}
// Normalise ref
ref, err := distref.ParseNormalizedNamed(refStr)
if err != nil {
log.Debugf("Failed to parse reference %s: %v: %v", refStr, err)
return ""
}
hostname := distref.Domain(ref)
// Special case for docker.io which currently is stored as https://index.docker.io/v1/ in authentication map.
// Other registries are stored under their domain name.
if hostname == registry.IndexName {
hostname = registry.IndexServer
}
// Look up in cache
if val, ok := authCache[hostname]; ok {
log.Debugf("Returning cached credentials for %s", hostname)
return val
}
authConfig, err := configFile.GetAuthConfig(hostname)
if err != nil {
log.Debugf("Failed to get authentication config for %s. Ignoring: %v", hostname, err)
return ""
}
log.Debugf("Looked up credentials for %s", hostname)
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
if err != nil {
return ""
}
authCache[hostname] = encodedAuth
return encodedAuth
}