From 516848c37d0e5d605123e1d76b637e63214011ab Mon Sep 17 00:00:00 2001 From: Yu-Ju Hong Date: Wed, 1 Mar 2017 17:18:02 -0800 Subject: [PATCH] Various fixes for the fake docker client * Properly return ImageNotFoundError * Support inject "Images" or "ImageInspects" and keep both in sync. * Remove the FakeDockerPuller and let FakeDockerClient subsumes its functinality. This reduces the overhead to maintain both objects. * Various small fixes and refactoring of the testing utils. --- .../dockershim/docker_container_test.go | 9 +- pkg/kubelet/dockershim/docker_image_test.go | 4 +- pkg/kubelet/dockershim/helpers_test.go | 33 ++-- .../dockertools/docker_manager_test.go | 68 +++---- pkg/kubelet/dockertools/fake_docker_client.go | 180 ++++++++++-------- pkg/kubelet/dockertools/fake_manager.go | 2 +- 6 files changed, 166 insertions(+), 130 deletions(-) diff --git a/pkg/kubelet/dockershim/docker_container_test.go b/pkg/kubelet/dockershim/docker_container_test.go index b215f4c613f..3427f76ca6c 100644 --- a/pkg/kubelet/dockershim/docker_container_test.go +++ b/pkg/kubelet/dockershim/docker_container_test.go @@ -23,6 +23,7 @@ import ( "testing" "time" + dockertypes "github.com/docker/engine-api/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -102,14 +103,15 @@ func TestContainerStatus(t *testing.T) { sConfig := makeSandboxConfig("foo", "bar", "1", 0) labels := map[string]string{"abc.xyz": "foo"} annotations := map[string]string{"foo.bar.baz": "abc"} - config := makeContainerConfig(sConfig, "pause", "iamimage", 0, labels, annotations) + imageName := "iamimage" + config := makeContainerConfig(sConfig, "pause", imageName, 0, labels, annotations) var defaultTime time.Time dt := defaultTime.UnixNano() ct, st, ft := dt, dt, dt state := runtimeapi.ContainerState_CONTAINER_CREATED + imageRef := DockerImageIDPrefix + imageName // The following variables are not set in FakeDockerClient. - imageRef := DockerImageIDPrefix + "" exitCode := int32(0) var reason, message string @@ -129,11 +131,14 @@ func TestContainerStatus(t *testing.T) { Annotations: config.Annotations, } + fDocker.InjectImages([]dockertypes.Image{{ID: imageName}}) + // Create the container. fClock.SetTime(time.Now().Add(-1 * time.Hour)) expected.CreatedAt = fClock.Now().UnixNano() const sandboxId = "sandboxid" id, err := ds.CreateContainer(sandboxId, config, sConfig) + assert.NoError(t, err) // Check internal labels c, err := fDocker.InspectContainer(id) diff --git a/pkg/kubelet/dockershim/docker_image_test.go b/pkg/kubelet/dockershim/docker_image_test.go index 7ad366d018c..8096f2ca66b 100644 --- a/pkg/kubelet/dockershim/docker_image_test.go +++ b/pkg/kubelet/dockershim/docker_image_test.go @@ -28,7 +28,7 @@ import ( func TestRemoveImage(t *testing.T) { ds, fakeDocker, _ := newTestDockerService() id := "1111" - fakeDocker.Image = &dockertypes.ImageInspect{ID: id, RepoTags: []string{"foo"}} + fakeDocker.InjectImageInspects([]dockertypes.ImageInspect{{ID: id, RepoTags: []string{"foo"}}}) ds.RemoveImage(&runtimeapi.ImageSpec{Image: id}) fakeDocker.AssertCallDetails(dockertools.NewCalledDetail("inspect_image", nil), dockertools.NewCalledDetail("remove_image", []interface{}{id, dockertypes.ImageRemoveOptions{PruneChildren: true}})) @@ -37,7 +37,7 @@ func TestRemoveImage(t *testing.T) { func TestRemoveImageWithMultipleTags(t *testing.T) { ds, fakeDocker, _ := newTestDockerService() id := "1111" - fakeDocker.Image = &dockertypes.ImageInspect{ID: id, RepoTags: []string{"foo", "bar"}} + fakeDocker.InjectImageInspects([]dockertypes.ImageInspect{{ID: id, RepoTags: []string{"foo", "bar"}}}) ds.RemoveImage(&runtimeapi.ImageSpec{Image: id}) fakeDocker.AssertCallDetails(dockertools.NewCalledDetail("inspect_image", nil), dockertools.NewCalledDetail("remove_image", []interface{}{"foo", dockertypes.ImageRemoveOptions{PruneChildren: true}}), diff --git a/pkg/kubelet/dockershim/helpers_test.go b/pkg/kubelet/dockershim/helpers_test.go index 56cae261e7a..deb2afdc837 100644 --- a/pkg/kubelet/dockershim/helpers_test.go +++ b/pkg/kubelet/dockershim/helpers_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/blang/semver" + dockertypes "github.com/docker/engine-api/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -265,29 +266,37 @@ func TestGetSecurityOptSeparator(t *testing.T) { } func TestEnsureSandboxImageExists(t *testing.T) { + sandboxImage := "gcr.io/test/image" for desc, test := range map[string]struct { - inject error - calls []string - err bool + injectImage bool + injectErr error + calls []string + err bool }{ "should not pull image when it already exists": { - inject: nil, - calls: []string{"inspect_image"}, + injectImage: true, + injectErr: nil, + calls: []string{"inspect_image"}, }, "should pull image when it doesn't exist": { - inject: dockertools.ImageNotFoundError{ID: "image_id"}, - calls: []string{"inspect_image", "pull"}, + injectImage: false, + injectErr: dockertools.ImageNotFoundError{ID: "image_id"}, + calls: []string{"inspect_image", "pull"}, }, "should return error when inspect image fails": { - inject: fmt.Errorf("arbitrary error"), - calls: []string{"inspect_image"}, - err: true, + injectImage: false, + injectErr: fmt.Errorf("arbitrary error"), + calls: []string{"inspect_image"}, + err: true, }, } { t.Logf("TestCase: %q", desc) _, fakeDocker, _ := newTestDockerService() - fakeDocker.InjectError("inspect_image", test.inject) - err := ensureSandboxImageExists(fakeDocker, "gcr.io/test/image") + if test.injectImage { + fakeDocker.InjectImages([]dockertypes.Image{{ID: sandboxImage}}) + } + fakeDocker.InjectError("inspect_image", test.injectErr) + err := ensureSandboxImageExists(fakeDocker, sandboxImage) assert.NoError(t, fakeDocker.AssertCalls(test.calls)) assert.Equal(t, test.err, err != nil) } diff --git a/pkg/kubelet/dockertools/docker_manager_test.go b/pkg/kubelet/dockertools/docker_manager_test.go index 06c8aef17ee..d1b98a0b381 100644 --- a/pkg/kubelet/dockertools/docker_manager_test.go +++ b/pkg/kubelet/dockertools/docker_manager_test.go @@ -398,7 +398,7 @@ func TestListImages(t *testing.T) { func TestDeleteImage(t *testing.T) { manager, fakeDocker := newTestDockerManager() - fakeDocker.Image = &dockertypes.ImageInspect{ID: "1111", RepoTags: []string{"foo"}} + fakeDocker.InjectImages([]dockertypes.Image{{ID: "1111", RepoTags: []string{"foo"}}}) manager.RemoveImage(kubecontainer.ImageSpec{Image: "1111"}) fakeDocker.AssertCallDetails(NewCalledDetail("inspect_image", nil), NewCalledDetail("remove_image", []interface{}{"1111", dockertypes.ImageRemoveOptions{PruneChildren: true}})) @@ -406,7 +406,7 @@ func TestDeleteImage(t *testing.T) { func TestDeleteImageWithMultipleTags(t *testing.T) { manager, fakeDocker := newTestDockerManager() - fakeDocker.Image = &dockertypes.ImageInspect{ID: "1111", RepoTags: []string{"foo", "bar"}} + fakeDocker.InjectImages([]dockertypes.Image{{ID: "1111", RepoTags: []string{"foo", "bar"}}}) manager.RemoveImage(kubecontainer.ImageSpec{Image: "1111"}) fakeDocker.AssertCallDetails(NewCalledDetail("inspect_image", nil), NewCalledDetail("remove_image", []interface{}{"foo", dockertypes.ImageRemoveOptions{PruneChildren: true}}), @@ -609,9 +609,6 @@ func TestSyncPodCreateNetAndContainer(t *testing.T) { func TestSyncPodCreatesNetAndContainerPullsImage(t *testing.T) { dm, fakeDocker := newTestDockerManagerWithRealImageManager() dm.podInfraContainerImage = "foo/infra_image:v1" - puller := dm.dockerPuller.(*FakeDockerPuller) - puller.HasImages = []string{} - dm.podInfraContainerImage = "foo/infra_image:v1" pod := makePod("foo", &v1.PodSpec{ Containers: []v1.Container{ {Name: "bar", Image: "foo/something:v0", ImagePullPolicy: "IfNotPresent"}, @@ -622,17 +619,12 @@ func TestSyncPodCreatesNetAndContainerPullsImage(t *testing.T) { verifyCalls(t, fakeDocker, []string{ // Create pod infra container. - "inspect_image", "create", "start", "inspect_container", "inspect_container", + "inspect_image", "pull", "inspect_image", "create", "start", "inspect_container", "inspect_container", // Create container. - "inspect_image", "create", "start", "inspect_container", + "inspect_image", "pull", "inspect_image", "create", "start", "inspect_container", }) - fakeDocker.Lock() - if !reflect.DeepEqual(puller.ImagesPulled, []string{"foo/infra_image:v1", "foo/something:v0"}) { - t.Errorf("unexpected pulled containers: %v", puller.ImagesPulled) - } - fakeDocker.Unlock() - + assert.NoError(t, fakeDocker.AssertImagesPulled([]string{"foo/infra_image:v1", "foo/something:v0"})) assert.NoError(t, fakeDocker.AssertCreatedByNameWithOrder([]string{"POD", "bar"})) } @@ -1277,22 +1269,25 @@ func TestVerifyNonRoot(t *testing.T) { // success cases "non-root runAsUser": { container: &v1.Container{ + Image: "foobar", SecurityContext: &v1.SecurityContext{ RunAsUser: &nonRootUid, }, }, }, "numeric non-root image user": { - container: &v1.Container{}, + container: &v1.Container{Image: "foobar"}, inspectImage: &dockertypes.ImageInspect{ + ID: "foobar", Config: &dockercontainer.Config{ User: "1", }, }, }, "numeric non-root image user with gid": { - container: &v1.Container{}, + container: &v1.Container{Image: "foobar"}, inspectImage: &dockertypes.ImageInspect{ + ID: "foobar", Config: &dockercontainer.Config{ User: "1:2", }, @@ -1302,6 +1297,7 @@ func TestVerifyNonRoot(t *testing.T) { // failure cases "root runAsUser": { container: &v1.Container{ + Image: "foobar", SecurityContext: &v1.SecurityContext{ RunAsUser: &rootUid, }, @@ -1309,8 +1305,9 @@ func TestVerifyNonRoot(t *testing.T) { expectedError: "container's runAsUser breaks non-root policy", }, "non-numeric image user": { - container: &v1.Container{}, + container: &v1.Container{Image: "foobar"}, inspectImage: &dockertypes.ImageInspect{ + ID: "foobar", Config: &dockercontainer.Config{ User: "foo", }, @@ -1318,8 +1315,9 @@ func TestVerifyNonRoot(t *testing.T) { expectedError: "non-numeric user", }, "numeric root image user": { - container: &v1.Container{}, + container: &v1.Container{Image: "foobar"}, inspectImage: &dockertypes.ImageInspect{ + ID: "foobar", Config: &dockercontainer.Config{ User: "0", }, @@ -1327,8 +1325,9 @@ func TestVerifyNonRoot(t *testing.T) { expectedError: "container has no runAsUser and image will run as root", }, "numeric root image user with gid": { - container: &v1.Container{}, + container: &v1.Container{Image: "foobar"}, inspectImage: &dockertypes.ImageInspect{ + ID: "foobar", Config: &dockercontainer.Config{ User: "0:1", }, @@ -1336,19 +1335,22 @@ func TestVerifyNonRoot(t *testing.T) { expectedError: "container has no runAsUser and image will run as root", }, "nil image in inspect": { - container: &v1.Container{}, + container: &v1.Container{Image: "foobar"}, inspectImage: nil, - expectedError: "unable to inspect image", + expectedError: ImageNotFoundError{"foobar"}.Error(), }, "nil config in image inspect": { - container: &v1.Container{}, - inspectImage: &dockertypes.ImageInspect{}, + container: &v1.Container{Image: "foobar"}, + inspectImage: &dockertypes.ImageInspect{ID: "foobar"}, expectedError: "unable to inspect image", }, } for k, v := range tests { - fakeDocker.Image = v.inspectImage + fakeDocker.ResetImages() + if v.inspectImage != nil { + fakeDocker.InjectImageInspects([]dockertypes.ImageInspect{*v.inspectImage}) + } err := dm.verifyNonRoot(v.container) if v.expectedError == "" && err != nil { t.Errorf("case[%q]: unexpected error: %v", k, err) @@ -1435,8 +1437,8 @@ func TestGetIPCMode(t *testing.T) { func TestSyncPodWithPullPolicy(t *testing.T) { dm, fakeDocker := newTestDockerManagerWithRealImageManager() - puller := dm.dockerPuller.(*FakeDockerPuller) - puller.HasImages = []string{"foo/existing_one:v1", "foo/want:latest"} + fakeDocker.InjectImages([]dockertypes.Image{{ID: "foo/existing_one:v1"}, {ID: "foo/want:latest"}}) + dm.podInfraContainerImage = "foo/infra_image:v1" pod := makePod("foo", &v1.PodSpec{ @@ -1465,13 +1467,11 @@ func TestSyncPodWithPullPolicy(t *testing.T) { result := runSyncPod(t, dm, fakeDocker, pod, nil, true) verifySyncResults(t, expectedResults, result) + assert.NoError(t, fakeDocker.AssertImagesPulled([]string{"foo/infra_image:v1", "foo/pull_always_image:v1", "foo/pull_if_not_present_image:v1"})) + fakeDocker.Lock() defer fakeDocker.Unlock() - pulledImageSorted := puller.ImagesPulled[:] - sort.Strings(pulledImageSorted) - assert.Equal(t, []string{"foo/infra_image:v1", "foo/pull_always_image:v1", "foo/pull_if_not_present_image:v1"}, pulledImageSorted) - if len(fakeDocker.Created) != 5 { t.Errorf("unexpected containers created %v", fakeDocker.Created) } @@ -1485,33 +1485,28 @@ func TestSyncPodWithFailure(t *testing.T) { tests := map[string]struct { container v1.Container dockerError map[string]error - pullerError []error expected []*kubecontainer.SyncResult }{ "PullImageFailure": { v1.Container{Name: "bar", Image: "foo/real_image:v1", ImagePullPolicy: v1.PullAlways}, - map[string]error{}, - []error{fmt.Errorf("can't pull image")}, + map[string]error{"pull": fmt.Errorf("can't pull image")}, []*kubecontainer.SyncResult{{kubecontainer.StartContainer, "bar", images.ErrImagePull, "can't pull image"}}, }, "CreateContainerFailure": { v1.Container{Name: "bar", Image: "foo/already_present:v2"}, map[string]error{"create": fmt.Errorf("can't create container")}, - []error{}, []*kubecontainer.SyncResult{{kubecontainer.StartContainer, "bar", kubecontainer.ErrRunContainer, "can't create container"}}, }, "StartContainerFailure": { v1.Container{Name: "bar", Image: "foo/already_present:v2"}, map[string]error{"start": fmt.Errorf("can't start container")}, - []error{}, []*kubecontainer.SyncResult{{kubecontainer.StartContainer, "bar", kubecontainer.ErrRunContainer, "can't start container"}}, }, } for _, test := range tests { dm, fakeDocker := newTestDockerManagerWithRealImageManager() - puller := dm.dockerPuller.(*FakeDockerPuller) - puller.HasImages = []string{test.container.Image} + fakeDocker.InjectImages([]dockertypes.Image{{ID: test.container.Image}}) // Pretend that the pod infra container has already been created, so that // we can run the user containers. fakeDocker.SetFakeRunningContainers([]*FakeContainer{{ @@ -1519,7 +1514,6 @@ func TestSyncPodWithFailure(t *testing.T) { Name: "/k8s_POD." + strconv.FormatUint(generatePodInfraContainerHash(pod), 16) + "_foo_new_12345678_0", }}) fakeDocker.InjectErrors(test.dockerError) - puller.ErrorsToInject = test.pullerError pod.Spec.Containers = []v1.Container{test.container} result := runSyncPod(t, dm, fakeDocker, pod, nil, true) verifySyncResults(t, test.expected, result) diff --git a/pkg/kubelet/dockertools/fake_docker_client.go b/pkg/kubelet/dockertools/fake_docker_client.go index e780cfee1f0..b3ac10cb21c 100644 --- a/pkg/kubelet/dockertools/fake_docker_client.go +++ b/pkg/kubelet/dockertools/fake_docker_client.go @@ -33,7 +33,6 @@ import ( dockercontainer "github.com/docker/engine-api/types/container" "k8s.io/client-go/util/clock" - "k8s.io/kubernetes/pkg/api/v1" ) type calledDetail struct { @@ -53,7 +52,7 @@ type FakeDockerClient struct { RunningContainerList []dockertypes.Container ExitedContainerList []dockertypes.Container ContainerMap map[string]*dockertypes.ContainerJSON - Image *dockertypes.ImageInspect + ImageInspects map[string]*dockertypes.ImageInspect Images []dockertypes.Image Errors map[string]error called []calledDetail @@ -61,10 +60,13 @@ type FakeDockerClient struct { EnableTrace bool // Created, Started, Stopped and Removed all contain container docker ID - Created []string - Started []string - Stopped []string - Removed []string + Created []string + Started []string + Stopped []string + Removed []string + // Images pulled by ref (name or ID). + ImagesPulled []string + VersionInfo dockertypes.Version Information dockertypes.Info ExecInspect *dockertypes.ContainerExecInspect @@ -87,10 +89,9 @@ func NewFakeDockerClient() *FakeDockerClient { Errors: make(map[string]error), ContainerMap: make(map[string]*dockertypes.ContainerJSON), Clock: clock.RealClock{}, - // default this to an empty result, so that we never have a nil non-error response from InspectImage - Image: &dockertypes.ImageInspect{}, // default this to true, so that we trace calls, image pulls and container lifecycle - EnableTrace: true, + EnableTrace: true, + ImageInspects: make(map[string]*dockertypes.ImageInspect), } } @@ -207,8 +208,9 @@ func convertFakeContainer(f *FakeContainer) *dockertypes.ContainerJSON { } return &dockertypes.ContainerJSON{ ContainerJSONBase: &dockertypes.ContainerJSONBase{ - ID: f.ID, - Name: f.Name, + ID: f.ID, + Name: f.Name, + Image: f.Config.Image, State: &dockertypes.ContainerState{ Running: f.Running, ExitCode: f.ExitCode, @@ -313,28 +315,17 @@ func (f *FakeDockerClient) AssertCreatedByName(created []string) error { if err != nil { return err } - sort.StringSlice(created).Sort() - sort.StringSlice(actualCreated).Sort() - if !reflect.DeepEqual(created, actualCreated) { - return fmt.Errorf("expected %#v, got %#v", created, actualCreated) - } - return nil + return sortedStringSlicesEqual(created, actualCreated) } func (f *FakeDockerClient) AssertStoppedByName(stopped []string) error { f.Lock() defer f.Unlock() - actualStopped, err := f.idsToNames(f.Stopped) if err != nil { return err } - sort.StringSlice(stopped).Sort() - sort.StringSlice(actualStopped).Sort() - if !reflect.DeepEqual(stopped, actualStopped) { - return fmt.Errorf("expected %#v, got %#v", stopped, actualStopped) - } - return nil + return sortedStringSlicesEqual(stopped, actualStopped) } func (f *FakeDockerClient) AssertStopped(stopped []string) error { @@ -342,10 +333,22 @@ func (f *FakeDockerClient) AssertStopped(stopped []string) error { defer f.Unlock() // Copy stopped to avoid modifying it. actualStopped := append([]string{}, f.Stopped...) - sort.StringSlice(stopped).Sort() - sort.StringSlice(actualStopped).Sort() - if !reflect.DeepEqual(stopped, actualStopped) { - return fmt.Errorf("expected %#v, got %#v", stopped, actualStopped) + return sortedStringSlicesEqual(stopped, actualStopped) +} + +func (f *FakeDockerClient) AssertImagesPulled(pulled []string) error { + f.Lock() + defer f.Unlock() + // Copy pulled to avoid modifying it. + actualPulled := append([]string{}, f.ImagesPulled...) + return sortedStringSlicesEqual(pulled, actualPulled) +} + +func sortedStringSlicesEqual(expected, actual []string) error { + sort.StringSlice(expected).Sort() + sort.StringSlice(actual).Sort() + if !reflect.DeepEqual(expected, actual) { + return fmt.Errorf("expected %#v, got %#v", expected, actual) } return nil } @@ -453,8 +456,13 @@ func (f *FakeDockerClient) InspectImageByRef(name string) (*dockertypes.ImageIns f.Lock() defer f.Unlock() f.appendCalled(calledDetail{name: "inspect_image"}) - err := f.popError("inspect_image") - return f.Image, err + if err := f.popError("inspect_image"); err != nil { + return nil, err + } + if result, ok := f.ImageInspects[name]; ok { + return result, nil + } + return nil, ImageNotFoundError{name} } // InspectImageByID is a test-spy implementation of DockerInterface.InspectImageByID. @@ -463,8 +471,13 @@ func (f *FakeDockerClient) InspectImageByID(name string) (*dockertypes.ImageInsp f.Lock() defer f.Unlock() f.appendCalled(calledDetail{name: "inspect_image"}) - err := f.popError("inspect_image") - return f.Image, err + if err := f.popError("inspect_image"); err != nil { + return nil, err + } + if result, ok := f.ImageInspects[name]; ok { + return result, nil + } + return nil, ImageNotFoundError{name} } // Sleeps random amount of time with the normal distribution with given mean and stddev @@ -508,7 +521,9 @@ func (f *FakeDockerClient) CreateContainer(c dockertypes.ContainerCreateConfig) }, f.RunningContainerList...) f.ContainerMap[id] = convertFakeContainer(&FakeContainer{ ID: id, Name: name, Config: c.Config, HostConfig: c.HostConfig, CreatedAt: f.Clock.Now()}) + f.normalSleep(100, 25, 25) + return &dockertypes.ContainerCreateResponse{ID: id}, nil } @@ -615,15 +630,11 @@ func (f *FakeDockerClient) PullImage(image string, auth dockertypes.AuthConfig, err := f.popError("pull") if err == nil { authJson, _ := json.Marshal(auth) - f.Image = &dockertypes.ImageInspect{ - ID: image, - RepoTags: []string{image}, - // Image size is required to be non-zero for CRI integration. - VirtualSize: fakeImageSize, - Size: fakeImageSize, - Config: &dockercontainer.Config{}, - } + inspect := createImageInspectFromRef(image) + f.ImageInspects[image] = inspect f.appendPulled(fmt.Sprintf("%s using %s", image, string(authJson))) + f.Images = append(f.Images, *createImageFromImageInspect(*inspect)) + f.ImagesPulled = append(f.ImagesPulled, image) } return err } @@ -666,12 +677,16 @@ func (f *FakeDockerClient) InspectExec(id string) (*dockertypes.ContainerExecIns } func (f *FakeDockerClient) ListImages(opts dockertypes.ImageListOptions) ([]dockertypes.Image, error) { + f.Lock() + defer f.Unlock() f.appendCalled(calledDetail{name: "list_images"}) err := f.popError("list_images") return f.Images, err } func (f *FakeDockerClient) RemoveImage(image string, opts dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDelete, error) { + f.Lock() + defer f.Unlock() f.appendCalled(calledDetail{name: "remove_image", arguments: []interface{}{image, opts}}) err := f.popError("remove_image") if err == nil { @@ -689,6 +704,25 @@ func (f *FakeDockerClient) InjectImages(images []dockertypes.Image) { f.Lock() defer f.Unlock() f.Images = append(f.Images, images...) + for _, i := range images { + f.ImageInspects[i.ID] = createImageInspectFromImage(i) + } +} + +func (f *FakeDockerClient) ResetImages() { + f.Lock() + defer f.Unlock() + f.Images = []dockertypes.Image{} + f.ImageInspects = make(map[string]*dockertypes.ImageInspect) +} + +func (f *FakeDockerClient) InjectImageInspects(inspects []dockertypes.ImageInspect) { + f.Lock() + defer f.Unlock() + for _, i := range inspects { + f.Images = append(f.Images, *createImageFromImageInspect(i)) + f.ImageInspects[i.ID] = &i + } } func (f *FakeDockerClient) updateContainerStatus(id, status string) { @@ -713,44 +747,43 @@ func (f *FakeDockerClient) ResizeContainerTTY(id string, height, width int) erro return nil } -// FakeDockerPuller is a stub implementation of DockerPuller. -type FakeDockerPuller struct { - sync.Mutex - - HasImages []string - ImagesPulled []string - - // Every pull will return the first error here, and then reslice - // to remove it. Will give nil errors if this slice is empty. - ErrorsToInject []error +func createImageInspectFromRef(ref string) *dockertypes.ImageInspect { + return &dockertypes.ImageInspect{ + ID: ref, + RepoTags: []string{ref}, + // Image size is required to be non-zero for CRI integration. + VirtualSize: fakeImageSize, + Size: fakeImageSize, + Config: &dockercontainer.Config{}, + } } -// Pull records the image pull attempt, and optionally injects an error. -func (f *FakeDockerPuller) Pull(image string, secrets []v1.Secret) (err error) { - f.Lock() - defer f.Unlock() - f.ImagesPulled = append(f.ImagesPulled, image) - - if len(f.ErrorsToInject) > 0 { - err = f.ErrorsToInject[0] - f.ErrorsToInject = f.ErrorsToInject[1:] +func createImageInspectFromImage(image dockertypes.Image) *dockertypes.ImageInspect { + return &dockertypes.ImageInspect{ + ID: image.ID, + RepoTags: image.RepoTags, + // Image size is required to be non-zero for CRI integration. + VirtualSize: fakeImageSize, + Size: fakeImageSize, + Config: &dockercontainer.Config{}, } - return err } -func (f *FakeDockerPuller) GetImageRef(name string) (string, error) { - f.Lock() - defer f.Unlock() - if f.HasImages == nil { - return name, nil +func createImageFromImageInspect(inspect dockertypes.ImageInspect) *dockertypes.Image { + return &dockertypes.Image{ + ID: inspect.ID, + RepoTags: inspect.RepoTags, + // Image size is required to be non-zero for CRI integration. + VirtualSize: fakeImageSize, + Size: fakeImageSize, } - for _, s := range f.HasImages { - if s == name { - return s, nil - } - } - return "", nil } + +// dockerTimestampToString converts the timestamp to string +func dockerTimestampToString(t time.Time) string { + return t.Format(time.RFC3339Nano) +} + func (f *FakeDockerClient) ImageHistory(id string) ([]dockertypes.ImageHistory, error) { f.Lock() defer f.Unlock() @@ -764,8 +797,3 @@ func (f *FakeDockerClient) InjectImageHistory(data map[string][]dockertypes.Imag defer f.Unlock() f.ImageHistoryMap = data } - -// dockerTimestampToString converts the timestamp to string -func dockerTimestampToString(t time.Time) string { - return t.Format(time.RFC3339Nano) -} diff --git a/pkg/kubelet/dockertools/fake_manager.go b/pkg/kubelet/dockertools/fake_manager.go index a1b0b9a1fa9..e3b017af826 100644 --- a/pkg/kubelet/dockertools/fake_manager.go +++ b/pkg/kubelet/dockertools/fake_manager.go @@ -52,7 +52,7 @@ func NewFakeDockerManager( dm := NewDockerManager(client, recorder, livenessManager, containerRefManager, fakePodGetter, machineInfo, podInfraContainerImage, qps, burst, containerLogsDir, osInterface, networkPlugin, runtimeHelper, httpClient, &NativeExecHandler{}, fakeOOMAdjuster, fakeProcFs, false, imageBackOff, false, false, true, "/var/lib/kubelet/seccomp") - dm.dockerPuller = &FakeDockerPuller{} + dm.dockerPuller = newDockerPuller(client) // ttl of version cache is set to 0 so we always call version api directly in tests. dm.versionCache = cache.NewObjectCache(