github.com/deemoprobe/k8s-first-commit@v0.0.0-20230430165612-a541f1982be3/pkg/kubelet/kubelet_test.go (about)

     1  /*
     2  Copyright 2014 Google Inc. All rights reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  package kubelet
    17  
    18  import (
    19  	"encoding/json"
    20  	"fmt"
    21  	"net/http"
    22  	"net/http/httptest"
    23  	"sync"
    24  	"testing"
    25  
    26  	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
    27  	"github.com/GoogleCloudPlatform/kubernetes/pkg/registry"
    28  	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
    29  	"github.com/coreos/go-etcd/etcd"
    30  	"github.com/fsouza/go-dockerclient"
    31  )
    32  
    33  // TODO: This doesn't reduce typing enough to make it worth the less readable errors. Remove.
    34  func expectNoError(t *testing.T, err error) {
    35  	if err != nil {
    36  		t.Errorf("Unexpected error: %#v", err)
    37  	}
    38  }
    39  
    40  // These are used for testing extract json (below)
    41  type TestData struct {
    42  	Value  string
    43  	Number int
    44  }
    45  
    46  type TestObject struct {
    47  	Name string
    48  	Data TestData
    49  }
    50  
    51  func verifyStringEquals(t *testing.T, actual, expected string) {
    52  	if actual != expected {
    53  		t.Errorf("Verification failed.  Expected: %s, Found %s", expected, actual)
    54  	}
    55  }
    56  
    57  func verifyIntEquals(t *testing.T, actual, expected int) {
    58  	if actual != expected {
    59  		t.Errorf("Verification failed.  Expected: %d, Found %d", expected, actual)
    60  	}
    61  }
    62  
    63  func verifyNoError(t *testing.T, e error) {
    64  	if e != nil {
    65  		t.Errorf("Expected no error, found %#v", e)
    66  	}
    67  }
    68  
    69  func verifyError(t *testing.T, e error) {
    70  	if e == nil {
    71  		t.Errorf("Expected error, found nil")
    72  	}
    73  }
    74  
    75  func TestExtractJSON(t *testing.T) {
    76  	obj := TestObject{}
    77  	kubelet := Kubelet{}
    78  	data := `{ "name": "foo", "data": { "value": "bar", "number": 10 } }`
    79  	kubelet.ExtractYAMLData([]byte(data), &obj)
    80  
    81  	verifyStringEquals(t, obj.Name, "foo")
    82  	verifyStringEquals(t, obj.Data.Value, "bar")
    83  	verifyIntEquals(t, obj.Data.Number, 10)
    84  }
    85  
    86  type FakeDockerClient struct {
    87  	containerList []docker.APIContainers
    88  	container     *docker.Container
    89  	err           error
    90  	called        []string
    91  }
    92  
    93  func (f *FakeDockerClient) clearCalls() {
    94  	f.called = []string{}
    95  }
    96  
    97  func (f *FakeDockerClient) appendCall(call string) {
    98  	f.called = append(f.called, call)
    99  }
   100  
   101  func (f *FakeDockerClient) ListContainers(options docker.ListContainersOptions) ([]docker.APIContainers, error) {
   102  	f.appendCall("list")
   103  	return f.containerList, f.err
   104  }
   105  
   106  func (f *FakeDockerClient) InspectContainer(id string) (*docker.Container, error) {
   107  	f.appendCall("inspect")
   108  	return f.container, f.err
   109  }
   110  
   111  func (f *FakeDockerClient) CreateContainer(docker.CreateContainerOptions) (*docker.Container, error) {
   112  	f.appendCall("create")
   113  	return nil, nil
   114  }
   115  
   116  func (f *FakeDockerClient) StartContainer(id string, hostConfig *docker.HostConfig) error {
   117  	f.appendCall("start")
   118  	return nil
   119  }
   120  
   121  func (f *FakeDockerClient) StopContainer(id string, timeout uint) error {
   122  	f.appendCall("stop")
   123  	return nil
   124  }
   125  
   126  func verifyCalls(t *testing.T, fakeDocker FakeDockerClient, calls []string) {
   127  	verifyStringArrayEquals(t, fakeDocker.called, calls)
   128  }
   129  
   130  func verifyStringArrayEquals(t *testing.T, actual, expected []string) {
   131  	invalid := len(actual) != len(expected)
   132  	for ix, value := range actual {
   133  		if expected[ix] != value {
   134  			invalid = true
   135  		}
   136  	}
   137  	if invalid {
   138  		t.Errorf("Expected: %#v, Actual: %#v", expected, actual)
   139  	}
   140  }
   141  
   142  func verifyPackUnpack(t *testing.T, manifestId, containerName string) {
   143  	name := manifestAndContainerToDockerName(
   144  		&api.ContainerManifest{Id: manifestId},
   145  		&api.Container{Name: containerName},
   146  	)
   147  	returnedManifestId, returnedContainerName := dockerNameToManifestAndContainer(name)
   148  	if manifestId != returnedManifestId || containerName != returnedContainerName {
   149  		t.Errorf("For (%s, %s), unpacked (%s, %s)", manifestId, containerName, returnedManifestId, returnedContainerName)
   150  	}
   151  }
   152  
   153  func TestContainerManifestNaming(t *testing.T) {
   154  	verifyPackUnpack(t, "manifest1234", "container5678")
   155  	verifyPackUnpack(t, "manifest--", "container__")
   156  	verifyPackUnpack(t, "--manifest", "__container")
   157  	verifyPackUnpack(t, "m___anifest_", "container-_-")
   158  	verifyPackUnpack(t, "_m___anifest", "-_-container")
   159  }
   160  
   161  func TestContainerExists(t *testing.T) {
   162  	fakeDocker := FakeDockerClient{
   163  		err: nil,
   164  	}
   165  	kubelet := Kubelet{
   166  		DockerClient: &fakeDocker,
   167  	}
   168  	manifest := api.ContainerManifest{
   169  		Id: "qux",
   170  	}
   171  	container := api.Container{
   172  		Name: "foo",
   173  	}
   174  	fakeDocker.containerList = []docker.APIContainers{
   175  		docker.APIContainers{
   176  			Names: []string{"foo--qux--1234"},
   177  		},
   178  		docker.APIContainers{
   179  			Names: []string{"bar--qux--1234"},
   180  		},
   181  	}
   182  	fakeDocker.container = &docker.Container{
   183  		ID: "foobar",
   184  	}
   185  
   186  	exists, _, err := kubelet.ContainerExists(&manifest, &container)
   187  	verifyCalls(t, fakeDocker, []string{"list", "list", "inspect"})
   188  	if !exists {
   189  		t.Errorf("Failed to find container %#v", container)
   190  	}
   191  	if err != nil {
   192  		t.Errorf("Unexpected error: %#v", err)
   193  	}
   194  }
   195  
   196  func TestGetContainerID(t *testing.T) {
   197  	fakeDocker := FakeDockerClient{
   198  		err: nil,
   199  	}
   200  	kubelet := Kubelet{
   201  		DockerClient: &fakeDocker,
   202  	}
   203  	fakeDocker.containerList = []docker.APIContainers{
   204  		docker.APIContainers{
   205  			Names: []string{"foo"},
   206  			ID:    "1234",
   207  		},
   208  		docker.APIContainers{
   209  			Names: []string{"bar"},
   210  			ID:    "4567",
   211  		},
   212  	}
   213  
   214  	id, err := kubelet.GetContainerID("foo")
   215  	verifyStringEquals(t, id, "1234")
   216  	verifyNoError(t, err)
   217  	verifyCalls(t, fakeDocker, []string{"list"})
   218  	fakeDocker.clearCalls()
   219  
   220  	id, err = kubelet.GetContainerID("bar")
   221  	verifyStringEquals(t, id, "4567")
   222  	verifyNoError(t, err)
   223  	verifyCalls(t, fakeDocker, []string{"list"})
   224  	fakeDocker.clearCalls()
   225  
   226  	id, err = kubelet.GetContainerID("NotFound")
   227  	verifyError(t, err)
   228  	verifyCalls(t, fakeDocker, []string{"list"})
   229  }
   230  
   231  func TestGetContainerByName(t *testing.T) {
   232  	fakeDocker := FakeDockerClient{
   233  		err: nil,
   234  	}
   235  	kubelet := Kubelet{
   236  		DockerClient: &fakeDocker,
   237  	}
   238  	fakeDocker.containerList = []docker.APIContainers{
   239  		docker.APIContainers{
   240  			Names: []string{"foo"},
   241  		},
   242  		docker.APIContainers{
   243  			Names: []string{"bar"},
   244  		},
   245  	}
   246  	fakeDocker.container = &docker.Container{
   247  		ID: "foobar",
   248  	}
   249  
   250  	container, err := kubelet.GetContainerByName("foo")
   251  	verifyCalls(t, fakeDocker, []string{"list", "inspect"})
   252  	if container == nil {
   253  		t.Errorf("Unexpected nil container")
   254  	}
   255  	verifyStringEquals(t, container.ID, "foobar")
   256  	verifyNoError(t, err)
   257  }
   258  
   259  func TestListContainers(t *testing.T) {
   260  	fakeDocker := FakeDockerClient{
   261  		err: nil,
   262  	}
   263  	kubelet := Kubelet{
   264  		DockerClient: &fakeDocker,
   265  	}
   266  	fakeDocker.containerList = []docker.APIContainers{
   267  		docker.APIContainers{
   268  			Names: []string{"foo"},
   269  		},
   270  		docker.APIContainers{
   271  			Names: []string{"bar"},
   272  		},
   273  	}
   274  
   275  	containers, err := kubelet.ListContainers()
   276  	verifyStringArrayEquals(t, containers, []string{"foo", "bar"})
   277  	verifyNoError(t, err)
   278  	verifyCalls(t, fakeDocker, []string{"list"})
   279  }
   280  
   281  func TestKillContainerWithError(t *testing.T) {
   282  	fakeDocker := FakeDockerClient{
   283  		err: fmt.Errorf("Sample Error"),
   284  		containerList: []docker.APIContainers{
   285  			docker.APIContainers{
   286  				Names: []string{"foo"},
   287  			},
   288  			docker.APIContainers{
   289  				Names: []string{"bar"},
   290  			},
   291  		},
   292  	}
   293  	kubelet := Kubelet{
   294  		DockerClient: &fakeDocker,
   295  	}
   296  	err := kubelet.KillContainer("foo")
   297  	verifyError(t, err)
   298  	verifyCalls(t, fakeDocker, []string{"list"})
   299  }
   300  
   301  func TestKillContainer(t *testing.T) {
   302  	fakeDocker := FakeDockerClient{
   303  		err: nil,
   304  	}
   305  	kubelet := Kubelet{
   306  		DockerClient: &fakeDocker,
   307  	}
   308  	fakeDocker.containerList = []docker.APIContainers{
   309  		docker.APIContainers{
   310  			Names: []string{"foo"},
   311  		},
   312  		docker.APIContainers{
   313  			Names: []string{"bar"},
   314  		},
   315  	}
   316  	fakeDocker.container = &docker.Container{
   317  		ID: "foobar",
   318  	}
   319  
   320  	err := kubelet.KillContainer("foo")
   321  	verifyNoError(t, err)
   322  	verifyCalls(t, fakeDocker, []string{"list", "stop"})
   323  }
   324  
   325  func TestSyncHTTP(t *testing.T) {
   326  	containers := api.ContainerManifest{
   327  		Containers: []api.Container{
   328  			api.Container{
   329  				Name:  "foo",
   330  				Image: "dockerfile/foo",
   331  			},
   332  			api.Container{
   333  				Name:  "bar",
   334  				Image: "dockerfile/bar",
   335  			},
   336  		},
   337  	}
   338  	data, _ := json.Marshal(containers)
   339  	fakeHandler := util.FakeHandler{
   340  		StatusCode:   200,
   341  		ResponseBody: string(data),
   342  	}
   343  	testServer := httptest.NewServer(&fakeHandler)
   344  	kubelet := Kubelet{}
   345  
   346  	var containersOut api.ContainerManifest
   347  	data, err := kubelet.SyncHTTP(&http.Client{}, testServer.URL, &containersOut)
   348  	if err != nil {
   349  		t.Errorf("Unexpected error: %#v", err)
   350  	}
   351  	if len(containers.Containers) != len(containersOut.Containers) {
   352  		t.Errorf("Container sizes don't match.  Expected: %d Received %d, %#v", len(containers.Containers), len(containersOut.Containers), containersOut)
   353  	}
   354  	expectedData, _ := json.Marshal(containers)
   355  	actualData, _ := json.Marshal(containersOut)
   356  	if string(expectedData) != string(actualData) {
   357  		t.Errorf("Container data doesn't match.  Expected: %s Received %s", string(expectedData), string(actualData))
   358  	}
   359  }
   360  
   361  func TestResponseToContainersNil(t *testing.T) {
   362  	kubelet := Kubelet{}
   363  	list, err := kubelet.ResponseToManifests(&etcd.Response{Node: nil})
   364  	if len(list) != 0 {
   365  		t.Errorf("Unexpected non-zero list: %#v", list)
   366  	}
   367  	if err == nil {
   368  		t.Error("Unexpected non-error")
   369  	}
   370  }
   371  
   372  func TestResponseToManifests(t *testing.T) {
   373  	kubelet := Kubelet{}
   374  	list, err := kubelet.ResponseToManifests(&etcd.Response{
   375  		Node: &etcd.Node{
   376  			Value: util.MakeJSONString([]api.ContainerManifest{
   377  				api.ContainerManifest{Id: "foo"},
   378  				api.ContainerManifest{Id: "bar"},
   379  			}),
   380  		},
   381  	})
   382  	if len(list) != 2 || list[0].Id != "foo" || list[1].Id != "bar" {
   383  		t.Errorf("Unexpected list: %#v", list)
   384  	}
   385  	expectNoError(t, err)
   386  }
   387  
   388  type channelReader struct {
   389  	list [][]api.ContainerManifest
   390  	wg   sync.WaitGroup
   391  }
   392  
   393  func startReading(channel <-chan []api.ContainerManifest) *channelReader {
   394  	cr := &channelReader{}
   395  	cr.wg.Add(1)
   396  	go func() {
   397  		for {
   398  			containers, ok := <-channel
   399  			if !ok {
   400  				break
   401  			}
   402  			cr.list = append(cr.list, containers)
   403  		}
   404  		cr.wg.Done()
   405  	}()
   406  	return cr
   407  }
   408  
   409  func (cr *channelReader) GetList() [][]api.ContainerManifest {
   410  	cr.wg.Wait()
   411  	return cr.list
   412  }
   413  
   414  func TestGetKubeletStateFromEtcdNoData(t *testing.T) {
   415  	fakeClient := registry.MakeFakeEtcdClient(t)
   416  	kubelet := Kubelet{
   417  		Client: fakeClient,
   418  	}
   419  	channel := make(chan []api.ContainerManifest)
   420  	reader := startReading(channel)
   421  	fakeClient.Data["/registry/hosts/machine/kubelet"] = registry.EtcdResponseWithError{
   422  		R: &etcd.Response{},
   423  		E: nil,
   424  	}
   425  	err := kubelet.getKubeletStateFromEtcd("/registry/hosts/machine", channel)
   426  	if err == nil {
   427  		t.Error("Unexpected no err.")
   428  	}
   429  	close(channel)
   430  	list := reader.GetList()
   431  	if len(list) != 0 {
   432  		t.Errorf("Unexpected list: %#v", list)
   433  	}
   434  }
   435  
   436  func TestGetKubeletStateFromEtcd(t *testing.T) {
   437  	fakeClient := registry.MakeFakeEtcdClient(t)
   438  	kubelet := Kubelet{
   439  		Client: fakeClient,
   440  	}
   441  	channel := make(chan []api.ContainerManifest)
   442  	reader := startReading(channel)
   443  	fakeClient.Data["/registry/hosts/machine/kubelet"] = registry.EtcdResponseWithError{
   444  		R: &etcd.Response{
   445  			Node: &etcd.Node{
   446  				Value: util.MakeJSONString([]api.Container{}),
   447  			},
   448  		},
   449  		E: nil,
   450  	}
   451  	err := kubelet.getKubeletStateFromEtcd("/registry/hosts/machine", channel)
   452  	expectNoError(t, err)
   453  	close(channel)
   454  	list := reader.GetList()
   455  	if len(list) != 1 {
   456  		t.Errorf("Unexpected list: %#v", list)
   457  	}
   458  }
   459  
   460  func TestGetKubeletStateFromEtcdNotFound(t *testing.T) {
   461  	fakeClient := registry.MakeFakeEtcdClient(t)
   462  	kubelet := Kubelet{
   463  		Client: fakeClient,
   464  	}
   465  	channel := make(chan []api.ContainerManifest)
   466  	reader := startReading(channel)
   467  	fakeClient.Data["/registry/hosts/machine/kubelet"] = registry.EtcdResponseWithError{
   468  		R: &etcd.Response{},
   469  		E: &etcd.EtcdError{
   470  			ErrorCode: 100,
   471  		},
   472  	}
   473  	err := kubelet.getKubeletStateFromEtcd("/registry/hosts/machine", channel)
   474  	expectNoError(t, err)
   475  	close(channel)
   476  	list := reader.GetList()
   477  	if len(list) != 0 {
   478  		t.Errorf("Unexpected list: %#v", list)
   479  	}
   480  }
   481  
   482  func TestGetKubeletStateFromEtcdError(t *testing.T) {
   483  	fakeClient := registry.MakeFakeEtcdClient(t)
   484  	kubelet := Kubelet{
   485  		Client: fakeClient,
   486  	}
   487  	channel := make(chan []api.ContainerManifest)
   488  	reader := startReading(channel)
   489  	fakeClient.Data["/registry/hosts/machine/kubelet"] = registry.EtcdResponseWithError{
   490  		R: &etcd.Response{},
   491  		E: &etcd.EtcdError{
   492  			ErrorCode: 200, // non not found error
   493  		},
   494  	}
   495  	err := kubelet.getKubeletStateFromEtcd("/registry/hosts/machine", channel)
   496  	if err == nil {
   497  		t.Error("Unexpected non-error")
   498  	}
   499  	close(channel)
   500  	list := reader.GetList()
   501  	if len(list) != 0 {
   502  		t.Errorf("Unexpected list: %#v", list)
   503  	}
   504  }
   505  
   506  func TestSyncManifestsDoesNothing(t *testing.T) {
   507  	fakeDocker := FakeDockerClient{
   508  		err: nil,
   509  	}
   510  	fakeDocker.containerList = []docker.APIContainers{
   511  		docker.APIContainers{
   512  			// format is <container-id>--<manifest-id>
   513  			Names: []string{"bar--foo"},
   514  			ID:    "1234",
   515  		},
   516  	}
   517  	fakeDocker.container = &docker.Container{
   518  		ID: "1234",
   519  	}
   520  	kubelet := Kubelet{
   521  		DockerClient: &fakeDocker,
   522  	}
   523  	err := kubelet.SyncManifests([]api.ContainerManifest{
   524  		api.ContainerManifest{
   525  			Id: "foo",
   526  			Containers: []api.Container{
   527  				api.Container{Name: "bar"},
   528  			},
   529  		},
   530  	})
   531  	expectNoError(t, err)
   532  	if len(fakeDocker.called) != 4 ||
   533  		fakeDocker.called[0] != "list" ||
   534  		fakeDocker.called[1] != "list" ||
   535  		fakeDocker.called[2] != "inspect" ||
   536  		fakeDocker.called[3] != "list" {
   537  		t.Errorf("Unexpected call sequence: %#v", fakeDocker.called)
   538  	}
   539  }
   540  
   541  func TestSyncManifestsDeletes(t *testing.T) {
   542  	fakeDocker := FakeDockerClient{
   543  		err: nil,
   544  	}
   545  	fakeDocker.containerList = []docker.APIContainers{
   546  		docker.APIContainers{
   547  			Names: []string{"foo"},
   548  			ID:    "1234",
   549  		},
   550  	}
   551  	kubelet := Kubelet{
   552  		DockerClient: &fakeDocker,
   553  	}
   554  	err := kubelet.SyncManifests([]api.ContainerManifest{})
   555  	expectNoError(t, err)
   556  	if len(fakeDocker.called) != 3 ||
   557  		fakeDocker.called[0] != "list" ||
   558  		fakeDocker.called[1] != "list" ||
   559  		fakeDocker.called[2] != "stop" {
   560  		t.Errorf("Unexpected call sequence: %#v", fakeDocker.called)
   561  	}
   562  }