Merge pull request #7578 from yujuhong/docker_manager

Add image-related methods to DockerManager
This commit is contained in:
Victor Marmol 2015-04-30 16:16:39 -07:00
commit 441a4e6f30
6 changed files with 190 additions and 52 deletions

View File

@ -54,18 +54,18 @@ type Runtime interface {
KillContainerInPod(api.Container, *api.Pod) error KillContainerInPod(api.Container, *api.Pod) error
// GetPodStatus retrieves the status of the pod, including the information of // GetPodStatus retrieves the status of the pod, including the information of
// all containers in the pod. // all containers in the pod.
GetPodStatus(*api.Pod) (api.PodStatus, error) GetPodStatus(*api.Pod) (*api.PodStatus, error)
// TODO(vmarmol): Merge RunInContainer and ExecInContainer. // TODO(vmarmol): Merge RunInContainer and ExecInContainer.
// Runs the command in the container of the specified pod using nsinit. // Runs the command in the container of the specified pod using nsinit.
// TODO(yifan): Use strong type for containerID. // 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. // Runs the command in the container of the specified pod using nsenter.
// Attaches the processes stdin, stdout, and stderr. Optionally uses a // Attaches the processes stdin, stdout, and stderr. Optionally uses a
// tty. // tty.
// TODO(yifan): Use strong type for containerID. // TODO(yifan): Use strong type for containerID.
ExecInContainer(containerID string, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool) error 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. // 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 pulls an image from the network to local storage.
PullImage(image string) error PullImage(image string) error
// IsImagePresent checks whether the container image is already in the local storage. // 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 maxReasonCacheEntries = 200
) )
// TODO: Eventually DockerManager should implement kubecontainer.Runtime // TODO(yjhong): DockerManager should implement the Runtime interface.
// interface.
type DockerManager struct { type DockerManager struct {
client DockerInterface client DockerInterface
recorder record.EventRecorder recorder record.EventRecorder
@ -640,21 +640,6 @@ func getDockerContainerNameInfo(c *docker.APIContainers) (*KubeletContainerName,
return dockerName, hash, nil 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. // Get pod UID, name, and namespace by examining the container names.
func getPodInfoFromContainer(c *docker.APIContainers) (types.UID, string, string, error) { func getPodInfoFromContainer(c *docker.APIContainers) (types.UID, string, string, error) {
dockerName, _, err := getDockerContainerNameInfo(c) dockerName, _, err := getDockerContainerNameInfo(c)
@ -678,7 +663,7 @@ func (dm *DockerManager) GetContainers(all bool) ([]*kubecontainer.Container, er
// Convert DockerContainers to []*kubecontainer.Container // Convert DockerContainers to []*kubecontainer.Container
result := make([]*kubecontainer.Container, 0, len(containers)) result := make([]*kubecontainer.Container, 0, len(containers))
for _, c := range containers { for _, c := range containers {
converted, err := convertDockerToRuntimeContainer(c) converted, err := toRuntimeContainer(c)
if err != nil { if err != nil {
glog.Errorf("Error examining the container: %v", err) glog.Errorf("Error examining the container: %v", err)
continue continue
@ -699,7 +684,7 @@ func (dm *DockerManager) GetPods(all bool) ([]*kubecontainer.Pod, error) {
// Group containers by pod. // Group containers by pod.
for _, c := range containers { for _, c := range containers {
converted, err := convertDockerToRuntimeContainer(c) converted, err := toRuntimeContainer(c)
if err != nil { if err != nil {
glog.Errorf("Error examining the container: %v", err) glog.Errorf("Error examining the container: %v", err)
continue continue
@ -730,14 +715,40 @@ func (dm *DockerManager) GetPods(all bool) ([]*kubecontainer.Pod, error) {
return result, nil 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) 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) { func (dm *DockerManager) IsImagePresent(image string) (bool, error) {
return dm.Puller.IsImagePresent(image) 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. // podInfraContainerChanged returns true if the pod infra container has changed.
func (dm *DockerManager) podInfraContainerChanged(pod *api.Pod, podInfraContainer *kubecontainer.Container) (bool, error) { func (dm *DockerManager) podInfraContainerChanged(pod *api.Pod, podInfraContainer *kubecontainer.Container) (bool, error) {
networkMode := "" networkMode := ""
@ -1128,7 +1139,7 @@ func (dm *DockerManager) CreatePodInfraContainer(pod *api.Pod, generator kubecon
return "", err return "", err
} }
if !ok { if !ok {
if err := dm.Pull(container.Image); err != nil { if err := dm.PullImage(container.Image); err != nil {
if ref != nil { if ref != nil {
dm.recorder.Eventf(ref, "failed", "Failed to pull image %q: %v", container.Image, err) 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. // verifyPods returns true if the two pod slices are equal.
func verifyPods(a, b []*kubecontainer.Pod) bool { func verifyPods(a, b []*kubecontainer.Pod) bool {
if len(a) != len(b) { 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 // Convert the docker containers. This does not affect the test coverage
// because the conversion is tested separately in // because the conversion is tested separately in convert_test.go
// TestConvertDockerToRuntimeContainer.
containers := make([]*kubecontainer.Container, len(dockerContainers)) containers := make([]*kubecontainer.Container, len(dockerContainers))
for i := range containers { for i := range containers {
c, err := convertDockerToRuntimeContainer(&dockerContainers[i]) c, err := toRuntimeContainer(&dockerContainers[i])
if err != nil { if err != nil {
t.Fatalf("unexpected error %v", err) t.Fatalf("unexpected error %v", err)
} }
@ -214,3 +189,24 @@ func TestGetPods(t *testing.T) {
t.Errorf("expected %#v, got %#v", expected, actual) 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

@ -883,7 +883,7 @@ func (kl *Kubelet) pullImage(pod *api.Pod, container *api.Container) error {
return nil return nil
} }
err = kl.containerManager.Pull(container.Image) err = kl.containerManager.PullImage(container.Image)
kl.runtimeHooks.ReportImagePull(pod, container, err) kl.runtimeHooks.ReportImagePull(pod, container, err)
return err return err
} }