Implements a credentialprovider library for use by DockerPuller.
This change refactors the way Kubelet's DockerPuller handles the docker config credentials to utilize a new credentialprovider library. The credentialprovider library is based on several of the files from the Kubelet's dockertools directory, but supports a new pluggable model for retrieving a .dockercfg-compatible JSON blob with credentials. With this change, the Kubelet will lazily ask for the docker config from a set of DockerConfigProvider extensions each time it needs a credential. This change provides common implementations of DockerConfigProvider for: - "Default": load .dockercfg from disk - "Caching": wraps another provider in a cache that expires after a pre-specified lifetime. GCP-only: - "google-dockercfg": reads a .dockercfg from a GCE instance's metadata - "google-dockercfg-url": reads a .dockercfg from a URL specified in a GCE instance's metadata. - "google-container-registry": reads an access token from GCE metadata into a password field.
This commit is contained in:
261
pkg/credentialprovider/keyring_test.go
Normal file
261
pkg/credentialprovider/keyring_test.go
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package credentialprovider
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDockerKeyringFromBytes(t *testing.T) {
|
||||
url := "hello.kubernetes.io"
|
||||
email := "foo@bar.baz"
|
||||
username := "foo"
|
||||
password := "bar"
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
|
||||
sampleDockerConfig := fmt.Sprintf(`{
|
||||
"https://%s": {
|
||||
"email": %q,
|
||||
"auth": %q
|
||||
}
|
||||
}`, url, email, auth)
|
||||
|
||||
keyring := &BasicDockerKeyring{}
|
||||
if cfg, err := readDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil {
|
||||
t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err)
|
||||
} else {
|
||||
keyring.Add(cfg)
|
||||
}
|
||||
|
||||
val, ok := keyring.Lookup(url + "/foo/bar")
|
||||
if !ok {
|
||||
t.Errorf("Didn't find expected URL: %s", url)
|
||||
}
|
||||
|
||||
if username != val.Username {
|
||||
t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username)
|
||||
}
|
||||
if password != val.Password {
|
||||
t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password)
|
||||
}
|
||||
if email != val.Email {
|
||||
t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyringMiss(t *testing.T) {
|
||||
url := "hello.kubernetes.io"
|
||||
email := "foo@bar.baz"
|
||||
username := "foo"
|
||||
password := "bar"
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
|
||||
sampleDockerConfig := fmt.Sprintf(`{
|
||||
"https://%s": {
|
||||
"email": %q,
|
||||
"auth": %q
|
||||
}
|
||||
}`, url, email, auth)
|
||||
|
||||
keyring := &BasicDockerKeyring{}
|
||||
if cfg, err := readDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil {
|
||||
t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err)
|
||||
} else {
|
||||
keyring.Add(cfg)
|
||||
}
|
||||
|
||||
val, ok := keyring.Lookup("world.mesos.org/foo/bar")
|
||||
if ok {
|
||||
t.Errorf("Found unexpected credential: %+v", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyringMissWithDockerHubCredentials(t *testing.T) {
|
||||
url := defaultRegistryHost
|
||||
email := "foo@bar.baz"
|
||||
username := "foo"
|
||||
password := "bar"
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
|
||||
sampleDockerConfig := fmt.Sprintf(`{
|
||||
"https://%s": {
|
||||
"email": %q,
|
||||
"auth": %q
|
||||
}
|
||||
}`, url, email, auth)
|
||||
|
||||
keyring := &BasicDockerKeyring{}
|
||||
if cfg, err := readDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil {
|
||||
t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err)
|
||||
} else {
|
||||
keyring.Add(cfg)
|
||||
}
|
||||
|
||||
val, ok := keyring.Lookup("world.mesos.org/foo/bar")
|
||||
if ok {
|
||||
t.Errorf("Found unexpected credential: %+v", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyringHitWithUnqualifiedDockerHub(t *testing.T) {
|
||||
url := defaultRegistryHost
|
||||
email := "foo@bar.baz"
|
||||
username := "foo"
|
||||
password := "bar"
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
|
||||
sampleDockerConfig := fmt.Sprintf(`{
|
||||
"https://%s": {
|
||||
"email": %q,
|
||||
"auth": %q
|
||||
}
|
||||
}`, url, email, auth)
|
||||
|
||||
keyring := &BasicDockerKeyring{}
|
||||
if cfg, err := readDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil {
|
||||
t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err)
|
||||
} else {
|
||||
keyring.Add(cfg)
|
||||
}
|
||||
|
||||
val, ok := keyring.Lookup("google/docker-registry")
|
||||
if !ok {
|
||||
t.Errorf("Didn't find expected URL: %s", url)
|
||||
}
|
||||
|
||||
if username != val.Username {
|
||||
t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username)
|
||||
}
|
||||
if password != val.Password {
|
||||
t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password)
|
||||
}
|
||||
if email != val.Email {
|
||||
t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyringHitWithUnqualifiedLibraryDockerHub(t *testing.T) {
|
||||
url := defaultRegistryHost
|
||||
email := "foo@bar.baz"
|
||||
username := "foo"
|
||||
password := "bar"
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
|
||||
sampleDockerConfig := fmt.Sprintf(`{
|
||||
"https://%s": {
|
||||
"email": %q,
|
||||
"auth": %q
|
||||
}
|
||||
}`, url, email, auth)
|
||||
|
||||
keyring := &BasicDockerKeyring{}
|
||||
if cfg, err := readDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil {
|
||||
t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err)
|
||||
} else {
|
||||
keyring.Add(cfg)
|
||||
}
|
||||
|
||||
val, ok := keyring.Lookup("jenkins")
|
||||
if !ok {
|
||||
t.Errorf("Didn't find expected URL: %s", url)
|
||||
}
|
||||
|
||||
if username != val.Username {
|
||||
t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username)
|
||||
}
|
||||
if password != val.Password {
|
||||
t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password)
|
||||
}
|
||||
if email != val.Email {
|
||||
t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyringHitWithQualifiedDockerHub(t *testing.T) {
|
||||
url := defaultRegistryHost
|
||||
email := "foo@bar.baz"
|
||||
username := "foo"
|
||||
password := "bar"
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
|
||||
sampleDockerConfig := fmt.Sprintf(`{
|
||||
"https://%s": {
|
||||
"email": %q,
|
||||
"auth": %q
|
||||
}
|
||||
}`, url, email, auth)
|
||||
|
||||
keyring := &BasicDockerKeyring{}
|
||||
if cfg, err := readDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil {
|
||||
t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err)
|
||||
} else {
|
||||
keyring.Add(cfg)
|
||||
}
|
||||
|
||||
val, ok := keyring.Lookup(url + "/google/docker-registry")
|
||||
if !ok {
|
||||
t.Errorf("Didn't find expected URL: %s", url)
|
||||
}
|
||||
|
||||
if username != val.Username {
|
||||
t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username)
|
||||
}
|
||||
if password != val.Password {
|
||||
t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password)
|
||||
}
|
||||
if email != val.Email {
|
||||
t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email)
|
||||
}
|
||||
}
|
||||
|
||||
type testProvider struct {
|
||||
Count int
|
||||
}
|
||||
|
||||
// Enabled implements dockerConfigProvider
|
||||
func (d *testProvider) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Provide implements dockerConfigProvider
|
||||
func (d *testProvider) Provide() DockerConfig {
|
||||
d.Count += 1
|
||||
return DockerConfig{}
|
||||
}
|
||||
|
||||
func TestLazyKeyring(t *testing.T) {
|
||||
provider := &testProvider{
|
||||
Count: 0,
|
||||
}
|
||||
lazy := &lazyDockerKeyring{
|
||||
Providers: []DockerConfigProvider{
|
||||
provider,
|
||||
},
|
||||
}
|
||||
|
||||
if provider.Count != 0 {
|
||||
t.Errorf("Unexpected number of Provide calls: %v", provider.Count)
|
||||
}
|
||||
lazy.Lookup("foo")
|
||||
if provider.Count != 1 {
|
||||
t.Errorf("Unexpected number of Provide calls: %v", provider.Count)
|
||||
}
|
||||
lazy.Lookup("foo")
|
||||
if provider.Count != 2 {
|
||||
t.Errorf("Unexpected number of Provide calls: %v", provider.Count)
|
||||
}
|
||||
lazy.Lookup("foo")
|
||||
if provider.Count != 3 {
|
||||
t.Errorf("Unexpected number of Provide calls: %v", provider.Count)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user