mirror of https://github.com/k3s-io/k3s
Merge pull request #12717 from tamnd/fixdockercfg
Fix new docker config format for private registriespull/6/head
commit
9698e957a6
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"}}`)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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{}
|
||||||
|
|
Loading…
Reference in New Issue