mirror of https://github.com/k3s-io/k3s
Support new docker config format for private registries
parent
5fe7029e68
commit
906b279080
|
@ -2073,6 +2073,15 @@ const (
|
|||
|
||||
// DockerConfigKey is the key of the required data for SecretTypeDockercfg secrets
|
||||
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 {
|
||||
|
|
|
@ -30,6 +30,13 @@ import (
|
|||
"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.
|
||||
// This config that represents the credentials that should be used
|
||||
// when pulling images from specific image repositories.
|
||||
|
@ -47,8 +54,11 @@ var (
|
|||
workingDirPath = ""
|
||||
homeDirPath = os.Getenv("HOME")
|
||||
rootDirPath = "/"
|
||||
homeJsonDirPath = filepath.Join(homeDirPath, ".docker")
|
||||
rootJsonDirPath = filepath.Join(rootDirPath, ".docker")
|
||||
|
||||
configFileName = ".dockercfg"
|
||||
configFileName = ".dockercfg"
|
||||
configJsonFileName = "config.json"
|
||||
)
|
||||
|
||||
func SetPreferredDockercfgPath(path string) {
|
||||
|
@ -64,6 +74,32 @@ func GetPreferredDockercfgPath() string {
|
|||
}
|
||||
|
||||
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}
|
||||
for _, configPath := range dockerConfigFileLocations {
|
||||
absDockerConfigFileLocation, err := filepath.Abs(filepath.Join(configPath, configFileName))
|
||||
|
@ -147,6 +183,16 @@ func readDockerConfigFileFromBytes(contents []byte) (cfg DockerConfig, err error
|
|||
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
|
||||
// into a dockerConfigEntry during JSON deserialization.
|
||||
type dockerConfigEntryWithAuth struct {
|
||||
|
|
|
@ -22,6 +22,35 @@ import (
|
|||
"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) {
|
||||
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) {
|
||||
passedCredentials := []DockerConfig{}
|
||||
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{}
|
||||
if err := json.Unmarshal(dockercfgBytes, &dockercfg); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -283,6 +283,12 @@ func TestPullWithSecrets(t *testing.T) {
|
|||
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 {
|
||||
imageName string
|
||||
passedSecrets []api.Secret
|
||||
|
@ -313,6 +319,12 @@ func TestPullWithSecrets(t *testing.T) {
|
|||
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"}`},
|
||||
},
|
||||
"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 {
|
||||
builtInKeyRing := &credentialprovider.BasicDockerKeyring{}
|
||||
|
|
Loading…
Reference in New Issue