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 }