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 (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2014-06-09 20:47:25 +00:00
|
|
|
"io/ioutil"
|
2014-06-06 23:40:48 +00:00
|
|
|
"net/http/httptest"
|
2014-06-09 20:47:25 +00:00
|
|
|
"reflect"
|
2014-06-06 23:40:48 +00:00
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
2014-06-30 19:00:14 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
2014-06-06 23:40:48 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
|
|
|
"github.com/coreos/go-etcd/etcd"
|
|
|
|
"github.com/fsouza/go-dockerclient"
|
2014-06-19 20:22:20 +00:00
|
|
|
"github.com/google/cadvisor/info"
|
|
|
|
"github.com/stretchr/testify/mock"
|
2014-06-06 23:40:48 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// TODO: This doesn't reduce typing enough to make it worth the less readable errors. Remove.
|
|
|
|
func expectNoError(t *testing.T, err error) {
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Unexpected error: %#v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// These are used for testing extract json (below)
|
|
|
|
type TestData struct {
|
|
|
|
Value string
|
|
|
|
Number int
|
|
|
|
}
|
|
|
|
|
|
|
|
type TestObject struct {
|
|
|
|
Name string
|
|
|
|
Data TestData
|
|
|
|
}
|
|
|
|
|
|
|
|
func verifyStringEquals(t *testing.T, actual, expected string) {
|
|
|
|
if actual != expected {
|
|
|
|
t.Errorf("Verification failed. Expected: %s, Found %s", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func verifyIntEquals(t *testing.T, actual, expected int) {
|
|
|
|
if actual != expected {
|
|
|
|
t.Errorf("Verification failed. Expected: %d, Found %d", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func verifyNoError(t *testing.T, e error) {
|
|
|
|
if e != nil {
|
|
|
|
t.Errorf("Expected no error, found %#v", e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func verifyError(t *testing.T, e error) {
|
|
|
|
if e == nil {
|
|
|
|
t.Errorf("Expected error, found nil")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-30 19:00:14 +00:00
|
|
|
func makeTestKubelet(t *testing.T) (*Kubelet, *tools.FakeEtcdClient, *FakeDockerClient) {
|
|
|
|
fakeEtcdClient := tools.MakeFakeEtcdClient(t)
|
2014-07-01 16:15:49 +00:00
|
|
|
fakeDocker := &FakeDockerClient{
|
|
|
|
err: nil,
|
2014-06-24 23:31:33 +00:00
|
|
|
}
|
2014-07-01 16:15:49 +00:00
|
|
|
|
|
|
|
kubelet := New()
|
|
|
|
kubelet.DockerClient = fakeDocker
|
|
|
|
kubelet.DockerPuller = &FakeDockerPuller{}
|
|
|
|
kubelet.EtcdClient = fakeEtcdClient
|
|
|
|
return kubelet, fakeEtcdClient, fakeDocker
|
2014-06-24 23:31:33 +00:00
|
|
|
}
|
|
|
|
|
2014-06-06 23:40:48 +00:00
|
|
|
func TestExtractJSON(t *testing.T) {
|
|
|
|
obj := TestObject{}
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, _, _ := makeTestKubelet(t)
|
2014-06-06 23:40:48 +00:00
|
|
|
data := `{ "name": "foo", "data": { "value": "bar", "number": 10 } }`
|
|
|
|
kubelet.ExtractYAMLData([]byte(data), &obj)
|
|
|
|
|
|
|
|
verifyStringEquals(t, obj.Name, "foo")
|
|
|
|
verifyStringEquals(t, obj.Data.Value, "bar")
|
|
|
|
verifyIntEquals(t, obj.Data.Number, 10)
|
|
|
|
}
|
|
|
|
|
2014-07-01 16:15:49 +00:00
|
|
|
func verifyCalls(t *testing.T, fakeDocker *FakeDockerClient, calls []string) {
|
2014-06-06 23:40:48 +00:00
|
|
|
verifyStringArrayEquals(t, fakeDocker.called, calls)
|
|
|
|
}
|
|
|
|
|
|
|
|
func verifyStringArrayEquals(t *testing.T, actual, expected []string) {
|
|
|
|
invalid := len(actual) != len(expected)
|
|
|
|
for ix, value := range actual {
|
|
|
|
if expected[ix] != value {
|
|
|
|
invalid = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if invalid {
|
|
|
|
t.Errorf("Expected: %#v, Actual: %#v", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-02 18:21:29 +00:00
|
|
|
func verifyPackUnpack(t *testing.T, manifestID, containerName string) {
|
2014-06-27 19:09:53 +00:00
|
|
|
name := buildDockerName(
|
2014-07-02 18:21:29 +00:00
|
|
|
&api.ContainerManifest{ID: manifestID},
|
2014-06-06 23:40:48 +00:00
|
|
|
&api.Container{Name: containerName},
|
|
|
|
)
|
2014-07-02 18:21:29 +00:00
|
|
|
returnedManifestID, returnedContainerName := parseDockerName(name)
|
|
|
|
if manifestID != returnedManifestID || containerName != returnedContainerName {
|
|
|
|
t.Errorf("For (%s, %s), unpacked (%s, %s)", manifestID, containerName, returnedManifestID, returnedContainerName)
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-09 23:50:44 +00:00
|
|
|
func verifyBoolean(t *testing.T, expected, value bool) {
|
|
|
|
if expected != value {
|
|
|
|
t.Errorf("Unexpected boolean. Expected %s. Found %s", expected, value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-06 23:40:48 +00:00
|
|
|
func TestContainerManifestNaming(t *testing.T) {
|
|
|
|
verifyPackUnpack(t, "manifest1234", "container5678")
|
|
|
|
verifyPackUnpack(t, "manifest--", "container__")
|
|
|
|
verifyPackUnpack(t, "--manifest", "__container")
|
|
|
|
verifyPackUnpack(t, "m___anifest_", "container-_-")
|
|
|
|
verifyPackUnpack(t, "_m___anifest", "-_-container")
|
|
|
|
}
|
|
|
|
|
2014-07-02 18:21:29 +00:00
|
|
|
func TestGetContainerID(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, _, fakeDocker := makeTestKubelet(t)
|
2014-06-06 23:40:48 +00:00
|
|
|
manifest := api.ContainerManifest{
|
2014-06-30 19:41:48 +00:00
|
|
|
ID: "qux",
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
container := api.Container{
|
|
|
|
Name: "foo",
|
|
|
|
}
|
|
|
|
fakeDocker.containerList = []docker.APIContainers{
|
2014-06-12 21:09:40 +00:00
|
|
|
{
|
2014-06-25 23:24:20 +00:00
|
|
|
ID: "foobar",
|
2014-06-21 20:16:20 +00:00
|
|
|
Names: []string{"/k8s--foo--qux--1234"},
|
2014-06-06 23:40:48 +00:00
|
|
|
},
|
2014-06-12 21:09:40 +00:00
|
|
|
{
|
2014-06-25 23:24:20 +00:00
|
|
|
ID: "barbar",
|
|
|
|
Names: []string{"/k8s--bar--qux--2565"},
|
2014-06-06 23:40:48 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
fakeDocker.container = &docker.Container{
|
|
|
|
ID: "foobar",
|
|
|
|
}
|
|
|
|
|
2014-07-02 18:21:29 +00:00
|
|
|
id, err := kubelet.getContainerID(&manifest, &container)
|
2014-06-25 23:24:20 +00:00
|
|
|
verifyCalls(t, fakeDocker, []string{"list"})
|
|
|
|
if id == "" {
|
2014-06-06 23:40:48 +00:00
|
|
|
t.Errorf("Failed to find container %#v", container)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Unexpected error: %#v", err)
|
|
|
|
}
|
2014-06-09 20:47:25 +00:00
|
|
|
|
|
|
|
fakeDocker.clearCalls()
|
2014-06-30 19:41:48 +00:00
|
|
|
missingManifest := api.ContainerManifest{ID: "foobar"}
|
2014-07-02 18:21:29 +00:00
|
|
|
id, err = kubelet.getContainerID(&missingManifest, &container)
|
2014-06-06 23:40:48 +00:00
|
|
|
verifyCalls(t, fakeDocker, []string{"list"})
|
2014-06-25 23:24:20 +00:00
|
|
|
if id != "" {
|
|
|
|
t.Errorf("Failed to not find container %#v", missingManifest)
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestKillContainerWithError(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
fakeDocker := &FakeDockerClient{
|
2014-06-13 22:45:19 +00:00
|
|
|
err: fmt.Errorf("sample error"),
|
2014-06-06 23:40:48 +00:00
|
|
|
containerList: []docker.APIContainers{
|
2014-06-12 21:09:40 +00:00
|
|
|
{
|
2014-06-25 23:24:20 +00:00
|
|
|
ID: "1234",
|
|
|
|
Names: []string{"/k8s--foo--qux--1234"},
|
2014-06-06 23:40:48 +00:00
|
|
|
},
|
2014-06-12 21:09:40 +00:00
|
|
|
{
|
2014-06-25 23:24:20 +00:00
|
|
|
ID: "5678",
|
|
|
|
Names: []string{"/k8s--bar--qux--5678"},
|
2014-06-06 23:40:48 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, _, _ := makeTestKubelet(t)
|
|
|
|
kubelet.DockerClient = fakeDocker
|
2014-06-25 23:24:20 +00:00
|
|
|
err := kubelet.killContainer(fakeDocker.containerList[0])
|
2014-06-06 23:40:48 +00:00
|
|
|
verifyError(t, err)
|
2014-06-25 23:24:20 +00:00
|
|
|
verifyCalls(t, fakeDocker, []string{"stop"})
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestKillContainer(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, _, fakeDocker := makeTestKubelet(t)
|
2014-06-06 23:40:48 +00:00
|
|
|
fakeDocker.containerList = []docker.APIContainers{
|
2014-06-12 21:09:40 +00:00
|
|
|
{
|
2014-06-25 23:24:20 +00:00
|
|
|
ID: "1234",
|
|
|
|
Names: []string{"/k8s--foo--qux--1234"},
|
2014-06-06 23:40:48 +00:00
|
|
|
},
|
2014-06-12 21:09:40 +00:00
|
|
|
{
|
2014-06-25 23:24:20 +00:00
|
|
|
ID: "5678",
|
|
|
|
Names: []string{"/k8s--bar--qux--5678"},
|
2014-06-06 23:40:48 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
fakeDocker.container = &docker.Container{
|
|
|
|
ID: "foobar",
|
|
|
|
}
|
|
|
|
|
2014-06-25 23:24:20 +00:00
|
|
|
err := kubelet.killContainer(fakeDocker.containerList[0])
|
2014-06-06 23:40:48 +00:00
|
|
|
verifyNoError(t, err)
|
2014-06-25 23:24:20 +00:00
|
|
|
verifyCalls(t, fakeDocker, []string{"stop"})
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestResponseToContainersNil(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, _, _ := makeTestKubelet(t)
|
2014-06-06 23:40:48 +00:00
|
|
|
list, err := kubelet.ResponseToManifests(&etcd.Response{Node: nil})
|
|
|
|
if len(list) != 0 {
|
|
|
|
t.Errorf("Unexpected non-zero list: %#v", list)
|
|
|
|
}
|
|
|
|
if err == nil {
|
|
|
|
t.Error("Unexpected non-error")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResponseToManifests(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, _, _ := makeTestKubelet(t)
|
2014-06-06 23:40:48 +00:00
|
|
|
list, err := kubelet.ResponseToManifests(&etcd.Response{
|
|
|
|
Node: &etcd.Node{
|
|
|
|
Value: util.MakeJSONString([]api.ContainerManifest{
|
2014-06-30 19:41:48 +00:00
|
|
|
{ID: "foo"},
|
|
|
|
{ID: "bar"},
|
2014-06-06 23:40:48 +00:00
|
|
|
}),
|
|
|
|
},
|
|
|
|
})
|
2014-06-30 19:41:48 +00:00
|
|
|
if len(list) != 2 || list[0].ID != "foo" || list[1].ID != "bar" {
|
2014-06-06 23:40:48 +00:00
|
|
|
t.Errorf("Unexpected list: %#v", list)
|
|
|
|
}
|
|
|
|
expectNoError(t, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
type channelReader struct {
|
|
|
|
list [][]api.ContainerManifest
|
|
|
|
wg sync.WaitGroup
|
|
|
|
}
|
|
|
|
|
2014-06-21 21:20:35 +00:00
|
|
|
func startReading(channel <-chan manifestUpdate) *channelReader {
|
2014-06-06 23:40:48 +00:00
|
|
|
cr := &channelReader{}
|
|
|
|
cr.wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
for {
|
2014-06-21 21:20:35 +00:00
|
|
|
update, ok := <-channel
|
2014-06-06 23:40:48 +00:00
|
|
|
if !ok {
|
|
|
|
break
|
|
|
|
}
|
2014-06-21 21:20:35 +00:00
|
|
|
cr.list = append(cr.list, update.manifests)
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
cr.wg.Done()
|
|
|
|
}()
|
|
|
|
return cr
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cr *channelReader) GetList() [][]api.ContainerManifest {
|
|
|
|
cr.wg.Wait()
|
|
|
|
return cr.list
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetKubeletStateFromEtcdNoData(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, fakeClient, _ := makeTestKubelet(t)
|
2014-06-21 21:20:35 +00:00
|
|
|
channel := make(chan manifestUpdate)
|
2014-06-06 23:40:48 +00:00
|
|
|
reader := startReading(channel)
|
2014-06-30 19:00:14 +00:00
|
|
|
fakeClient.Data["/registry/hosts/machine/kubelet"] = tools.EtcdResponseWithError{
|
2014-06-06 23:40:48 +00:00
|
|
|
R: &etcd.Response{},
|
|
|
|
E: nil,
|
|
|
|
}
|
2014-06-27 01:14:13 +00:00
|
|
|
err := kubelet.getKubeletStateFromEtcd("/registry/hosts/machine/kubelet", channel)
|
2014-06-06 23:40:48 +00:00
|
|
|
if err == nil {
|
|
|
|
t.Error("Unexpected no err.")
|
|
|
|
}
|
|
|
|
close(channel)
|
|
|
|
list := reader.GetList()
|
|
|
|
if len(list) != 0 {
|
|
|
|
t.Errorf("Unexpected list: %#v", list)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetKubeletStateFromEtcd(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, fakeClient, _ := makeTestKubelet(t)
|
2014-06-21 21:20:35 +00:00
|
|
|
channel := make(chan manifestUpdate)
|
2014-06-06 23:40:48 +00:00
|
|
|
reader := startReading(channel)
|
2014-06-30 19:00:14 +00:00
|
|
|
fakeClient.Data["/registry/hosts/machine/kubelet"] = tools.EtcdResponseWithError{
|
2014-06-06 23:40:48 +00:00
|
|
|
R: &etcd.Response{
|
|
|
|
Node: &etcd.Node{
|
|
|
|
Value: util.MakeJSONString([]api.Container{}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
E: nil,
|
|
|
|
}
|
2014-06-27 01:14:13 +00:00
|
|
|
err := kubelet.getKubeletStateFromEtcd("/registry/hosts/machine/kubelet", channel)
|
2014-06-06 23:40:48 +00:00
|
|
|
expectNoError(t, err)
|
|
|
|
close(channel)
|
|
|
|
list := reader.GetList()
|
|
|
|
if len(list) != 1 {
|
|
|
|
t.Errorf("Unexpected list: %#v", list)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetKubeletStateFromEtcdNotFound(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, fakeClient, _ := makeTestKubelet(t)
|
2014-06-21 21:20:35 +00:00
|
|
|
channel := make(chan manifestUpdate)
|
2014-06-06 23:40:48 +00:00
|
|
|
reader := startReading(channel)
|
2014-06-30 19:00:14 +00:00
|
|
|
fakeClient.Data["/registry/hosts/machine/kubelet"] = tools.EtcdResponseWithError{
|
2014-06-06 23:40:48 +00:00
|
|
|
R: &etcd.Response{},
|
|
|
|
E: &etcd.EtcdError{
|
|
|
|
ErrorCode: 100,
|
|
|
|
},
|
|
|
|
}
|
2014-06-27 01:14:13 +00:00
|
|
|
err := kubelet.getKubeletStateFromEtcd("/registry/hosts/machine/kubelet", channel)
|
2014-06-06 23:40:48 +00:00
|
|
|
expectNoError(t, err)
|
|
|
|
close(channel)
|
|
|
|
list := reader.GetList()
|
|
|
|
if len(list) != 0 {
|
|
|
|
t.Errorf("Unexpected list: %#v", list)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetKubeletStateFromEtcdError(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, fakeClient, _ := makeTestKubelet(t)
|
2014-06-21 21:20:35 +00:00
|
|
|
channel := make(chan manifestUpdate)
|
2014-06-06 23:40:48 +00:00
|
|
|
reader := startReading(channel)
|
2014-06-30 19:00:14 +00:00
|
|
|
fakeClient.Data["/registry/hosts/machine/kubelet"] = tools.EtcdResponseWithError{
|
2014-06-06 23:40:48 +00:00
|
|
|
R: &etcd.Response{},
|
|
|
|
E: &etcd.EtcdError{
|
|
|
|
ErrorCode: 200, // non not found error
|
|
|
|
},
|
|
|
|
}
|
2014-06-27 01:14:13 +00:00
|
|
|
err := kubelet.getKubeletStateFromEtcd("/registry/hosts/machine/kubelet", channel)
|
2014-06-06 23:40:48 +00:00
|
|
|
if err == nil {
|
|
|
|
t.Error("Unexpected non-error")
|
|
|
|
}
|
|
|
|
close(channel)
|
|
|
|
list := reader.GetList()
|
|
|
|
if len(list) != 0 {
|
|
|
|
t.Errorf("Unexpected list: %#v", list)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSyncManifestsDoesNothing(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, _, fakeDocker := makeTestKubelet(t)
|
2014-06-06 23:40:48 +00:00
|
|
|
fakeDocker.containerList = []docker.APIContainers{
|
2014-06-12 21:09:40 +00:00
|
|
|
{
|
2014-06-21 20:16:20 +00:00
|
|
|
// format is k8s--<container-id>--<manifest-id>
|
|
|
|
Names: []string{"/k8s--bar--foo"},
|
2014-06-06 23:40:48 +00:00
|
|
|
ID: "1234",
|
|
|
|
},
|
2014-06-20 03:30:42 +00:00
|
|
|
{
|
|
|
|
// network container
|
2014-06-21 20:16:20 +00:00
|
|
|
Names: []string{"/k8s--net--foo--"},
|
2014-06-20 03:30:42 +00:00
|
|
|
ID: "9876",
|
|
|
|
},
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
fakeDocker.container = &docker.Container{
|
|
|
|
ID: "1234",
|
|
|
|
}
|
|
|
|
err := kubelet.SyncManifests([]api.ContainerManifest{
|
2014-06-12 21:09:40 +00:00
|
|
|
{
|
2014-06-30 19:41:48 +00:00
|
|
|
ID: "foo",
|
2014-06-06 23:40:48 +00:00
|
|
|
Containers: []api.Container{
|
2014-06-12 21:09:40 +00:00
|
|
|
{Name: "bar"},
|
2014-06-06 23:40:48 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
expectNoError(t, err)
|
2014-06-25 23:24:20 +00:00
|
|
|
verifyCalls(t, fakeDocker, []string{"list", "list", "list"})
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSyncManifestsDeletes(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, _, fakeDocker := makeTestKubelet(t)
|
2014-06-06 23:40:48 +00:00
|
|
|
fakeDocker.containerList = []docker.APIContainers{
|
2014-06-12 21:09:40 +00:00
|
|
|
{
|
2014-06-21 20:16:20 +00:00
|
|
|
// the k8s prefix is required for the kubelet to manage the container
|
|
|
|
Names: []string{"/k8s--foo--bar"},
|
2014-06-06 23:40:48 +00:00
|
|
|
ID: "1234",
|
|
|
|
},
|
2014-06-20 03:30:42 +00:00
|
|
|
{
|
|
|
|
// network container
|
2014-06-21 20:16:20 +00:00
|
|
|
Names: []string{"/k8s--net--foo--"},
|
2014-06-20 03:30:42 +00:00
|
|
|
ID: "9876",
|
|
|
|
},
|
2014-06-13 19:09:48 +00:00
|
|
|
{
|
|
|
|
Names: []string{"foo"},
|
|
|
|
ID: "4567",
|
|
|
|
},
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
err := kubelet.SyncManifests([]api.ContainerManifest{})
|
|
|
|
expectNoError(t, err)
|
2014-06-25 23:24:20 +00:00
|
|
|
verifyCalls(t, fakeDocker, []string{"list", "stop", "stop"})
|
2014-06-27 22:29:13 +00:00
|
|
|
|
|
|
|
// A map interation is used to delete containers, so must not depend on
|
|
|
|
// order here.
|
|
|
|
expectedToStop := map[string]bool{
|
|
|
|
"1234": true,
|
|
|
|
"9876": true,
|
|
|
|
}
|
2014-06-25 23:24:20 +00:00
|
|
|
if len(fakeDocker.stopped) != 2 ||
|
2014-06-27 22:29:13 +00:00
|
|
|
!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
|
|
|
}
|
|
|
|
}
|
2014-06-09 20:47:25 +00:00
|
|
|
|
|
|
|
func TestEventWriting(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, fakeEtcd, _ := makeTestKubelet(t)
|
2014-06-09 20:47:25 +00:00
|
|
|
expectedEvent := api.Event{
|
|
|
|
Event: "test",
|
|
|
|
Container: &api.Container{
|
|
|
|
Name: "foo",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
err := kubelet.LogEvent(&expectedEvent)
|
|
|
|
expectNoError(t, err)
|
|
|
|
if fakeEtcd.Ix != 1 {
|
|
|
|
t.Errorf("Unexpected number of children added: %d, expected 1", fakeEtcd.Ix)
|
|
|
|
}
|
|
|
|
response, err := fakeEtcd.Get("/events/foo/1", false, false)
|
|
|
|
expectNoError(t, err)
|
|
|
|
var event api.Event
|
|
|
|
err = json.Unmarshal([]byte(response.Node.Value), &event)
|
|
|
|
expectNoError(t, err)
|
|
|
|
if event.Event != expectedEvent.Event ||
|
|
|
|
event.Container.Name != expectedEvent.Container.Name {
|
|
|
|
t.Errorf("Event's don't match. Expected: %#v Saw: %#v", expectedEvent, event)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestEventWritingError(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, fakeEtcd, _ := makeTestKubelet(t)
|
2014-06-13 22:45:19 +00:00
|
|
|
fakeEtcd.Err = fmt.Errorf("test error")
|
2014-06-09 20:47:25 +00:00
|
|
|
err := kubelet.LogEvent(&api.Event{
|
|
|
|
Event: "test",
|
|
|
|
Container: &api.Container{
|
|
|
|
Name: "foo",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
if err == nil {
|
|
|
|
t.Errorf("Unexpected non-error")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMakeEnvVariables(t *testing.T) {
|
|
|
|
container := api.Container{
|
|
|
|
Env: []api.EnvVar{
|
2014-06-13 01:34:47 +00:00
|
|
|
{
|
2014-06-09 20:47:25 +00:00
|
|
|
Name: "foo",
|
|
|
|
Value: "bar",
|
|
|
|
},
|
2014-06-13 01:34:47 +00:00
|
|
|
{
|
2014-06-09 20:47:25 +00:00
|
|
|
Name: "baz",
|
|
|
|
Value: "blah",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
vars := makeEnvironmentVariables(&container)
|
|
|
|
if len(vars) != len(container.Env) {
|
|
|
|
t.Errorf("Vars don't match. Expected: %#v Found: %#v", container.Env, vars)
|
|
|
|
}
|
|
|
|
for ix, env := range container.Env {
|
|
|
|
value := fmt.Sprintf("%s=%s", env.Name, env.Value)
|
|
|
|
if value != vars[ix] {
|
|
|
|
t.Errorf("Unexpected value: %s. Expected: %s", vars[ix], value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMakeVolumesAndBinds(t *testing.T) {
|
|
|
|
container := api.Container{
|
|
|
|
VolumeMounts: []api.VolumeMount{
|
2014-06-13 01:34:47 +00:00
|
|
|
{
|
2014-06-09 20:47:25 +00:00
|
|
|
MountPath: "/mnt/path",
|
|
|
|
Name: "disk",
|
|
|
|
ReadOnly: false,
|
|
|
|
},
|
2014-06-13 01:34:47 +00:00
|
|
|
{
|
2014-06-09 20:47:25 +00:00
|
|
|
MountPath: "/mnt/path2",
|
|
|
|
Name: "disk2",
|
|
|
|
ReadOnly: true,
|
2014-06-19 23:59:48 +00:00
|
|
|
MountType: "LOCAL",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
MountPath: "/mnt/path3",
|
|
|
|
Name: "disk3",
|
|
|
|
ReadOnly: false,
|
|
|
|
MountType: "HOST",
|
2014-06-09 20:47:25 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
volumes, binds := makeVolumesAndBinds(&container)
|
2014-06-19 23:59:48 +00:00
|
|
|
|
|
|
|
expectedVolumes := []string{"/mnt/path", "/mnt/path2"}
|
|
|
|
expectedBinds := []string{"/exports/disk:/mnt/path", "/exports/disk2:/mnt/path2:ro", "/mnt/path3:/mnt/path3"}
|
|
|
|
if len(volumes) != len(expectedVolumes) {
|
|
|
|
t.Errorf("Unexpected volumes. Expected %#v got %#v. Container was: %#v", expectedVolumes, volumes, container)
|
|
|
|
}
|
|
|
|
for _, expectedVolume := range expectedVolumes {
|
|
|
|
if _, ok := volumes[expectedVolume]; !ok {
|
|
|
|
t.Errorf("Volumes map is missing key: %s. %#v", expectedVolume, volumes)
|
2014-06-09 20:47:25 +00:00
|
|
|
}
|
|
|
|
}
|
2014-06-19 23:59:48 +00:00
|
|
|
if len(binds) != len(expectedBinds) {
|
|
|
|
t.Errorf("Unexpected binds: Expected %# got %#v. Container was: %#v", expectedBinds, binds, container)
|
|
|
|
}
|
|
|
|
verifyStringArrayEquals(t, binds, expectedBinds)
|
2014-06-09 20:47:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestMakePortsAndBindings(t *testing.T) {
|
|
|
|
container := api.Container{
|
|
|
|
Ports: []api.Port{
|
2014-06-13 01:34:47 +00:00
|
|
|
{
|
2014-06-09 20:47:25 +00:00
|
|
|
ContainerPort: 80,
|
|
|
|
HostPort: 8080,
|
|
|
|
},
|
2014-06-13 01:34:47 +00:00
|
|
|
{
|
2014-06-09 20:47:25 +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",
|
2014-06-09 20:47:25 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
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
|
|
|
}
|
|
|
|
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
|
|
|
}
|
|
|
|
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
|
|
|
}
|
|
|
|
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
|
|
|
}
|
2014-06-16 04:57:29 +00:00
|
|
|
}
|
|
|
|
}
|
2014-07-08 04:48:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestCheckHostPortConflicts(t *testing.T) {
|
|
|
|
successCaseAll := []api.ContainerManifest{
|
|
|
|
{Containers: []api.Container{{Ports: []api.Port{{HostPort: 80}}}}},
|
|
|
|
{Containers: []api.Container{{Ports: []api.Port{{HostPort: 81}}}}},
|
|
|
|
{Containers: []api.Container{{Ports: []api.Port{{HostPort: 82}}}}},
|
|
|
|
}
|
|
|
|
successCaseNew := api.ContainerManifest{
|
|
|
|
Containers: []api.Container{{Ports: []api.Port{{HostPort: 83}}}},
|
|
|
|
}
|
|
|
|
if err := checkHostPortConflicts(successCaseAll, &successCaseNew); err != nil {
|
|
|
|
t.Errorf("Expected success: %v", err)
|
|
|
|
}
|
2014-06-16 04:19:35 +00:00
|
|
|
|
2014-07-08 04:48:47 +00:00
|
|
|
failureCaseAll := []api.ContainerManifest{
|
|
|
|
{Containers: []api.Container{{Ports: []api.Port{{HostPort: 80}}}}},
|
|
|
|
{Containers: []api.Container{{Ports: []api.Port{{HostPort: 81}}}}},
|
|
|
|
{Containers: []api.Container{{Ports: []api.Port{{HostPort: 82}}}}},
|
|
|
|
}
|
|
|
|
failureCaseNew := api.ContainerManifest{
|
|
|
|
Containers: []api.Container{{Ports: []api.Port{{HostPort: 81}}}},
|
|
|
|
}
|
|
|
|
if err := checkHostPortConflicts(failureCaseAll, &failureCaseNew); err == nil {
|
|
|
|
t.Errorf("Expected failure")
|
|
|
|
}
|
2014-06-09 20:47:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestExtractFromNonExistentFile(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet := New()
|
2014-06-19 20:06:52 +00:00
|
|
|
_, err := kubelet.extractFromFile("/some/fake/file")
|
2014-06-09 20:47:25 +00:00
|
|
|
if err == nil {
|
|
|
|
t.Error("Unexpected non-error.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestExtractFromBadDataFile(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet := New()
|
2014-06-20 16:31:18 +00:00
|
|
|
|
|
|
|
badData := []byte{1, 2, 3}
|
2014-06-09 20:47:25 +00:00
|
|
|
file, err := ioutil.TempFile("", "foo")
|
|
|
|
expectNoError(t, err)
|
|
|
|
name := file.Name()
|
|
|
|
file.Close()
|
2014-06-20 16:31:18 +00:00
|
|
|
ioutil.WriteFile(name, badData, 0755)
|
2014-06-19 20:06:52 +00:00
|
|
|
_, err = kubelet.extractFromFile(name)
|
2014-06-09 20:47:25 +00:00
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
t.Error("Unexpected non-error.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-20 16:31:18 +00:00
|
|
|
func TestExtractFromValidDataFile(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet := New()
|
2014-06-20 16:31:18 +00:00
|
|
|
|
2014-06-30 19:41:48 +00:00
|
|
|
manifest := api.ContainerManifest{ID: "bar"}
|
2014-06-19 20:06:52 +00:00
|
|
|
data, err := json.Marshal(manifest)
|
2014-06-09 20:47:25 +00:00
|
|
|
expectNoError(t, err)
|
|
|
|
file, err := ioutil.TempFile("", "foo")
|
|
|
|
expectNoError(t, err)
|
|
|
|
name := file.Name()
|
|
|
|
expectNoError(t, file.Close())
|
2014-06-20 16:31:18 +00:00
|
|
|
ioutil.WriteFile(name, data, 0755)
|
2014-06-09 20:47:25 +00:00
|
|
|
|
2014-06-19 20:06:52 +00:00
|
|
|
read, err := kubelet.extractFromFile(name)
|
2014-06-09 20:47:25 +00:00
|
|
|
expectNoError(t, err)
|
2014-06-19 20:06:52 +00:00
|
|
|
if !reflect.DeepEqual(read, manifest) {
|
|
|
|
t.Errorf("Unexpected difference. Expected %#v, got %#v", manifest, read)
|
2014-06-09 20:47:25 +00:00
|
|
|
}
|
2014-06-19 20:06:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestExtractFromEmptyDir(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet := New()
|
2014-06-19 20:06:52 +00:00
|
|
|
|
|
|
|
dirName, err := ioutil.TempDir("", "foo")
|
|
|
|
expectNoError(t, err)
|
|
|
|
|
|
|
|
_, err = kubelet.extractFromDir(dirName)
|
|
|
|
expectNoError(t, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestExtractFromDir(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet := New()
|
2014-06-19 20:06:52 +00:00
|
|
|
|
|
|
|
manifests := []api.ContainerManifest{
|
2014-06-30 19:41:48 +00:00
|
|
|
{ID: "aaaa"},
|
|
|
|
{ID: "bbbb"},
|
2014-06-19 20:06:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dirName, err := ioutil.TempDir("", "foo")
|
|
|
|
expectNoError(t, err)
|
|
|
|
|
|
|
|
for _, manifest := range manifests {
|
|
|
|
data, err := json.Marshal(manifest)
|
|
|
|
expectNoError(t, err)
|
2014-06-30 19:41:48 +00:00
|
|
|
file, err := ioutil.TempFile(dirName, manifest.ID)
|
2014-06-19 20:06:52 +00:00
|
|
|
expectNoError(t, err)
|
|
|
|
name := file.Name()
|
|
|
|
expectNoError(t, file.Close())
|
|
|
|
ioutil.WriteFile(name, data, 0755)
|
|
|
|
}
|
|
|
|
|
|
|
|
read, err := kubelet.extractFromDir(dirName)
|
|
|
|
expectNoError(t, err)
|
|
|
|
if !reflect.DeepEqual(read, manifests) {
|
|
|
|
t.Errorf("Unexpected difference. Expected %#v, got %#v", manifests, read)
|
2014-06-09 20:47:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestExtractFromHttpBadness(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet := New()
|
2014-06-21 21:20:35 +00:00
|
|
|
updateChannel := make(chan manifestUpdate)
|
|
|
|
reader := startReading(updateChannel)
|
2014-06-20 16:31:18 +00:00
|
|
|
|
2014-06-21 21:20:35 +00:00
|
|
|
err := kubelet.extractFromHTTP("http://localhost:12345", updateChannel)
|
2014-06-09 20:47:25 +00:00
|
|
|
if err == nil {
|
|
|
|
t.Error("Unexpected non-error.")
|
|
|
|
}
|
2014-06-21 21:20:35 +00:00
|
|
|
close(updateChannel)
|
2014-06-20 16:31:18 +00:00
|
|
|
list := reader.GetList()
|
|
|
|
|
|
|
|
if len(list) != 0 {
|
|
|
|
t.Errorf("Unexpected list: %#v", list)
|
2014-06-09 20:47:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-23 22:47:25 +00:00
|
|
|
func TestExtractFromHttpSingle(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet := New()
|
2014-06-21 21:20:35 +00:00
|
|
|
updateChannel := make(chan manifestUpdate)
|
|
|
|
reader := startReading(updateChannel)
|
2014-06-09 20:47:25 +00:00
|
|
|
|
2014-06-20 16:31:18 +00:00
|
|
|
manifests := []api.ContainerManifest{
|
2014-06-30 19:41:48 +00:00
|
|
|
{Version: "v1beta1", ID: "foo"},
|
2014-06-09 20:47:25 +00:00
|
|
|
}
|
2014-06-23 22:47:25 +00:00
|
|
|
// Taking a single-manifest from a URL allows kubelet to be used
|
|
|
|
// in the implementation of google's container VM image.
|
2014-06-23 22:06:28 +00:00
|
|
|
data, err := json.Marshal(manifests[0])
|
2014-06-09 20:47:25 +00:00
|
|
|
|
|
|
|
fakeHandler := util.FakeHandler{
|
|
|
|
StatusCode: 200,
|
2014-06-20 16:31:18 +00:00
|
|
|
ResponseBody: string(data),
|
2014-06-09 20:47:25 +00:00
|
|
|
}
|
|
|
|
testServer := httptest.NewServer(&fakeHandler)
|
|
|
|
|
2014-06-21 21:20:35 +00:00
|
|
|
err = kubelet.extractFromHTTP(testServer.URL, updateChannel)
|
2014-06-09 20:47:25 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Unexpected error: %#v", err)
|
|
|
|
}
|
2014-06-21 21:20:35 +00:00
|
|
|
close(updateChannel)
|
2014-06-09 20:47:25 +00:00
|
|
|
|
|
|
|
read := reader.GetList()
|
|
|
|
|
|
|
|
if len(read) != 1 {
|
|
|
|
t.Errorf("Unexpected list: %#v", read)
|
2014-06-23 22:47:25 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(manifests, read[0]) {
|
|
|
|
t.Errorf("Unexpected difference. Expected: %#v, Saw: %#v", manifests, read[0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestExtractFromHttpMultiple(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet := New()
|
2014-06-23 22:47:25 +00:00
|
|
|
updateChannel := make(chan manifestUpdate)
|
|
|
|
reader := startReading(updateChannel)
|
|
|
|
|
|
|
|
manifests := []api.ContainerManifest{
|
2014-06-30 19:41:48 +00:00
|
|
|
{Version: "v1beta1", ID: "foo"},
|
|
|
|
{Version: "v1beta1", ID: "bar"},
|
2014-06-23 22:47:25 +00:00
|
|
|
}
|
|
|
|
data, err := json.Marshal(manifests)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Some weird json problem: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Logf("Serving: %v", string(data))
|
|
|
|
|
|
|
|
fakeHandler := util.FakeHandler{
|
|
|
|
StatusCode: 200,
|
|
|
|
ResponseBody: string(data),
|
|
|
|
}
|
|
|
|
testServer := httptest.NewServer(&fakeHandler)
|
|
|
|
|
|
|
|
err = kubelet.extractFromHTTP(testServer.URL, updateChannel)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Unexpected error: %#v", err)
|
|
|
|
}
|
|
|
|
close(updateChannel)
|
|
|
|
|
|
|
|
read := reader.GetList()
|
|
|
|
|
|
|
|
if len(read) != 1 {
|
|
|
|
t.Errorf("Unexpected list: %#v", read)
|
|
|
|
return
|
2014-06-09 20:47:25 +00:00
|
|
|
}
|
2014-06-20 16:31:18 +00:00
|
|
|
if !reflect.DeepEqual(manifests, read[0]) {
|
|
|
|
t.Errorf("Unexpected difference. Expected: %#v, Saw: %#v", manifests, read[0])
|
2014-06-09 20:47:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-28 06:46:02 +00:00
|
|
|
func TestExtractFromHttpEmptyArray(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet := New()
|
2014-06-28 06:46:02 +00:00
|
|
|
updateChannel := make(chan manifestUpdate)
|
|
|
|
reader := startReading(updateChannel)
|
|
|
|
|
|
|
|
manifests := []api.ContainerManifest{}
|
|
|
|
data, err := json.Marshal(manifests)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Some weird json problem: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Logf("Serving: %v", string(data))
|
|
|
|
|
|
|
|
fakeHandler := util.FakeHandler{
|
|
|
|
StatusCode: 200,
|
|
|
|
ResponseBody: string(data),
|
|
|
|
}
|
|
|
|
testServer := httptest.NewServer(&fakeHandler)
|
|
|
|
|
|
|
|
err = kubelet.extractFromHTTP(testServer.URL, updateChannel)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Unexpected error: %#v", err)
|
|
|
|
}
|
|
|
|
close(updateChannel)
|
|
|
|
|
|
|
|
read := reader.GetList()
|
|
|
|
|
|
|
|
if len(read) != 1 {
|
|
|
|
t.Errorf("Unexpected list: %#v", read)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if len(read[0]) != 0 {
|
|
|
|
t.Errorf("Unexpected manifests: %#v", read[0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-09 20:47:25 +00:00
|
|
|
func TestWatchEtcd(t *testing.T) {
|
|
|
|
watchChannel := make(chan *etcd.Response)
|
2014-06-21 21:20:35 +00:00
|
|
|
updateChannel := make(chan manifestUpdate)
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet := New()
|
2014-06-21 21:20:35 +00:00
|
|
|
reader := startReading(updateChannel)
|
2014-06-09 20:47:25 +00:00
|
|
|
|
|
|
|
manifest := []api.ContainerManifest{
|
2014-06-13 01:34:47 +00:00
|
|
|
{
|
2014-06-30 19:41:48 +00:00
|
|
|
ID: "foo",
|
2014-06-09 20:47:25 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
data, err := json.Marshal(manifest)
|
|
|
|
expectNoError(t, err)
|
|
|
|
|
2014-07-01 22:15:40 +00:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
kubelet.WatchEtcd(watchChannel, updateChannel)
|
|
|
|
wg.Done()
|
|
|
|
}()
|
2014-06-09 20:47:25 +00:00
|
|
|
|
|
|
|
watchChannel <- &etcd.Response{
|
|
|
|
Node: &etcd.Node{
|
|
|
|
Value: string(data),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
close(watchChannel)
|
2014-07-01 22:15:40 +00:00
|
|
|
wg.Wait()
|
2014-06-21 21:20:35 +00:00
|
|
|
close(updateChannel)
|
2014-06-09 20:47:25 +00:00
|
|
|
|
|
|
|
read := reader.GetList()
|
2014-06-26 23:56:02 +00:00
|
|
|
if len(read) != 1 {
|
|
|
|
t.Errorf("Expected number of results: %v", len(read))
|
|
|
|
} else if !reflect.DeepEqual(read[0], manifest) {
|
2014-06-09 20:47:25 +00:00
|
|
|
t.Errorf("Unexpected manifest(s) %#v %#v", read[0], manifest)
|
|
|
|
}
|
|
|
|
}
|
2014-06-19 20:22:20 +00:00
|
|
|
|
|
|
|
type mockCadvisorClient struct {
|
|
|
|
mock.Mock
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *mockCadvisorClient) ContainerInfo(name string) (*info.ContainerInfo, error) {
|
|
|
|
args := self.Called(name)
|
|
|
|
return args.Get(0).(*info.ContainerInfo), args.Error(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *mockCadvisorClient) MachineInfo() (*info.MachineInfo, error) {
|
|
|
|
args := self.Called()
|
|
|
|
return args.Get(0).(*info.MachineInfo), args.Error(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func areSamePercentiles(
|
|
|
|
cadvisorPercentiles []info.Percentile,
|
|
|
|
kubePercentiles []api.Percentile,
|
|
|
|
t *testing.T,
|
|
|
|
) {
|
|
|
|
if len(cadvisorPercentiles) != len(kubePercentiles) {
|
|
|
|
t.Errorf("cadvisor gives %v percentiles; kubelet got %v", len(cadvisorPercentiles), len(kubePercentiles))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, ap := range cadvisorPercentiles {
|
|
|
|
found := false
|
|
|
|
for _, kp := range kubePercentiles {
|
|
|
|
if ap.Percentage == kp.Percentage {
|
|
|
|
found = true
|
|
|
|
if ap.Value != kp.Value {
|
|
|
|
t.Errorf("%v percentile from cadvisor is %v; kubelet got %v",
|
|
|
|
ap.Percentage,
|
|
|
|
ap.Value,
|
|
|
|
kp.Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
t.Errorf("Unable to find %v percentile in kubelet's data", ap.Percentage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetContainerStats(t *testing.T) {
|
2014-07-02 18:21:29 +00:00
|
|
|
containerID := "ab2cdf"
|
|
|
|
containerPath := fmt.Sprintf("/docker/%v", containerID)
|
2014-06-19 20:22:20 +00:00
|
|
|
containerInfo := &info.ContainerInfo{
|
|
|
|
ContainerReference: info.ContainerReference{
|
|
|
|
Name: containerPath,
|
|
|
|
},
|
|
|
|
StatsPercentiles: &info.ContainerStatsPercentiles{
|
|
|
|
MaxMemoryUsage: 1024000,
|
|
|
|
MemoryUsagePercentiles: []info.Percentile{
|
2014-06-19 20:59:20 +00:00
|
|
|
{50, 100},
|
|
|
|
{80, 180},
|
|
|
|
{90, 190},
|
2014-06-19 20:22:20 +00:00
|
|
|
},
|
|
|
|
CpuUsagePercentiles: []info.Percentile{
|
2014-06-19 20:59:20 +00:00
|
|
|
{51, 101},
|
|
|
|
{81, 181},
|
|
|
|
{91, 191},
|
2014-06-19 20:22:20 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
mockCadvisor := &mockCadvisorClient{}
|
|
|
|
mockCadvisor.On("ContainerInfo", containerPath).Return(containerInfo, nil)
|
|
|
|
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, _, fakeDocker := makeTestKubelet(t)
|
|
|
|
kubelet.CadvisorClient = mockCadvisor
|
2014-06-19 20:22:20 +00:00
|
|
|
fakeDocker.containerList = []docker.APIContainers{
|
|
|
|
{
|
2014-07-01 21:05:10 +00:00
|
|
|
ID: containerID,
|
|
|
|
// pod id: qux
|
|
|
|
// container id: foo
|
|
|
|
Names: []string{"/k8s--foo--qux--1234"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
stats, err := kubelet.GetContainerStats("qux", "foo")
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if stats.MaxMemoryUsage != containerInfo.StatsPercentiles.MaxMemoryUsage {
|
|
|
|
t.Errorf("wrong max memory usage")
|
|
|
|
}
|
|
|
|
areSamePercentiles(containerInfo.StatsPercentiles.CpuUsagePercentiles, stats.CpuUsagePercentiles, t)
|
|
|
|
areSamePercentiles(containerInfo.StatsPercentiles.MemoryUsagePercentiles, stats.MemoryUsagePercentiles, t)
|
|
|
|
mockCadvisor.AssertExpectations(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetMachineStats(t *testing.T) {
|
|
|
|
containerPath := "/"
|
|
|
|
containerInfo := &info.ContainerInfo{
|
|
|
|
ContainerReference: info.ContainerReference{
|
|
|
|
Name: containerPath,
|
|
|
|
}, StatsPercentiles: &info.ContainerStatsPercentiles{MaxMemoryUsage: 1024000, MemoryUsagePercentiles: []info.Percentile{{50, 100}, {80, 180},
|
|
|
|
{90, 190},
|
2014-06-19 20:22:20 +00:00
|
|
|
},
|
2014-07-01 21:05:10 +00:00
|
|
|
CpuUsagePercentiles: []info.Percentile{
|
|
|
|
{51, 101},
|
|
|
|
{81, 181},
|
|
|
|
{91, 191},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
fakeDocker := FakeDockerClient{
|
|
|
|
err: nil,
|
2014-06-19 20:22:20 +00:00
|
|
|
}
|
|
|
|
|
2014-07-01 21:05:10 +00:00
|
|
|
mockCadvisor := &mockCadvisorClient{}
|
|
|
|
mockCadvisor.On("ContainerInfo", containerPath).Return(containerInfo, nil)
|
|
|
|
|
|
|
|
kubelet := Kubelet{
|
|
|
|
DockerClient: &fakeDocker,
|
|
|
|
DockerPuller: &FakeDockerPuller{},
|
|
|
|
CadvisorClient: mockCadvisor,
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the container name is an empty string, then it means the root container.
|
|
|
|
stats, err := kubelet.GetMachineStats()
|
2014-06-19 20:22:20 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if stats.MaxMemoryUsage != containerInfo.StatsPercentiles.MaxMemoryUsage {
|
|
|
|
t.Errorf("wrong max memory usage")
|
|
|
|
}
|
|
|
|
areSamePercentiles(containerInfo.StatsPercentiles.CpuUsagePercentiles, stats.CpuUsagePercentiles, t)
|
|
|
|
areSamePercentiles(containerInfo.StatsPercentiles.MemoryUsagePercentiles, stats.MemoryUsagePercentiles, t)
|
|
|
|
mockCadvisor.AssertExpectations(t)
|
|
|
|
}
|
2014-06-19 20:25:54 +00:00
|
|
|
|
|
|
|
func TestGetContainerStatsWithoutCadvisor(t *testing.T) {
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, _, fakeDocker := makeTestKubelet(t)
|
2014-06-19 20:25:54 +00:00
|
|
|
fakeDocker.containerList = []docker.APIContainers{
|
|
|
|
{
|
2014-07-01 21:05:10 +00:00
|
|
|
ID: "foobar",
|
|
|
|
// pod id: qux
|
|
|
|
// container id: foo
|
|
|
|
Names: []string{"/k8s--foo--qux--1234"},
|
2014-06-19 20:25:54 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2014-07-01 21:05:10 +00:00
|
|
|
stats, _ := kubelet.GetContainerStats("qux", "foo")
|
2014-06-19 20:25:54 +00:00
|
|
|
// When there's no cAdvisor, the stats should be either nil or empty
|
|
|
|
if stats == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if stats.MaxMemoryUsage != 0 {
|
|
|
|
t.Errorf("MaxMemoryUsage is %v even if there's no cadvisor", stats.MaxMemoryUsage)
|
|
|
|
}
|
|
|
|
if len(stats.CpuUsagePercentiles) > 0 {
|
|
|
|
t.Errorf("Cpu usage percentiles is not empty (%+v) even if there's no cadvisor", stats.CpuUsagePercentiles)
|
|
|
|
}
|
|
|
|
if len(stats.MemoryUsagePercentiles) > 0 {
|
|
|
|
t.Errorf("Memory usage percentiles is not empty (%+v) even if there's no cadvisor", stats.MemoryUsagePercentiles)
|
|
|
|
}
|
|
|
|
}
|
2014-06-19 20:34:26 +00:00
|
|
|
|
|
|
|
func TestGetContainerStatsWhenCadvisorFailed(t *testing.T) {
|
2014-07-02 18:21:29 +00:00
|
|
|
containerID := "ab2cdf"
|
|
|
|
containerPath := fmt.Sprintf("/docker/%v", containerID)
|
2014-06-19 20:34:26 +00:00
|
|
|
|
|
|
|
containerInfo := &info.ContainerInfo{}
|
|
|
|
mockCadvisor := &mockCadvisorClient{}
|
|
|
|
expectedErr := fmt.Errorf("some error")
|
|
|
|
mockCadvisor.On("ContainerInfo", containerPath).Return(containerInfo, expectedErr)
|
|
|
|
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, _, fakeDocker := makeTestKubelet(t)
|
|
|
|
kubelet.CadvisorClient = mockCadvisor
|
2014-06-19 20:34:26 +00:00
|
|
|
fakeDocker.containerList = []docker.APIContainers{
|
|
|
|
{
|
2014-07-01 21:05:10 +00:00
|
|
|
ID: containerID,
|
|
|
|
// pod id: qux
|
|
|
|
// container id: foo
|
|
|
|
Names: []string{"/k8s--foo--qux--1234"},
|
2014-06-19 20:34:26 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2014-07-01 21:05:10 +00:00
|
|
|
stats, err := kubelet.GetContainerStats("qux", "foo")
|
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() != expectedErr.Error() {
|
|
|
|
t.Errorf("wrong error message. expect %v, got %v", err, expectedErr)
|
|
|
|
}
|
|
|
|
mockCadvisor.AssertExpectations(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetContainerStatsOnNonExistContainer(t *testing.T) {
|
|
|
|
mockCadvisor := &mockCadvisorClient{}
|
|
|
|
|
2014-07-01 16:15:49 +00:00
|
|
|
kubelet, _, fakeDocker := makeTestKubelet(t)
|
|
|
|
kubelet.CadvisorClient = mockCadvisor
|
2014-06-19 20:34:26 +00:00
|
|
|
fakeDocker.containerList = []docker.APIContainers{}
|
|
|
|
|
2014-07-01 21:05:10 +00:00
|
|
|
stats, _ := kubelet.GetContainerStats("qux", "foo")
|
2014-06-19 20:34:26 +00:00
|
|
|
if stats != nil {
|
|
|
|
t.Errorf("non-nil stats on non exist container")
|
|
|
|
}
|
|
|
|
mockCadvisor.AssertExpectations(t)
|
|
|
|
}
|
2014-06-19 12:29:42 +00:00
|
|
|
|
|
|
|
func TestParseImageName(t *testing.T) {
|
|
|
|
name, tag := parseImageName("ubuntu")
|
|
|
|
if name != "ubuntu" || tag != "" {
|
|
|
|
t.Fatal("Unexpected name/tag: %s/%s", name, tag)
|
|
|
|
}
|
|
|
|
|
|
|
|
name, tag = parseImageName("ubuntu:2342")
|
|
|
|
if name != "ubuntu" || tag != "2342" {
|
|
|
|
t.Fatal("Unexpected name/tag: %s/%s", name, tag)
|
|
|
|
}
|
|
|
|
|
|
|
|
name, tag = parseImageName("foo/bar:445566")
|
|
|
|
if name != "foo/bar" || tag != "445566" {
|
|
|
|
t.Fatal("Unexpected name/tag: %s/%s", name, tag)
|
|
|
|
}
|
|
|
|
|
|
|
|
name, tag = parseImageName("registry.example.com:5000/foobar")
|
|
|
|
if name != "registry.example.com:5000/foobar" || tag != "" {
|
|
|
|
t.Fatal("Unexpected name/tag: %s/%s", name, tag)
|
|
|
|
}
|
|
|
|
|
|
|
|
name, tag = parseImageName("registry.example.com:5000/foobar:5342")
|
|
|
|
if name != "registry.example.com:5000/foobar" || tag != "5342" {
|
|
|
|
t.Fatal("Unexpected name/tag: %s/%s", name, tag)
|
|
|
|
}
|
|
|
|
}
|