k3s/pkg/kubelet/kubelet_test.go

3375 lines
92 KiB
Go
Raw Normal View History

2014-06-06 23:40:48 +00:00
/*
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.
*/
2014-06-23 18:32:11 +00:00
2014-06-06 23:40:48 +00:00
package kubelet
import (
"bytes"
"errors"
2014-06-06 23:40:48 +00:00
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"reflect"
"regexp"
"strconv"
"strings"
2014-06-06 23:40:48 +00:00
"sync"
"testing"
"time"
2014-06-06 23:40:48 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
2015-02-23 21:04:45 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/cadvisor"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/metrics"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network"
2015-01-14 23:22:21 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/volume/host_path"
2014-06-06 23:40:48 +00:00
"github.com/fsouza/go-dockerclient"
cadvisorApi "github.com/google/cadvisor/info/v1"
2014-06-06 23:40:48 +00:00
)
func init() {
api.ForTesting_ReferencesAllowBlankSelfLinks = true
util.ReallyCrash = true
}
2015-02-23 21:04:45 +00:00
type TestKubelet struct {
kubelet *Kubelet
fakeDocker *dockertools.FakeDockerClient
fakeCadvisor *cadvisor.Mock
fakeKubeClient *client.Fake
waitGroup *sync.WaitGroup
fakeMirrorManager *fakeMirrorManager
2015-02-23 21:04:45 +00:00
}
func newTestKubelet(t *testing.T) *TestKubelet {
fakeDocker := &dockertools.FakeDockerClient{RemovedImages: util.StringSet{}}
2015-02-27 09:19:41 +00:00
fakeDockerCache := dockertools.NewFakeDockerCache(fakeDocker)
2015-02-23 21:04:45 +00:00
fakeRecorder := &record.FakeRecorder{}
fakeKubeClient := &client.Fake{}
kubelet := &Kubelet{}
kubelet.dockerClient = fakeDocker
2015-02-27 09:19:41 +00:00
kubelet.dockerCache = fakeDockerCache
2015-02-23 21:04:45 +00:00
kubelet.kubeClient = fakeKubeClient
kubelet.dockerPuller = &dockertools.FakeDockerPuller{}
2015-02-23 21:04:45 +00:00
kubelet.hostname = "testnode"
kubelet.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", network.NewFakeHost(nil))
if tempDir, err := ioutil.TempDir("/tmp", "kubelet_test."); err != nil {
t.Fatalf("can't make a temp rootdir: %v", err)
} else {
kubelet.rootDirectory = tempDir
}
if err := os.MkdirAll(kubelet.rootDirectory, 0750); err != nil {
t.Fatalf("can't mkdir(%q): %v", kubelet.rootDirectory, err)
}
2015-02-27 09:19:41 +00:00
waitGroup := new(sync.WaitGroup)
kubelet.podWorkers = newPodWorkers(
fakeDockerCache,
func(pod *api.Pod, hasMirrorPod bool, containers dockertools.DockerContainers) error {
err := kubelet.syncPod(pod, hasMirrorPod, containers)
2015-02-27 09:19:41 +00:00
waitGroup.Done()
return err
},
2015-02-23 21:04:45 +00:00
fakeRecorder)
kubelet.sourcesReady = func() bool { return true }
kubelet.masterServiceNamespace = api.NamespaceDefault
kubelet.serviceLister = testServiceLister{}
kubelet.nodeLister = testNodeLister{}
2015-02-02 18:51:52 +00:00
kubelet.readiness = newReadinessStates()
2015-02-23 21:04:45 +00:00
kubelet.recorder = fakeRecorder
kubelet.statusManager = newStatusManager(fakeKubeClient)
if err := kubelet.setupDataDirs(); err != nil {
t.Fatalf("can't initialize kubelet data dirs: %v", err)
}
mockCadvisor := &cadvisor.Mock{}
kubelet.cadvisor = mockCadvisor
podManager, fakeMirrorManager := newFakePodManager()
kubelet.podManager = podManager
return &TestKubelet{kubelet, fakeDocker, mockCadvisor, fakeKubeClient, waitGroup, fakeMirrorManager}
}
func verifyCalls(t *testing.T, fakeDocker *dockertools.FakeDockerClient, calls []string) {
err := fakeDocker.AssertCalls(calls)
if err != nil {
t.Error(err)
}
2014-06-06 23:40:48 +00:00
}
func verifyStringArrayEquals(t *testing.T, actual, expected []string) {
invalid := len(actual) != len(expected)
2014-07-03 05:35:50 +00:00
if !invalid {
for ix, value := range actual {
if expected[ix] != value {
invalid = true
}
2014-06-06 23:40:48 +00:00
}
}
if invalid {
t.Errorf("Expected: %#v, Actual: %#v", expected, actual)
}
}
func verifyStringArrayEqualsAnyOrder(t *testing.T, actual, expected []string) {
invalid := len(actual) != len(expected)
if !invalid {
for _, exp := range expected {
found := false
for _, act := range actual {
if exp == act {
found = true
break
}
}
if !found {
t.Errorf("Expected element %q not found in %#v", exp, actual)
}
}
}
if invalid {
t.Errorf("Expected: %#v, Actual: %#v", expected, actual)
}
}
2014-06-09 23:50:44 +00:00
func verifyBoolean(t *testing.T, expected, value bool) {
if expected != value {
2014-07-18 19:03:22 +00:00
t.Errorf("Unexpected boolean. Expected %t. Found %t", expected, value)
2014-06-09 23:50:44 +00:00
}
}
func TestKubeletDirs(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
root := kubelet.rootDirectory
var exp, got string
got = kubelet.getPodsDir()
exp = path.Join(root, "pods")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPluginsDir()
exp = path.Join(root, "plugins")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPluginDir("foobar")
exp = path.Join(root, "plugins/foobar")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPodDir("abc123")
exp = path.Join(root, "pods/abc123")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPodVolumesDir("abc123")
exp = path.Join(root, "pods/abc123/volumes")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPodVolumeDir("abc123", "plugin", "foobar")
exp = path.Join(root, "pods/abc123/volumes/plugin/foobar")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPodPluginsDir("abc123")
exp = path.Join(root, "pods/abc123/plugins")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPodPluginDir("abc123", "foobar")
exp = path.Join(root, "pods/abc123/plugins/foobar")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPodContainerDir("abc123", "def456")
exp = path.Join(root, "pods/abc123/containers/def456")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
}
func TestKubeletDirsCompat(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
root := kubelet.rootDirectory
if err := os.MkdirAll(root, 0750); err != nil {
t.Fatalf("can't mkdir(%q): %s", root, err)
}
var exp, got string
// Old-style pod dir.
if err := os.MkdirAll(fmt.Sprintf("%s/oldpod", root), 0750); err != nil {
t.Fatalf("can't mkdir(%q): %s", root, err)
}
// New-style pod dir.
if err := os.MkdirAll(fmt.Sprintf("%s/pods/newpod", root), 0750); err != nil {
t.Fatalf("can't mkdir(%q): %s", root, err)
}
// Both-style pod dir.
if err := os.MkdirAll(fmt.Sprintf("%s/bothpod", root), 0750); err != nil {
t.Fatalf("can't mkdir(%q): %s", root, err)
}
if err := os.MkdirAll(fmt.Sprintf("%s/pods/bothpod", root), 0750); err != nil {
t.Fatalf("can't mkdir(%q): %s", root, err)
}
got = kubelet.getPodDir("oldpod")
exp = path.Join(root, "oldpod")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPodDir("newpod")
exp = path.Join(root, "pods/newpod")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPodDir("bothpod")
exp = path.Join(root, "pods/bothpod")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPodDir("neitherpod")
exp = path.Join(root, "pods/neitherpod")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
root = kubelet.getPodDir("newpod")
// Old-style container dir.
if err := os.MkdirAll(fmt.Sprintf("%s/oldctr", root), 0750); err != nil {
t.Fatalf("can't mkdir(%q): %s", root, err)
}
// New-style container dir.
if err := os.MkdirAll(fmt.Sprintf("%s/containers/newctr", root), 0750); err != nil {
t.Fatalf("can't mkdir(%q): %s", root, err)
}
// Both-style container dir.
if err := os.MkdirAll(fmt.Sprintf("%s/bothctr", root), 0750); err != nil {
t.Fatalf("can't mkdir(%q): %s", root, err)
}
if err := os.MkdirAll(fmt.Sprintf("%s/containers/bothctr", root), 0750); err != nil {
t.Fatalf("can't mkdir(%q): %s", root, err)
}
got = kubelet.getPodContainerDir("newpod", "oldctr")
exp = path.Join(root, "oldctr")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPodContainerDir("newpod", "newctr")
exp = path.Join(root, "containers/newctr")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPodContainerDir("newpod", "bothctr")
exp = path.Join(root, "containers/bothctr")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
got = kubelet.getPodContainerDir("newpod", "neitherctr")
exp = path.Join(root, "containers/neitherctr")
if got != exp {
t.Errorf("expected %q', got %q", exp, got)
}
}
2014-06-06 23:40:48 +00:00
func TestKillContainerWithError(t *testing.T) {
2015-02-08 21:22:19 +00:00
containers := []docker.APIContainers{
{
ID: "1234",
Names: []string{"/k8s_foo_qux_1234_42"},
2014-06-06 23:40:48 +00:00
},
2015-02-08 21:22:19 +00:00
{
ID: "5678",
Names: []string{"/k8s_bar_qux_5678_42"},
},
}
fakeDocker := &dockertools.FakeDockerClient{
Err: fmt.Errorf("sample error"),
ContainerList: append([]docker.APIContainers{}, containers...),
2014-06-06 23:40:48 +00:00
}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
2015-02-08 21:22:19 +00:00
for _, c := range fakeDocker.ContainerList {
kubelet.readiness.set(c.ID, true)
}
kubelet.dockerClient = fakeDocker
err := kubelet.killContainer(&fakeDocker.ContainerList[0])
if err == nil {
t.Errorf("expected error, found nil")
}
verifyCalls(t, fakeDocker, []string{"stop"})
2015-02-08 21:22:19 +00:00
killedContainer := containers[0]
liveContainer := containers[1]
if _, found := kubelet.readiness.states[killedContainer.ID]; found {
t.Errorf("exepcted container entry ID '%v' to not be found. states: %+v", killedContainer.ID, kubelet.readiness.states)
}
if _, found := kubelet.readiness.states[liveContainer.ID]; !found {
t.Errorf("exepcted container entry ID '%v' to be found. states: %+v", liveContainer.ID, kubelet.readiness.states)
}
2014-06-06 23:40:48 +00:00
}
func TestKillContainer(t *testing.T) {
2015-02-08 21:22:19 +00:00
containers := []docker.APIContainers{
2014-06-12 21:09:40 +00:00
{
ID: "1234",
Names: []string{"/k8s_foo_qux_1234_42"},
2014-06-06 23:40:48 +00:00
},
2014-06-12 21:09:40 +00:00
{
ID: "5678",
Names: []string{"/k8s_bar_qux_5678_42"},
2014-06-06 23:40:48 +00:00
},
}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
2015-02-08 21:22:19 +00:00
fakeDocker.ContainerList = append([]docker.APIContainers{}, containers...)
fakeDocker.Container = &docker.Container{
2014-10-22 17:02:02 +00:00
Name: "foobar",
2014-06-06 23:40:48 +00:00
}
2015-02-08 21:22:19 +00:00
for _, c := range fakeDocker.ContainerList {
kubelet.readiness.set(c.ID, true)
}
2014-06-06 23:40:48 +00:00
err := kubelet.killContainer(&fakeDocker.ContainerList[0])
if err != nil {
t.Errorf("unexpected error: %v", err)
}
verifyCalls(t, fakeDocker, []string{"stop"})
2015-02-08 21:22:19 +00:00
killedContainer := containers[0]
liveContainer := containers[1]
if _, found := kubelet.readiness.states[killedContainer.ID]; found {
t.Errorf("exepcted container entry ID '%v' to not be found. states: %+v", killedContainer.ID, kubelet.readiness.states)
}
if _, found := kubelet.readiness.states[liveContainer.ID]; !found {
t.Errorf("exepcted container entry ID '%v' to be found. states: %+v", liveContainer.ID, kubelet.readiness.states)
}
2014-06-06 23:40:48 +00:00
}
type channelReader struct {
2015-03-13 13:19:07 +00:00
list [][]api.Pod
2014-06-06 23:40:48 +00:00
wg sync.WaitGroup
}
func startReading(channel <-chan interface{}) *channelReader {
2014-06-06 23:40:48 +00:00
cr := &channelReader{}
cr.wg.Add(1)
go func() {
for {
update, ok := <-channel
2014-06-06 23:40:48 +00:00
if !ok {
break
}
cr.list = append(cr.list, update.(PodUpdate).Pods)
2014-06-06 23:40:48 +00:00
}
cr.wg.Done()
}()
return cr
}
2015-03-13 13:19:07 +00:00
func (cr *channelReader) GetList() [][]api.Pod {
2014-06-06 23:40:48 +00:00
cr.wg.Wait()
return cr.list
}
var emptyPodUIDs map[types.UID]metrics.SyncPodType
func TestSyncPodsDoesNothing(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
2015-02-23 21:04:45 +00:00
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
waitGroup := testKubelet.waitGroup
container := api.Container{Name: "bar"}
fakeDocker.ContainerList = []docker.APIContainers{
2014-06-12 21:09:40 +00:00
{
// format is // k8s_<container-id>_<pod-fullname>_<pod-uid>_<random>
Names: []string{"/k8s_bar." + strconv.FormatUint(dockertools.HashContainer(&container), 16) + "_foo_new_12345678_0"},
2014-06-06 23:40:48 +00:00
ID: "1234",
},
{
2015-01-21 00:59:26 +00:00
// pod infra container
Names: []string{"/k8s_POD_foo_new_12345678_0"},
ID: "9876",
},
2014-06-06 23:40:48 +00:00
}
pods := []api.Pod{
2014-06-12 21:09:40 +00:00
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
container,
},
2014-06-06 23:40:48 +00:00
},
},
}
kubelet.podManager.SetPods(pods)
2015-02-27 09:19:41 +00:00
waitGroup.Add(1)
err := kubelet.SyncPods(pods, emptyPodUIDs, *newMirrorPods(), time.Now())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
2015-02-27 09:19:41 +00:00
waitGroup.Wait()
verifyCalls(t, fakeDocker, []string{"list", "list", "list", "inspect_container", "inspect_container", "list", "inspect_container", "inspect_container"})
2014-06-06 23:40:48 +00:00
}
func TestSyncPodsWithTerminationLog(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
2015-02-23 21:04:45 +00:00
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
waitGroup := testKubelet.waitGroup
container := api.Container{
Name: "bar",
TerminationMessagePath: "/dev/somepath",
}
fakeDocker.ContainerList = []docker.APIContainers{}
pods := []api.Pod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
container,
},
},
},
}
kubelet.podManager.SetPods(pods)
2015-02-27 09:19:41 +00:00
waitGroup.Add(1)
err := kubelet.SyncPods(pods, emptyPodUIDs, *newMirrorPods(), time.Now())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
2015-02-27 09:19:41 +00:00
waitGroup.Wait()
verifyCalls(t, fakeDocker, []string{
"list", "list", "list", "create", "start", "inspect_container", "create", "start", "list", "inspect_container", "inspect_container"})
fakeDocker.Lock()
parts := strings.Split(fakeDocker.Container.HostConfig.Binds[0], ":")
if !matchString(t, kubelet.getPodContainerDir("12345678", "bar")+"/k8s_bar\\.[a-f0-9]", parts[0]) {
t.Errorf("Unexpected host path: %s", parts[0])
}
if parts[1] != "/dev/somepath" {
t.Errorf("Unexpected container path: %s", parts[1])
}
fakeDocker.Unlock()
}
func matchString(t *testing.T, pattern, str string) bool {
match, err := regexp.MatchString(pattern, str)
if err != nil {
t.Logf("unexpected error: %v", err)
}
return match
}
func TestSyncPodsCreatesNetAndContainer(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
2015-02-23 21:04:45 +00:00
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
waitGroup := testKubelet.waitGroup
2015-01-21 00:59:26 +00:00
kubelet.podInfraContainerImage = "custom_image_name"
fakeDocker.ContainerList = []docker.APIContainers{}
pods := []api.Pod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "bar"},
},
},
},
}
kubelet.podManager.SetPods(pods)
2015-02-27 09:19:41 +00:00
waitGroup.Add(1)
err := kubelet.SyncPods(pods, emptyPodUIDs, *newMirrorPods(), time.Now())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
2015-02-27 09:19:41 +00:00
waitGroup.Wait()
verifyCalls(t, fakeDocker, []string{
"list", "list", "list", "create", "start", "inspect_container", "create", "start", "list", "inspect_container", "inspect_container"})
fakeDocker.Lock()
found := false
for _, c := range fakeDocker.ContainerList {
2015-01-21 00:59:26 +00:00
if c.Image == "custom_image_name" && strings.HasPrefix(c.Names[0], "/k8s_POD") {
found = true
}
}
if !found {
t.Errorf("Custom pod infra container not found: %v", fakeDocker.ContainerList)
}
if len(fakeDocker.Created) != 2 ||
!matchString(t, "k8s_POD\\.[a-f0-9]+_foo_new_", fakeDocker.Created[0]) ||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo_new_", fakeDocker.Created[1]) {
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
}
fakeDocker.Unlock()
}
func TestSyncPodsCreatesNetAndContainerPullsImage(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
2015-02-23 21:04:45 +00:00
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
waitGroup := testKubelet.waitGroup
puller := kubelet.dockerPuller.(*dockertools.FakeDockerPuller)
puller.HasImages = []string{}
2015-01-21 00:59:26 +00:00
kubelet.podInfraContainerImage = "custom_image_name"
fakeDocker.ContainerList = []docker.APIContainers{}
pods := []api.Pod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
2015-01-21 04:30:42 +00:00
{Name: "bar", Image: "something", ImagePullPolicy: "IfNotPresent"},
},
},
},
}
2015-02-27 09:19:41 +00:00
waitGroup.Add(1)
kubelet.podManager.SetPods(pods)
err := kubelet.SyncPods(pods, emptyPodUIDs, *newMirrorPods(), time.Now())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
2015-02-27 09:19:41 +00:00
waitGroup.Wait()
verifyCalls(t, fakeDocker, []string{
"list", "list", "list", "create", "start", "inspect_container", "create", "start", "list", "inspect_container", "inspect_container"})
fakeDocker.Lock()
if !reflect.DeepEqual(puller.ImagesPulled, []string{"custom_image_name", "something"}) {
t.Errorf("Unexpected pulled containers: %v", puller.ImagesPulled)
}
if len(fakeDocker.Created) != 2 ||
!matchString(t, "k8s_POD\\.[a-f0-9]+_foo_new_", fakeDocker.Created[0]) ||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo_new_", fakeDocker.Created[1]) {
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
}
fakeDocker.Unlock()
}
func TestSyncPodsWithPodInfraCreatesContainer(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
2015-02-23 21:04:45 +00:00
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
waitGroup := testKubelet.waitGroup
fakeDocker.ContainerList = []docker.APIContainers{
{
2015-01-21 00:59:26 +00:00
// pod infra container
Names: []string{"/k8s_POD_foo_new_12345678_0"},
ID: "9876",
},
}
pods := []api.Pod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "bar"},
},
},
},
}
2015-02-27 09:19:41 +00:00
waitGroup.Add(1)
kubelet.podManager.SetPods(pods)
err := kubelet.SyncPods(pods, emptyPodUIDs, *newMirrorPods(), time.Now())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
2015-02-27 09:19:41 +00:00
waitGroup.Wait()
verifyCalls(t, fakeDocker, []string{
2015-03-18 16:44:50 +00:00
"list", "list", "list", "inspect_container", "list", "create", "start", "list", "inspect_container", "inspect_container"})
fakeDocker.Lock()
if len(fakeDocker.Created) != 1 ||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo_new_", fakeDocker.Created[0]) {
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
}
fakeDocker.Unlock()
}
func TestSyncPodsWithPodInfraCreatesContainerCallsHandler(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
2015-02-23 21:04:45 +00:00
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
waitGroup := testKubelet.waitGroup
fakeHttp := fakeHTTP{}
kubelet.httpClient = &fakeHttp
fakeDocker.ContainerList = []docker.APIContainers{
{
2015-01-21 00:59:26 +00:00
// pod infra container
Names: []string{"/k8s_POD_foo_new_12345678_0"},
ID: "9876",
},
}
pods := []api.Pod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "bar",
Lifecycle: &api.Lifecycle{
PostStart: &api.Handler{
HTTPGet: &api.HTTPGetAction{
Host: "foo",
Port: util.IntOrString{IntVal: 8080, Kind: util.IntstrInt},
Path: "bar",
},
},
},
},
},
},
},
}
2015-02-27 09:19:41 +00:00
waitGroup.Add(1)
kubelet.podManager.SetPods(pods)
err := kubelet.SyncPods(pods, emptyPodUIDs, *newMirrorPods(), time.Now())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
2015-02-27 09:19:41 +00:00
waitGroup.Wait()
verifyCalls(t, fakeDocker, []string{
2015-03-18 16:44:50 +00:00
"list", "list", "list", "inspect_container", "list", "create", "start", "list", "inspect_container", "inspect_container"})
fakeDocker.Lock()
if len(fakeDocker.Created) != 1 ||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo_new_", fakeDocker.Created[0]) {
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
}
fakeDocker.Unlock()
if fakeHttp.url != "http://foo:8080/bar" {
t.Errorf("Unexpected handler: %s", fakeHttp.url)
}
}
func TestSyncPodsDeletesWithNoPodInfraContainer(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
2015-02-23 21:04:45 +00:00
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
waitGroup := testKubelet.waitGroup
fakeDocker.ContainerList = []docker.APIContainers{
{
// format is // k8s_<container-id>_<pod-fullname>_<pod-uid>
Names: []string{"/k8s_bar1_foo1_new_12345678_0"},
ID: "1234",
},
{
// format is // k8s_<container-id>_<pod-fullname>_<pod-uid>
Names: []string{"/k8s_bar2_foo2_new_87654321_0"},
ID: "5678",
},
{
// format is // k8s_<container-id>_<pod-fullname>_<pod-uid>
Names: []string{"/k8s_POD_foo2_new_87654321_0"},
ID: "8765",
},
}
pods := []api.Pod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo1",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "bar1"},
},
},
},
{
ObjectMeta: api.ObjectMeta{
UID: "87654321",
Name: "foo2",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "bar2"},
},
},
},
}
waitGroup.Add(2)
kubelet.podManager.SetPods(pods)
err := kubelet.SyncPods(pods, emptyPodUIDs, *newMirrorPods(), time.Now())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
2015-02-27 09:19:41 +00:00
waitGroup.Wait()
verifyCalls(t, fakeDocker, []string{
"list", "list", "list", "list", "inspect_container", "inspect_container", "list", "inspect_container", "inspect_container", "stop", "create", "start", "inspect_container", "create", "start", "list", "inspect_container", "inspect_container"})
// A map iteration is used to delete containers, so must not depend on
// order here.
expectedToStop := map[string]bool{
"1234": true,
}
fakeDocker.Lock()
if len(fakeDocker.Stopped) != 1 || !expectedToStop[fakeDocker.Stopped[0]] {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
}
fakeDocker.Unlock()
}
func TestSyncPodsDeletesWhenSourcesAreReady(t *testing.T) {
ready := false
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
2015-02-23 21:04:45 +00:00
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
kubelet.sourcesReady = func() bool { return ready }
fakeDocker.ContainerList = []docker.APIContainers{
{
// the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s_foo_bar_new_12345678_42"},
ID: "1234",
},
{
2015-01-21 00:59:26 +00:00
// pod infra container
Names: []string{"/k8s_POD_foo_new_12345678_42"},
ID: "9876",
},
}
if err := kubelet.SyncPods([]api.Pod{}, emptyPodUIDs, *newMirrorPods(), time.Now()); err != nil {
t.Errorf("unexpected error: %v", err)
}
// Validate nothing happened.
verifyCalls(t, fakeDocker, []string{"list"})
fakeDocker.ClearCalls()
ready = true
if err := kubelet.SyncPods([]api.Pod{}, emptyPodUIDs, *newMirrorPods(), time.Now()); err != nil {
t.Errorf("unexpected error: %v", err)
}
verifyCalls(t, fakeDocker, []string{"list", "stop", "stop", "inspect_container", "inspect_container"})
// A map iteration is used to delete containers, so must not depend on
// order here.
expectedToStop := map[string]bool{
"1234": true,
"9876": true,
}
if len(fakeDocker.Stopped) != 2 ||
!expectedToStop[fakeDocker.Stopped[0]] ||
!expectedToStop[fakeDocker.Stopped[1]] {
2015-01-13 05:47:49 +00:00
t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
}
}
func TestSyncPodsDeletes(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
2015-02-23 21:04:45 +00:00
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
fakeDocker.ContainerList = []docker.APIContainers{
2014-06-12 21:09:40 +00:00
{
// the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s_foo_bar_new_12345678_42"},
2014-06-06 23:40:48 +00:00
ID: "1234",
},
{
2015-01-21 00:59:26 +00:00
// pod infra container
Names: []string{"/k8s_POD_foo_new_12345678_42"},
ID: "9876",
},
{
Names: []string{"foo"},
ID: "4567",
},
2014-06-06 23:40:48 +00:00
}
err := kubelet.SyncPods([]api.Pod{}, emptyPodUIDs, *newMirrorPods(), time.Now())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
verifyCalls(t, fakeDocker, []string{"list", "stop", "stop", "inspect_container", "inspect_container"})
// A map iteration is used to delete containers, so must not depend on
// order here.
expectedToStop := map[string]bool{
"1234": true,
"9876": true,
}
if len(fakeDocker.Stopped) != 2 ||
!expectedToStop[fakeDocker.Stopped[0]] ||
!expectedToStop[fakeDocker.Stopped[1]] {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
2014-06-06 23:40:48 +00:00
}
}
func TestSyncPodDeletesDuplicate(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
dockerContainers := dockertools.DockerContainers{
"1234": &docker.APIContainers{
// the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s_foo_bar_new_12345678_1111"},
ID: "1234",
},
"9876": &docker.APIContainers{
2015-01-21 00:59:26 +00:00
// pod infra container
Names: []string{"/k8s_POD_bar_new_12345678_2222"},
ID: "9876",
},
"4567": &docker.APIContainers{
// Duplicate for the same container.
Names: []string{"/k8s_foo_bar_new_12345678_3333"},
ID: "4567",
},
}
2015-03-13 13:19:07 +00:00
bound := api.Pod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "bar",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "foo"},
},
},
}
pods := []api.Pod{bound}
kubelet.podManager.SetPods(pods)
err := kubelet.syncPod(&bound, false, dockerContainers)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
verifyCalls(t, fakeDocker, []string{"list", "stop", "list"})
// Expect one of the duplicates to be killed.
if len(fakeDocker.Stopped) != 1 || (fakeDocker.Stopped[0] != "1234" && fakeDocker.Stopped[0] != "4567") {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
}
}
func TestSyncPodBadHash(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
dockerContainers := dockertools.DockerContainers{
"1234": &docker.APIContainers{
// the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s_bar.1234_foo_new_12345678_42"},
ID: "1234",
},
"9876": &docker.APIContainers{
2015-01-21 00:59:26 +00:00
// pod infra container
Names: []string{"/k8s_POD_foo_new_12345678_42"},
ID: "9876",
},
}
2015-03-13 13:19:07 +00:00
bound := api.Pod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "bar"},
},
},
}
pods := []api.Pod{bound}
kubelet.podManager.SetPods(pods)
err := kubelet.syncPod(&bound, false, dockerContainers)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
//verifyCalls(t, fakeDocker, []string{"list", "stop", "list", "create", "start", "stop", "create", "start", "inspect_container"})
verifyCalls(t, fakeDocker, []string{"list", "stop", "stop", "create", "start", "inspect_container", "create", "start", "list", "inspect_container", "inspect_container"})
// A map interation is used to delete containers, so must not depend on
// order here.
expectedToStop := map[string]bool{
"1234": true,
"9876": true,
}
if len(fakeDocker.Stopped) != 2 ||
(!expectedToStop[fakeDocker.Stopped[0]] &&
!expectedToStop[fakeDocker.Stopped[1]]) {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
}
}
func TestSyncPodUnhealthy(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
dockerContainers := dockertools.DockerContainers{
"1234": &docker.APIContainers{
2014-07-03 05:35:50 +00:00
// the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s_bar_foo_new_12345678_42"},
2014-07-03 05:35:50 +00:00
ID: "1234",
},
"9876": &docker.APIContainers{
2015-01-21 00:59:26 +00:00
// pod infra container
Names: []string{"/k8s_POD_foo_new_12345678_42"},
2014-07-03 05:35:50 +00:00
ID: "9876",
},
}
2015-03-13 13:19:07 +00:00
bound := api.Pod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "bar",
LivenessProbe: &api.Probe{
2014-09-28 04:16:30 +00:00
// Always returns healthy == false
2014-07-03 05:35:50 +00:00
},
},
},
},
}
pods := []api.Pod{bound}
kubelet.podManager.SetPods(pods)
err := kubelet.syncPod(&bound, false, dockerContainers)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
verifyCalls(t, fakeDocker, []string{"list", "stop", "create", "start", "list", "inspect_container"})
2014-07-03 05:35:50 +00:00
// A map interation is used to delete containers, so must not depend on
// order here.
expectedToStop := map[string]bool{
"1234": true,
}
if len(fakeDocker.Stopped) != len(expectedToStop) ||
!expectedToStop[fakeDocker.Stopped[0]] {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
2014-07-03 05:35:50 +00:00
}
}
func TestMountExternalVolumes(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
kubelet.volumePluginMgr.InitPlugins([]volume.VolumePlugin{&volume.FakeVolumePlugin{"fake", nil}}, &volumeHost{kubelet})
2015-03-13 13:19:07 +00:00
pod := api.Pod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
2014-10-22 17:02:02 +00:00
Name: "foo",
Namespace: "test",
},
Spec: api.PodSpec{
Volumes: []api.Volume{
{
Name: "vol1",
VolumeSource: api.VolumeSource{},
},
},
},
}
podVolumes, err := kubelet.mountExternalVolumes(&pod)
if err != nil {
t.Errorf("Expected sucess: %v", err)
}
expectedPodVolumes := []string{"vol1"}
if len(expectedPodVolumes) != len(podVolumes) {
t.Errorf("Unexpected volumes. Expected %#v got %#v. Manifest was: %#v", expectedPodVolumes, podVolumes, pod)
}
for _, name := range expectedPodVolumes {
if _, ok := podVolumes[name]; !ok {
2015-03-13 13:19:07 +00:00
t.Errorf("api.Pod volumes map is missing key: %s. %#v", name, podVolumes)
}
}
}
func TestGetPodVolumesFromDisk(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
plug := &volume.FakeVolumePlugin{"fake", nil}
kubelet.volumePluginMgr.InitPlugins([]volume.VolumePlugin{plug}, &volumeHost{kubelet})
volsOnDisk := []struct {
podUID types.UID
volName string
}{
{"pod1", "vol1"},
{"pod1", "vol2"},
{"pod2", "vol1"},
}
expectedPaths := []string{}
for i := range volsOnDisk {
fv := volume.FakeVolume{volsOnDisk[i].podUID, volsOnDisk[i].volName, plug}
fv.SetUp()
expectedPaths = append(expectedPaths, fv.GetPath())
}
volumesFound := kubelet.getPodVolumesFromDisk()
if len(volumesFound) != len(expectedPaths) {
t.Errorf("Expected to find %d cleaners, got %d", len(expectedPaths), len(volumesFound))
}
for _, ep := range expectedPaths {
found := false
for _, cl := range volumesFound {
if ep == cl.GetPath() {
found = true
break
}
}
if !found {
t.Errorf("Could not find a volume with path %s", ep)
}
}
}
type stubVolume struct {
path string
}
func (f *stubVolume) GetPath() string {
return f.path
}
func TestMakeVolumesAndBinds(t *testing.T) {
container := api.Container{
VolumeMounts: []api.VolumeMount{
2014-06-13 01:34:47 +00:00
{
MountPath: "/mnt/path",
Name: "disk",
ReadOnly: false,
},
2014-06-19 23:59:48 +00:00
{
MountPath: "/mnt/path3",
2014-08-27 05:08:06 +00:00
Name: "disk",
ReadOnly: true,
},
{
MountPath: "/mnt/path4",
Name: "disk4",
ReadOnly: false,
},
{
MountPath: "/mnt/path5",
Name: "disk5",
ReadOnly: false,
},
},
}
2014-08-27 05:08:06 +00:00
podVolumes := volumeMap{
"disk": &stubVolume{"/mnt/disk"},
"disk4": &stubVolume{"/mnt/host"},
"disk5": &stubVolume{"/var/lib/kubelet/podID/volumes/empty/disk5"},
2014-08-27 05:08:06 +00:00
}
2014-06-19 23:59:48 +00:00
2015-03-13 13:19:07 +00:00
binds := makeBinds(&container, podVolumes)
2014-08-27 05:08:06 +00:00
expectedBinds := []string{
"/mnt/disk:/mnt/path",
"/mnt/disk:/mnt/path3:ro",
"/mnt/host:/mnt/path4",
"/var/lib/kubelet/podID/volumes/empty/disk5:/mnt/path5",
}
2014-08-27 05:08:06 +00:00
2014-06-19 23:59:48 +00:00
if len(binds) != len(expectedBinds) {
2014-07-18 19:03:22 +00:00
t.Errorf("Unexpected binds: Expected %#v got %#v. Container was: %#v", expectedBinds, binds, container)
2014-06-19 23:59:48 +00:00
}
verifyStringArrayEquals(t, binds, expectedBinds)
}
func TestMakePortsAndBindings(t *testing.T) {
container := api.Container{
Ports: []api.ContainerPort{
2014-06-13 01:34:47 +00:00
{
ContainerPort: 80,
HostPort: 8080,
HostIP: "127.0.0.1",
},
2014-06-13 01:34:47 +00:00
{
ContainerPort: 443,
HostPort: 443,
2014-06-16 04:57:29 +00:00
Protocol: "tcp",
2014-06-16 04:19:35 +00:00
},
{
ContainerPort: 444,
HostPort: 444,
2014-06-16 04:57:29 +00:00
Protocol: "udp",
2014-06-16 04:19:35 +00:00
},
{
ContainerPort: 445,
HostPort: 445,
2014-06-16 04:57:29 +00:00
Protocol: "foobar",
},
},
}
exposedPorts, bindings := makePortsAndBindings(&container)
if len(container.Ports) != len(exposedPorts) ||
len(container.Ports) != len(bindings) {
t.Errorf("Unexpected ports and bindings, %#v %#v %#v", container, exposedPorts, bindings)
}
2014-06-16 04:57:29 +00:00
for key, value := range bindings {
switch value[0].HostPort {
2014-06-16 04:19:35 +00:00
case "8080":
2014-06-16 04:57:29 +00:00
if !reflect.DeepEqual(docker.Port("80/tcp"), key) {
t.Errorf("Unexpected docker port: %#v", key)
2014-06-16 04:19:35 +00:00
}
if value[0].HostIP != "127.0.0.1" {
t.Errorf("Unexpected host IP: %s", value[0].HostIP)
}
2014-06-16 04:19:35 +00:00
case "443":
2014-06-16 04:57:29 +00:00
if !reflect.DeepEqual(docker.Port("443/tcp"), key) {
t.Errorf("Unexpected docker port: %#v", key)
2014-06-16 04:19:35 +00:00
}
if value[0].HostIP != "" {
t.Errorf("Unexpected host IP: %s", value[0].HostIP)
}
2014-06-16 04:19:35 +00:00
case "444":
2014-06-16 04:57:29 +00:00
if !reflect.DeepEqual(docker.Port("444/udp"), key) {
t.Errorf("Unexpected docker port: %#v", key)
2014-06-16 04:19:35 +00:00
}
if value[0].HostIP != "" {
t.Errorf("Unexpected host IP: %s", value[0].HostIP)
}
2014-06-16 04:19:35 +00:00
case "445":
2014-06-16 04:57:29 +00:00
if !reflect.DeepEqual(docker.Port("445/tcp"), key) {
t.Errorf("Unexpected docker port: %#v", key)
2014-06-16 04:19:35 +00:00
}
if value[0].HostIP != "" {
t.Errorf("Unexpected host IP: %s", value[0].HostIP)
}
2014-06-16 04:57:29 +00:00
}
}
}
func TestFieldPath(t *testing.T) {
2015-03-13 13:19:07 +00:00
pod := &api.Pod{Spec: api.PodSpec{Containers: []api.Container{
{Name: "foo"},
{Name: "bar"},
{Name: ""},
{Name: "baz"},
}}}
table := map[string]struct {
2015-03-13 13:19:07 +00:00
pod *api.Pod
container *api.Container
path string
success bool
}{
"basic": {pod, &api.Container{Name: "foo"}, "spec.containers{foo}", true},
"basic2": {pod, &api.Container{Name: "baz"}, "spec.containers{baz}", true},
"emptyName": {pod, &api.Container{Name: ""}, "spec.containers[2]", true},
"basicSamePointer": {pod, &pod.Spec.Containers[0], "spec.containers{foo}", true},
"missing": {pod, &api.Container{Name: "qux"}, "", false},
}
for name, item := range table {
res, err := fieldPath(item.pod, item.container)
if item.success == false {
if err == nil {
t.Errorf("%v: unexpected non-error", name)
}
continue
}
if err != nil {
t.Errorf("%v: unexpected error: %v", name, err)
continue
}
if e, a := item.path, res; e != a {
t.Errorf("%v: wanted %v, got %v", name, e, a)
}
}
}
type errorTestingDockerClient struct {
dockertools.FakeDockerClient
listContainersError error
containerList []docker.APIContainers
}
func (f *errorTestingDockerClient) ListContainers(options docker.ListContainersOptions) ([]docker.APIContainers, error) {
return f.containerList, f.listContainersError
}
2014-07-15 22:40:02 +00:00
func TestGetContainerInfo(t *testing.T) {
2014-07-02 18:21:29 +00:00
containerID := "ab2cdf"
containerPath := fmt.Sprintf("/docker/%v", containerID)
containerInfo := cadvisorApi.ContainerInfo{
ContainerReference: cadvisorApi.ContainerReference{
2014-06-19 20:22:20 +00:00
Name: containerPath,
},
}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
mockCadvisor := testKubelet.fakeCadvisor
cadvisorReq := &cadvisorApi.ContainerInfoRequest{}
2014-12-01 11:10:59 +00:00
mockCadvisor.On("DockerContainer", containerID, cadvisorReq).Return(containerInfo, nil)
2015-02-23 21:04:45 +00:00
fakeDocker.ContainerList = []docker.APIContainers{
2014-06-19 20:22:20 +00:00
{
2014-07-01 21:05:10 +00:00
ID: containerID,
// pod id: qux
// container id: foo
Names: []string{"/k8s_foo_qux_ns_1234_42"},
2014-07-01 21:05:10 +00:00
},
}
stats, err := kubelet.GetContainerInfo("qux_ns", "", "foo", cadvisorReq)
2014-07-01 21:05:10 +00:00
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if stats == nil {
t.Fatalf("stats should not be nil")
}
2014-07-01 21:05:10 +00:00
mockCadvisor.AssertExpectations(t)
}
func TestGetRootInfo(t *testing.T) {
2014-07-01 21:05:10 +00:00
containerPath := "/"
containerInfo := &cadvisorApi.ContainerInfo{
ContainerReference: cadvisorApi.ContainerReference{
2014-07-01 21:05:10 +00:00
Name: containerPath,
},
}
fakeDocker := dockertools.FakeDockerClient{}
2014-06-19 20:22:20 +00:00
mockCadvisor := &cadvisor.Mock{}
cadvisorReq := &cadvisorApi.ContainerInfoRequest{}
mockCadvisor.On("ContainerInfo", containerPath, cadvisorReq).Return(containerInfo, nil)
2014-07-01 21:05:10 +00:00
kubelet := Kubelet{
dockerClient: &fakeDocker,
dockerPuller: &dockertools.FakeDockerPuller{},
cadvisor: mockCadvisor,
2014-07-01 21:05:10 +00:00
}
// If the container name is an empty string, then it means the root container.
2014-10-20 03:54:52 +00:00
_, err := kubelet.GetRootInfo(cadvisorReq)
2014-06-19 20:22:20 +00:00
if err != nil {
t.Errorf("unexpected error: %v", err)
}
mockCadvisor.AssertExpectations(t)
}
2014-06-19 20:25:54 +00:00
2014-07-15 22:40:02 +00:00
func TestGetContainerInfoWhenCadvisorFailed(t *testing.T) {
2014-07-02 18:21:29 +00:00
containerID := "ab2cdf"
2014-06-19 20:34:26 +00:00
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
mockCadvisor := testKubelet.fakeCadvisor
cadvisorApiFailure := fmt.Errorf("cAdvisor failure")
containerInfo := cadvisorApi.ContainerInfo{}
cadvisorReq := &cadvisorApi.ContainerInfoRequest{}
mockCadvisor.On("DockerContainer", containerID, cadvisorReq).Return(containerInfo, cadvisorApiFailure)
fakeDocker.ContainerList = []docker.APIContainers{
2014-06-19 20:34:26 +00:00
{
2014-07-01 21:05:10 +00:00
ID: containerID,
// pod id: qux
// container id: foo
Names: []string{"/k8s_foo_qux_ns_uuid_1234"},
2014-06-19 20:34:26 +00:00
},
}
stats, err := kubelet.GetContainerInfo("qux_ns", "uuid", "foo", cadvisorReq)
2014-06-19 20:34:26 +00:00
if stats != nil {
t.Errorf("non-nil stats on error")
}
if err == nil {
t.Errorf("expect error but received nil error")
return
}
if err.Error() != cadvisorApiFailure.Error() {
t.Errorf("wrong error message. expect %v, got %v", cadvisorApiFailure, err)
2014-06-19 20:34:26 +00:00
}
mockCadvisor.AssertExpectations(t)
}
2014-07-15 22:40:02 +00:00
func TestGetContainerInfoOnNonExistContainer(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
mockCadvisor := testKubelet.fakeCadvisor
fakeDocker.ContainerList = []docker.APIContainers{}
2014-06-19 20:34:26 +00:00
2014-09-05 09:49:11 +00:00
stats, _ := kubelet.GetContainerInfo("qux", "", "foo", nil)
2014-06-19 20:34:26 +00:00
if stats != nil {
t.Errorf("non-nil stats on non exist container")
}
mockCadvisor.AssertExpectations(t)
}
func TestGetContainerInfoWhenDockerToolsFailed(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
mockCadvisor := testKubelet.fakeCadvisor
expectedErr := fmt.Errorf("List containers error")
kubelet.dockerClient = &errorTestingDockerClient{listContainersError: expectedErr}
stats, err := kubelet.GetContainerInfo("qux", "", "foo", nil)
if err == nil {
t.Errorf("Expected error from dockertools, got none")
}
if err.Error() != expectedErr.Error() {
t.Errorf("Expected error %v got %v", expectedErr.Error(), err.Error())
}
if stats != nil {
t.Errorf("non-nil stats when dockertools failed")
}
mockCadvisor.AssertExpectations(t)
}
func TestGetContainerInfoWithNoContainers(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
mockCadvisor := testKubelet.fakeCadvisor
kubelet.dockerClient = &errorTestingDockerClient{listContainersError: nil}
stats, err := kubelet.GetContainerInfo("qux_ns", "", "foo", nil)
if err == nil {
t.Errorf("Expected error from cadvisor client, got none")
}
if err != ErrNoKubeletContainers {
t.Errorf("Expected error %v, got %v", ErrNoKubeletContainers.Error(), err.Error())
}
if stats != nil {
t.Errorf("non-nil stats when dockertools returned no containers")
}
mockCadvisor.AssertExpectations(t)
}
func TestGetContainerInfoWithNoMatchingContainers(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
mockCadvisor := testKubelet.fakeCadvisor
containerList := []docker.APIContainers{
{
ID: "fakeId",
Names: []string{"/k8s_bar_qux_ns_1234_42"},
},
}
kubelet.dockerClient = &errorTestingDockerClient{listContainersError: nil, containerList: containerList}
stats, err := kubelet.GetContainerInfo("qux_ns", "", "foo", nil)
if err == nil {
t.Errorf("Expected error from cadvisor client, got none")
}
if err != ErrContainerNotFound {
t.Errorf("Expected error %v, got %v", ErrContainerNotFound.Error(), err.Error())
}
if stats != nil {
t.Errorf("non-nil stats when dockertools returned no containers")
}
mockCadvisor.AssertExpectations(t)
}
2014-08-07 18:15:11 +00:00
type fakeContainerCommandRunner struct {
Cmd []string
ID string
E error
Stdin io.Reader
Stdout io.WriteCloser
Stderr io.WriteCloser
TTY bool
Port uint16
Stream io.ReadWriteCloser
2014-08-07 18:15:11 +00:00
}
func (f *fakeContainerCommandRunner) RunInContainer(id string, cmd []string) ([]byte, error) {
f.Cmd = cmd
f.ID = id
return []byte{}, f.E
}
func (f *fakeContainerCommandRunner) GetDockerServerVersion() ([]uint, error) {
return nil, nil
}
func (f *fakeContainerCommandRunner) ExecInContainer(id string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool) error {
f.Cmd = cmd
f.ID = id
f.Stdin = in
f.Stdout = out
f.Stderr = err
f.TTY = tty
return f.E
}
func (f *fakeContainerCommandRunner) PortForward(podInfraContainerID string, port uint16, stream io.ReadWriteCloser) error {
f.ID = podInfraContainerID
f.Port = port
f.Stream = stream
return nil
}
2014-08-07 18:15:11 +00:00
func TestRunInContainerNoSuchPod(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
fakeDocker.ContainerList = []docker.APIContainers{}
2014-08-07 18:15:11 +00:00
kubelet.runner = &fakeCommandRunner
podName := "podFoo"
2015-03-11 23:40:20 +00:00
podNamespace := "nsFoo"
2014-08-07 18:15:11 +00:00
containerName := "containerFoo"
output, err := kubelet.RunInContainer(
2015-03-13 13:19:07 +00:00
GetPodFullName(&api.Pod{ObjectMeta: api.ObjectMeta{Name: podName, Namespace: podNamespace}}),
2014-09-05 09:49:11 +00:00
"",
2014-08-07 18:15:11 +00:00
containerName,
[]string{"ls"})
if output != nil {
t.Errorf("unexpected non-nil command: %v", output)
}
if err == nil {
t.Error("unexpected non-error")
}
}
func TestRunInContainer(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
2014-08-07 18:15:11 +00:00
kubelet.runner = &fakeCommandRunner
containerID := "abc1234"
podName := "podFoo"
2015-03-11 23:40:20 +00:00
podNamespace := "nsFoo"
2014-08-07 18:15:11 +00:00
containerName := "containerFoo"
fakeDocker.ContainerList = []docker.APIContainers{
2014-08-07 18:15:11 +00:00
{
ID: containerID,
Names: []string{"/k8s_" + containerName + "_" + podName + "_" + podNamespace + "_12345678_42"},
2014-08-07 18:15:11 +00:00
},
}
cmd := []string{"ls"}
_, err := kubelet.RunInContainer(
2015-03-13 13:19:07 +00:00
GetPodFullName(&api.Pod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: podName,
Namespace: podNamespace,
},
}),
2014-09-05 09:49:11 +00:00
"",
2014-08-07 18:15:11 +00:00
containerName,
cmd)
if fakeCommandRunner.ID != containerID {
2015-01-18 07:32:34 +00:00
t.Errorf("unexpected Name: %s", fakeCommandRunner.ID)
2014-08-07 18:15:11 +00:00
}
if !reflect.DeepEqual(fakeCommandRunner.Cmd, cmd) {
2015-01-18 07:32:34 +00:00
t.Errorf("unexpected command: %s", fakeCommandRunner.Cmd)
2014-08-07 18:15:11 +00:00
}
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}
func TestRunHandlerExec(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
kubelet.runner = &fakeCommandRunner
containerID := "abc1234"
podName := "podFoo"
2015-03-11 23:40:20 +00:00
podNamespace := "nsFoo"
containerName := "containerFoo"
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: containerID,
Names: []string{"/k8s_" + containerName + "_" + podName + "_" + podNamespace + "_12345678_42"},
},
}
container := api.Container{
Name: containerName,
Lifecycle: &api.Lifecycle{
PostStart: &api.Handler{
Exec: &api.ExecAction{
Command: []string{"ls", "-a"},
},
},
},
}
err := kubelet.runHandler(podName+"_"+podNamespace, "", &container, container.Lifecycle.PostStart)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if fakeCommandRunner.ID != containerID ||
!reflect.DeepEqual(container.Lifecycle.PostStart.Exec.Command, fakeCommandRunner.Cmd) {
t.Errorf("unexpected commands: %v", fakeCommandRunner)
}
}
type fakeHTTP struct {
url string
err error
}
func (f *fakeHTTP) Get(url string) (*http.Response, error) {
f.url = url
return nil, f.err
}
func TestRunHandlerHttp(t *testing.T) {
fakeHttp := fakeHTTP{}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
kubelet.httpClient = &fakeHttp
podName := "podFoo"
2015-03-11 23:40:20 +00:00
podNamespace := "nsFoo"
containerName := "containerFoo"
container := api.Container{
Name: containerName,
Lifecycle: &api.Lifecycle{
PostStart: &api.Handler{
HTTPGet: &api.HTTPGetAction{
Host: "foo",
Port: util.IntOrString{IntVal: 8080, Kind: util.IntstrInt},
Path: "bar",
},
},
},
}
err := kubelet.runHandler(podName+"_"+podNamespace, "", &container, container.Lifecycle.PostStart)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if fakeHttp.url != "http://foo:8080/bar" {
t.Errorf("unexpected url: %s", fakeHttp.url)
}
}
func TestNewHandler(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
handler := &api.Handler{
HTTPGet: &api.HTTPGetAction{
Host: "foo",
Port: util.IntOrString{IntVal: 8080, Kind: util.IntstrInt},
Path: "bar",
},
}
actionHandler := kubelet.newActionHandler(handler)
if actionHandler == nil {
t.Error("unexpected nil action handler.")
}
handler = &api.Handler{
Exec: &api.ExecAction{
Command: []string{"ls", "-l"},
},
}
actionHandler = kubelet.newActionHandler(handler)
if actionHandler == nil {
t.Error("unexpected nil action handler.")
}
handler = &api.Handler{}
actionHandler = kubelet.newActionHandler(handler)
if actionHandler != nil {
t.Errorf("unexpected non-nil action handler: %v", actionHandler)
}
}
func TestSyncPodEventHandlerFails(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
kubelet.httpClient = &fakeHTTP{
err: fmt.Errorf("test error"),
}
dockerContainers := dockertools.DockerContainers{
"9876": &docker.APIContainers{
2015-01-21 00:59:26 +00:00
// pod infra container
Names: []string{"/k8s_POD_foo_new_12345678_42"},
ID: "9876",
},
}
2015-03-13 13:19:07 +00:00
bound := api.Pod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "bar",
Lifecycle: &api.Lifecycle{
PostStart: &api.Handler{
HTTPGet: &api.HTTPGetAction{
Host: "does.no.exist",
Port: util.IntOrString{IntVal: 8080, Kind: util.IntstrInt},
Path: "bar",
},
},
},
},
},
},
}
pods := []api.Pod{bound}
kubelet.podManager.SetPods(pods)
err := kubelet.syncPod(&bound, false, dockerContainers)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
verifyCalls(t, fakeDocker, []string{"list", "list", "create", "start", "stop", "list"})
if len(fakeDocker.Stopped) != 1 {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
}
}
2014-10-28 00:29:55 +00:00
2014-11-15 00:05:29 +00:00
func TestSyncPodsWithPullPolicy(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
2015-02-23 21:04:45 +00:00
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
waitGroup := testKubelet.waitGroup
2014-11-15 00:05:29 +00:00
puller := kubelet.dockerPuller.(*dockertools.FakeDockerPuller)
puller.HasImages = []string{"existing_one", "want:latest"}
2015-01-21 00:59:26 +00:00
kubelet.podInfraContainerImage = "custom_image_name"
2014-11-15 00:05:29 +00:00
fakeDocker.ContainerList = []docker.APIContainers{}
2015-02-27 09:19:41 +00:00
waitGroup.Add(1)
2015-03-13 13:19:07 +00:00
err := kubelet.SyncPods([]api.Pod{
2014-11-15 00:05:29 +00:00
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
2014-11-15 00:05:29 +00:00
},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "bar", Image: "pull_always_image", ImagePullPolicy: api.PullAlways},
{Name: "bar1", Image: "pull_never_image", ImagePullPolicy: api.PullNever},
{Name: "bar2", Image: "pull_if_not_present_image", ImagePullPolicy: api.PullIfNotPresent},
{Name: "bar3", Image: "existing_one", ImagePullPolicy: api.PullIfNotPresent},
{Name: "bar4", Image: "want:latest", ImagePullPolicy: api.PullIfNotPresent},
},
},
},
}, emptyPodUIDs, *newMirrorPods(), time.Now())
2014-11-15 00:05:29 +00:00
if err != nil {
t.Errorf("unexpected error: %v", err)
}
2015-02-27 09:19:41 +00:00
waitGroup.Wait()
2014-11-15 00:05:29 +00:00
fakeDocker.Lock()
2015-03-10 14:09:55 +00:00
pulledImageSet := make(map[string]empty)
for v := range puller.ImagesPulled {
pulledImageSet[puller.ImagesPulled[v]] = empty{}
}
if !reflect.DeepEqual(pulledImageSet, map[string]empty{
"custom_image_name": {},
"pull_always_image": {},
"pull_if_not_present_image": {},
}) {
2014-11-15 00:05:29 +00:00
t.Errorf("Unexpected pulled containers: %v", puller.ImagesPulled)
}
if len(fakeDocker.Created) != 6 {
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
}
fakeDocker.Unlock()
}
func TestParseResolvConf(t *testing.T) {
testCases := []struct {
data string
nameservers []string
searches []string
}{
{"", []string{}, []string{}},
{" ", []string{}, []string{}},
{"\n", []string{}, []string{}},
{"\t\n\t", []string{}, []string{}},
{"#comment\n", []string{}, []string{}},
{" #comment\n", []string{}, []string{}},
{"#comment\n#comment", []string{}, []string{}},
{"#comment\nnameserver", []string{}, []string{}},
{"#comment\nnameserver\nsearch", []string{}, []string{}},
{"nameserver 1.2.3.4", []string{"1.2.3.4"}, []string{}},
{" nameserver 1.2.3.4", []string{"1.2.3.4"}, []string{}},
{"\tnameserver 1.2.3.4", []string{"1.2.3.4"}, []string{}},
{"nameserver\t1.2.3.4", []string{"1.2.3.4"}, []string{}},
{"nameserver \t 1.2.3.4", []string{"1.2.3.4"}, []string{}},
{"nameserver 1.2.3.4\nnameserver 5.6.7.8", []string{"1.2.3.4", "5.6.7.8"}, []string{}},
{"search foo", []string{}, []string{"foo"}},
{"search foo bar", []string{}, []string{"foo", "bar"}},
{"search foo bar bat\n", []string{}, []string{"foo", "bar", "bat"}},
{"search foo\nsearch bar", []string{}, []string{"bar"}},
{"nameserver 1.2.3.4\nsearch foo bar", []string{"1.2.3.4"}, []string{"foo", "bar"}},
{"nameserver 1.2.3.4\nsearch foo\nnameserver 5.6.7.8\nsearch bar", []string{"1.2.3.4", "5.6.7.8"}, []string{"bar"}},
{"#comment\nnameserver 1.2.3.4\n#comment\nsearch foo\ncomment", []string{"1.2.3.4"}, []string{"foo"}},
}
for i, tc := range testCases {
ns, srch, err := parseResolvConf(strings.NewReader(tc.data))
if err != nil {
t.Errorf("expected success, got %v", err)
continue
}
if !reflect.DeepEqual(ns, tc.nameservers) {
t.Errorf("[%d] expected nameservers %#v, got %#v", i, tc.nameservers, ns)
}
if !reflect.DeepEqual(srch, tc.searches) {
t.Errorf("[%d] expected searches %#v, got %#v", i, tc.searches, srch)
}
}
}
type testServiceLister struct {
services []api.Service
}
func (ls testServiceLister) List() (api.ServiceList, error) {
return api.ServiceList{
Items: ls.services,
}, nil
}
type testNodeLister struct {
nodes []api.Node
}
func (ls testNodeLister) GetNodeInfo(id string) (*api.Node, error) {
return nil, errors.New("not implemented")
}
func (ls testNodeLister) List() (api.NodeList, error) {
return api.NodeList{
Items: ls.nodes,
}, nil
}
func TestMakeEnvironmentVariables(t *testing.T) {
services := []api.Service{
{
ObjectMeta: api.ObjectMeta{Name: "kubernetes", Namespace: api.NamespaceDefault},
Spec: api.ServiceSpec{
Port: 8081,
PortalIP: "1.2.3.1",
},
},
{
ObjectMeta: api.ObjectMeta{Name: "kubernetes-ro", Namespace: api.NamespaceDefault},
Spec: api.ServiceSpec{
Port: 8082,
PortalIP: "1.2.3.2",
},
},
{
ObjectMeta: api.ObjectMeta{Name: "kubernetes-ro", Namespace: api.NamespaceDefault},
Spec: api.ServiceSpec{
Port: 8082,
PortalIP: "None",
},
},
{
ObjectMeta: api.ObjectMeta{Name: "kubernetes-ro", Namespace: api.NamespaceDefault},
Spec: api.ServiceSpec{
Port: 8082,
PortalIP: "",
},
},
{
ObjectMeta: api.ObjectMeta{Name: "test", Namespace: "test1"},
Spec: api.ServiceSpec{
Port: 8083,
PortalIP: "1.2.3.3",
},
},
{
ObjectMeta: api.ObjectMeta{Name: "kubernetes", Namespace: "test2"},
Spec: api.ServiceSpec{
Port: 8084,
PortalIP: "1.2.3.4",
},
},
{
ObjectMeta: api.ObjectMeta{Name: "test", Namespace: "test2"},
Spec: api.ServiceSpec{
Port: 8085,
PortalIP: "1.2.3.5",
},
},
{
ObjectMeta: api.ObjectMeta{Name: "test", Namespace: "test2"},
Spec: api.ServiceSpec{
Port: 8085,
PortalIP: "None",
},
},
{
ObjectMeta: api.ObjectMeta{Name: "test", Namespace: "test2"},
Spec: api.ServiceSpec{
Port: 8085,
},
},
{
ObjectMeta: api.ObjectMeta{Name: "kubernetes", Namespace: "kubernetes"},
Spec: api.ServiceSpec{
Port: 8086,
PortalIP: "1.2.3.6",
},
},
{
ObjectMeta: api.ObjectMeta{Name: "kubernetes-ro", Namespace: "kubernetes"},
Spec: api.ServiceSpec{
Port: 8087,
PortalIP: "1.2.3.7",
},
},
{
ObjectMeta: api.ObjectMeta{Name: "not-special", Namespace: "kubernetes"},
Spec: api.ServiceSpec{
Port: 8088,
PortalIP: "1.2.3.8",
},
},
{
ObjectMeta: api.ObjectMeta{Name: "not-special", Namespace: "kubernetes"},
Spec: api.ServiceSpec{
Port: 8088,
PortalIP: "None",
},
},
{
ObjectMeta: api.ObjectMeta{Name: "not-special", Namespace: "kubernetes"},
Spec: api.ServiceSpec{
Port: 8088,
PortalIP: "",
},
},
}
testCases := []struct {
name string // the name of the test case
ns string // the namespace to generate environment for
container *api.Container // the container to use
masterServiceNamespace string // the namespace to read master service info from
nilLister bool // whether the lister should be nil
expectedEnvs util.StringSet // a set of expected environment vars
expectedEnvSize int // total number of expected env vars
}{
{
"api server = Y, kubelet = Y",
"test1",
&api.Container{
Env: []api.EnvVar{
{Name: "FOO", Value: "BAR"},
{Name: "TEST_SERVICE_HOST", Value: "1.2.3.3"},
{Name: "TEST_SERVICE_PORT", Value: "8083"},
{Name: "TEST_PORT", Value: "tcp://1.2.3.3:8083"},
{Name: "TEST_PORT_8083_TCP", Value: "tcp://1.2.3.3:8083"},
{Name: "TEST_PORT_8083_TCP_PROTO", Value: "tcp"},
{Name: "TEST_PORT_8083_TCP_PORT", Value: "8083"},
{Name: "TEST_PORT_8083_TCP_ADDR", Value: "1.2.3.3"},
},
},
api.NamespaceDefault,
false,
util.NewStringSet("FOO=BAR",
"TEST_SERVICE_HOST=1.2.3.3",
"TEST_SERVICE_PORT=8083",
"TEST_PORT=tcp://1.2.3.3:8083",
"TEST_PORT_8083_TCP=tcp://1.2.3.3:8083",
"TEST_PORT_8083_TCP_PROTO=tcp",
"TEST_PORT_8083_TCP_PORT=8083",
"TEST_PORT_8083_TCP_ADDR=1.2.3.3",
"KUBERNETES_SERVICE_HOST=1.2.3.1",
"KUBERNETES_SERVICE_PORT=8081",
"KUBERNETES_PORT=tcp://1.2.3.1:8081",
"KUBERNETES_PORT_8081_TCP=tcp://1.2.3.1:8081",
"KUBERNETES_PORT_8081_TCP_PROTO=tcp",
"KUBERNETES_PORT_8081_TCP_PORT=8081",
"KUBERNETES_PORT_8081_TCP_ADDR=1.2.3.1",
"KUBERNETES_RO_SERVICE_HOST=1.2.3.2",
"KUBERNETES_RO_SERVICE_PORT=8082",
"KUBERNETES_RO_PORT=tcp://1.2.3.2:8082",
"KUBERNETES_RO_PORT_8082_TCP=tcp://1.2.3.2:8082",
"KUBERNETES_RO_PORT_8082_TCP_PROTO=tcp",
"KUBERNETES_RO_PORT_8082_TCP_PORT=8082",
"KUBERNETES_RO_PORT_8082_TCP_ADDR=1.2.3.2"),
22,
},
{
"api server = Y, kubelet = N",
"test1",
&api.Container{
Env: []api.EnvVar{
{Name: "FOO", Value: "BAR"},
{Name: "TEST_SERVICE_HOST", Value: "1.2.3.3"},
{Name: "TEST_SERVICE_PORT", Value: "8083"},
{Name: "TEST_PORT", Value: "tcp://1.2.3.3:8083"},
{Name: "TEST_PORT_8083_TCP", Value: "tcp://1.2.3.3:8083"},
{Name: "TEST_PORT_8083_TCP_PROTO", Value: "tcp"},
{Name: "TEST_PORT_8083_TCP_PORT", Value: "8083"},
{Name: "TEST_PORT_8083_TCP_ADDR", Value: "1.2.3.3"},
},
},
api.NamespaceDefault,
true,
util.NewStringSet("FOO=BAR",
"TEST_SERVICE_HOST=1.2.3.3",
"TEST_SERVICE_PORT=8083",
"TEST_PORT=tcp://1.2.3.3:8083",
"TEST_PORT_8083_TCP=tcp://1.2.3.3:8083",
"TEST_PORT_8083_TCP_PROTO=tcp",
"TEST_PORT_8083_TCP_PORT=8083",
"TEST_PORT_8083_TCP_ADDR=1.2.3.3"),
8,
},
{
"api server = N; kubelet = Y",
"test1",
&api.Container{
Env: []api.EnvVar{
{Name: "FOO", Value: "BAZ"},
},
},
api.NamespaceDefault,
false,
util.NewStringSet("FOO=BAZ",
"TEST_SERVICE_HOST=1.2.3.3",
"TEST_SERVICE_PORT=8083",
"TEST_PORT=tcp://1.2.3.3:8083",
"TEST_PORT_8083_TCP=tcp://1.2.3.3:8083",
"TEST_PORT_8083_TCP_PROTO=tcp",
"TEST_PORT_8083_TCP_PORT=8083",
"TEST_PORT_8083_TCP_ADDR=1.2.3.3",
"KUBERNETES_SERVICE_HOST=1.2.3.1",
"KUBERNETES_SERVICE_PORT=8081",
"KUBERNETES_PORT=tcp://1.2.3.1:8081",
"KUBERNETES_PORT_8081_TCP=tcp://1.2.3.1:8081",
"KUBERNETES_PORT_8081_TCP_PROTO=tcp",
"KUBERNETES_PORT_8081_TCP_PORT=8081",
"KUBERNETES_PORT_8081_TCP_ADDR=1.2.3.1",
"KUBERNETES_RO_SERVICE_HOST=1.2.3.2",
"KUBERNETES_RO_SERVICE_PORT=8082",
"KUBERNETES_RO_PORT=tcp://1.2.3.2:8082",
"KUBERNETES_RO_PORT_8082_TCP=tcp://1.2.3.2:8082",
"KUBERNETES_RO_PORT_8082_TCP_PROTO=tcp",
"KUBERNETES_RO_PORT_8082_TCP_PORT=8082",
"KUBERNETES_RO_PORT_8082_TCP_ADDR=1.2.3.2"),
22,
},
{
"master service in pod ns",
"test2",
&api.Container{
Env: []api.EnvVar{
{Name: "FOO", Value: "ZAP"},
},
},
"kubernetes",
false,
util.NewStringSet("FOO=ZAP",
"TEST_SERVICE_HOST=1.2.3.5",
"TEST_SERVICE_PORT=8085",
"TEST_PORT=tcp://1.2.3.5:8085",
"TEST_PORT_8085_TCP=tcp://1.2.3.5:8085",
"TEST_PORT_8085_TCP_PROTO=tcp",
"TEST_PORT_8085_TCP_PORT=8085",
"TEST_PORT_8085_TCP_ADDR=1.2.3.5",
"KUBERNETES_SERVICE_HOST=1.2.3.4",
"KUBERNETES_SERVICE_PORT=8084",
"KUBERNETES_PORT=tcp://1.2.3.4:8084",
"KUBERNETES_PORT_8084_TCP=tcp://1.2.3.4:8084",
"KUBERNETES_PORT_8084_TCP_PROTO=tcp",
"KUBERNETES_PORT_8084_TCP_PORT=8084",
"KUBERNETES_PORT_8084_TCP_ADDR=1.2.3.4",
"KUBERNETES_RO_SERVICE_HOST=1.2.3.7",
"KUBERNETES_RO_SERVICE_PORT=8087",
"KUBERNETES_RO_PORT=tcp://1.2.3.7:8087",
"KUBERNETES_RO_PORT_8087_TCP=tcp://1.2.3.7:8087",
"KUBERNETES_RO_PORT_8087_TCP_PROTO=tcp",
"KUBERNETES_RO_PORT_8087_TCP_PORT=8087",
"KUBERNETES_RO_PORT_8087_TCP_ADDR=1.2.3.7"),
22,
},
{
"pod in master service ns",
"kubernetes",
&api.Container{},
"kubernetes",
false,
util.NewStringSet(
"NOT_SPECIAL_SERVICE_HOST=1.2.3.8",
"NOT_SPECIAL_SERVICE_PORT=8088",
"NOT_SPECIAL_PORT=tcp://1.2.3.8:8088",
"NOT_SPECIAL_PORT_8088_TCP=tcp://1.2.3.8:8088",
"NOT_SPECIAL_PORT_8088_TCP_PROTO=tcp",
"NOT_SPECIAL_PORT_8088_TCP_PORT=8088",
"NOT_SPECIAL_PORT_8088_TCP_ADDR=1.2.3.8",
"KUBERNETES_SERVICE_HOST=1.2.3.6",
"KUBERNETES_SERVICE_PORT=8086",
"KUBERNETES_PORT=tcp://1.2.3.6:8086",
"KUBERNETES_PORT_8086_TCP=tcp://1.2.3.6:8086",
"KUBERNETES_PORT_8086_TCP_PROTO=tcp",
"KUBERNETES_PORT_8086_TCP_PORT=8086",
"KUBERNETES_PORT_8086_TCP_ADDR=1.2.3.6",
"KUBERNETES_RO_SERVICE_HOST=1.2.3.7",
"KUBERNETES_RO_SERVICE_PORT=8087",
"KUBERNETES_RO_PORT=tcp://1.2.3.7:8087",
"KUBERNETES_RO_PORT_8087_TCP=tcp://1.2.3.7:8087",
"KUBERNETES_RO_PORT_8087_TCP_PROTO=tcp",
"KUBERNETES_RO_PORT_8087_TCP_PORT=8087",
"KUBERNETES_RO_PORT_8087_TCP_ADDR=1.2.3.7"),
21,
},
}
for _, tc := range testCases {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kl := testKubelet.kubelet
kl.masterServiceNamespace = tc.masterServiceNamespace
if tc.nilLister {
kl.serviceLister = nil
} else {
kl.serviceLister = testServiceLister{services}
}
result, err := kl.makeEnvironmentVariables(tc.ns, tc.container)
if err != nil {
t.Errorf("[%v] Unexpected error: %v", tc.name, err)
}
resultSet := util.NewStringSet(result...)
if !resultSet.IsSuperset(tc.expectedEnvs) {
t.Errorf("[%v] Unexpected env entries; expected {%v}, got {%v}", tc.name, tc.expectedEnvs, resultSet)
}
if a := len(resultSet); a != tc.expectedEnvSize {
t.Errorf("[%v] Unexpected number of env vars; expected %v, got %v", tc.name, tc.expectedEnvSize, a)
}
}
}
2015-01-28 17:56:35 +00:00
func TestPodPhaseWithRestartAlways(t *testing.T) {
desiredState := api.PodSpec{
Containers: []api.Container{
{Name: "containerA"},
{Name: "containerB"},
},
RestartPolicy: api.RestartPolicyAlways,
2015-01-28 17:56:35 +00:00
}
currentState := api.PodStatus{
Host: "machine",
}
runningState := api.ContainerStatus{
State: api.ContainerState{
Running: &api.ContainerStateRunning{},
},
}
stoppedState := api.ContainerStatus{
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{},
},
}
tests := []struct {
pod *api.Pod
status api.PodPhase
test string
}{
{&api.Pod{Spec: desiredState, Status: currentState}, api.PodPending, "waiting"},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
"containerB": runningState,
},
Host: "machine",
},
},
api.PodRunning,
"all running",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": stoppedState,
"containerB": stoppedState,
},
Host: "machine",
},
},
api.PodRunning,
"all stopped with restart always",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
"containerB": stoppedState,
},
Host: "machine",
},
},
api.PodRunning,
"mixed state #1 with restart always",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
},
Host: "machine",
},
},
api.PodPending,
"mixed state #2 with restart always",
},
}
for _, test := range tests {
if status := getPhase(&test.pod.Spec, test.pod.Status.Info); status != test.status {
t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status)
}
}
}
func TestPodPhaseWithRestartNever(t *testing.T) {
desiredState := api.PodSpec{
Containers: []api.Container{
{Name: "containerA"},
{Name: "containerB"},
},
RestartPolicy: api.RestartPolicyNever,
2015-01-28 17:56:35 +00:00
}
currentState := api.PodStatus{
Host: "machine",
}
runningState := api.ContainerStatus{
State: api.ContainerState{
Running: &api.ContainerStateRunning{},
},
}
succeededState := api.ContainerStatus{
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{
ExitCode: 0,
},
},
}
failedState := api.ContainerStatus{
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{
ExitCode: -1,
},
},
}
tests := []struct {
pod *api.Pod
status api.PodPhase
test string
}{
{&api.Pod{Spec: desiredState, Status: currentState}, api.PodPending, "waiting"},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
"containerB": runningState,
},
Host: "machine",
},
},
api.PodRunning,
"all running with restart never",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": succeededState,
"containerB": succeededState,
},
Host: "machine",
},
},
api.PodSucceeded,
"all succeeded with restart never",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": failedState,
"containerB": failedState,
},
Host: "machine",
},
},
api.PodFailed,
"all failed with restart never",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
"containerB": succeededState,
},
Host: "machine",
},
},
api.PodRunning,
"mixed state #1 with restart never",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
},
Host: "machine",
},
},
api.PodPending,
"mixed state #2 with restart never",
},
}
for _, test := range tests {
if status := getPhase(&test.pod.Spec, test.pod.Status.Info); status != test.status {
t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status)
}
}
}
func TestPodPhaseWithRestartOnFailure(t *testing.T) {
desiredState := api.PodSpec{
Containers: []api.Container{
{Name: "containerA"},
{Name: "containerB"},
},
RestartPolicy: api.RestartPolicyOnFailure,
2015-01-28 17:56:35 +00:00
}
currentState := api.PodStatus{
Host: "machine",
}
runningState := api.ContainerStatus{
State: api.ContainerState{
Running: &api.ContainerStateRunning{},
},
}
succeededState := api.ContainerStatus{
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{
ExitCode: 0,
},
},
}
failedState := api.ContainerStatus{
State: api.ContainerState{
Termination: &api.ContainerStateTerminated{
ExitCode: -1,
},
},
}
tests := []struct {
pod *api.Pod
status api.PodPhase
test string
}{
{&api.Pod{Spec: desiredState, Status: currentState}, api.PodPending, "waiting"},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
"containerB": runningState,
},
Host: "machine",
},
},
api.PodRunning,
"all running with restart onfailure",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": succeededState,
"containerB": succeededState,
},
Host: "machine",
},
},
api.PodSucceeded,
"all succeeded with restart onfailure",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": failedState,
"containerB": failedState,
},
Host: "machine",
},
},
api.PodRunning,
"all failed with restart never",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
"containerB": succeededState,
},
Host: "machine",
},
},
api.PodRunning,
"mixed state #1 with restart onfailure",
},
{
&api.Pod{
Spec: desiredState,
Status: api.PodStatus{
Info: map[string]api.ContainerStatus{
"containerA": runningState,
},
Host: "machine",
},
},
api.PodPending,
"mixed state #2 with restart onfailure",
},
}
for _, test := range tests {
if status := getPhase(&test.pod.Spec, test.pod.Status.Info); status != test.status {
t.Errorf("In test %s, expected %v, got %v", test.test, test.status, status)
}
}
}
2015-02-08 21:22:19 +00:00
func TestGetPodReadyCondition(t *testing.T) {
ready := []api.PodCondition{{
Type: api.PodReady,
2015-02-08 21:22:19 +00:00
Status: api.ConditionFull,
}}
unready := []api.PodCondition{{
Type: api.PodReady,
2015-02-08 21:22:19 +00:00
Status: api.ConditionNone,
}}
tests := []struct {
spec *api.PodSpec
info api.PodInfo
expected []api.PodCondition
}{
{
spec: nil,
info: nil,
expected: unready,
},
{
spec: &api.PodSpec{},
info: api.PodInfo{},
expected: ready,
},
{
spec: &api.PodSpec{
Containers: []api.Container{
{Name: "1234"},
},
},
info: api.PodInfo{},
expected: unready,
},
{
spec: &api.PodSpec{
Containers: []api.Container{
{Name: "1234"},
},
},
info: api.PodInfo{
"1234": api.ContainerStatus{Ready: true},
},
expected: ready,
},
{
spec: &api.PodSpec{
Containers: []api.Container{
{Name: "1234"},
{Name: "5678"},
},
},
info: api.PodInfo{
"1234": api.ContainerStatus{Ready: true},
"5678": api.ContainerStatus{Ready: true},
},
expected: ready,
},
{
spec: &api.PodSpec{
Containers: []api.Container{
{Name: "1234"},
{Name: "5678"},
},
},
info: api.PodInfo{
"1234": api.ContainerStatus{Ready: true},
},
expected: unready,
},
{
spec: &api.PodSpec{
Containers: []api.Container{
{Name: "1234"},
{Name: "5678"},
},
},
info: api.PodInfo{
"1234": api.ContainerStatus{Ready: true},
"5678": api.ContainerStatus{Ready: false},
},
expected: unready,
},
}
for i, test := range tests {
condition := getPodReadyCondition(test.spec, test.info)
if !reflect.DeepEqual(condition, test.expected) {
t.Errorf("On test case %v, expected:\n%+v\ngot\n%+v\n", i, test.expected, condition)
}
}
}
func TestExecInContainerNoSuchPod(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
fakeDocker.ContainerList = []docker.APIContainers{}
kubelet.runner = &fakeCommandRunner
podName := "podFoo"
2015-03-11 23:40:20 +00:00
podNamespace := "nsFoo"
containerName := "containerFoo"
err := kubelet.ExecInContainer(
2015-03-13 13:19:07 +00:00
GetPodFullName(&api.Pod{ObjectMeta: api.ObjectMeta{Name: podName, Namespace: podNamespace}}),
"",
containerName,
[]string{"ls"},
nil,
nil,
nil,
false,
)
if err == nil {
t.Fatal("unexpected non-error")
}
if fakeCommandRunner.ID != "" {
t.Fatal("unexpected invocation of runner.ExecInContainer")
}
}
func TestExecInContainerNoSuchContainer(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
kubelet.runner = &fakeCommandRunner
podName := "podFoo"
2015-03-11 23:40:20 +00:00
podNamespace := "nsFoo"
containerID := "containerFoo"
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: "notfound",
Names: []string{"/k8s_notfound_" + podName + "_" + podNamespace + "_12345678_42"},
},
}
err := kubelet.ExecInContainer(
2015-03-13 13:19:07 +00:00
GetPodFullName(&api.Pod{ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: podName,
Namespace: podNamespace,
}}),
"",
containerID,
[]string{"ls"},
nil,
nil,
nil,
false,
)
if err == nil {
t.Fatal("unexpected non-error")
}
if fakeCommandRunner.ID != "" {
t.Fatal("unexpected invocation of runner.ExecInContainer")
}
}
type fakeReadWriteCloser struct{}
func (f *fakeReadWriteCloser) Write(data []byte) (int, error) {
return 0, nil
}
func (f *fakeReadWriteCloser) Read(data []byte) (int, error) {
return 0, nil
}
func (f *fakeReadWriteCloser) Close() error {
return nil
}
func TestExecInContainer(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
kubelet.runner = &fakeCommandRunner
podName := "podFoo"
2015-03-11 23:40:20 +00:00
podNamespace := "nsFoo"
containerID := "containerFoo"
command := []string{"ls"}
stdin := &bytes.Buffer{}
stdout := &fakeReadWriteCloser{}
stderr := &fakeReadWriteCloser{}
tty := true
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: containerID,
Names: []string{"/k8s_" + containerID + "_" + podName + "_" + podNamespace + "_12345678_42"},
},
}
2015-02-08 21:22:19 +00:00
err := kubelet.ExecInContainer(
2015-03-13 13:19:07 +00:00
GetPodFullName(&api.Pod{ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: podName,
Namespace: podNamespace,
}}),
"",
containerID,
[]string{"ls"},
stdin,
stdout,
stderr,
tty,
)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if e, a := containerID, fakeCommandRunner.ID; e != a {
t.Fatalf("container id: expected %s, got %s", e, a)
}
if e, a := command, fakeCommandRunner.Cmd; !reflect.DeepEqual(e, a) {
t.Fatalf("command: expected '%v', got '%v'", e, a)
}
if e, a := stdin, fakeCommandRunner.Stdin; e != a {
t.Fatalf("stdin: expected %#v, got %#v", e, a)
}
if e, a := stdout, fakeCommandRunner.Stdout; e != a {
t.Fatalf("stdout: expected %#v, got %#v", e, a)
}
if e, a := stderr, fakeCommandRunner.Stderr; e != a {
t.Fatalf("stderr: expected %#v, got %#v", e, a)
}
if e, a := tty, fakeCommandRunner.TTY; e != a {
t.Fatalf("tty: expected %t, got %t", e, a)
}
}
func TestPortForwardNoSuchPod(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
fakeDocker.ContainerList = []docker.APIContainers{}
kubelet.runner = &fakeCommandRunner
podName := "podFoo"
2015-03-11 23:40:20 +00:00
podNamespace := "nsFoo"
var port uint16 = 5000
err := kubelet.PortForward(
2015-03-13 13:19:07 +00:00
GetPodFullName(&api.Pod{ObjectMeta: api.ObjectMeta{Name: podName, Namespace: podNamespace}}),
"",
port,
nil,
)
if err == nil {
t.Fatal("unexpected non-error")
}
if fakeCommandRunner.ID != "" {
t.Fatal("unexpected invocation of runner.PortForward")
}
}
func TestPortForwardNoSuchContainer(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
kubelet.runner = &fakeCommandRunner
podName := "podFoo"
2015-03-11 23:40:20 +00:00
podNamespace := "nsFoo"
var port uint16 = 5000
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: "notfound",
Names: []string{"/k8s_notfound_" + podName + "_" + podNamespace + "_12345678_42"},
},
}
err := kubelet.PortForward(
2015-03-13 13:19:07 +00:00
GetPodFullName(&api.Pod{ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: podName,
Namespace: podNamespace,
}}),
"",
port,
nil,
)
if err == nil {
t.Fatal("unexpected non-error")
}
if fakeCommandRunner.ID != "" {
t.Fatal("unexpected invocation of runner.PortForward")
}
}
func TestPortForward(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{}
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
kubelet.runner = &fakeCommandRunner
podName := "podFoo"
2015-03-11 23:40:20 +00:00
podNamespace := "nsFoo"
containerID := "containerFoo"
var port uint16 = 5000
stream := &fakeReadWriteCloser{}
infraContainerID := "infra"
kubelet.podInfraContainerImage = "POD"
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: infraContainerID,
Names: []string{"/k8s_" + kubelet.podInfraContainerImage + "_" + podName + "_" + podNamespace + "_12345678_42"},
},
{
ID: containerID,
Names: []string{"/k8s_" + containerID + "_" + podName + "_" + podNamespace + "_12345678_42"},
},
}
err := kubelet.PortForward(
2015-03-13 13:19:07 +00:00
GetPodFullName(&api.Pod{ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: podName,
Namespace: podNamespace,
}}),
"",
port,
stream,
)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if e, a := infraContainerID, fakeCommandRunner.ID; e != a {
t.Fatalf("container id: expected %s, got %s", e, a)
}
if e, a := port, fakeCommandRunner.Port; e != a {
t.Fatalf("port: expected %v, got %v", e, a)
}
if e, a := stream, fakeCommandRunner.Stream; e != a {
t.Fatalf("stream: expected %v, got %v", e, a)
}
2015-02-08 21:22:19 +00:00
}
// Tests that identify the host port conflicts are detected correctly.
func TestGetHostPortConflicts(t *testing.T) {
2015-03-13 13:19:07 +00:00
pods := []api.Pod{
{Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 80}}}}}},
{Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 81}}}}}},
{Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 82}}}}}},
{Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 83}}}}}},
}
// Pods should not cause any conflict.
_, conflicts := checkHostPortConflicts(pods)
if len(conflicts) != 0 {
t.Errorf("expected no conflicts, Got %#v", conflicts)
}
// The new pod should cause conflict and be reported.
2015-03-13 13:19:07 +00:00
expected := api.Pod{
Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 81}}}}},
}
pods = append(pods, expected)
if _, actual := checkHostPortConflicts(pods); !reflect.DeepEqual(actual, []api.Pod{expected}) {
t.Errorf("expected %#v, Got %#v", expected, actual)
}
}
// Tests that we handle port conflicts correctly by setting the failed status in status map.
func TestHandlePortConflicts(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kl := testKubelet.kubelet
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
spec := api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 80}}}}}
2015-03-13 13:19:07 +00:00
pods := []api.Pod{
{
ObjectMeta: api.ObjectMeta{
UID: "123456789",
Name: "newpod",
Namespace: "foo",
},
Spec: spec,
},
{
ObjectMeta: api.ObjectMeta{
UID: "987654321",
Name: "oldpod",
Namespace: "foo",
},
Spec: spec,
},
}
2015-03-13 13:19:07 +00:00
// Make sure the Pods are in the reverse order of creation time.
pods[1].CreationTimestamp = util.NewTime(time.Now())
pods[0].CreationTimestamp = util.NewTime(time.Now().Add(1 * time.Second))
// The newer pod should be rejected.
conflictedPodName := GetPodFullName(&pods[0])
kl.handleNotFittingPods(pods)
// Check pod status stored in the status map.
status, err := kl.GetPodStatus(conflictedPodName)
if err != nil {
t.Fatalf("status of pod %q is not found in the status map: ", conflictedPodName, err)
}
if status.Phase != api.PodFailed {
t.Fatalf("expected pod status %q. Got %q.", api.PodFailed, status.Phase)
}
// Check if we can retrieve the pod status from GetPodStatus().
kl.podManager.SetPods(pods)
status, err = kl.GetPodStatus(conflictedPodName)
if err != nil {
t.Fatalf("unable to retrieve pod status for pod %q: %#v.", conflictedPodName, err)
}
if status.Phase != api.PodFailed {
t.Fatalf("expected pod status %q. Got %q.", api.PodFailed, status.Phase)
}
}
// Tests that we handle not matching labels selector correctly by setting the failed status in status map.
func TestHandleNodeSelector(t *testing.T) {
testKubelet := newTestKubelet(t)
kl := testKubelet.kubelet
kl.nodeLister = testNodeLister{nodes: []api.Node{
{ObjectMeta: api.ObjectMeta{Name: "testnode", Labels: map[string]string{"key": "B"}}},
}}
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
pods := []api.Pod{
{
ObjectMeta: api.ObjectMeta{
UID: "123456789",
Name: "podA",
Namespace: "foo",
},
Spec: api.PodSpec{NodeSelector: map[string]string{"key": "A"}},
},
{
ObjectMeta: api.ObjectMeta{
UID: "987654321",
Name: "podB",
Namespace: "foo",
},
Spec: api.PodSpec{NodeSelector: map[string]string{"key": "B"}},
},
}
// The first pod should be rejected.
notfittingPodName := GetPodFullName(&pods[0])
kl.handleNotFittingPods(pods)
// Check pod status stored in the status map.
status, err := kl.GetPodStatus(notfittingPodName)
if err != nil {
t.Fatalf("status of pod %q is not found in the status map: %#v", notfittingPodName, err)
}
if status.Phase != api.PodFailed {
t.Fatalf("expected pod status %q. Got %q.", api.PodFailed, status.Phase)
}
// Check if we can retrieve the pod status from GetPodStatus().
kl.podManager.SetPods(pods)
status, err = kl.GetPodStatus(notfittingPodName)
if err != nil {
t.Fatalf("unable to retrieve pod status for pod %q: %#v.", notfittingPodName, err)
}
if status.Phase != api.PodFailed {
t.Fatalf("expected pod status %q. Got %q.", api.PodFailed, status.Phase)
}
}
// Tests that we handle exceeded resources correctly by setting the failed status in status map.
func TestHandleMemExceeded(t *testing.T) {
testKubelet := newTestKubelet(t)
kl := testKubelet.kubelet
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{MemoryCapacity: 100}, nil)
spec := api.PodSpec{Containers: []api.Container{{Resources: api.ResourceRequirements{
Limits: api.ResourceList{
"memory": resource.MustParse("90"),
},
}}}}
pods := []api.Pod{
{
ObjectMeta: api.ObjectMeta{
UID: "123456789",
Name: "newpod",
Namespace: "foo",
},
Spec: spec,
},
{
ObjectMeta: api.ObjectMeta{
UID: "987654321",
Name: "oldpod",
Namespace: "foo",
},
Spec: spec,
},
}
// Make sure the Pods are in the reverse order of creation time.
pods[1].CreationTimestamp = util.NewTime(time.Now())
pods[0].CreationTimestamp = util.NewTime(time.Now().Add(1 * time.Second))
// The newer pod should be rejected.
notfittingPodName := GetPodFullName(&pods[0])
kl.handleNotFittingPods(pods)
// Check pod status stored in the status map.
status, err := kl.GetPodStatus(notfittingPodName)
if err != nil {
t.Fatalf("status of pod %q is not found in the status map: ", notfittingPodName, err)
}
if status.Phase != api.PodFailed {
t.Fatalf("expected pod status %q. Got %q.", api.PodFailed, status.Phase)
}
// Check if we can retrieve the pod status from GetPodStatus().
kl.podManager.SetPods(pods)
status, err = kl.GetPodStatus(notfittingPodName)
if err != nil {
t.Fatalf("unable to retrieve pod status for pod %q: #v.", notfittingPodName, err)
}
if status.Phase != api.PodFailed {
t.Fatalf("expected pod status %q. Got %q.", api.PodFailed, status.Phase)
}
}
// TODO(filipg): This test should be removed once StatusSyncer can do garbage collection without external signal.
func TestPurgingObsoleteStatusMapEntries(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
2015-02-23 21:04:45 +00:00
kl := testKubelet.kubelet
2015-03-13 13:19:07 +00:00
pods := []api.Pod{
{ObjectMeta: api.ObjectMeta{Name: "pod1"}, Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 80}}}}}},
{ObjectMeta: api.ObjectMeta{Name: "pod2"}, Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.ContainerPort{{HostPort: 80}}}}}},
}
// Run once to populate the status map.
kl.handleNotFittingPods(pods)
if _, err := kl.GetPodStatus(BuildPodFullName("pod2", "")); err != nil {
t.Fatalf("expected to have status cached for %q: %v", "pod2", err)
}
// Sync with empty pods so that the entry in status map will be removed.
kl.SyncPods([]api.Pod{}, emptyPodUIDs, *newMirrorPods(), time.Now())
if _, err := kl.GetPodStatus(BuildPodFullName("pod2", "")); err == nil {
t.Fatalf("expected to not have status cached for %q: %v", "pod2", err)
}
}
func TestValidatePodStatus(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
testCases := []struct {
podPhase api.PodPhase
success bool
}{
{api.PodRunning, true},
{api.PodSucceeded, true},
{api.PodFailed, true},
{api.PodPending, false},
{api.PodUnknown, false},
}
for i, tc := range testCases {
err := kubelet.validatePodPhase(&api.PodStatus{Phase: tc.podPhase})
if tc.success {
if err != nil {
t.Errorf("[case %d]: unexpected failure - %v", i, err)
}
} else if err == nil {
t.Errorf("[case %d]: unexpected success", i)
}
}
}
func TestValidateContainerStatus(t *testing.T) {
2015-02-23 21:04:45 +00:00
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
containerName := "x"
testCases := []struct {
podInfo api.PodInfo
success bool
}{
{
podInfo: api.PodInfo{containerName: api.ContainerStatus{State: api.ContainerState{Running: &api.ContainerStateRunning{}}}},
success: true,
},
{
podInfo: api.PodInfo{containerName: api.ContainerStatus{State: api.ContainerState{Termination: &api.ContainerStateTerminated{}}}},
success: true,
},
{
podInfo: api.PodInfo{containerName: api.ContainerStatus{State: api.ContainerState{Waiting: &api.ContainerStateWaiting{}}}},
success: false,
},
}
for i, tc := range testCases {
_, err := kubelet.validateContainerStatus(&api.PodStatus{
Info: tc.podInfo,
}, containerName)
if tc.success {
if err != nil {
t.Errorf("[case %d]: unexpected failure - %v", i, err)
}
} else if err == nil {
t.Errorf("[case %d]: unexpected success", i)
}
}
if _, err := kubelet.validateContainerStatus(&api.PodStatus{
Info: testCases[0].podInfo,
}, "blah"); err == nil {
t.Errorf("expected error with invalid container name")
}
}
2015-02-23 21:04:45 +00:00
func TestUpdateNewNodeStatus(t *testing.T) {
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
kubeClient := testKubelet.fakeKubeClient
mockCadvisor := testKubelet.fakeCadvisor
kubeClient.MinionsList = api.NodeList{Items: []api.Node{
{ObjectMeta: api.ObjectMeta{Name: "testnode"}},
}}
machineInfo := &cadvisorApi.MachineInfo{MachineID: "123", SystemUUID: "abc", NumCores: 2, MemoryCapacity: 1024}
mockCadvisor.On("MachineInfo").Return(machineInfo, nil)
expectedNode := &api.Node{
ObjectMeta: api.ObjectMeta{Name: "testnode"},
Spec: api.NodeSpec{
Capacity: api.ResourceList{
api.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
api.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
},
},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{
{
Type: api.NodeReady,
Status: api.ConditionFull,
Reason: fmt.Sprintf("kubelet is posting ready status"),
LastProbeTime: util.Time{},
},
},
NodeInfo: api.NodeSystemInfo{
MachineID: "123",
SystemUUID: "abc",
},
},
}
if err := kubelet.updateNodeStatus(); err != nil {
t.Errorf("unexpected error: %v", err)
}
if len(kubeClient.Actions) != 2 {
t.Errorf("unexpected actions: %v", kubeClient.Actions)
}
updatedNode, ok := kubeClient.Actions[1].Value.(*api.Node)
if !ok {
t.Errorf("unexpected object type")
}
if updatedNode.Status.Conditions[0].LastProbeTime.IsZero() {
t.Errorf("unexpected zero last probe timestamp")
}
updatedNode.Status.Conditions[0].LastProbeTime = util.Time{}
if !reflect.DeepEqual(expectedNode, updatedNode) {
t.Errorf("expected \n%v\n, got \n%v", expectedNode, updatedNode)
}
}
func TestUpdateExistingNodeStatus(t *testing.T) {
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
kubeClient := testKubelet.fakeKubeClient
mockCadvisor := testKubelet.fakeCadvisor
kubeClient.MinionsList = api.NodeList{Items: []api.Node{
{
ObjectMeta: api.ObjectMeta{Name: "testnode"},
Spec: api.NodeSpec{
Capacity: api.ResourceList{
api.ResourceCPU: *resource.NewMilliQuantity(3000, resource.DecimalSI),
api.ResourceMemory: *resource.NewQuantity(2048, resource.BinarySI),
},
},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{
{
Type: api.NodeReady,
Status: api.ConditionFull,
Reason: fmt.Sprintf("kubelet is posting ready status"),
LastProbeTime: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
},
},
}}
machineInfo := &cadvisorApi.MachineInfo{MachineID: "123", SystemUUID: "abc", NumCores: 2, MemoryCapacity: 1024}
mockCadvisor.On("MachineInfo").Return(machineInfo, nil)
expectedNode := &api.Node{
ObjectMeta: api.ObjectMeta{Name: "testnode"},
Spec: api.NodeSpec{
Capacity: api.ResourceList{
api.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
api.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
},
},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{
{
Type: api.NodeReady,
Status: api.ConditionFull,
Reason: fmt.Sprintf("kubelet is posting ready status"),
LastProbeTime: util.Time{}, // placeholder
},
},
NodeInfo: api.NodeSystemInfo{
MachineID: "123",
SystemUUID: "abc",
},
},
}
if err := kubelet.updateNodeStatus(); err != nil {
t.Errorf("unexpected error: %v", err)
}
if len(kubeClient.Actions) != 2 {
t.Errorf("unexpected actions: %v", kubeClient.Actions)
}
updatedNode, ok := kubeClient.Actions[1].Value.(*api.Node)
if !ok {
t.Errorf("unexpected object type")
}
if reflect.DeepEqual(updatedNode.Status.Conditions[0].LastProbeTime, util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC)) {
t.Errorf("expected \n%v\n, got \n%v", updatedNode.Status.Conditions[0].LastProbeTime,
util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC))
}
updatedNode.Status.Conditions[0].LastProbeTime = util.Time{}
if !reflect.DeepEqual(expectedNode, updatedNode) {
t.Errorf("expected \n%v\n, got \n%v", expectedNode, updatedNode)
}
}
func TestUpdateNodeStatusError(t *testing.T) {
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
kubeClient := testKubelet.fakeKubeClient
// No matching node for the kubelet
kubeClient.MinionsList = api.NodeList{Items: []api.Node{}}
if err := kubelet.updateNodeStatus(); err == nil {
t.Errorf("unexpected non error: %v")
}
if len(kubeClient.Actions) != nodeStatusUpdateRetry {
t.Errorf("unexpected actions: %v", kubeClient.Actions)
}
}
func TestCreateMirrorPod(t *testing.T) {
testKubelet := newTestKubelet(t)
kl := testKubelet.kubelet
manager := testKubelet.fakeMirrorManager
pod := api.Pod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "bar",
Namespace: "foo",
Annotations: map[string]string{
ConfigSourceAnnotationKey: "file",
},
},
}
pods := []api.Pod{pod}
kl.podManager.SetPods(pods)
hasMirrorPod := false
err := kl.syncPod(&pod, hasMirrorPod, dockertools.DockerContainers{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
podFullName := GetPodFullName(&pod)
if !manager.HasPod(podFullName) {
t.Errorf("expected mirror pod %q to be created", podFullName)
}
if manager.NumOfPods() != 1 || !manager.HasPod(podFullName) {
t.Errorf("expected one mirror pod %q, got %v", podFullName, manager.GetPods())
}
}
func TestDeleteOrphanedMirrorPods(t *testing.T) {
testKubelet := newTestKubelet(t)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorApi.MachineInfo{}, nil)
kl := testKubelet.kubelet
manager := testKubelet.fakeMirrorManager
orphanPods := []api.Pod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "pod1",
Namespace: "ns",
Annotations: map[string]string{
ConfigSourceAnnotationKey: "api",
ConfigMirrorAnnotationKey: "mirror",
},
},
},
{
ObjectMeta: api.ObjectMeta{
UID: "12345679",
Name: "pod2",
Namespace: "ns",
Annotations: map[string]string{
ConfigSourceAnnotationKey: "api",
ConfigMirrorAnnotationKey: "mirror",
},
},
},
}
mirrorPods := newMirrorPods()
for _, pod := range orphanPods {
mirrorPods.Insert(&pod)
}
// Sync with an empty pod list to delete all mirror pods.
err := kl.SyncPods([]api.Pod{}, emptyPodUIDs, *mirrorPods, time.Now())
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if manager.NumOfPods() != 0 {
t.Errorf("expected zero mirror pods, got %v", manager.GetPods())
}
for _, pod := range orphanPods {
name := GetPodFullName(&pod)
creates, deletes := manager.GetCounts(name)
if creates != 0 || deletes != 1 {
t.Errorf("expected 0 creation and one deletion of %q, got %d, %d", name, creates, deletes)
}
}
}
func TestGetContainerInfoForMirrorPods(t *testing.T) {
// pods contain one static and one mirror pod with the same name but
// different UIDs.
pods := []api.Pod{
{
ObjectMeta: api.ObjectMeta{
UID: "1234",
Name: "qux",
Namespace: "ns",
Annotations: map[string]string{
ConfigSourceAnnotationKey: "file",
},
},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "foo"},
},
},
},
{
ObjectMeta: api.ObjectMeta{
UID: "5678",
Name: "qux",
Namespace: "ns",
Annotations: map[string]string{
ConfigSourceAnnotationKey: "api",
ConfigMirrorAnnotationKey: "mirror",
},
},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "foo"},
},
},
},
}
containerID := "ab2cdf"
containerPath := fmt.Sprintf("/docker/%v", containerID)
containerInfo := cadvisorApi.ContainerInfo{
ContainerReference: cadvisorApi.ContainerReference{
Name: containerPath,
},
}
testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet
fakeDocker := testKubelet.fakeDocker
mockCadvisor := testKubelet.fakeCadvisor
cadvisorReq := &cadvisorApi.ContainerInfoRequest{}
mockCadvisor.On("DockerContainer", containerID, cadvisorReq).Return(containerInfo, nil)
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: containerID,
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
}
kubelet.podManager.SetPods(pods)
// Use the mirror pod UID to retrieve the stats.
stats, err := kubelet.GetContainerInfo("qux_ns", "5678", "foo", cadvisorReq)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if stats == nil {
t.Fatalf("stats should not be nil")
}
mockCadvisor.AssertExpectations(t)
}