Add image-related methods to DockerManager

This change is part of the efforts to make DockerManager implement the Runtime
interface.

The change also modifies the interface slightly to work with existing
code, and aggregates the type converting functions to convert.go.
pull/6/head
Yu-Ju Hong 2015-04-30 10:12:23 -07:00
parent 2d37650c65
commit 83c599e4d4
6 changed files with 190 additions and 52 deletions

View File

@ -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.

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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())
}
}

View File

@ -887,7 +887,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
}