From 135f87dc15186b494b27a47b298ed24d95cbf889 Mon Sep 17 00:00:00 2001 From: Solly Ross Date: Fri, 30 Sep 2016 14:51:26 -0400 Subject: [PATCH] Kubelet: Use RepoDigest for ImageID when available Previously, we used the docker config digest (also called "image ID" by Docker) for the value of the `ImageID` field in the container status. This was not particularly useful, since the config manifest is not what's used to identify the image in a registry, which uses the manifest digest instead. Docker 1.12+ always populates the RepoDigests field with the manifest digests, and Docker 1.10 and 1.11 populate it when images are pulled by digest. This commit changes `ImageID` to point to the the manifest digest when available, using the prefix `docker-pullable://` (instead of `docker://`) --- pkg/kubelet/dockertools/docker.go | 1 + pkg/kubelet/dockertools/docker_manager.go | 17 ++++- pkg/kubelet/dockertools/fake_docker_client.go | 3 + test/e2e_node/image_id_test.go | 66 +++++++++++++++++++ test/e2e_node/image_list.go | 1 + 5 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 test/e2e_node/image_id_test.go diff --git a/pkg/kubelet/dockertools/docker.go b/pkg/kubelet/dockertools/docker.go index 425f433ce65..89aae7f7e22 100644 --- a/pkg/kubelet/dockertools/docker.go +++ b/pkg/kubelet/dockertools/docker.go @@ -43,6 +43,7 @@ import ( const ( PodInfraContainerName = leaky.PodInfraContainerName DockerPrefix = "docker://" + DockerPullablePrefix = "docker-pullable://" LogSuffix = "log" ext4MaxFileNameLen = 255 ) diff --git a/pkg/kubelet/dockertools/docker_manager.go b/pkg/kubelet/dockertools/docker_manager.go index 2ed8705e505..438d20d92cf 100644 --- a/pkg/kubelet/dockertools/docker_manager.go +++ b/pkg/kubelet/dockertools/docker_manager.go @@ -397,11 +397,26 @@ func (dm *DockerManager) inspectContainer(id string, podName, podNamespace strin parseTimestampError("FinishedAt", iResult.State.FinishedAt) } + // default to the image ID, but try and inspect for the RepoDigests + imageID := DockerPrefix + iResult.Image + imgInspectResult, err := dm.client.InspectImageByID(iResult.Image) + if err != nil { + utilruntime.HandleError(fmt.Errorf("unable to inspect docker image %q while inspecting docker container %q: %v", containerName, iResult.Image, err)) + } else { + if len(imgInspectResult.RepoDigests) > 1 { + glog.V(4).Infof("Container %q had more than one associated RepoDigest (%v), only using the first", containerName, imgInspectResult.RepoDigests) + } + + if len(imgInspectResult.RepoDigests) > 0 { + imageID = DockerPullablePrefix + imgInspectResult.RepoDigests[0] + } + } + status := kubecontainer.ContainerStatus{ Name: containerName, RestartCount: containerInfo.RestartCount, Image: iResult.Config.Image, - ImageID: DockerPrefix + iResult.Image, + ImageID: imageID, ID: kubecontainer.DockerID(id).ContainerID(), ExitCode: iResult.State.ExitCode, CreatedAt: createdAt, diff --git a/pkg/kubelet/dockertools/fake_docker_client.go b/pkg/kubelet/dockertools/fake_docker_client.go index 5a6c64164d1..0f6908c739e 100644 --- a/pkg/kubelet/dockertools/fake_docker_client.go +++ b/pkg/kubelet/dockertools/fake_docker_client.go @@ -86,6 +86,9 @@ func newClientWithVersionAndClock(version, apiVersion string, c clock.Clock) *Fa Errors: make(map[string]error), ContainerMap: make(map[string]*dockertypes.ContainerJSON), Clock: c, + + // default this to an empty result, so that we never have a nil non-error response from InspectImage + Image: &dockertypes.ImageInspect{}, } } diff --git a/test/e2e_node/image_id_test.go b/test/e2e_node/image_id_test.go new file mode 100644 index 00000000000..d81ac51a688 --- /dev/null +++ b/test/e2e_node/image_id_test.go @@ -0,0 +1,66 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 e2e_node + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/kubelet/dockertools" + "k8s.io/kubernetes/test/e2e/framework" + + "github.com/davecgh/go-spew/spew" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = framework.KubeDescribe("ImageID", func() { + + busyBoxImage := "gcr.io/google_containers/busybox@sha256:4bdd623e848417d96127e16037743f0cd8b528c026e9175e22a84f639eca58ff" + + f := framework.NewDefaultFramework("image-id-test") + + It("should be set to the manifest digest (from RepoDigests) when available", func() { + podDesc := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "pod-with-repodigest", + }, + Spec: api.PodSpec{ + Containers: []api.Container{{ + Name: "test", + Image: busyBoxImage, + Command: []string{"sh"}, + }}, + RestartPolicy: api.RestartPolicyNever, + }, + } + + pod := f.PodClient().Create(podDesc) + + framework.ExpectNoError(framework.WaitTimeoutForPodNoLongerRunningInNamespace( + f.Client, pod.Name, f.Namespace.Name, "", framework.PodStartTimeout)) + runningPod, err := f.PodClient().Get(pod.Name) + framework.ExpectNoError(err) + + status := runningPod.Status + + if len(status.ContainerStatuses) == 0 { + framework.Failf("Unexpected pod status; %s", spew.Sdump(status)) + return + } + + Expect(status.ContainerStatuses[0].ImageID).To(Equal(dockertools.DockerPullablePrefix + busyBoxImage)) + }) +}) diff --git a/test/e2e_node/image_list.go b/test/e2e_node/image_list.go index a6031703661..b6a8b93bf40 100644 --- a/test/e2e_node/image_list.go +++ b/test/e2e_node/image_list.go @@ -41,6 +41,7 @@ var NodeImageWhiteList = sets.NewString( "google/cadvisor:latest", "gcr.io/google-containers/stress:v1", "gcr.io/google_containers/busybox:1.24", + "gcr.io/google_containers/busybox@sha256:4bdd623e848417d96127e16037743f0cd8b528c026e9175e22a84f639eca58ff", "gcr.io/google_containers/nginx-slim:0.7", "gcr.io/google_containers/serve_hostname:v1.4", "gcr.io/google_containers/netexec:1.7",