diff --git a/pkg/kubelet/dockertools/docker.go b/pkg/kubelet/dockertools/docker.go index 9954370591e..d0945931aaf 100644 --- a/pkg/kubelet/dockertools/docker.go +++ b/pkg/kubelet/dockertools/docker.go @@ -19,6 +19,7 @@ package dockertools import ( "fmt" "math/rand" + "net/http" "os" "strconv" "strings" @@ -31,6 +32,7 @@ import ( "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/jsonmessage" "github.com/docker/docker/pkg/parsers" docker "github.com/fsouza/go-dockerclient" "github.com/golang/glog" @@ -113,6 +115,22 @@ func parseImageName(image string) (string, string) { return parsers.ParseRepositoryTag(image) } +func filterHTTPError(err error, image string) error { + // docker/docker/pull/11314 prints detailed error info for docker pull. + // When it hits 502, it returns a verbose html output including an inline svg, + // which makes the output of kubectl get pods much harder to parse. + // Here converts such verbose output to a concise one. + jerr, ok := err.(*jsonmessage.JSONError) + if ok && (jerr.Code == http.StatusBadGateway || + jerr.Code == http.StatusServiceUnavailable || + jerr.Code == http.StatusGatewayTimeout) { + glog.V(2).Infof("Pulling image %q failed: %v", image, err) + return fmt.Errorf("image pull failed for %s because the registry is temporarily unavailbe.", image) + } else { + return err + } +} + func (p dockerPuller) Pull(image string, secrets []api.Secret) error { repoToPull, tag := parseImageName(image) @@ -149,7 +167,7 @@ func (p dockerPuller) Pull(image string, secrets []api.Secret) error { 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 + return filterHTTPError(err, image) } var pullErrs []error @@ -160,7 +178,7 @@ func (p dockerPuller) Pull(image string, secrets []api.Secret) error { return nil } - pullErrs = append(pullErrs, err) + pullErrs = append(pullErrs, filterHTTPError(err, image)) } return utilerrors.NewAggregate(pullErrs) diff --git a/pkg/kubelet/dockertools/docker_test.go b/pkg/kubelet/dockertools/docker_test.go index 5b7b2e863dc..97c260ff942 100644 --- a/pkg/kubelet/dockertools/docker_test.go +++ b/pkg/kubelet/dockertools/docker_test.go @@ -22,6 +22,7 @@ import ( "hash/adler32" "reflect" "sort" + "strings" "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" @@ -31,6 +32,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network" "github.com/GoogleCloudPlatform/kubernetes/pkg/types" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + "github.com/docker/docker/pkg/jsonmessage" docker "github.com/fsouza/go-dockerclient" ) @@ -238,6 +240,40 @@ func TestPullWithNoSecrets(t *testing.T) { } } +func TestPullWithJSONError(t *testing.T) { + tests := map[string]struct { + imageName string + err error + expectedError string + }{ + "Json error": { + "ubuntu", + &jsonmessage.JSONError{Code: 50, Message: "Json error"}, + "Json error", + }, + "Bad gateway": { + "ubuntu", + &jsonmessage.JSONError{Code: 502, Message: "\n\n \n \n \n

Oops, there was an error!

\n

We have been contacted of this error, feel free to check out status.docker.com\n to see if there is a bigger issue.

\n\n \n"}, + "because the registry is temporarily unavailbe", + }, + } + for i, test := range tests { + fakeKeyring := &credentialprovider.FakeKeyring{} + fakeClient := &FakeDockerClient{ + Errors: map[string]error{"pull": test.err}, + } + puller := &dockerPuller{ + client: fakeClient, + keyring: fakeKeyring, + } + err := puller.Pull(test.imageName, []api.Secret{}) + if err == nil || !strings.Contains(err.Error(), test.expectedError) { + t.Errorf("%d: expect error %s, got : %s", i, test.expectedError, err) + continue + } + } +} + func TestPullWithSecrets(t *testing.T) { // auth value is equivalent to: "username":"passed-user","password":"passed-password" dockerCfg := map[string]map[string]string{"index.docker.io/v1/": {"email": "passed-email", "auth": "cGFzc2VkLXVzZXI6cGFzc2VkLXBhc3N3b3Jk"}}