Merge pull request #7971 from deads2k/make-docker-keyring-handle-multiple-hits

make the dockerkeyring handle mutiple matching credentials
This commit is contained in:
Brendan Burns
2015-05-11 17:01:37 -07:00
9 changed files with 142 additions and 65 deletions

View File

@@ -30,6 +30,7 @@ import (
kubeletTypes "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/types"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
utilerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
"github.com/docker/docker/pkg/parsers"
docker "github.com/fsouza/go-dockerclient"
"github.com/golang/glog"
@@ -124,25 +125,39 @@ func (p dockerPuller) Pull(image string) error {
Tag: tag,
}
creds, ok := p.keyring.Lookup(repoToPull)
if !ok {
creds, haveCredentials := p.keyring.Lookup(repoToPull)
if !haveCredentials {
glog.V(1).Infof("Pulling image %s without credentials", image)
}
err := p.client.PullImage(opts, creds)
// If there was no error, or we had credentials, just return the error.
if err == nil || ok {
err := p.client.PullImage(opts, docker.AuthConfiguration{})
if err == nil {
return nil
}
// Image spec: [<registry>/]<repository>/<image>[:<version] so we count '/'
explicitRegistry := (strings.Count(image, "/") == 2)
// Hack, look for a private registry, and decorate the error with the lack of
// credentials. This is heuristic, and really probably could be done better
// by talking to the registry API directly from the kubelet here.
if explicitRegistry {
return fmt.Errorf("image pull failed for %s, this may be because there are no credentials on this request. details: (%v)", image, err)
}
return err
}
// Image spec: [<registry>/]<repository>/<image>[:<version] so we count '/'
explicitRegistry := (strings.Count(image, "/") == 2)
// Hack, look for a private registry, and decorate the error with the lack of
// credentials. This is heuristic, and really probably could be done better
// by talking to the registry API directly from the kubelet here.
if explicitRegistry {
return fmt.Errorf("image pull failed for %s, this may be because there are no credentials on this request. details: (%v)", image, err)
var pullErrs []error
for _, currentCreds := range creds {
err := p.client.PullImage(opts, currentCreds)
// If there was no error, return success
if err == nil {
return nil
}
pullErrs = append(pullErrs, err)
}
return err
return utilerrors.NewAggregate(pullErrs)
}
func (p throttledDockerPuller) Pull(image string) error {

View File

@@ -198,18 +198,18 @@ func TestParseImageName(t *testing.T) {
}
}
func TestPull(t *testing.T) {
func TestPullWithNoSecrets(t *testing.T) {
tests := []struct {
imageName string
expectedImage string
}{
{"ubuntu", "ubuntu:latest"},
{"ubuntu:2342", "ubuntu:2342"},
{"ubuntu:latest", "ubuntu:latest"},
{"foo/bar:445566", "foo/bar:445566"},
{"registry.example.com:5000/foobar", "registry.example.com:5000/foobar:latest"},
{"registry.example.com:5000/foobar:5342", "registry.example.com:5000/foobar:5342"},
{"registry.example.com:5000/foobar:latest", "registry.example.com:5000/foobar:latest"},
{"ubuntu", "ubuntu:latest using {}"},
{"ubuntu:2342", "ubuntu:2342 using {}"},
{"ubuntu:latest", "ubuntu:latest using {}"},
{"foo/bar:445566", "foo/bar:445566 using {}"},
{"registry.example.com:5000/foobar", "registry.example.com:5000/foobar:latest using {}"},
{"registry.example.com:5000/foobar:5342", "registry.example.com:5000/foobar:5342 using {}"},
{"registry.example.com:5000/foobar:latest", "registry.example.com:5000/foobar:latest using {}"},
}
for _, test := range tests {
fakeKeyring := &credentialprovider.FakeKeyring{}
@@ -259,7 +259,6 @@ func TestDockerKeyringLookupFails(t *testing.T) {
}
func TestDockerKeyringLookup(t *testing.T) {
empty := docker.AuthConfiguration{}
ada := docker.AuthConfiguration{
Username: "ada",
@@ -289,27 +288,27 @@ func TestDockerKeyringLookup(t *testing.T) {
tests := []struct {
image string
match docker.AuthConfiguration
match []docker.AuthConfiguration
ok bool
}{
// direct match
{"bar.example.com", ada, true},
{"bar.example.com", []docker.AuthConfiguration{ada}, true},
// direct match deeper than other possible matches
{"bar.example.com/pong", grace, true},
{"bar.example.com/pong", []docker.AuthConfiguration{grace, ada}, true},
// no direct match, deeper path ignored
{"bar.example.com/ping", ada, true},
{"bar.example.com/ping", []docker.AuthConfiguration{ada}, true},
// match first part of path token
{"bar.example.com/pongz", grace, true},
{"bar.example.com/pongz", []docker.AuthConfiguration{grace, ada}, true},
// match regardless of sub-path
{"bar.example.com/pong/pang", grace, true},
{"bar.example.com/pong/pang", []docker.AuthConfiguration{grace, ada}, true},
// no host match
{"example.com", empty, false},
{"foo.example.com", empty, false},
{"example.com", []docker.AuthConfiguration{}, false},
{"foo.example.com", []docker.AuthConfiguration{}, false},
}
for i, tt := range tests {
@@ -345,15 +344,15 @@ func TestIssue3797(t *testing.T) {
tests := []struct {
image string
match docker.AuthConfiguration
match []docker.AuthConfiguration
ok bool
}{
// direct match
{"quay.io", rex, true},
{"quay.io", []docker.AuthConfiguration{rex}, true},
// partial matches
{"quay.io/foo", rex, true},
{"quay.io/foo/bar", rex, true},
{"quay.io/foo", []docker.AuthConfiguration{rex}, true},
{"quay.io/foo/bar", []docker.AuthConfiguration{rex}, true},
}
for i, tt := range tests {

View File

@@ -17,14 +17,16 @@ limitations under the License.
package dockertools
import (
"encoding/json"
"fmt"
"os"
"reflect"
"sort"
"sync"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/fsouza/go-dockerclient"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
// FakeDockerClient is a simple fake docker client, so that kubelet can be run for testing without requiring a real docker setup.
@@ -264,7 +266,8 @@ func (f *FakeDockerClient) PullImage(opts docker.PullImageOptions, auth docker.A
if len(registry) != 0 {
registry = registry + "/"
}
f.pulled = append(f.pulled, fmt.Sprintf("%s%s:%s", registry, opts.Repository, opts.Tag))
authJson, _ := json.Marshal(auth)
f.pulled = append(f.pulled, fmt.Sprintf("%s%s:%s using %s", registry, opts.Repository, opts.Tag, string(authJson)))
}
return err
}

View File

@@ -723,7 +723,7 @@ func (dm *DockerManager) ListImages() ([]kubecontainer.Image, error) {
// TODO(vmarmol): Consider unexporting.
// PullImage pulls an image from network to local storage.
func (dm *DockerManager) PullImage(image kubecontainer.ImageSpec, _ []api.Secret) error {
func (dm *DockerManager) PullImage(image kubecontainer.ImageSpec, secrets []api.Secret) error {
return dm.Puller.Pull(image.Image)
}
@@ -1145,6 +1145,7 @@ func (dm *DockerManager) createPodInfraContainer(pod *api.Pod) (kubeletTypes.Doc
return "", err
}
if !ok {
// TODO get the pull secrets from the container's ImageSpec and the pod's service account
if err := dm.PullImage(spec, nil); err != nil {
if ref != nil {
dm.recorder.Eventf(ref, "failed", "Failed to pull image %q: %v", container.Image, err)
@@ -1335,6 +1336,7 @@ func (dm *DockerManager) pullImage(pod *api.Pod, container *api.Container) error
return nil
}
// TODO get the pull secrets from the container's ImageSpec and the pod's service account
err = dm.PullImage(spec, nil)
dm.runtimeHooks.ReportImagePull(pod, container, err)
return err

View File

@@ -696,7 +696,13 @@ func (r *runtime) Version() (kubecontainer.Version, error) {
// writeDockerAuthConfig writes the docker credentials to rkt auth config files.
// This enables rkt to pull docker images from docker registry with credentials.
func (r *runtime) writeDockerAuthConfig(image string, creds docker.AuthConfiguration) error {
func (r *runtime) writeDockerAuthConfig(image string, credsSlice []docker.AuthConfiguration) error {
creds := docker.AuthConfiguration{}
// TODO handle multiple creds
if len(credsSlice) >= 1 {
creds = credsSlice[0]
}
registry := "index.docker.io"
// Image spec: [<registry>/]<repository>/<image>[:<version]
explicitRegistry := (strings.Count(image, "/") == 2)
@@ -735,7 +741,7 @@ func (r *runtime) writeDockerAuthConfig(image string, creds docker.AuthConfigura
//
// https://github.com/GoogleCloudPlatform/kubernetes/issues/7203
//
func (r *runtime) PullImage(image kubecontainer.ImageSpec, _ []api.Secret) error {
func (r *runtime) PullImage(image kubecontainer.ImageSpec, pullSecrets []api.Secret) error {
img := image.Image
// TODO(yifan): The credential operation is a copy from dockertools package,
// Need to resolve the code duplication.