diff --git a/pkg/kubelet/container/runtime.go b/pkg/kubelet/container/runtime.go index 7c093a3ddf7..f83649f2810 100644 --- a/pkg/kubelet/container/runtime.go +++ b/pkg/kubelet/container/runtime.go @@ -54,18 +54,18 @@ type Runtime interface { KillContainerInPod(api.Container, *api.Pod) error // GetPodStatus retrieves the status of the pod, including the information of // all containers in the pod. - GetPodStatus(*api.Pod) (api.PodStatus, error) + GetPodStatus(*api.Pod) (*api.PodStatus, error) // TODO(vmarmol): Merge RunInContainer and ExecInContainer. // Runs the command in the container of the specified pod using nsinit. // TODO(yifan): Use strong type for containerID. - RunInContainer(containerID string, cmd []string) error + RunInContainer(containerID string, cmd []string) ([]byte, error) // Runs the command in the container of the specified pod using nsenter. // Attaches the processes stdin, stdout, and stderr. Optionally uses a // tty. // TODO(yifan): Use strong type for containerID. ExecInContainer(containerID string, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool) error // Forward the specified port from the specified pod to the stream. - PortForward(pod Pod, port uint16, stream io.ReadWriteCloser) error + PortForward(pod *Pod, port uint16, stream io.ReadWriteCloser) error // PullImage pulls an image from the network to local storage. PullImage(image string) error // IsImagePresent checks whether the container image is already in the local storage. diff --git a/pkg/kubelet/dockertools/convert.go b/pkg/kubelet/dockertools/convert.go new file mode 100644 index 00000000000..eb73290bc95 --- /dev/null +++ b/pkg/kubelet/dockertools/convert.go @@ -0,0 +1,60 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 dockertools + +import ( + "fmt" + + kubecontainer "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/container" + "github.com/GoogleCloudPlatform/kubernetes/pkg/types" + docker "github.com/fsouza/go-dockerclient" +) + +// This file contains helper functions to convert docker API types to runtime +// (kubecontainer) types. + +// Converts docker.APIContainers to kubecontainer.Container. +func toRuntimeContainer(c *docker.APIContainers) (*kubecontainer.Container, error) { + if c == nil { + return nil, fmt.Errorf("unable to convert a nil pointer to a runtime container") + } + + dockerName, hash, err := getDockerContainerNameInfo(c) + if err != nil { + return nil, err + } + return &kubecontainer.Container{ + ID: types.UID(c.ID), + Name: dockerName.ContainerName, + Image: c.Image, + Hash: hash, + Created: c.Created, + }, nil +} + +// Converts docker.APIImages to kubecontainer.Image. +func toRuntimeImage(image *docker.APIImages) (*kubecontainer.Image, error) { + if image == nil { + return nil, fmt.Errorf("unable to convert a nil pointer to a runtime image") + } + + return &kubecontainer.Image{ + ID: image.ID, + Tags: image.RepoTags, + Size: image.Size, + }, nil +} diff --git a/pkg/kubelet/dockertools/convert_test.go b/pkg/kubelet/dockertools/convert_test.go new file mode 100644 index 00000000000..faf84be2255 --- /dev/null +++ b/pkg/kubelet/dockertools/convert_test.go @@ -0,0 +1,71 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 dockertools + +import ( + "reflect" + "testing" + + kubecontainer "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/container" + "github.com/GoogleCloudPlatform/kubernetes/pkg/types" + docker "github.com/fsouza/go-dockerclient" +) + +func TestToRuntimeContainer(t *testing.T) { + original := &docker.APIContainers{ + ID: "ab2cdf", + Image: "bar_image", + Created: 12345, + Names: []string{"/k8s_bar.5678_foo_ns_1234_42"}, + } + expected := &kubecontainer.Container{ + ID: types.UID("ab2cdf"), + Name: "bar", + Image: "bar_image", + Hash: 0x5678, + Created: 12345, + } + + actual, err := toRuntimeContainer(original) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %#v, got %#v", expected, actual) + } +} + +func TestToRuntimeImage(t *testing.T) { + original := &docker.APIImages{ + ID: "aeeea", + RepoTags: []string{"abc", "def"}, + Size: 1234, + } + expected := &kubecontainer.Image{ + ID: "aeeea", + Tags: []string{"abc", "def"}, + Size: 1234, + } + + actual, err := toRuntimeImage(original) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %#v, got %#v", expected, actual) + } +} diff --git a/pkg/kubelet/dockertools/manager.go b/pkg/kubelet/dockertools/manager.go index 351b1693c67..98123b128a6 100644 --- a/pkg/kubelet/dockertools/manager.go +++ b/pkg/kubelet/dockertools/manager.go @@ -53,8 +53,8 @@ const ( maxReasonCacheEntries = 200 ) -// TODO: Eventually DockerManager should implement kubecontainer.Runtime -// interface. +// TODO(yjhong): DockerManager should implement the Runtime interface. + type DockerManager struct { client DockerInterface recorder record.EventRecorder @@ -640,21 +640,6 @@ func getDockerContainerNameInfo(c *docker.APIContainers) (*KubeletContainerName, return dockerName, hash, nil } -// Converts docker.APIContainers to kubecontainer.Container. -func convertDockerToRuntimeContainer(c *docker.APIContainers) (*kubecontainer.Container, error) { - dockerName, hash, err := getDockerContainerNameInfo(c) - if err != nil { - return nil, err - } - return &kubecontainer.Container{ - ID: types.UID(c.ID), - Name: dockerName.ContainerName, - Image: c.Image, - Hash: hash, - Created: c.Created, - }, nil -} - // Get pod UID, name, and namespace by examining the container names. func getPodInfoFromContainer(c *docker.APIContainers) (types.UID, string, string, error) { dockerName, _, err := getDockerContainerNameInfo(c) @@ -678,7 +663,7 @@ func (dm *DockerManager) GetContainers(all bool) ([]*kubecontainer.Container, er // Convert DockerContainers to []*kubecontainer.Container result := make([]*kubecontainer.Container, 0, len(containers)) for _, c := range containers { - converted, err := convertDockerToRuntimeContainer(c) + converted, err := toRuntimeContainer(c) if err != nil { glog.Errorf("Error examining the container: %v", err) continue @@ -699,7 +684,7 @@ func (dm *DockerManager) GetPods(all bool) ([]*kubecontainer.Pod, error) { // Group containers by pod. for _, c := range containers { - converted, err := convertDockerToRuntimeContainer(c) + converted, err := toRuntimeContainer(c) if err != nil { glog.Errorf("Error examining the container: %v", err) continue @@ -730,14 +715,40 @@ func (dm *DockerManager) GetPods(all bool) ([]*kubecontainer.Pod, error) { return result, nil } -func (dm *DockerManager) Pull(image string) error { +// List all images in the local storage. +func (dm *DockerManager) ListImages() ([]kubecontainer.Image, error) { + var images []kubecontainer.Image + + dockerImages, err := dm.client.ListImages(docker.ListImagesOptions{}) + if err != nil { + return images, err + } + + for _, di := range dockerImages { + image, err := toRuntimeImage(&di) + if err != nil { + continue + } + images = append(images, *image) + } + return images, nil +} + +// PullImage pulls an image from network to local storage. +func (dm *DockerManager) PullImage(image string) error { return dm.Puller.Pull(image) } +// IsImagePresent checks whether the container image is already in the local storage. func (dm *DockerManager) IsImagePresent(image string) (bool, error) { return dm.Puller.IsImagePresent(image) } +// Removes the specified image. +func (dm *DockerManager) RemoveImage(image string) error { + return dm.client.RemoveImage(image) +} + // podInfraContainerChanged returns true if the pod infra container has changed. func (dm *DockerManager) podInfraContainerChanged(pod *api.Pod, podInfraContainer *kubecontainer.Container) (bool, error) { networkMode := "" @@ -1128,7 +1139,7 @@ func (dm *DockerManager) CreatePodInfraContainer(pod *api.Pod, generator kubecon return "", err } if !ok { - if err := dm.Pull(container.Image); err != nil { + if err := dm.PullImage(container.Image); err != nil { if ref != nil { dm.recorder.Eventf(ref, "failed", "Failed to pull image %q: %v", container.Image, err) } diff --git a/pkg/kubelet/dockertools/manager_test.go b/pkg/kubelet/dockertools/manager_test.go index ec8b194b8d3..1d6d14c5678 100644 --- a/pkg/kubelet/dockertools/manager_test.go +++ b/pkg/kubelet/dockertools/manager_test.go @@ -116,30 +116,6 @@ func TestSetEntrypointAndCommand(t *testing.T) { } } -func TestConvertDockerToRuntimeContainer(t *testing.T) { - dockerContainer := &docker.APIContainers{ - ID: "ab2cdf", - Image: "bar_image", - Created: 12345, - Names: []string{"/k8s_bar.5678_foo_ns_1234_42"}, - } - expected := &kubecontainer.Container{ - ID: types.UID("ab2cdf"), - Name: "bar", - Image: "bar_image", - Hash: 0x5678, - Created: 12345, - } - - actual, err := convertDockerToRuntimeContainer(dockerContainer) - if err != nil { - t.Fatalf("unexpected error %v", err) - } - if !reflect.DeepEqual(expected, actual) { - t.Errorf("expected %#v, got %#v", expected, actual) - } -} - // verifyPods returns true if the two pod slices are equal. func verifyPods(a, b []*kubecontainer.Pod) bool { if len(a) != len(b) { @@ -179,11 +155,10 @@ func TestGetPods(t *testing.T) { } // Convert the docker containers. This does not affect the test coverage - // because the conversion is tested separately in - // TestConvertDockerToRuntimeContainer. + // because the conversion is tested separately in convert_test.go containers := make([]*kubecontainer.Container, len(dockerContainers)) for i := range containers { - c, err := convertDockerToRuntimeContainer(&dockerContainers[i]) + c, err := toRuntimeContainer(&dockerContainers[i]) if err != nil { t.Fatalf("unexpected error %v", err) } @@ -214,3 +189,24 @@ func TestGetPods(t *testing.T) { t.Errorf("expected %#v, got %#v", expected, actual) } } + +func TestListImages(t *testing.T) { + manager, fakeDocker := NewFakeDockerManager() + dockerImages := []docker.APIImages{{ID: "1111"}, {ID: "2222"}, {ID: "3333"}} + expected := util.NewStringSet([]string{"1111", "2222", "3333"}...) + + fakeDocker.Images = dockerImages + actualImages, err := manager.ListImages() + if err != nil { + t.Fatalf("unexpected error %v", err) + } + actual := util.NewStringSet() + for _, i := range actualImages { + actual.Insert(i.ID) + } + // We can compare the two sets directly because util.StringSet.List() + // returns a "sorted" list. + if !reflect.DeepEqual(expected.List(), actual.List()) { + t.Errorf("expected %#v, got %#v", expected.List(), actual.List()) + } +} diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 9eabc5a588a..581e34511b3 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -883,7 +883,7 @@ func (kl *Kubelet) pullImage(pod *api.Pod, container *api.Container) error { return nil } - err = kl.containerManager.Pull(container.Image) + err = kl.containerManager.PullImage(container.Image) kl.runtimeHooks.ReportImagePull(pod, container, err) return err }