github.com/google/cadvisor@v0.49.1/manager/manager_test.go (about) 1 // Copyright 2014 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Per-container manager. 16 17 package manager 18 19 import ( 20 "fmt" 21 "reflect" 22 "strings" 23 "testing" 24 "time" 25 26 "github.com/google/cadvisor/cache/memory" 27 "github.com/google/cadvisor/collector" 28 "github.com/google/cadvisor/container" 29 containertest "github.com/google/cadvisor/container/testing" 30 info "github.com/google/cadvisor/info/v1" 31 itest "github.com/google/cadvisor/info/v1/test" 32 v2 "github.com/google/cadvisor/info/v2" 33 "github.com/google/cadvisor/utils/sysfs/fakesysfs" 34 35 "github.com/stretchr/testify/assert" 36 clock "k8s.io/utils/clock/testing" 37 38 // install all the container runtimes included in the library version for testing. 39 // as these are moved to cmd/internal/container, remove them from here. 40 _ "github.com/google/cadvisor/container/containerd/install" 41 _ "github.com/google/cadvisor/container/crio/install" 42 _ "github.com/google/cadvisor/container/docker/install" 43 _ "github.com/google/cadvisor/container/systemd/install" 44 ) 45 46 // TODO(vmarmol): Refactor these tests. 47 48 func createManagerAndAddContainers( 49 memoryCache *memory.InMemoryCache, 50 sysfs *fakesysfs.FakeSysFs, 51 containers []string, 52 f func(*containertest.MockContainerHandler), 53 t *testing.T, 54 ) *manager { 55 container.ClearContainerHandlerFactories() 56 mif := &manager{ 57 containers: make(map[namespacedContainerName]*containerData), 58 quitChannels: make([]chan error, 0, 2), 59 memoryCache: memoryCache, 60 } 61 for _, name := range containers { 62 mockHandler := containertest.NewMockContainerHandler(name) 63 spec := itest.GenerateRandomContainerSpec(4) 64 mockHandler.On("GetSpec").Return( 65 spec, 66 nil, 67 ).Once() 68 cont, err := newContainerData(name, memoryCache, mockHandler, false, &collector.GenericCollectorManager{}, 60*time.Second, true, clock.NewFakeClock(time.Now())) 69 if err != nil { 70 t.Fatal(err) 71 } 72 mif.containers[namespacedContainerName{ 73 Name: name, 74 }] = cont 75 // Add Docker containers under their namespace. 76 if strings.HasPrefix(name, "/docker") { 77 mif.containers[namespacedContainerName{ 78 Namespace: DockerNamespace, 79 Name: strings.TrimPrefix(name, "/docker/"), 80 }] = cont 81 } 82 f(mockHandler) 83 } 84 return mif 85 } 86 87 func createManagerAndAddSubContainers( 88 memoryCache *memory.InMemoryCache, 89 sysfs *fakesysfs.FakeSysFs, 90 containers []string, 91 f func(*containertest.MockContainerHandler), 92 t *testing.T, 93 ) *manager { 94 container.ClearContainerHandlerFactories() 95 mif := &manager{ 96 containers: make(map[namespacedContainerName]*containerData), 97 quitChannels: make([]chan error, 0, 2), 98 memoryCache: memoryCache, 99 } 100 101 subcontainers1 := []info.ContainerReference{ 102 {Name: "/kubepods/besteffort"}, 103 {Name: "/kubepods/burstable"}, 104 } 105 subcontainers2 := []info.ContainerReference(nil) 106 subcontainers3 := []info.ContainerReference{ 107 {Name: "/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd"}, 108 {Name: "/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12ce"}, 109 } 110 subcontainers4 := []info.ContainerReference{ 111 {Name: "/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd/22f44d2a517778590e2d8bcafafe501f79e8a509e5b6de70b7700c4d37722bce"}, 112 {Name: "/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd/ae9465f98d275998e148b6fc12f5f92e5d4a64fca0d255f6dc3a13cc6f93a10f"}, 113 } 114 115 subcontainers5 := []info.ContainerReference(nil) 116 subcontainers6 := []info.ContainerReference(nil) 117 118 subcontainerList := [][]info.ContainerReference{subcontainers1, subcontainers2, subcontainers3, subcontainers4, subcontainers5, subcontainers6} 119 120 for idx, name := range containers { 121 mockHandler := containertest.NewMockContainerHandler(name) 122 spec := itest.GenerateRandomContainerSpec(4) 123 mockHandler.On("GetSpec").Return( 124 spec, 125 nil, 126 ).Once() 127 mockHandler.On("ListContainers", container.ListSelf).Return( 128 subcontainerList[idx], 129 nil, 130 ) 131 cont, err := newContainerData(name, memoryCache, mockHandler, false, &collector.GenericCollectorManager{}, 60*time.Second, true, clock.NewFakeClock(time.Now())) 132 if err != nil { 133 t.Fatal(err) 134 } 135 mif.containers[namespacedContainerName{ 136 Name: name, 137 }] = cont 138 // Add Docker containers under their namespace. 139 if strings.HasPrefix(name, "/docker") { 140 mif.containers[namespacedContainerName{ 141 Namespace: DockerNamespace, 142 Name: strings.TrimPrefix(name, "/docker/"), 143 }] = cont 144 } 145 f(mockHandler) 146 } 147 return mif 148 } 149 150 // Expect a manager with the specified containers and query. Returns the manager, map of ContainerInfo objects, 151 // and map of MockContainerHandler objects.} 152 func expectManagerWithContainers(containers []string, query *info.ContainerInfoRequest, t *testing.T) (*manager, map[string]*info.ContainerInfo, map[string]*containertest.MockContainerHandler) { 153 infosMap := make(map[string]*info.ContainerInfo, len(containers)) 154 handlerMap := make(map[string]*containertest.MockContainerHandler, len(containers)) 155 156 for _, container := range containers { 157 infosMap[container] = itest.GenerateRandomContainerInfo(container, 4, query, 1*time.Second) 158 } 159 160 memoryCache := memory.New(time.Duration(query.NumStats)*time.Second, nil) 161 sysfs := &fakesysfs.FakeSysFs{} 162 m := createManagerAndAddContainers( 163 memoryCache, 164 sysfs, 165 containers, 166 func(h *containertest.MockContainerHandler) { 167 cinfo := infosMap[h.Name] 168 ref, err := h.ContainerReference() 169 if err != nil { 170 t.Error(err) 171 } 172 173 cInfo := info.ContainerInfo{ 174 ContainerReference: ref, 175 } 176 for _, stat := range cinfo.Stats { 177 err = memoryCache.AddStats(&cInfo, stat) 178 if err != nil { 179 t.Error(err) 180 } 181 } 182 spec := cinfo.Spec 183 184 h.On("ListContainers", container.ListSelf).Return( 185 []info.ContainerReference(nil), 186 nil, 187 ) 188 h.On("GetSpec").Return( 189 spec, 190 nil, 191 ).Once() 192 handlerMap[h.Name] = h 193 }, 194 t, 195 ) 196 197 return m, infosMap, handlerMap 198 } 199 200 // Expect a manager with the specified containers and query. Returns the manager, map of ContainerInfo objects, 201 // and map of MockContainerHandler objects.} 202 func expectManagerWithSubContainers(containers []string, query *info.ContainerInfoRequest, t *testing.T) (*manager, map[string]*info.ContainerInfo, map[string]*containertest.MockContainerHandler) { 203 infosMap := make(map[string]*info.ContainerInfo, len(containers)) 204 handlerMap := make(map[string]*containertest.MockContainerHandler, len(containers)) 205 206 for _, container := range containers { 207 infosMap[container] = itest.GenerateRandomContainerInfo(container, 4, query, 1*time.Second) 208 } 209 210 memoryCache := memory.New(time.Duration(query.NumStats)*time.Second, nil) 211 sysfs := &fakesysfs.FakeSysFs{} 212 m := createManagerAndAddSubContainers( 213 memoryCache, 214 sysfs, 215 containers, 216 func(h *containertest.MockContainerHandler) { 217 cinfo := infosMap[h.Name] 218 ref, err := h.ContainerReference() 219 if err != nil { 220 t.Error(err) 221 } 222 223 cInfo := info.ContainerInfo{ 224 ContainerReference: ref, 225 } 226 for _, stat := range cinfo.Stats { 227 err = memoryCache.AddStats(&cInfo, stat) 228 if err != nil { 229 t.Error(err) 230 } 231 } 232 spec := cinfo.Spec 233 h.On("GetSpec").Return( 234 spec, 235 nil, 236 ).Once() 237 handlerMap[h.Name] = h 238 }, 239 t, 240 ) 241 242 return m, infosMap, handlerMap 243 } 244 245 // Expect a manager with the specified containers and query. Returns the manager, map of ContainerInfo objects, 246 // and map of MockContainerHandler objects.} 247 func expectManagerWithContainersV2(containers []string, query *info.ContainerInfoRequest, t *testing.T) (*manager, map[string]*info.ContainerInfo, map[string]*containertest.MockContainerHandler) { 248 infosMap := make(map[string]*info.ContainerInfo, len(containers)) 249 handlerMap := make(map[string]*containertest.MockContainerHandler, len(containers)) 250 251 for _, container := range containers { 252 infosMap[container] = itest.GenerateRandomContainerInfo(container, 4, query, 1*time.Second) 253 } 254 255 memoryCache := memory.New(time.Duration(query.NumStats)*time.Second, nil) 256 sysfs := &fakesysfs.FakeSysFs{} 257 m := createManagerAndAddContainers( 258 memoryCache, 259 sysfs, 260 containers, 261 func(h *containertest.MockContainerHandler) { 262 cinfo := infosMap[h.Name] 263 ref, err := h.ContainerReference() 264 if err != nil { 265 t.Error(err) 266 } 267 268 cInfo := info.ContainerInfo{ 269 ContainerReference: ref, 270 } 271 272 for _, stat := range cinfo.Stats { 273 err = memoryCache.AddStats(&cInfo, stat) 274 if err != nil { 275 t.Error(err) 276 } 277 } 278 spec := cinfo.Spec 279 280 h.On("GetSpec").Return( 281 spec, 282 nil, 283 ).Once() 284 handlerMap[h.Name] = h 285 }, 286 t, 287 ) 288 289 return m, infosMap, handlerMap 290 } 291 292 func TestGetContainerInfo(t *testing.T) { 293 containers := []string{ 294 "/c1", 295 "/c2", 296 } 297 298 query := &info.ContainerInfoRequest{ 299 NumStats: 256, 300 } 301 302 m, infosMap, handlerMap := expectManagerWithContainers(containers, query, t) 303 304 returnedInfos := make(map[string]*info.ContainerInfo, len(containers)) 305 306 for _, container := range containers { 307 cinfo, err := m.GetContainerInfo(container, query) 308 if err != nil { 309 t.Fatalf("Unable to get info for container %v: %v", container, err) 310 } 311 returnedInfos[container] = cinfo 312 } 313 314 for container, handler := range handlerMap { 315 handler.AssertExpectations(t) 316 returned := returnedInfos[container] 317 expected := infosMap[container] 318 if !reflect.DeepEqual(returned, expected) { 319 t.Errorf("returned unexpected info for container %v; returned %+v; expected %+v", container, returned, expected) 320 } 321 } 322 } 323 324 func TestGetContainerInfoV2(t *testing.T) { 325 containers := []string{ 326 "/", 327 "/c1", 328 "/c2", 329 } 330 331 options := v2.RequestOptions{ 332 IdType: v2.TypeName, 333 Count: 1, 334 Recursive: true, 335 } 336 query := &info.ContainerInfoRequest{ 337 NumStats: 2, 338 } 339 340 m, _, handlerMap := expectManagerWithContainersV2(containers, query, t) 341 342 infos, err := m.GetContainerInfoV2("/", options) 343 if err != nil { 344 t.Fatalf("GetContainerInfoV2 failed: %v", err) 345 } 346 347 for container, handler := range handlerMap { 348 handler.AssertExpectations(t) 349 info, ok := infos[container] 350 assert.True(t, ok, "Missing info for container %q", container) 351 assert.NotEqual(t, v2.ContainerSpec{}, info.Spec, "Empty spec for container %q", container) 352 assert.NotEmpty(t, info.Stats, "Missing stats for container %q", container) 353 } 354 } 355 356 func TestGetContainerInfoV2Failure(t *testing.T) { 357 successful := "/" 358 statless := "/c1" 359 failing := "/c2" 360 containers := []string{ 361 successful, statless, failing, 362 } 363 364 options := v2.RequestOptions{ 365 IdType: v2.TypeName, 366 Count: 1, 367 Recursive: true, 368 } 369 query := &info.ContainerInfoRequest{ 370 NumStats: 2, 371 } 372 373 m, _, handlerMap := expectManagerWithContainers(containers, query, t) 374 375 // Remove /c1 stats 376 err := m.memoryCache.RemoveContainer(statless) 377 if err != nil { 378 t.Fatalf("RemoveContainer failed: %v", err) 379 } 380 381 // Make GetSpec fail on /c2 382 mockErr := fmt.Errorf("intentional GetSpec failure") 383 _, err = handlerMap[failing].GetSpec() 384 assert.NoError(t, err) // Use up default GetSpec call, and replace below 385 handlerMap[failing].On("GetSpec").Return(info.ContainerSpec{}, mockErr) 386 handlerMap[failing].On("Exists").Return(true) 387 m.containers[namespacedContainerName{Name: failing}].infoLastUpdatedTime = time.Time{} // Force GetSpec. 388 389 infos, err := m.GetContainerInfoV2("/", options) 390 if err == nil { 391 t.Error("Expected error calling GetContainerInfoV2") 392 } 393 394 // Successful containers still successful. 395 info, ok := infos[successful] 396 assert.True(t, ok, "Missing info for container %q", successful) 397 assert.NotEqual(t, v2.ContainerSpec{}, info.Spec, "Empty spec for container %q", successful) 398 assert.NotEmpty(t, info.Stats, "Missing stats for container %q", successful) 399 400 // "/c1" present with spec. 401 info, ok = infos[statless] 402 assert.True(t, ok, "Missing info for container %q", statless) 403 assert.NotEqual(t, v2.ContainerSpec{}, info.Spec, "Empty spec for container %q", statless) 404 assert.Empty(t, info.Stats, "Missing stats for container %q", successful) 405 406 // "/c2" should be present but empty. 407 info, ok = infos[failing] 408 assert.True(t, ok, "Missing info for failed container") 409 assert.Equal(t, v2.ContainerInfo{}, info, "Empty spec for failed container") 410 assert.Empty(t, info.Stats, "Missing stats for failed container") 411 } 412 413 func TestSubcontainersInfo(t *testing.T) { 414 containers := []string{ 415 "/c1", 416 "/c2", 417 } 418 419 query := &info.ContainerInfoRequest{ 420 NumStats: 64, 421 } 422 423 m, _, _ := expectManagerWithContainers(containers, query, t) 424 425 result, err := m.SubcontainersInfo("/", query) 426 if err != nil { 427 t.Fatalf("expected to succeed: %s", err) 428 } 429 if len(result) != len(containers) { 430 t.Errorf("expected to received containers: %v, but received: %v", containers, result) 431 } 432 for _, res := range result { 433 found := false 434 for _, name := range containers { 435 if res.Name == name { 436 found = true 437 break 438 } 439 } 440 if !found { 441 t.Errorf("unexpected container %q in result, expected one of %v", res.Name, containers) 442 } 443 } 444 } 445 446 func TestSubcontainersInfoError(t *testing.T) { 447 containers := []string{ 448 "/kubepods", 449 "/kubepods/besteffort", 450 "/kubepods/burstable", 451 "/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd", 452 "/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd/22f44d2a517778590e2d8bcafafe501f79e8a509e5b6de70b7700c4d37722bce", 453 "/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd/ae9465f98d275998e148b6fc12f5f92e5d4a64fca0d255f6dc3a13cc6f93a10f", 454 } 455 query := &info.ContainerInfoRequest{ 456 NumStats: 1, 457 } 458 459 m, _, _ := expectManagerWithSubContainers(containers, query, t) 460 result, err := m.SubcontainersInfo("/kubepods", query) 461 if err != nil { 462 t.Fatalf("expected to succeed: %s", err) 463 } 464 465 if len(result) != len(containers) { 466 t.Errorf("expected to received containers: %v, but received: %v", containers, result) 467 } 468 469 totalBurstable := 0 470 burstableCount := 0 471 totalBesteffort := 0 472 besteffortCount := 0 473 474 for _, res := range result { 475 found := false 476 if res.Name == "/kubepods/burstable" { 477 totalBurstable = len(res.Subcontainers) 478 } else if res.Name == "/kubepods/besteffort" { 479 totalBesteffort = len(res.Subcontainers) 480 } else if strings.HasPrefix(res.Name, "/kubepods/burstable") && len(res.Name) == len("/kubepods/burstable/pod01042b28-179d-446a-954a-7266557e12cd") { 481 burstableCount++ 482 } else if strings.HasPrefix(res.Name, "/kubepods/besteffort") && len(res.Name) == len("/kubepods/besteffort/pod01042b28-179d-446a-954a-7266557e12cd") { 483 besteffortCount++ 484 } 485 for _, name := range containers { 486 if res.Name == name { 487 found = true 488 break 489 } 490 } 491 if !found { 492 t.Errorf("unexpected container %q in result, expected one of %v", res.Name, containers) 493 } 494 } 495 496 assert.NotEqual(t, totalBurstable, burstableCount) 497 assert.Equal(t, totalBesteffort, besteffortCount) 498 } 499 500 func TestDockerContainersInfo(t *testing.T) { 501 containers := []string{ 502 "/docker/c1a", 503 "/docker/c2a", 504 } 505 506 query := &info.ContainerInfoRequest{ 507 NumStats: 2, 508 } 509 510 m, _, _ := expectManagerWithContainers(containers, query, t) 511 512 result, err := m.DockerContainer("c1a", query) 513 if err != nil { 514 t.Fatalf("expected to succeed: %s", err) 515 } 516 if result.Name != containers[0] { 517 t.Errorf("Unexpected container %q in result. Expected container %q", result.Name, containers[0]) 518 } 519 520 result, err = m.DockerContainer("c2", query) 521 if err != nil { 522 t.Fatalf("expected to succeed: %s", err) 523 } 524 if result.Name != containers[1] { 525 t.Errorf("Unexpected container %q in result. Expected container %q", result.Name, containers[1]) 526 } 527 528 result, err = m.DockerContainer("c", query) 529 expectedError := "unable to find container. Container \"c\" is not unique" 530 if err == nil { 531 t.Errorf("expected error %q but received %q", expectedError, err) 532 } 533 }