Merge pull request #12717 from tamnd/fixdockercfg

Fix new docker config format for private registries
pull/6/head
Tim Hockin 2015-09-18 14:43:38 -07:00
commit 9698e957a6
5 changed files with 105 additions and 2 deletions

View File

@ -1870,6 +1870,15 @@ const (
// DockerConfigKey is the key of the required data for SecretTypeDockercfg secrets // DockerConfigKey is the key of the required data for SecretTypeDockercfg secrets
DockerConfigKey = ".dockercfg" DockerConfigKey = ".dockercfg"
// SecretTypeDockerConfigJson contains a dockercfg file that follows the same format rules as ~/.docker/config.json
//
// Required fields:
// - Secret.Data[".dockerconfigjson"] - a serialized ~/.docker/config.json file
SecretTypeDockerConfigJson SecretType = "kubernetes.io/dockerconfigjson"
// DockerConfigJsonKey is the key of the required data for SecretTypeDockerConfigJson secrets
DockerConfigJsonKey = ".dockerconfigjson"
) )
type SecretList struct { type SecretList struct {

View File

@ -30,6 +30,13 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
) )
// DockerConfigJson represents ~/.docker/config.json file info
// see https://github.com/docker/docker/pull/12009
type DockerConfigJson struct {
Auths DockerConfig `json:"auths"`
HttpHeaders map[string]string `json:"HttpHeaders,omitempty"`
}
// DockerConfig represents the config file used by the docker CLI. // DockerConfig represents the config file used by the docker CLI.
// This config that represents the credentials that should be used // This config that represents the credentials that should be used
// when pulling images from specific image repositories. // when pulling images from specific image repositories.
@ -47,8 +54,11 @@ var (
workingDirPath = "" workingDirPath = ""
homeDirPath = os.Getenv("HOME") homeDirPath = os.Getenv("HOME")
rootDirPath = "/" rootDirPath = "/"
homeJsonDirPath = filepath.Join(homeDirPath, ".docker")
rootJsonDirPath = filepath.Join(rootDirPath, ".docker")
configFileName = ".dockercfg" configFileName = ".dockercfg"
configJsonFileName = "config.json"
) )
func SetPreferredDockercfgPath(path string) { func SetPreferredDockercfgPath(path string) {
@ -64,6 +74,32 @@ func GetPreferredDockercfgPath() string {
} }
func ReadDockerConfigFile() (cfg DockerConfig, err error) { func ReadDockerConfigFile() (cfg DockerConfig, err error) {
// Try happy path first - latest config file
dockerConfigJsonLocations := []string{GetPreferredDockercfgPath(), workingDirPath, homeJsonDirPath, rootJsonDirPath}
for _, configPath := range dockerConfigJsonLocations {
absDockerConfigFileLocation, err := filepath.Abs(filepath.Join(configPath, configJsonFileName))
if err != nil {
glog.Errorf("while trying to canonicalize %s: %v", configPath, err)
continue
}
glog.V(4).Infof("looking for .docker/config.json at %s", absDockerConfigFileLocation)
contents, err := ioutil.ReadFile(absDockerConfigFileLocation)
if os.IsNotExist(err) {
continue
}
if err != nil {
glog.V(4).Infof("while trying to read %s: %v", absDockerConfigFileLocation, err)
continue
}
cfg, err := readDockerConfigJsonFileFromBytes(contents)
if err == nil {
glog.V(4).Infof("found .docker/config.json at %s", absDockerConfigFileLocation)
return cfg, nil
}
}
glog.V(4).Infof("couldn't find valid .docker/config.json after checking in %v", dockerConfigJsonLocations)
// Can't find latest config file so check for the old one
dockerConfigFileLocations := []string{GetPreferredDockercfgPath(), workingDirPath, homeDirPath, rootDirPath} dockerConfigFileLocations := []string{GetPreferredDockercfgPath(), workingDirPath, homeDirPath, rootDirPath}
for _, configPath := range dockerConfigFileLocations { for _, configPath := range dockerConfigFileLocations {
absDockerConfigFileLocation, err := filepath.Abs(filepath.Join(configPath, configFileName)) absDockerConfigFileLocation, err := filepath.Abs(filepath.Join(configPath, configFileName))
@ -147,6 +183,16 @@ func readDockerConfigFileFromBytes(contents []byte) (cfg DockerConfig, err error
return return
} }
func readDockerConfigJsonFileFromBytes(contents []byte) (cfg DockerConfig, err error) {
var cfgJson DockerConfigJson
if err = json.Unmarshal(contents, &cfgJson); err != nil {
glog.Errorf("while trying to parse blob %q: %v", contents, err)
return nil, err
}
cfg = cfgJson.Auths
return
}
// dockerConfigEntryWithAuth is used solely for deserializing the Auth field // dockerConfigEntryWithAuth is used solely for deserializing the Auth field
// into a dockerConfigEntry during JSON deserialization. // into a dockerConfigEntry during JSON deserialization.
type dockerConfigEntryWithAuth struct { type dockerConfigEntryWithAuth struct {

View File

@ -22,6 +22,35 @@ import (
"testing" "testing"
) )
func TestDockerConfigJsonJSONDecode(t *testing.T) {
input := []byte(`{"auths": {"http://foo.example.com":{"username": "foo", "password": "bar", "email": "foo@example.com"}, "http://bar.example.com":{"username": "bar", "password": "baz", "email": "bar@example.com"}}}`)
expect := DockerConfigJson{
Auths: DockerConfig(map[string]DockerConfigEntry{
"http://foo.example.com": {
Username: "foo",
Password: "bar",
Email: "foo@example.com",
},
"http://bar.example.com": {
Username: "bar",
Password: "baz",
Email: "bar@example.com",
},
}),
}
var output DockerConfigJson
err := json.Unmarshal(input, &output)
if err != nil {
t.Errorf("Received unexpected error: %v", err)
}
if !reflect.DeepEqual(expect, output) {
t.Errorf("Received unexpected output. Expected %#v, got %#v", expect, output)
}
}
func TestDockerConfigJSONDecode(t *testing.T) { func TestDockerConfigJSONDecode(t *testing.T) {
input := []byte(`{"http://foo.example.com":{"username": "foo", "password": "bar", "email": "foo@example.com"}, "http://bar.example.com":{"username": "bar", "password": "baz", "email": "bar@example.com"}}`) input := []byte(`{"http://foo.example.com":{"username": "foo", "password": "bar", "email": "foo@example.com"}, "http://bar.example.com":{"username": "bar", "password": "baz", "email": "bar@example.com"}}`)

View File

@ -277,7 +277,14 @@ func (k *unionDockerKeyring) Lookup(image string) ([]docker.AuthConfiguration, b
func MakeDockerKeyring(passedSecrets []api.Secret, defaultKeyring DockerKeyring) (DockerKeyring, error) { func MakeDockerKeyring(passedSecrets []api.Secret, defaultKeyring DockerKeyring) (DockerKeyring, error) {
passedCredentials := []DockerConfig{} passedCredentials := []DockerConfig{}
for _, passedSecret := range passedSecrets { for _, passedSecret := range passedSecrets {
if dockercfgBytes, dockercfgExists := passedSecret.Data[api.DockerConfigKey]; (passedSecret.Type == api.SecretTypeDockercfg) && dockercfgExists && (len(dockercfgBytes) > 0) { if dockerConfigJsonBytes, dockerConfigJsonExists := passedSecret.Data[api.DockerConfigJsonKey]; (passedSecret.Type == api.SecretTypeDockerConfigJson) && dockerConfigJsonExists && (len(dockerConfigJsonBytes) > 0) {
dockerConfigJson := DockerConfigJson{}
if err := json.Unmarshal(dockerConfigJsonBytes, &dockerConfigJson); err != nil {
return nil, err
}
passedCredentials = append(passedCredentials, dockerConfigJson.Auths)
} else if dockercfgBytes, dockercfgExists := passedSecret.Data[api.DockerConfigKey]; (passedSecret.Type == api.SecretTypeDockercfg) && dockercfgExists && (len(dockercfgBytes) > 0) {
dockercfg := DockerConfig{} dockercfg := DockerConfig{}
if err := json.Unmarshal(dockercfgBytes, &dockercfg); err != nil { if err := json.Unmarshal(dockercfgBytes, &dockercfg); err != nil {
return nil, err return nil, err

View File

@ -284,6 +284,12 @@ func TestPullWithSecrets(t *testing.T) {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
dockerConfigJson := map[string]map[string]map[string]string{"auths": dockerCfg}
dockerConfigJsonContent, err := json.Marshal(dockerConfigJson)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
tests := map[string]struct { tests := map[string]struct {
imageName string imageName string
passedSecrets []api.Secret passedSecrets []api.Secret
@ -314,6 +320,12 @@ func TestPullWithSecrets(t *testing.T) {
credentialprovider.DockerConfig(map[string]credentialprovider.DockerConfigEntry{"index.docker.io/v1/": {"built-in", "password", "email"}}), credentialprovider.DockerConfig(map[string]credentialprovider.DockerConfigEntry{"index.docker.io/v1/": {"built-in", "password", "email"}}),
[]string{`ubuntu:latest using {"username":"passed-user","password":"passed-password","email":"passed-email"}`}, []string{`ubuntu:latest using {"username":"passed-user","password":"passed-password","email":"passed-email"}`},
}, },
"builtin keyring secrets, but use passed with new docker config": {
"ubuntu",
[]api.Secret{{Type: api.SecretTypeDockerConfigJson, Data: map[string][]byte{api.DockerConfigJsonKey: dockerConfigJsonContent}}},
credentialprovider.DockerConfig(map[string]credentialprovider.DockerConfigEntry{"index.docker.io/v1/": {"built-in", "password", "email"}}),
[]string{`ubuntu:latest using {"username":"passed-user","password":"passed-password","email":"passed-email"}`},
},
} }
for _, test := range tests { for _, test := range tests {
builtInKeyRing := &credentialprovider.BasicDockerKeyring{} builtInKeyRing := &credentialprovider.BasicDockerKeyring{}