kubelet: move docker-related code into sub-package

pull/6/head
Brian Waldon 2014-09-08 21:33:17 -07:00
parent 7de0989a4e
commit 0bf4fabc19
7 changed files with 316 additions and 260 deletions

View File

@ -34,6 +34,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/controller" "github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/master" "github.com/GoogleCloudPlatform/kubernetes/pkg/master"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
@ -46,7 +47,7 @@ import (
) )
var ( var (
fakeDocker1, fakeDocker2 kubelet.FakeDockerClient fakeDocker1, fakeDocker2 dockertools.FakeDockerClient
) )
type fakePodInfoGetter struct{} type fakePodInfoGetter struct{}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package kubelet package dockertools
import ( import (
"errors" "errors"
@ -111,7 +111,7 @@ type DockerContainers map[DockerID]*docker.APIContainers
func (c DockerContainers) FindPodContainer(podFullName, uuid, containerName string) (*docker.APIContainers, bool, uint64) { func (c DockerContainers) FindPodContainer(podFullName, uuid, containerName string) (*docker.APIContainers, bool, uint64) {
for _, dockerContainer := range c { for _, dockerContainer := range c {
dockerManifestID, dockerUUID, dockerContainerName, hash := parseDockerName(dockerContainer.Names[0]) dockerManifestID, dockerUUID, dockerContainerName, hash := ParseDockerName(dockerContainer.Names[0])
if dockerManifestID == podFullName && if dockerManifestID == podFullName &&
(uuid == "" || dockerUUID == uuid) && (uuid == "" || dockerUUID == uuid) &&
dockerContainerName == containerName { dockerContainerName == containerName {
@ -126,7 +126,7 @@ func (c DockerContainers) FindContainersByPodFullName(podFullName string) map[st
containers := make(map[string]*docker.APIContainers) containers := make(map[string]*docker.APIContainers)
for _, dockerContainer := range c { for _, dockerContainer := range c {
dockerManifestID, _, dockerContainerName, _ := parseDockerName(dockerContainer.Names[0]) dockerManifestID, _, dockerContainerName, _ := ParseDockerName(dockerContainer.Names[0])
if dockerManifestID == podFullName { if dockerManifestID == podFullName {
containers[dockerContainerName] = dockerContainer containers[dockerContainerName] = dockerContainer
} }
@ -135,7 +135,7 @@ func (c DockerContainers) FindContainersByPodFullName(podFullName string) map[st
} }
// GetKubeletDockerContainers returns a map of docker containers that we manage. The map key is the docker container ID // GetKubeletDockerContainers returns a map of docker containers that we manage. The map key is the docker container ID
func getKubeletDockerContainers(client DockerInterface) (DockerContainers, error) { func GetKubeletDockerContainers(client DockerInterface) (DockerContainers, error) {
result := make(DockerContainers) result := make(DockerContainers)
containers, err := client.ListContainers(docker.ListContainersOptions{}) containers, err := client.ListContainers(docker.ListContainersOptions{})
if err != nil { if err != nil {
@ -153,16 +153,16 @@ func getKubeletDockerContainers(client DockerInterface) (DockerContainers, error
return result, nil return result, nil
} }
// getRecentDockerContainersWithName returns a list of dead docker containers which matches the name // GetRecentDockerContainersWithNameAndUUID returns a list of dead docker containers which matches the name
// and uuid given. // and uuid given.
func getRecentDockerContainersWithNameAndUUID(client DockerInterface, podFullName, uuid, containerName string) ([]*docker.Container, error) { func GetRecentDockerContainersWithNameAndUUID(client DockerInterface, podFullName, uuid, containerName string) ([]*docker.Container, error) {
var result []*docker.Container var result []*docker.Container
containers, err := client.ListContainers(docker.ListContainersOptions{All: true}) containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, dockerContainer := range containers { for _, dockerContainer := range containers {
dockerPodName, dockerUUID, dockerContainerName, _ := parseDockerName(dockerContainer.Names[0]) dockerPodName, dockerUUID, dockerContainerName, _ := ParseDockerName(dockerContainer.Names[0])
if dockerPodName != podFullName { if dockerPodName != podFullName {
continue continue
} }
@ -184,7 +184,7 @@ func getRecentDockerContainersWithNameAndUUID(client DockerInterface, podFullNam
var ErrNoContainersInPod = errors.New("no containers exist for this pod") var ErrNoContainersInPod = errors.New("no containers exist for this pod")
// GetDockerPodInfo returns docker info for all containers in the pod/manifest. // GetDockerPodInfo returns docker info for all containers in the pod/manifest.
func getDockerPodInfo(client DockerInterface, podFullName, uuid string) (api.PodInfo, error) { func GetDockerPodInfo(client DockerInterface, podFullName, uuid string) (api.PodInfo, error) {
info := api.PodInfo{} info := api.PodInfo{}
containers, err := client.ListContainers(docker.ListContainersOptions{All: true}) containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
@ -193,7 +193,7 @@ func getDockerPodInfo(client DockerInterface, podFullName, uuid string) (api.Pod
} }
for _, value := range containers { for _, value := range containers {
dockerManifestID, dockerUUID, dockerContainerName, _ := parseDockerName(value.Names[0]) dockerManifestID, dockerUUID, dockerContainerName, _ := ParseDockerName(value.Names[0])
if dockerManifestID != podFullName { if dockerManifestID != podFullName {
continue continue
} }
@ -239,35 +239,35 @@ func unescapeDash(in string) (out string) {
const containerNamePrefix = "k8s" const containerNamePrefix = "k8s"
func hashContainer(container *api.Container) uint64 { func HashContainer(container *api.Container) uint64 {
hash := adler32.New() hash := adler32.New()
fmt.Fprintf(hash, "%#v", *container) fmt.Fprintf(hash, "%#v", *container)
return uint64(hash.Sum32()) return uint64(hash.Sum32())
} }
// Creates a name which can be reversed to identify both full pod name and container name. // Creates a name which can be reversed to identify both full pod name and container name.
func buildDockerName(pod *Pod, container *api.Container) string { func BuildDockerName(manifestUUID, podFullName string, container *api.Container) string {
containerName := escapeDash(container.Name) + "." + strconv.FormatUint(hashContainer(container), 16) containerName := escapeDash(container.Name) + "." + strconv.FormatUint(HashContainer(container), 16)
// Note, manifest.ID could be blank. // Note, manifest.ID could be blank.
if len(pod.Manifest.UUID) == 0 { if len(manifestUUID) == 0 {
return fmt.Sprintf("%s--%s--%s--%08x", return fmt.Sprintf("%s--%s--%s--%08x",
containerNamePrefix, containerNamePrefix,
containerName, containerName,
escapeDash(GetPodFullName(pod)), escapeDash(podFullName),
rand.Uint32()) rand.Uint32())
} else { } else {
return fmt.Sprintf("%s--%s--%s--%s--%08x", return fmt.Sprintf("%s--%s--%s--%s--%08x",
containerNamePrefix, containerNamePrefix,
containerName, containerName,
escapeDash(GetPodFullName(pod)), escapeDash(podFullName),
escapeDash(pod.Manifest.UUID), escapeDash(manifestUUID),
rand.Uint32()) rand.Uint32())
} }
} }
// Upacks a container name, returning the pod full name and container name we would have used to // Upacks a container name, returning the pod full name and container name we would have used to
// construct the docker name. If the docker name isn't one we created, we may return empty strings. // construct the docker name. If the docker name isn't one we created, we may return empty strings.
func parseDockerName(name string) (podFullName, uuid, containerName string, hash uint64) { func ParseDockerName(name string) (podFullName, uuid, containerName string, hash uint64) {
// For some reason docker appears to be appending '/' to names. // For some reason docker appears to be appending '/' to names.
// If it's there, strip it. // If it's there, strip it.
if name[0] == '/' { if name[0] == '/' {
@ -319,3 +319,7 @@ func parseImageName(image string) (string, string) {
} }
return image, tag return image, tag
} }
type ContainerCommandRunner interface {
RunInContainer(containerID string, cmd []string) ([]byte, error)
}

View File

@ -0,0 +1,153 @@
/*
Copyright 2014 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"
"hash/adler32"
"reflect"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/fsouza/go-dockerclient"
)
func verifyCalls(t *testing.T, fakeDocker *FakeDockerClient, calls []string) {
fakeDocker.Lock()
defer fakeDocker.Unlock()
verifyStringArrayEquals(t, fakeDocker.called, calls)
}
func verifyStringArrayEquals(t *testing.T, actual, expected []string) {
invalid := len(actual) != len(expected)
if !invalid {
for ix, value := range actual {
if expected[ix] != value {
invalid = true
}
}
}
if invalid {
t.Errorf("Expected: %#v, Actual: %#v", expected, actual)
}
}
func TestGetContainerID(t *testing.T) {
fakeDocker := &FakeDockerClient{}
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: "foobar",
Names: []string{"/k8s--foo--qux--1234"},
},
{
ID: "barbar",
Names: []string{"/k8s--bar--qux--2565"},
},
}
fakeDocker.Container = &docker.Container{
ID: "foobar",
}
dockerContainers, err := GetKubeletDockerContainers(fakeDocker)
if err != nil {
t.Errorf("Expected no error, Got %#v", err)
}
if len(dockerContainers) != 2 {
t.Errorf("Expected %#v, Got %#v", fakeDocker.ContainerList, dockerContainers)
}
verifyCalls(t, fakeDocker, []string{"list"})
dockerContainer, found, _ := dockerContainers.FindPodContainer("qux", "", "foo")
if dockerContainer == nil || !found {
t.Errorf("Failed to find container %#v", dockerContainer)
}
fakeDocker.clearCalls()
dockerContainer, found, _ = dockerContainers.FindPodContainer("foobar", "", "foo")
verifyCalls(t, fakeDocker, []string{})
if dockerContainer != nil || found {
t.Errorf("Should not have found container %#v", dockerContainer)
}
}
func verifyPackUnpack(t *testing.T, podNamespace, podName, containerName string) {
container := &api.Container{Name: containerName}
hasher := adler32.New()
data := fmt.Sprintf("%#v", *container)
hasher.Write([]byte(data))
computedHash := uint64(hasher.Sum32())
podFullName := fmt.Sprintf("%s.%s", podName, podNamespace)
name := BuildDockerName("", podFullName, container)
returnedPodFullName, _, returnedContainerName, hash := ParseDockerName(name)
if podFullName != returnedPodFullName || containerName != returnedContainerName || computedHash != hash {
t.Errorf("For (%s, %s, %d), unpacked (%s, %s, %d)", podFullName, containerName, computedHash, returnedPodFullName, returnedContainerName, hash)
}
}
func TestContainerManifestNaming(t *testing.T) {
verifyPackUnpack(t, "file", "manifest1234", "container5678")
verifyPackUnpack(t, "file", "manifest--", "container__")
verifyPackUnpack(t, "file", "--manifest", "__container")
verifyPackUnpack(t, "", "m___anifest_", "container-_-")
verifyPackUnpack(t, "other", "_m___anifest", "-_-container")
container := &api.Container{Name: "container"}
podName := "foo"
podNamespace := "test"
name := fmt.Sprintf("k8s--%s--%s.%s--12345", container.Name, podName, podNamespace)
podFullName := fmt.Sprintf("%s.%s", podName, podNamespace)
returnedPodFullName, _, returnedContainerName, hash := ParseDockerName(name)
if returnedPodFullName != podFullName || returnedContainerName != container.Name || hash != 0 {
t.Errorf("unexpected parse: %s %s %d", returnedPodFullName, returnedContainerName, hash)
}
}
func TestDockerContainerCommand(t *testing.T) {
runner := dockerContainerCommandRunner{}
containerID := "1234"
command := []string{"ls"}
cmd, _ := runner.getRunInContainerCommand(containerID, command)
if cmd.Dir != "/var/lib/docker/execdriver/native/"+containerID {
t.Errorf("unexpected command CWD: %s", cmd.Dir)
}
if !reflect.DeepEqual(cmd.Args, []string{"/usr/sbin/nsinit", "exec", "ls"}) {
t.Errorf("unexpectd command args: %s", cmd.Args)
}
}
var parseImageNameTests = []struct {
imageName string
name string
tag string
}{
{"ubuntu", "ubuntu", ""},
{"ubuntu:2342", "ubuntu", "2342"},
{"ubuntu:latest", "ubuntu", "latest"},
{"foo/bar:445566", "foo/bar", "445566"},
{"registry.example.com:5000/foobar", "registry.example.com:5000/foobar", ""},
{"registry.example.com:5000/foobar:5342", "registry.example.com:5000/foobar", "5342"},
{"registry.example.com:5000/foobar:latest", "registry.example.com:5000/foobar", "latest"},
}
func TestParseImageName(t *testing.T) {
for _, tt := range parseImageNameTests {
name, tag := parseImageName(tt.imageName)
if name != tt.name || tag != tt.tag {
t.Errorf("Expected name/tag: %s/%s, got %s/%s", tt.name, tt.tag, name, tag)
}
}
}

View File

@ -14,10 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package kubelet package dockertools
import ( import (
"fmt" "fmt"
"reflect"
"sync" "sync"
"github.com/fsouza/go-dockerclient" "github.com/fsouza/go-dockerclient"
@ -25,93 +26,105 @@ import (
// FakeDockerClient is a simple fake docker client, so that kubelet can be run for testing without requiring a real docker setup. // FakeDockerClient is a simple fake docker client, so that kubelet can be run for testing without requiring a real docker setup.
type FakeDockerClient struct { type FakeDockerClient struct {
lock sync.Mutex sync.Mutex
containerList []docker.APIContainers ContainerList []docker.APIContainers
container *docker.Container Container *docker.Container
err error Err error
called []string called []string
stopped []string Stopped []string
pulled []string pulled []string
Created []string Created []string
} }
func (f *FakeDockerClient) clearCalls() { func (f *FakeDockerClient) clearCalls() {
f.lock.Lock() f.Lock()
defer f.lock.Unlock() defer f.Unlock()
f.called = []string{} f.called = []string{}
} }
func (f *FakeDockerClient) AssertCalls(calls []string) (err error) {
f.Lock()
defer f.Unlock()
if !reflect.DeepEqual(calls, f.called) {
err = fmt.Errorf("expected %#v, got %#v", calls, f.called)
}
return
}
// ListContainers is a test-spy implementation of DockerInterface.ListContainers. // ListContainers is a test-spy implementation of DockerInterface.ListContainers.
// It adds an entry "list" to the internal method call record. // It adds an entry "list" to the internal method call record.
func (f *FakeDockerClient) ListContainers(options docker.ListContainersOptions) ([]docker.APIContainers, error) { func (f *FakeDockerClient) ListContainers(options docker.ListContainersOptions) ([]docker.APIContainers, error) {
f.lock.Lock() f.Lock()
defer f.lock.Unlock() defer f.Unlock()
f.called = append(f.called, "list") f.called = append(f.called, "list")
return f.containerList, f.err return f.ContainerList, f.Err
} }
// InspectContainer is a test-spy implementation of DockerInterface.InspectContainer. // InspectContainer is a test-spy implementation of DockerInterface.InspectContainer.
// It adds an entry "inspect" to the internal method call record. // It adds an entry "inspect" to the internal method call record.
func (f *FakeDockerClient) InspectContainer(id string) (*docker.Container, error) { func (f *FakeDockerClient) InspectContainer(id string) (*docker.Container, error) {
f.lock.Lock() f.Lock()
defer f.lock.Unlock() defer f.Unlock()
f.called = append(f.called, "inspect") f.called = append(f.called, "inspect")
return f.container, f.err return f.Container, f.Err
} }
// CreateContainer is a test-spy implementation of DockerInterface.CreateContainer. // CreateContainer is a test-spy implementation of DockerInterface.CreateContainer.
// It adds an entry "create" to the internal method call record. // It adds an entry "create" to the internal method call record.
func (f *FakeDockerClient) CreateContainer(c docker.CreateContainerOptions) (*docker.Container, error) { func (f *FakeDockerClient) CreateContainer(c docker.CreateContainerOptions) (*docker.Container, error) {
f.lock.Lock() f.Lock()
defer f.lock.Unlock() defer f.Unlock()
f.called = append(f.called, "create") f.called = append(f.called, "create")
f.Created = append(f.Created, c.Name) f.Created = append(f.Created, c.Name)
// This is not a very good fake. We'll just add this container's name to the list. // This is not a very good fake. We'll just add this container's name to the list.
// Docker likes to add a '/', so copy that behavior. // Docker likes to add a '/', so copy that behavior.
name := "/" + c.Name name := "/" + c.Name
f.containerList = append(f.containerList, docker.APIContainers{ID: name, Names: []string{name}}) f.ContainerList = append(f.ContainerList, docker.APIContainers{ID: name, Names: []string{name}})
return &docker.Container{ID: name}, nil return &docker.Container{ID: name}, nil
} }
// StartContainer is a test-spy implementation of DockerInterface.StartContainer. // StartContainer is a test-spy implementation of DockerInterface.StartContainer.
// It adds an entry "start" to the internal method call record. // It adds an entry "start" to the internal method call record.
func (f *FakeDockerClient) StartContainer(id string, hostConfig *docker.HostConfig) error { func (f *FakeDockerClient) StartContainer(id string, hostConfig *docker.HostConfig) error {
f.lock.Lock() f.Lock()
defer f.lock.Unlock() defer f.Unlock()
f.called = append(f.called, "start") f.called = append(f.called, "start")
return f.err return f.Err
} }
// StopContainer is a test-spy implementation of DockerInterface.StopContainer. // StopContainer is a test-spy implementation of DockerInterface.StopContainer.
// It adds an entry "stop" to the internal method call record. // It adds an entry "stop" to the internal method call record.
func (f *FakeDockerClient) StopContainer(id string, timeout uint) error { func (f *FakeDockerClient) StopContainer(id string, timeout uint) error {
f.lock.Lock() f.Lock()
defer f.lock.Unlock() defer f.Unlock()
f.called = append(f.called, "stop") f.called = append(f.called, "stop")
f.stopped = append(f.stopped, id) f.Stopped = append(f.Stopped, id)
var newList []docker.APIContainers var newList []docker.APIContainers
for _, container := range f.containerList { for _, container := range f.ContainerList {
if container.ID != id { if container.ID != id {
newList = append(newList, container) newList = append(newList, container)
} }
} }
f.containerList = newList f.ContainerList = newList
return f.err return f.Err
} }
// PullImage is a test-spy implementation of DockerInterface.StopContainer. // PullImage is a test-spy implementation of DockerInterface.StopContainer.
// It adds an entry "pull" to the internal method call record. // It adds an entry "pull" to the internal method call record.
func (f *FakeDockerClient) PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration) error { func (f *FakeDockerClient) PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration) error {
f.lock.Lock() f.Lock()
defer f.lock.Unlock() defer f.Unlock()
f.called = append(f.called, "pull") f.called = append(f.called, "pull")
f.pulled = append(f.pulled, fmt.Sprintf("%s/%s:%s", opts.Repository, opts.Registry, opts.Tag)) f.pulled = append(f.pulled, fmt.Sprintf("%s/%s:%s", opts.Repository, opts.Registry, opts.Tag))
return f.err return f.Err
} }
// FakeDockerPuller is a stub implementation of DockerPuller. // FakeDockerPuller is a stub implementation of DockerPuller.
type FakeDockerPuller struct { type FakeDockerPuller struct {
lock sync.Mutex sync.Mutex
ImagesPulled []string ImagesPulled []string
// Every pull will return the first error here, and then reslice // Every pull will return the first error here, and then reslice
@ -121,8 +134,8 @@ type FakeDockerPuller struct {
// Pull records the image pull attempt, and optionally injects an error. // Pull records the image pull attempt, and optionally injects an error.
func (f *FakeDockerPuller) Pull(image string) (err error) { func (f *FakeDockerPuller) Pull(image string) (err error) {
f.lock.Lock() f.Lock()
defer f.lock.Unlock() defer f.Unlock()
f.ImagesPulled = append(f.ImagesPulled, image) f.ImagesPulled = append(f.ImagesPulled, image)
if len(f.ErrorsToInject) > 0 { if len(f.ErrorsToInject) > 0 {

View File

@ -30,6 +30,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/health" "github.com/GoogleCloudPlatform/kubernetes/pkg/health"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume" "github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
@ -62,7 +63,7 @@ type volumeMap map[string]volume.Interface
// New creates a new Kubelet for use in main // New creates a new Kubelet for use in main
func NewMainKubelet( func NewMainKubelet(
hn string, hn string,
dc DockerInterface, dc dockertools.DockerInterface,
cc CadvisorInterface, cc CadvisorInterface,
ec tools.EtcdClient, ec tools.EtcdClient,
rd string, rd string,
@ -75,27 +76,23 @@ func NewMainKubelet(
rootDirectory: rd, rootDirectory: rd,
resyncInterval: ri, resyncInterval: ri,
podWorkers: newPodWorkers(), podWorkers: newPodWorkers(),
runner: NewDockerContainerCommandRunner(), runner: dockertools.NewDockerContainerCommandRunner(),
httpClient: &http.Client{}, httpClient: &http.Client{},
} }
} }
// NewIntegrationTestKubelet creates a new Kubelet for use in integration tests. // NewIntegrationTestKubelet creates a new Kubelet for use in integration tests.
// TODO: add more integration tests, and expand parameter list as needed. // TODO: add more integration tests, and expand parameter list as needed.
func NewIntegrationTestKubelet(hn string, dc DockerInterface) *Kubelet { func NewIntegrationTestKubelet(hn string, dc dockertools.DockerInterface) *Kubelet {
return &Kubelet{ return &Kubelet{
hostname: hn, hostname: hn,
dockerClient: dc, dockerClient: dc,
dockerPuller: &FakeDockerPuller{}, dockerPuller: &dockertools.FakeDockerPuller{},
resyncInterval: 3 * time.Second, resyncInterval: 3 * time.Second,
podWorkers: newPodWorkers(), podWorkers: newPodWorkers(),
} }
} }
type ContainerCommandRunner interface {
RunInContainer(containerID string, cmd []string) ([]byte, error)
}
type httpGetInterface interface { type httpGetInterface interface {
Get(url string) (*http.Response, error) Get(url string) (*http.Response, error)
} }
@ -103,7 +100,7 @@ type httpGetInterface interface {
// Kubelet is the main kubelet implementation. // Kubelet is the main kubelet implementation.
type Kubelet struct { type Kubelet struct {
hostname string hostname string
dockerClient DockerInterface dockerClient dockertools.DockerInterface
rootDirectory string rootDirectory string
podWorkers podWorkers podWorkers podWorkers
resyncInterval time.Duration resyncInterval time.Duration
@ -115,11 +112,11 @@ type Kubelet struct {
// Optional, defaults to simple implementaiton // Optional, defaults to simple implementaiton
healthChecker health.HealthChecker healthChecker health.HealthChecker
// Optional, defaults to simple Docker implementation // Optional, defaults to simple Docker implementation
dockerPuller DockerPuller dockerPuller dockertools.DockerPuller
// Optional, defaults to /logs/ from /var/log // Optional, defaults to /logs/ from /var/log
logServer http.Handler logServer http.Handler
// Optional, defaults to simple Docker implementation // Optional, defaults to simple Docker implementation
runner ContainerCommandRunner runner dockertools.ContainerCommandRunner
// Optional, client for http requests, defaults to empty client // Optional, client for http requests, defaults to empty client
httpClient httpGetInterface httpClient httpGetInterface
} }
@ -130,7 +127,7 @@ func (kl *Kubelet) Run(updates <-chan PodUpdate) {
kl.logServer = http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/"))) kl.logServer = http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/")))
} }
if kl.dockerPuller == nil { if kl.dockerPuller == nil {
kl.dockerPuller = NewDockerPuller(kl.dockerClient) kl.dockerPuller = dockertools.NewDockerPuller(kl.dockerClient)
} }
if kl.healthChecker == nil { if kl.healthChecker == nil {
kl.healthChecker = health.NewHealthChecker() kl.healthChecker = health.NewHealthChecker()
@ -316,13 +313,13 @@ func (kl *Kubelet) runHandler(podFullName, uuid string, container *api.Container
} }
// Run a single container from a pod. Returns the docker container ID // Run a single container from a pod. Returns the docker container ID
func (kl *Kubelet) runContainer(pod *Pod, container *api.Container, podVolumes volumeMap, netMode string) (id DockerID, err error) { func (kl *Kubelet) runContainer(pod *Pod, container *api.Container, podVolumes volumeMap, netMode string) (id dockertools.DockerID, err error) {
envVariables := makeEnvironmentVariables(container) envVariables := makeEnvironmentVariables(container)
binds := makeBinds(pod, container, podVolumes) binds := makeBinds(pod, container, podVolumes)
exposedPorts, portBindings := makePortsAndBindings(container) exposedPorts, portBindings := makePortsAndBindings(container)
opts := docker.CreateContainerOptions{ opts := docker.CreateContainerOptions{
Name: buildDockerName(pod, container), Name: dockertools.BuildDockerName(pod.Manifest.UUID, GetPodFullName(pod), container),
Config: &docker.Config{ Config: &docker.Config{
Cmd: container.Command, Cmd: container.Command,
Env: envVariables, Env: envVariables,
@ -347,10 +344,10 @@ func (kl *Kubelet) runContainer(pod *Pod, container *api.Container, podVolumes v
handlerErr := kl.runHandler(GetPodFullName(pod), pod.Manifest.UUID, container, container.Lifecycle.PostStart) handlerErr := kl.runHandler(GetPodFullName(pod), pod.Manifest.UUID, container, container.Lifecycle.PostStart)
if handlerErr != nil { if handlerErr != nil {
kl.killContainerByID(dockerContainer.ID, "") kl.killContainerByID(dockerContainer.ID, "")
return DockerID(""), fmt.Errorf("failed to call event handler: %v", handlerErr) return dockertools.DockerID(""), fmt.Errorf("failed to call event handler: %v", handlerErr)
} }
} }
return DockerID(dockerContainer.ID), err return dockertools.DockerID(dockerContainer.ID), err
} }
// Kill a docker container // Kill a docker container
@ -364,7 +361,7 @@ func (kl *Kubelet) killContainerByID(ID, name string) error {
if len(name) == 0 { if len(name) == 0 {
return err return err
} }
podFullName, uuid, containerName, _ := parseDockerName(name) podFullName, uuid, containerName, _ := dockertools.ParseDockerName(name)
kl.LogEvent(&api.Event{ kl.LogEvent(&api.Event{
Event: "STOP", Event: "STOP",
Manifest: &api.ContainerManifest{ Manifest: &api.ContainerManifest{
@ -386,7 +383,7 @@ const (
) )
// createNetworkContainer starts the network container for a pod. Returns the docker container ID of the newly created container. // createNetworkContainer starts the network container for a pod. Returns the docker container ID of the newly created container.
func (kl *Kubelet) createNetworkContainer(pod *Pod) (DockerID, error) { func (kl *Kubelet) createNetworkContainer(pod *Pod) (dockertools.DockerID, error) {
var ports []api.Port var ports []api.Port
// Docker only exports ports from the network container. Let's // Docker only exports ports from the network container. Let's
// collect all of the relevant ports and export them. // collect all of the relevant ports and export them.
@ -404,7 +401,7 @@ func (kl *Kubelet) createNetworkContainer(pod *Pod) (DockerID, error) {
// Delete all containers in a pod (except the network container) returns the number of containers deleted // Delete all containers in a pod (except the network container) returns the number of containers deleted
// and an error if one occurs. // and an error if one occurs.
func (kl *Kubelet) deleteAllContainers(pod *Pod, podFullName string, dockerContainers DockerContainers) (int, error) { func (kl *Kubelet) deleteAllContainers(pod *Pod, podFullName string, dockerContainers dockertools.DockerContainers) (int, error) {
count := 0 count := 0
errs := make(chan error, len(pod.Manifest.Containers)) errs := make(chan error, len(pod.Manifest.Containers))
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
@ -436,16 +433,16 @@ func (kl *Kubelet) deleteAllContainers(pod *Pod, podFullName string, dockerConta
type empty struct{} type empty struct{}
func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error { func (kl *Kubelet) syncPod(pod *Pod, dockerContainers dockertools.DockerContainers) error {
podFullName := GetPodFullName(pod) podFullName := GetPodFullName(pod)
uuid := pod.Manifest.UUID uuid := pod.Manifest.UUID
containersToKeep := make(map[DockerID]empty) containersToKeep := make(map[dockertools.DockerID]empty)
killedContainers := make(map[DockerID]empty) killedContainers := make(map[dockertools.DockerID]empty)
// Make sure we have a network container // Make sure we have a network container
var netID DockerID var netID dockertools.DockerID
if networkDockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uuid, networkContainerName); found { if networkDockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uuid, networkContainerName); found {
netID = DockerID(networkDockerContainer.ID) netID = dockertools.DockerID(networkDockerContainer.ID)
} else { } else {
glog.Infof("Network container doesn't exist, creating") glog.Infof("Network container doesn't exist, creating")
count, err := kl.deleteAllContainers(pod, podFullName, dockerContainers) count, err := kl.deleteAllContainers(pod, podFullName, dockerContainers)
@ -460,7 +457,7 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
netID = dockerNetworkID netID = dockerNetworkID
if count > 0 { if count > 0 {
// relist everything, otherwise we'll think we're ok // relist everything, otherwise we'll think we're ok
dockerContainers, err = getKubeletDockerContainers(kl.dockerClient) dockerContainers, err = dockertools.GetKubeletDockerContainers(kl.dockerClient)
if err != nil { if err != nil {
glog.Errorf("Error listing containers %#v", dockerContainers) glog.Errorf("Error listing containers %#v", dockerContainers)
return err return err
@ -487,9 +484,9 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
} }
for _, container := range pod.Manifest.Containers { for _, container := range pod.Manifest.Containers {
expectedHash := hashContainer(&container) expectedHash := dockertools.HashContainer(&container)
if dockerContainer, found, hash := dockerContainers.FindPodContainer(podFullName, uuid, container.Name); found { if dockerContainer, found, hash := dockerContainers.FindPodContainer(podFullName, uuid, container.Name); found {
containerID := DockerID(dockerContainer.ID) containerID := dockertools.DockerID(dockerContainer.ID)
glog.V(1).Infof("pod %s container %s exists as %v", podFullName, container.Name, containerID) glog.V(1).Infof("pod %s container %s exists as %v", podFullName, container.Name, containerID)
// look for changes in the container. // look for changes in the container.
@ -517,7 +514,7 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
} }
// Check RestartPolicy for container // Check RestartPolicy for container
recentContainers, err := getRecentDockerContainersWithNameAndUUID(kl.dockerClient, podFullName, uuid, container.Name) recentContainers, err := dockertools.GetRecentDockerContainersWithNameAndUUID(kl.dockerClient, podFullName, uuid, container.Name)
if err != nil { if err != nil {
glog.Errorf("Error listing recent containers with name and uuid:%s--%s--%s", podFullName, uuid, container.Name) glog.Errorf("Error listing recent containers with name and uuid:%s--%s--%s", podFullName, uuid, container.Name)
// TODO(dawnchen): error handling here? // TODO(dawnchen): error handling here?
@ -556,7 +553,7 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
// Kill any containers in this pod which were not identified above (guards against duplicates). // Kill any containers in this pod which were not identified above (guards against duplicates).
for id, container := range dockerContainers { for id, container := range dockerContainers {
curPodFullName, curUUID, _, _ := parseDockerName(container.Names[0]) curPodFullName, curUUID, _, _ := dockertools.ParseDockerName(container.Names[0])
if curPodFullName == podFullName && curUUID == uuid { if curPodFullName == podFullName && curUUID == uuid {
// Don't kill containers we want to keep or those we already killed. // Don't kill containers we want to keep or those we already killed.
_, keep := containersToKeep[id] _, keep := containersToKeep[id]
@ -618,7 +615,7 @@ func (kl *Kubelet) SyncPods(pods []Pod) error {
var err error var err error
desiredContainers := make(map[podContainer]empty) desiredContainers := make(map[podContainer]empty)
dockerContainers, err := getKubeletDockerContainers(kl.dockerClient) dockerContainers, err := dockertools.GetKubeletDockerContainers(kl.dockerClient)
if err != nil { if err != nil {
glog.Errorf("Error listing containers %#v", dockerContainers) glog.Errorf("Error listing containers %#v", dockerContainers)
return err return err
@ -646,14 +643,14 @@ func (kl *Kubelet) SyncPods(pods []Pod) error {
} }
// Kill any containers we don't need // Kill any containers we don't need
existingContainers, err := getKubeletDockerContainers(kl.dockerClient) existingContainers, err := dockertools.GetKubeletDockerContainers(kl.dockerClient)
if err != nil { if err != nil {
glog.Errorf("Error listing containers: %v", err) glog.Errorf("Error listing containers: %v", err)
return err return err
} }
for _, container := range existingContainers { for _, container := range existingContainers {
// Don't kill containers that are in the desired pods. // Don't kill containers that are in the desired pods.
podFullName, uuid, containerName, _ := parseDockerName(container.Names[0]) podFullName, uuid, containerName, _ := dockertools.ParseDockerName(container.Names[0])
if _, ok := desiredContainers[podContainer{podFullName, uuid, containerName}]; !ok { if _, ok := desiredContainers[podContainer{podFullName, uuid, containerName}]; !ok {
err = kl.killContainer(container) err = kl.killContainer(container)
if err != nil { if err != nil {
@ -745,7 +742,7 @@ func (kl *Kubelet) statsFromContainerPath(containerPath string, req *info.Contai
// GetPodInfo returns information from Docker about the containers in a pod // GetPodInfo returns information from Docker about the containers in a pod
func (kl *Kubelet) GetPodInfo(podFullName, uuid string) (api.PodInfo, error) { func (kl *Kubelet) GetPodInfo(podFullName, uuid string) (api.PodInfo, error) {
return getDockerPodInfo(kl.dockerClient, podFullName, uuid) return dockertools.GetDockerPodInfo(kl.dockerClient, podFullName, uuid)
} }
// GetContainerInfo returns stats (from Cadvisor) for a container. // GetContainerInfo returns stats (from Cadvisor) for a container.
@ -753,7 +750,7 @@ func (kl *Kubelet) GetContainerInfo(podFullName, uuid, containerName string, req
if kl.cadvisorClient == nil { if kl.cadvisorClient == nil {
return nil, nil return nil, nil
} }
dockerContainers, err := getKubeletDockerContainers(kl.dockerClient) dockerContainers, err := dockertools.GetKubeletDockerContainers(kl.dockerClient)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -798,7 +795,7 @@ func (kl *Kubelet) RunInContainer(podFullName, uuid, container string, cmd []str
if kl.runner == nil { if kl.runner == nil {
return nil, fmt.Errorf("no runner specified.") return nil, fmt.Errorf("no runner specified.")
} }
dockerContainers, err := getKubeletDockerContainers(kl.dockerClient) dockerContainers, err := dockertools.GetKubeletDockerContainers(kl.dockerClient)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -19,7 +19,6 @@ package kubelet
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"hash/adler32"
"net/http" "net/http"
"reflect" "reflect"
"regexp" "regexp"
@ -30,6 +29,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/health" "github.com/GoogleCloudPlatform/kubernetes/pkg/health"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume" "github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
@ -38,25 +38,24 @@ import (
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
) )
func newTestKubelet(t *testing.T) (*Kubelet, *tools.FakeEtcdClient, *FakeDockerClient) { func newTestKubelet(t *testing.T) (*Kubelet, *tools.FakeEtcdClient, *dockertools.FakeDockerClient) {
fakeEtcdClient := tools.NewFakeEtcdClient(t) fakeEtcdClient := tools.NewFakeEtcdClient(t)
fakeDocker := &FakeDockerClient{ fakeDocker := &dockertools.FakeDockerClient{}
err: nil,
}
kubelet := &Kubelet{} kubelet := &Kubelet{}
kubelet.dockerClient = fakeDocker kubelet.dockerClient = fakeDocker
kubelet.dockerPuller = &FakeDockerPuller{} kubelet.dockerPuller = &dockertools.FakeDockerPuller{}
kubelet.etcdClient = fakeEtcdClient kubelet.etcdClient = fakeEtcdClient
kubelet.rootDirectory = "/tmp/kubelet" kubelet.rootDirectory = "/tmp/kubelet"
kubelet.podWorkers = newPodWorkers() kubelet.podWorkers = newPodWorkers()
return kubelet, fakeEtcdClient, fakeDocker return kubelet, fakeEtcdClient, fakeDocker
} }
func verifyCalls(t *testing.T, fakeDocker *FakeDockerClient, calls []string) { func verifyCalls(t *testing.T, fakeDocker *dockertools.FakeDockerClient, calls []string) {
fakeDocker.lock.Lock() err := fakeDocker.AssertCalls(calls)
defer fakeDocker.lock.Unlock() if err != nil {
verifyStringArrayEquals(t, fakeDocker.called, calls) t.Error(err)
}
} }
func verifyStringArrayEquals(t *testing.T, actual, expected []string) { func verifyStringArrayEquals(t *testing.T, actual, expected []string) {
@ -73,89 +72,16 @@ func verifyStringArrayEquals(t *testing.T, actual, expected []string) {
} }
} }
func verifyPackUnpack(t *testing.T, podNamespace, podName, containerName string) {
container := &api.Container{Name: containerName}
hasher := adler32.New()
data := fmt.Sprintf("%#v", *container)
hasher.Write([]byte(data))
computedHash := uint64(hasher.Sum32())
name := buildDockerName(
&Pod{Name: podName, Namespace: podNamespace},
container,
)
podFullName := fmt.Sprintf("%s.%s", podName, podNamespace)
returnedPodFullName, _, returnedContainerName, hash := parseDockerName(name)
if podFullName != returnedPodFullName || containerName != returnedContainerName || computedHash != hash {
t.Errorf("For (%s, %s, %d), unpacked (%s, %s, %d)", podFullName, containerName, computedHash, returnedPodFullName, returnedContainerName, hash)
}
}
func verifyBoolean(t *testing.T, expected, value bool) { func verifyBoolean(t *testing.T, expected, value bool) {
if expected != value { if expected != value {
t.Errorf("Unexpected boolean. Expected %t. Found %t", expected, value) t.Errorf("Unexpected boolean. Expected %t. Found %t", expected, value)
} }
} }
func TestContainerManifestNaming(t *testing.T) {
verifyPackUnpack(t, "file", "manifest1234", "container5678")
verifyPackUnpack(t, "file", "manifest--", "container__")
verifyPackUnpack(t, "file", "--manifest", "__container")
verifyPackUnpack(t, "", "m___anifest_", "container-_-")
verifyPackUnpack(t, "other", "_m___anifest", "-_-container")
container := &api.Container{Name: "container"}
pod := &Pod{Name: "foo", Namespace: "test"}
name := fmt.Sprintf("k8s--%s--%s.%s--12345", container.Name, pod.Name, pod.Namespace)
podFullName := fmt.Sprintf("%s.%s", pod.Name, pod.Namespace)
returnedPodFullName, _, returnedContainerName, hash := parseDockerName(name)
if returnedPodFullName != podFullName || returnedContainerName != container.Name || hash != 0 {
t.Errorf("unexpected parse: %s %s %d", returnedPodFullName, returnedContainerName, hash)
}
}
func TestGetContainerID(t *testing.T) {
_, _, fakeDocker := newTestKubelet(t)
fakeDocker.containerList = []docker.APIContainers{
{
ID: "foobar",
Names: []string{"/k8s--foo--qux--1234"},
},
{
ID: "barbar",
Names: []string{"/k8s--bar--qux--2565"},
},
}
fakeDocker.container = &docker.Container{
ID: "foobar",
}
dockerContainers, err := getKubeletDockerContainers(fakeDocker)
if err != nil {
t.Errorf("Expected no error, Got %#v", err)
}
if len(dockerContainers) != 2 {
t.Errorf("Expected %#v, Got %#v", fakeDocker.containerList, dockerContainers)
}
verifyCalls(t, fakeDocker, []string{"list"})
dockerContainer, found, _ := dockerContainers.FindPodContainer("qux", "", "foo")
if dockerContainer == nil || !found {
t.Errorf("Failed to find container %#v", dockerContainer)
}
fakeDocker.clearCalls()
dockerContainer, found, _ = dockerContainers.FindPodContainer("foobar", "", "foo")
verifyCalls(t, fakeDocker, []string{})
if dockerContainer != nil || found {
t.Errorf("Should not have found container %#v", dockerContainer)
}
}
func TestKillContainerWithError(t *testing.T) { func TestKillContainerWithError(t *testing.T) {
fakeDocker := &FakeDockerClient{ fakeDocker := &dockertools.FakeDockerClient{
err: fmt.Errorf("sample error"), Err: fmt.Errorf("sample error"),
containerList: []docker.APIContainers{ ContainerList: []docker.APIContainers{
{ {
ID: "1234", ID: "1234",
Names: []string{"/k8s--foo--qux--1234"}, Names: []string{"/k8s--foo--qux--1234"},
@ -168,7 +94,7 @@ func TestKillContainerWithError(t *testing.T) {
} }
kubelet, _, _ := newTestKubelet(t) kubelet, _, _ := newTestKubelet(t)
kubelet.dockerClient = fakeDocker kubelet.dockerClient = fakeDocker
err := kubelet.killContainer(&fakeDocker.containerList[0]) err := kubelet.killContainer(&fakeDocker.ContainerList[0])
if err == nil { if err == nil {
t.Errorf("expected error, found nil") t.Errorf("expected error, found nil")
} }
@ -177,7 +103,7 @@ func TestKillContainerWithError(t *testing.T) {
func TestKillContainer(t *testing.T) { func TestKillContainer(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
fakeDocker.containerList = []docker.APIContainers{ fakeDocker.ContainerList = []docker.APIContainers{
{ {
ID: "1234", ID: "1234",
Names: []string{"/k8s--foo--qux--1234"}, Names: []string{"/k8s--foo--qux--1234"},
@ -187,11 +113,11 @@ func TestKillContainer(t *testing.T) {
Names: []string{"/k8s--bar--qux--5678"}, Names: []string{"/k8s--bar--qux--5678"},
}, },
} }
fakeDocker.container = &docker.Container{ fakeDocker.Container = &docker.Container{
ID: "foobar", ID: "foobar",
} }
err := kubelet.killContainer(&fakeDocker.containerList[0]) err := kubelet.killContainer(&fakeDocker.ContainerList[0])
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
@ -227,10 +153,10 @@ func (cr *channelReader) GetList() [][]Pod {
func TestSyncPodsDoesNothing(t *testing.T) { func TestSyncPodsDoesNothing(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
container := api.Container{Name: "bar"} container := api.Container{Name: "bar"}
fakeDocker.containerList = []docker.APIContainers{ fakeDocker.ContainerList = []docker.APIContainers{
{ {
// format is k8s--<container-id>--<pod-fullname> // format is k8s--<container-id>--<pod-fullname>
Names: []string{"/k8s--bar." + strconv.FormatUint(hashContainer(&container), 16) + "--foo.test"}, Names: []string{"/k8s--bar." + strconv.FormatUint(dockertools.HashContainer(&container), 16) + "--foo.test"},
ID: "1234", ID: "1234",
}, },
{ {
@ -281,7 +207,7 @@ func matchString(t *testing.T, pattern, str string) bool {
func TestSyncPodsCreatesNetAndContainer(t *testing.T) { func TestSyncPodsCreatesNetAndContainer(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
fakeDocker.containerList = []docker.APIContainers{} fakeDocker.ContainerList = []docker.APIContainers{}
err := kubelet.SyncPods([]Pod{ err := kubelet.SyncPods([]Pod{
{ {
Name: "foo", Name: "foo",
@ -302,18 +228,18 @@ func TestSyncPodsCreatesNetAndContainer(t *testing.T) {
verifyCalls(t, fakeDocker, []string{ verifyCalls(t, fakeDocker, []string{
"list", "list", "create", "start", "list", "inspect", "list", "create", "start"}) "list", "list", "create", "start", "list", "inspect", "list", "create", "start"})
fakeDocker.lock.Lock() fakeDocker.Lock()
if len(fakeDocker.Created) != 2 || if len(fakeDocker.Created) != 2 ||
!matchString(t, "k8s--net\\.[a-f0-9]+--foo.test--", fakeDocker.Created[0]) || !matchString(t, "k8s--net\\.[a-f0-9]+--foo.test--", fakeDocker.Created[0]) ||
!matchString(t, "k8s--bar\\.[a-f0-9]+--foo.test--", fakeDocker.Created[1]) { !matchString(t, "k8s--bar\\.[a-f0-9]+--foo.test--", fakeDocker.Created[1]) {
t.Errorf("Unexpected containers created %v", fakeDocker.Created) t.Errorf("Unexpected containers created %v", fakeDocker.Created)
} }
fakeDocker.lock.Unlock() fakeDocker.Unlock()
} }
func TestSyncPodsWithNetCreatesContainer(t *testing.T) { func TestSyncPodsWithNetCreatesContainer(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
fakeDocker.containerList = []docker.APIContainers{ fakeDocker.ContainerList = []docker.APIContainers{
{ {
// network container // network container
Names: []string{"/k8s--net--foo.test--"}, Names: []string{"/k8s--net--foo.test--"},
@ -340,19 +266,19 @@ func TestSyncPodsWithNetCreatesContainer(t *testing.T) {
verifyCalls(t, fakeDocker, []string{ verifyCalls(t, fakeDocker, []string{
"list", "list", "list", "inspect", "list", "create", "start"}) "list", "list", "list", "inspect", "list", "create", "start"})
fakeDocker.lock.Lock() fakeDocker.Lock()
if len(fakeDocker.Created) != 1 || if len(fakeDocker.Created) != 1 ||
!matchString(t, "k8s--bar\\.[a-f0-9]+--foo.test--", fakeDocker.Created[0]) { !matchString(t, "k8s--bar\\.[a-f0-9]+--foo.test--", fakeDocker.Created[0]) {
t.Errorf("Unexpected containers created %v", fakeDocker.Created) t.Errorf("Unexpected containers created %v", fakeDocker.Created)
} }
fakeDocker.lock.Unlock() fakeDocker.Unlock()
} }
func TestSyncPodsWithNetCreatesContainerCallsHandler(t *testing.T) { func TestSyncPodsWithNetCreatesContainerCallsHandler(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
fakeHttp := fakeHTTP{} fakeHttp := fakeHTTP{}
kubelet.httpClient = &fakeHttp kubelet.httpClient = &fakeHttp
fakeDocker.containerList = []docker.APIContainers{ fakeDocker.ContainerList = []docker.APIContainers{
{ {
// network container // network container
Names: []string{"/k8s--net--foo.test--"}, Names: []string{"/k8s--net--foo.test--"},
@ -390,12 +316,12 @@ func TestSyncPodsWithNetCreatesContainerCallsHandler(t *testing.T) {
verifyCalls(t, fakeDocker, []string{ verifyCalls(t, fakeDocker, []string{
"list", "list", "list", "inspect", "list", "create", "start"}) "list", "list", "list", "inspect", "list", "create", "start"})
fakeDocker.lock.Lock() fakeDocker.Lock()
if len(fakeDocker.Created) != 1 || if len(fakeDocker.Created) != 1 ||
!matchString(t, "k8s--bar\\.[a-f0-9]+--foo.test--", fakeDocker.Created[0]) { !matchString(t, "k8s--bar\\.[a-f0-9]+--foo.test--", fakeDocker.Created[0]) {
t.Errorf("Unexpected containers created %v", fakeDocker.Created) t.Errorf("Unexpected containers created %v", fakeDocker.Created)
} }
fakeDocker.lock.Unlock() fakeDocker.Unlock()
if fakeHttp.url != "http://foo:8080/bar" { if fakeHttp.url != "http://foo:8080/bar" {
t.Errorf("Unexpected handler: %s", fakeHttp.url) t.Errorf("Unexpected handler: %s", fakeHttp.url)
} }
@ -403,7 +329,7 @@ func TestSyncPodsWithNetCreatesContainerCallsHandler(t *testing.T) {
func TestSyncPodsDeletesWithNoNetContainer(t *testing.T) { func TestSyncPodsDeletesWithNoNetContainer(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
fakeDocker.containerList = []docker.APIContainers{ fakeDocker.ContainerList = []docker.APIContainers{
{ {
// format is k8s--<container-id>--<pod-fullname> // format is k8s--<container-id>--<pod-fullname>
Names: []string{"/k8s--bar--foo.test"}, Names: []string{"/k8s--bar--foo.test"},
@ -435,16 +361,16 @@ func TestSyncPodsDeletesWithNoNetContainer(t *testing.T) {
expectedToStop := map[string]bool{ expectedToStop := map[string]bool{
"1234": true, "1234": true,
} }
fakeDocker.lock.Lock() fakeDocker.Lock()
if len(fakeDocker.stopped) != 1 || !expectedToStop[fakeDocker.stopped[0]] { if len(fakeDocker.Stopped) != 1 || !expectedToStop[fakeDocker.Stopped[0]] {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.stopped) t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
} }
fakeDocker.lock.Unlock() fakeDocker.Unlock()
} }
func TestSyncPodsDeletes(t *testing.T) { func TestSyncPodsDeletes(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
fakeDocker.containerList = []docker.APIContainers{ fakeDocker.ContainerList = []docker.APIContainers{
{ {
// the k8s prefix is required for the kubelet to manage the container // the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s--foo--bar.test"}, Names: []string{"/k8s--foo--bar.test"},
@ -473,16 +399,16 @@ func TestSyncPodsDeletes(t *testing.T) {
"1234": true, "1234": true,
"9876": true, "9876": true,
} }
if len(fakeDocker.stopped) != 2 || if len(fakeDocker.Stopped) != 2 ||
!expectedToStop[fakeDocker.stopped[0]] || !expectedToStop[fakeDocker.Stopped[0]] ||
!expectedToStop[fakeDocker.stopped[1]] { !expectedToStop[fakeDocker.Stopped[1]] {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.stopped) t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
} }
} }
func TestSyncPodDeletesDuplicate(t *testing.T) { func TestSyncPodDeletesDuplicate(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
dockerContainers := DockerContainers{ dockerContainers := dockertools.DockerContainers{
"1234": &docker.APIContainers{ "1234": &docker.APIContainers{
// the k8s prefix is required for the kubelet to manage the container // the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s--foo--bar.test--1"}, Names: []string{"/k8s--foo--bar.test--1"},
@ -521,8 +447,8 @@ func TestSyncPodDeletesDuplicate(t *testing.T) {
verifyCalls(t, fakeDocker, []string{"list", "stop"}) verifyCalls(t, fakeDocker, []string{"list", "stop"})
// Expect one of the duplicates to be killed. // Expect one of the duplicates to be killed.
if len(fakeDocker.stopped) != 1 || (len(fakeDocker.stopped) != 0 && fakeDocker.stopped[0] != "1234" && fakeDocker.stopped[0] != "4567") { if len(fakeDocker.Stopped) != 1 || (len(fakeDocker.Stopped) != 0 && fakeDocker.Stopped[0] != "1234" && fakeDocker.Stopped[0] != "4567") {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.stopped) t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
} }
} }
@ -535,7 +461,7 @@ func (f *FalseHealthChecker) HealthCheck(podFullName string, state api.PodState,
func TestSyncPodBadHash(t *testing.T) { func TestSyncPodBadHash(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
kubelet.healthChecker = &FalseHealthChecker{} kubelet.healthChecker = &FalseHealthChecker{}
dockerContainers := DockerContainers{ dockerContainers := dockertools.DockerContainers{
"1234": &docker.APIContainers{ "1234": &docker.APIContainers{
// the k8s prefix is required for the kubelet to manage the container // the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s--bar.1234--foo.test"}, Names: []string{"/k8s--bar.1234--foo.test"},
@ -568,16 +494,16 @@ func TestSyncPodBadHash(t *testing.T) {
expectedToStop := map[string]bool{ expectedToStop := map[string]bool{
"1234": true, "1234": true,
} }
if len(fakeDocker.stopped) != 1 || if len(fakeDocker.Stopped) != 1 ||
!expectedToStop[fakeDocker.stopped[0]] { !expectedToStop[fakeDocker.Stopped[0]] {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.stopped) t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
} }
} }
func TestSyncPodUnhealthy(t *testing.T) { func TestSyncPodUnhealthy(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
kubelet.healthChecker = &FalseHealthChecker{} kubelet.healthChecker = &FalseHealthChecker{}
dockerContainers := DockerContainers{ dockerContainers := dockertools.DockerContainers{
"1234": &docker.APIContainers{ "1234": &docker.APIContainers{
// the k8s prefix is required for the kubelet to manage the container // the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s--bar--foo.test"}, Names: []string{"/k8s--bar--foo.test"},
@ -615,9 +541,9 @@ func TestSyncPodUnhealthy(t *testing.T) {
expectedToStop := map[string]bool{ expectedToStop := map[string]bool{
"1234": true, "1234": true,
} }
if len(fakeDocker.stopped) != 1 || if len(fakeDocker.Stopped) != 1 ||
!expectedToStop[fakeDocker.stopped[0]] { !expectedToStop[fakeDocker.Stopped[0]] {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.stopped) t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
} }
} }
@ -934,7 +860,7 @@ func TestGetContainerInfo(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
kubelet.cadvisorClient = mockCadvisor kubelet.cadvisorClient = mockCadvisor
fakeDocker.containerList = []docker.APIContainers{ fakeDocker.ContainerList = []docker.APIContainers{
{ {
ID: containerID, ID: containerID,
// pod id: qux // pod id: qux
@ -958,7 +884,7 @@ func TestGetContainerInfo(t *testing.T) {
mockCadvisor.AssertExpectations(t) mockCadvisor.AssertExpectations(t)
} }
func TestGetRooInfo(t *testing.T) { func TestGetRootInfo(t *testing.T) {
containerPath := "/" containerPath := "/"
containerInfo := &info.ContainerInfo{ containerInfo := &info.ContainerInfo{
ContainerReference: info.ContainerReference{ ContainerReference: info.ContainerReference{
@ -973,9 +899,7 @@ func TestGetRooInfo(t *testing.T) {
}, },
}, },
} }
fakeDocker := FakeDockerClient{ fakeDocker := dockertools.FakeDockerClient{}
err: nil,
}
mockCadvisor := &mockCadvisorClient{} mockCadvisor := &mockCadvisorClient{}
req := &info.ContainerInfoRequest{} req := &info.ContainerInfoRequest{}
@ -984,7 +908,7 @@ func TestGetRooInfo(t *testing.T) {
kubelet := Kubelet{ kubelet := Kubelet{
dockerClient: &fakeDocker, dockerClient: &fakeDocker,
dockerPuller: &FakeDockerPuller{}, dockerPuller: &dockertools.FakeDockerPuller{},
cadvisorClient: mockCadvisor, cadvisorClient: mockCadvisor,
podWorkers: newPodWorkers(), podWorkers: newPodWorkers(),
} }
@ -1004,7 +928,7 @@ func TestGetRooInfo(t *testing.T) {
func TestGetContainerInfoWithoutCadvisor(t *testing.T) { func TestGetContainerInfoWithoutCadvisor(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
fakeDocker.containerList = []docker.APIContainers{ fakeDocker.ContainerList = []docker.APIContainers{
{ {
ID: "foobar", ID: "foobar",
// pod id: qux // pod id: qux
@ -1042,7 +966,7 @@ func TestGetContainerInfoWhenCadvisorFailed(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
kubelet.cadvisorClient = mockCadvisor kubelet.cadvisorClient = mockCadvisor
fakeDocker.containerList = []docker.APIContainers{ fakeDocker.ContainerList = []docker.APIContainers{
{ {
ID: containerID, ID: containerID,
// pod id: qux // pod id: qux
@ -1070,7 +994,7 @@ func TestGetContainerInfoOnNonExistContainer(t *testing.T) {
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
kubelet.cadvisorClient = mockCadvisor kubelet.cadvisorClient = mockCadvisor
fakeDocker.containerList = []docker.APIContainers{} fakeDocker.ContainerList = []docker.APIContainers{}
stats, _ := kubelet.GetContainerInfo("qux", "", "foo", nil) stats, _ := kubelet.GetContainerInfo("qux", "", "foo", nil)
if stats != nil { if stats != nil {
@ -1094,7 +1018,7 @@ func (f *fakeContainerCommandRunner) RunInContainer(id string, cmd []string) ([]
func TestRunInContainerNoSuchPod(t *testing.T) { func TestRunInContainerNoSuchPod(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{} fakeCommandRunner := fakeContainerCommandRunner{}
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
fakeDocker.containerList = []docker.APIContainers{} fakeDocker.ContainerList = []docker.APIContainers{}
kubelet.runner = &fakeCommandRunner kubelet.runner = &fakeCommandRunner
podName := "podFoo" podName := "podFoo"
@ -1123,7 +1047,7 @@ func TestRunInContainer(t *testing.T) {
podNamespace := "etcd" podNamespace := "etcd"
containerName := "containerFoo" containerName := "containerFoo"
fakeDocker.containerList = []docker.APIContainers{ fakeDocker.ContainerList = []docker.APIContainers{
{ {
ID: containerID, ID: containerID,
Names: []string{"/k8s--" + containerName + "--" + podName + "." + podNamespace + "--1234"}, Names: []string{"/k8s--" + containerName + "--" + podName + "." + podNamespace + "--1234"},
@ -1147,43 +1071,6 @@ func TestRunInContainer(t *testing.T) {
} }
} }
func TestDockerContainerCommand(t *testing.T) {
runner := dockerContainerCommandRunner{}
containerID := "1234"
command := []string{"ls"}
cmd, _ := runner.getRunInContainerCommand(containerID, command)
if cmd.Dir != "/var/lib/docker/execdriver/native/"+containerID {
t.Errorf("unexpected command CWD: %s", cmd.Dir)
}
if !reflect.DeepEqual(cmd.Args, []string{"/usr/sbin/nsinit", "exec", "ls"}) {
t.Errorf("unexpectd command args: %s", cmd.Args)
}
}
var parseImageNameTests = []struct {
imageName string
name string
tag string
}{
{"ubuntu", "ubuntu", ""},
{"ubuntu:2342", "ubuntu", "2342"},
{"ubuntu:latest", "ubuntu", "latest"},
{"foo/bar:445566", "foo/bar", "445566"},
{"registry.example.com:5000/foobar", "registry.example.com:5000/foobar", ""},
{"registry.example.com:5000/foobar:5342", "registry.example.com:5000/foobar", "5342"},
{"registry.example.com:5000/foobar:latest", "registry.example.com:5000/foobar", "latest"},
}
func TestParseImageName(t *testing.T) {
for _, tt := range parseImageNameTests {
name, tag := parseImageName(tt.imageName)
if name != tt.name || tag != tt.tag {
t.Errorf("Expected name/tag: %s/%s, got %s/%s", tt.name, tt.tag, name, tag)
}
}
}
func TestRunHandlerExec(t *testing.T) { func TestRunHandlerExec(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{} fakeCommandRunner := fakeContainerCommandRunner{}
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
@ -1194,7 +1081,7 @@ func TestRunHandlerExec(t *testing.T) {
podNamespace := "etcd" podNamespace := "etcd"
containerName := "containerFoo" containerName := "containerFoo"
fakeDocker.containerList = []docker.APIContainers{ fakeDocker.ContainerList = []docker.APIContainers{
{ {
ID: containerID, ID: containerID,
Names: []string{"/k8s--" + containerName + "--" + podName + "." + podNamespace + "--1234"}, Names: []string{"/k8s--" + containerName + "--" + podName + "." + podNamespace + "--1234"},
@ -1298,7 +1185,7 @@ func TestSyncPodEventHandlerFails(t *testing.T) {
kubelet.httpClient = &fakeHTTP{ kubelet.httpClient = &fakeHTTP{
err: fmt.Errorf("test error"), err: fmt.Errorf("test error"),
} }
dockerContainers := DockerContainers{ dockerContainers := dockertools.DockerContainers{
"9876": &docker.APIContainers{ "9876": &docker.APIContainers{
// network container // network container
Names: []string{"/k8s--net--foo.test--"}, Names: []string{"/k8s--net--foo.test--"},
@ -1331,7 +1218,7 @@ func TestSyncPodEventHandlerFails(t *testing.T) {
verifyCalls(t, fakeDocker, []string{"list", "list", "create", "start", "stop"}) verifyCalls(t, fakeDocker, []string{"list", "list", "create", "start", "stop"})
if len(fakeDocker.stopped) != 1 { if len(fakeDocker.Stopped) != 1 {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.stopped) t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
} }
} }

View File

@ -33,6 +33,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/healthz" "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz"
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog" "github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/google/cadvisor/info" "github.com/google/cadvisor/info"
"gopkg.in/v1/yaml" "gopkg.in/v1/yaml"
@ -159,7 +160,7 @@ func (s *Server) handlePodInfo(w http.ResponseWriter, req *http.Request) {
// TODO: backwards compatibility with existing API, needs API change // TODO: backwards compatibility with existing API, needs API change
podFullName := GetPodFullName(&Pod{Name: podID, Namespace: "etcd"}) podFullName := GetPodFullName(&Pod{Name: podID, Namespace: "etcd"})
info, err := s.host.GetPodInfo(podFullName, podUUID) info, err := s.host.GetPodInfo(podFullName, podUUID)
if err == ErrNoContainersInPod { if err == dockertools.ErrNoContainersInPod {
http.Error(w, "Pod does not exist", http.StatusNotFound) http.Error(w, "Pod does not exist", http.StatusNotFound)
return return
} }