diff --git a/pkg/kubelet/api/testing/fake_image_service.go b/pkg/kubelet/api/testing/fake_image_service.go index d5a138f7295..cde7ee77ea2 100644 --- a/pkg/kubelet/api/testing/fake_image_service.go +++ b/pkg/kubelet/api/testing/fake_image_service.go @@ -18,30 +18,104 @@ package testing import ( "fmt" + "sync" - internalApi "k8s.io/kubernetes/pkg/kubelet/api" runtimeApi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" + "k8s.io/kubernetes/pkg/kubelet/util/sliceutils" ) -type fakeImageService struct { +var ( + fakeImageSize uint64 = 1 +) + +type FakeImageService struct { + sync.Mutex + + Called []string + Images map[string]*runtimeApi.Image } -func NewFakeImageService() internalApi.ImageManagerService { - return &fakeImageService{} +func (r *FakeImageService) SetFakeImages(images []string) { + r.Lock() + defer r.Unlock() + + r.Images = make(map[string]*runtimeApi.Image) + for _, image := range images { + r.Images[image] = makeFakeImage(image) + } } -func (r *fakeImageService) ListImages(filter *runtimeApi.ImageFilter) ([]*runtimeApi.Image, error) { - return nil, fmt.Errorf("not implemented") +func NewFakeImageService() *FakeImageService { + return &FakeImageService{ + Called: make([]string, 0), + Images: make(map[string]*runtimeApi.Image), + } } -func (r *fakeImageService) ImageStatus(image *runtimeApi.ImageSpec) (*runtimeApi.Image, error) { - return nil, fmt.Errorf("not implemented") +func makeFakeImage(image string) *runtimeApi.Image { + return &runtimeApi.Image{ + Id: &image, + Size_: &fakeImageSize, + RepoTags: []string{image}, + } } -func (r *fakeImageService) PullImage(image *runtimeApi.ImageSpec, auth *runtimeApi.AuthConfig) error { - return fmt.Errorf("not implemented") +func (r *FakeImageService) ListImages(filter *runtimeApi.ImageFilter) ([]*runtimeApi.Image, error) { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "ListImages") + + images := make([]*runtimeApi.Image, 0) + for _, img := range r.Images { + if filter != nil && filter.Image != nil { + if !sliceutils.StringInSlice(filter.Image.GetImage(), img.RepoTags) { + continue + } + } + + images = append(images, img) + } + return images, nil } -func (r *fakeImageService) RemoveImage(image *runtimeApi.ImageSpec) error { - return fmt.Errorf("not implemented") +func (r *FakeImageService) ImageStatus(image *runtimeApi.ImageSpec) (*runtimeApi.Image, error) { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "ImageStatus") + + if img, ok := r.Images[image.GetImage()]; ok { + return img, nil + } + + return nil, fmt.Errorf("image %q not found", image.GetImage()) +} + +func (r *FakeImageService) PullImage(image *runtimeApi.ImageSpec, auth *runtimeApi.AuthConfig) error { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "PullImage") + + // ImageID should be randomized for real container runtime, but here just use + // image's name for easily making fake images. + imageID := image.GetImage() + if _, ok := r.Images[imageID]; !ok { + r.Images[imageID] = makeFakeImage(image.GetImage()) + } + + return nil +} + +func (r *FakeImageService) RemoveImage(image *runtimeApi.ImageSpec) error { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "RemoveImage") + + // Remove the image + delete(r.Images, image.GetImage()) + + return nil } diff --git a/pkg/kubelet/api/testing/fake_runtime_service.go b/pkg/kubelet/api/testing/fake_runtime_service.go index 7d76468e6d5..a8074a6aaca 100644 --- a/pkg/kubelet/api/testing/fake_runtime_service.go +++ b/pkg/kubelet/api/testing/fake_runtime_service.go @@ -19,66 +19,354 @@ package testing import ( "fmt" "io" + "sync" + "time" - internalApi "k8s.io/kubernetes/pkg/kubelet/api" runtimeApi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" ) -type fakeRuntimeService struct { +var ( + version = "0.1.0" + + fakeRuntimeName = "fakeRuntime" + fakePodSandboxIP = "192.168.192.168" +) + +type FakePodSandbox struct { + // PodSandbox contains minimal information about a sandbox. + runtimeApi.PodSandbox + + // Annotations is an unstructured key value map that may be set by external + // tools to store and retrieve arbitrary metadata. + Annotations map[string]string } -func NewFakeRuntimeService() internalApi.RuntimeService { - return &fakeRuntimeService{} +type FakeContainer struct { + // ContainerStatus contains the runtime information for a container. + runtimeApi.ContainerStatus + + // the sandbox id of this container + SandboxID string } -func (r *fakeRuntimeService) Version(apiVersion string) (*runtimeApi.VersionResponse, error) { - return nil, fmt.Errorf("not implemented") +type FakeRuntimeService struct { + sync.Mutex + + Called []string + + Containers map[string]*FakeContainer + Sandboxes map[string]*FakePodSandbox } -func (r *fakeRuntimeService) CreatePodSandbox(config *runtimeApi.PodSandboxConfig) (string, error) { - return "", fmt.Errorf("not implemented") +func (r *FakeRuntimeService) SetFakeSandboxes(sandboxes []*FakePodSandbox) { + r.Lock() + defer r.Unlock() + + r.Sandboxes = make(map[string]*FakePodSandbox) + for _, sandbox := range sandboxes { + sandboxID := sandbox.GetId() + r.Sandboxes[sandboxID] = sandbox + } } -func (r *fakeRuntimeService) StopPodSandbox(podSandboxID string) error { - return fmt.Errorf("not implemented") +func (r *FakeRuntimeService) SetFakeContainers(containers []*FakeContainer) { + r.Lock() + defer r.Unlock() + + r.Containers = make(map[string]*FakeContainer) + for _, c := range containers { + containerID := c.GetName() + r.Containers[containerID] = c + } + } -func (r *fakeRuntimeService) DeletePodSandbox(podSandboxID string) error { - return fmt.Errorf("not implemented") +func NewFakeRuntimeService() *FakeRuntimeService { + return &FakeRuntimeService{ + Called: make([]string, 0), + Containers: make(map[string]*FakeContainer), + Sandboxes: make(map[string]*FakePodSandbox), + } } -func (r *fakeRuntimeService) PodSandboxStatus(podSandboxID string) (*runtimeApi.PodSandboxStatus, error) { - return nil, fmt.Errorf("not implemented") +func (r *FakeRuntimeService) Version(apiVersion string) (*runtimeApi.VersionResponse, error) { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "Version") + + return &runtimeApi.VersionResponse{ + Version: &version, + RuntimeName: &fakeRuntimeName, + RuntimeVersion: &version, + RuntimeApiVersion: &version, + }, nil } -func (r *fakeRuntimeService) ListPodSandbox(filter *runtimeApi.PodSandboxFilter) ([]*runtimeApi.PodSandbox, error) { - return nil, fmt.Errorf("not implemented") +func (r *FakeRuntimeService) CreatePodSandbox(config *runtimeApi.PodSandboxConfig) (string, error) { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "CreatePodSandbox") + + // PodSandboxID should be randomized for real container runtime, but here just use + // sandbox's name for easily making fake sandboxes. + podSandboxID := config.GetName() + createdAt := time.Now().Unix() + readyState := runtimeApi.PodSandBoxState_READY + r.Sandboxes[podSandboxID] = &FakePodSandbox{ + PodSandbox: runtimeApi.PodSandbox{ + Id: &podSandboxID, + Name: config.Name, + State: &readyState, + CreatedAt: &createdAt, + Labels: config.Labels, + }, + Annotations: config.Annotations, + } + + return podSandboxID, nil } -func (r *fakeRuntimeService) CreateContainer(podSandboxID string, config *runtimeApi.ContainerConfig, sandboxConfig *runtimeApi.PodSandboxConfig) (string, error) { - return "", fmt.Errorf("not implemented") +func (r *FakeRuntimeService) StopPodSandbox(podSandboxID string) error { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "StopPodSandbox") + + notReadyState := runtimeApi.PodSandBoxState_NOTREADY + if s, ok := r.Sandboxes[podSandboxID]; ok { + s.State = ¬ReadyState + } else { + return fmt.Errorf("pod sandbox %s not found", podSandboxID) + } + + return nil } -func (r *fakeRuntimeService) StartContainer(rawContainerID string) error { - return fmt.Errorf("not implemented") +func (r *FakeRuntimeService) DeletePodSandbox(podSandboxID string) error { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "DeletePodSandbox") + + // Remove the pod sandbox + delete(r.Sandboxes, podSandboxID) + + return nil } -func (r *fakeRuntimeService) StopContainer(rawContainerID string, timeout int64) error { - return fmt.Errorf("not implemented") +func (r *FakeRuntimeService) PodSandboxStatus(podSandboxID string) (*runtimeApi.PodSandboxStatus, error) { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "PodSandboxStatus") + + s, ok := r.Sandboxes[podSandboxID] + if !ok { + return nil, fmt.Errorf("pod sandbox %q not found", podSandboxID) + } + + return &runtimeApi.PodSandboxStatus{ + Id: &podSandboxID, + Name: s.Name, + CreatedAt: s.CreatedAt, + State: s.State, + Network: &runtimeApi.PodSandboxNetworkStatus{ + Ip: &fakePodSandboxIP, + }, + Labels: s.Labels, + Annotations: s.Annotations, + }, nil } -func (r *fakeRuntimeService) RemoveContainer(rawContainerID string) error { - return fmt.Errorf("not implemented") +func (r *FakeRuntimeService) ListPodSandbox(filter *runtimeApi.PodSandboxFilter) ([]*runtimeApi.PodSandbox, error) { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "ListPodSandbox") + + result := make([]*runtimeApi.PodSandbox, 0) + for id, s := range r.Sandboxes { + if filter != nil { + if filter.Id != nil && filter.GetId() != id { + continue + } + if filter.Name != nil && filter.GetName() != s.GetName() { + continue + } + if filter.State != nil && filter.GetState() != s.GetState() { + continue + } + if filter.LabelSelector != nil && !filterInLabels(filter.LabelSelector, s.GetLabels()) { + continue + } + } + + result = append(result, &runtimeApi.PodSandbox{ + Id: s.Id, + Name: s.Name, + State: s.State, + CreatedAt: s.CreatedAt, + Labels: s.Labels, + }) + } + + return result, nil } -func (r *fakeRuntimeService) ListContainers(filter *runtimeApi.ContainerFilter) ([]*runtimeApi.Container, error) { - return nil, fmt.Errorf("not implemented") +func (r *FakeRuntimeService) CreateContainer(podSandboxID string, config *runtimeApi.ContainerConfig, sandboxConfig *runtimeApi.PodSandboxConfig) (string, error) { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "CreateContainer") + + // ContainerID should be randomized for real container runtime, but here just use + // container's name for easily making fake containers. + containerID := config.GetName() + createdAt := time.Now().Unix() + createdState := runtimeApi.ContainerState_CREATED + imageRef := config.Image.GetImage() + r.Containers[containerID] = &FakeContainer{ + ContainerStatus: runtimeApi.ContainerStatus{ + Id: &containerID, + Name: config.Name, + Image: config.Image, + ImageRef: &imageRef, + CreatedAt: &createdAt, + State: &createdState, + Labels: config.Labels, + Annotations: config.Annotations, + }, + SandboxID: podSandboxID, + } + + return containerID, nil } -func (r *fakeRuntimeService) ContainerStatus(rawContainerID string) (*runtimeApi.ContainerStatus, error) { - return nil, fmt.Errorf("not implemented") +func (r *FakeRuntimeService) StartContainer(rawContainerID string) error { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "StartContainer") + + c, ok := r.Containers[rawContainerID] + if !ok { + return fmt.Errorf("container %s not found", rawContainerID) + } + + // Set container to running. + startedAt := time.Now().Unix() + runningState := runtimeApi.ContainerState_RUNNING + c.State = &runningState + c.StartedAt = &startedAt + + return nil } -func (r *fakeRuntimeService) Exec(rawContainerID string, cmd []string, tty bool, stdin io.Reader, stdout, stderr io.WriteCloser) error { - return fmt.Errorf("not implemented") +func (r *FakeRuntimeService) StopContainer(rawContainerID string, timeout int64) error { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "StopContainer") + + c, ok := r.Containers[rawContainerID] + if !ok { + return fmt.Errorf("container %q not found", rawContainerID) + } + + // Set container to exited state. + finishedAt := time.Now().Unix() + exitedState := runtimeApi.ContainerState_EXITED + c.State = &exitedState + c.FinishedAt = &finishedAt + + return nil +} + +func (r *FakeRuntimeService) RemoveContainer(rawContainerID string) error { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "RemoveContainer") + + // Remove the container + delete(r.Containers, rawContainerID) + + return nil +} + +func (r *FakeRuntimeService) ListContainers(filter *runtimeApi.ContainerFilter) ([]*runtimeApi.Container, error) { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "ListContainers") + + result := make([]*runtimeApi.Container, 0) + for _, s := range r.Containers { + if filter != nil { + if filter.Id != nil && filter.GetId() != s.GetId() { + continue + } + if filter.Name != nil && filter.GetName() != s.GetName() { + continue + } + if filter.PodSandboxId != nil && filter.GetPodSandboxId() != s.SandboxID { + continue + } + if filter.State != nil && filter.GetState() != s.GetState() { + continue + } + if filter.LabelSelector != nil && !filterInLabels(filter.LabelSelector, s.GetLabels()) { + continue + } + } + + result = append(result, &runtimeApi.Container{ + Id: s.Id, + Name: s.Name, + State: s.State, + Image: s.Image, + ImageRef: s.ImageRef, + Labels: s.Labels, + }) + } + + return result, nil +} + +func (r *FakeRuntimeService) ContainerStatus(rawContainerID string) (*runtimeApi.ContainerStatus, error) { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "ContainerStatus") + + c, ok := r.Containers[rawContainerID] + if !ok { + return nil, fmt.Errorf("container %q not found", rawContainerID) + } + + return &runtimeApi.ContainerStatus{ + Id: c.Id, + Name: c.Name, + State: c.State, + CreatedAt: c.CreatedAt, + Image: c.Image, + ImageRef: c.ImageRef, + Labels: c.Labels, + Annotations: c.Annotations, + ExitCode: c.ExitCode, + StartedAt: c.StartedAt, + FinishedAt: c.FinishedAt, + Reason: c.Reason, + Mounts: c.Mounts, + }, nil +} + +func (r *FakeRuntimeService) Exec(rawContainerID string, cmd []string, tty bool, stdin io.Reader, stdout, stderr io.WriteCloser) error { + r.Lock() + defer r.Unlock() + + r.Called = append(r.Called, "Exec") + return nil } diff --git a/pkg/kubelet/api/testing/utils.go b/pkg/kubelet/api/testing/utils.go new file mode 100644 index 00000000000..660ca338160 --- /dev/null +++ b/pkg/kubelet/api/testing/utils.go @@ -0,0 +1,31 @@ +/* +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 testing + +func filterInLabels(filter, labels map[string]string) bool { + for k, v := range filter { + if value, ok := labels[k]; ok { + if value != v { + return false + } + } else { + return false + } + } + + return true +} diff --git a/pkg/kubelet/util/sliceutils/sliceutils.go b/pkg/kubelet/util/sliceutils/sliceutils.go new file mode 100644 index 00000000000..18e89848fee --- /dev/null +++ b/pkg/kubelet/util/sliceutils/sliceutils.go @@ -0,0 +1,27 @@ +/* +Copyright 2015 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 sliceutils + +func StringInSlice(s string, list []string) bool { + for _, v := range list { + if v == s { + return true + } + } + + return false +}