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