Merge pull request #7971 from deads2k/make-docker-keyring-handle-multiple-hits
make the dockerkeyring handle mutiple matching credentials
This commit is contained in:
@@ -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 {
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
Reference in New Issue
Block a user