1/* 2Copyright 2016 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package testing 18 19import ( 20 "fmt" 21 "reflect" 22 "sync" 23 "time" 24 25 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" 26) 27 28var ( 29 // FakeVersion is a version of a fake runtime. 30 FakeVersion = "0.1.0" 31 32 // FakeRuntimeName is the name of the fake runtime. 33 FakeRuntimeName = "fakeRuntime" 34 35 // FakePodSandboxIPs is an IP address of the fake runtime. 36 FakePodSandboxIPs = []string{"192.168.192.168"} 37) 38 39// FakePodSandbox is the fake implementation of runtimeapi.PodSandboxStatus. 40type FakePodSandbox struct { 41 // PodSandboxStatus contains the runtime information for a sandbox. 42 runtimeapi.PodSandboxStatus 43 // RuntimeHandler is the runtime handler that was issued with the RunPodSandbox request. 44 RuntimeHandler string 45} 46 47// FakeContainer is a fake container. 48type FakeContainer struct { 49 // ContainerStatus contains the runtime information for a container. 50 runtimeapi.ContainerStatus 51 52 // LinuxResources contains the resources specific to linux containers. 53 LinuxResources *runtimeapi.LinuxContainerResources 54 55 // the sandbox id of this container 56 SandboxID string 57} 58 59// FakeRuntimeService is a fake runetime service. 60type FakeRuntimeService struct { 61 sync.Mutex 62 63 Called []string 64 Errors map[string][]error 65 66 FakeStatus *runtimeapi.RuntimeStatus 67 Containers map[string]*FakeContainer 68 Sandboxes map[string]*FakePodSandbox 69 FakeContainerStats map[string]*runtimeapi.ContainerStats 70 71 ErrorOnSandboxCreate bool 72} 73 74// GetContainerID returns the unique container ID from the FakeRuntimeService. 75func (r *FakeRuntimeService) GetContainerID(sandboxID, name string, attempt uint32) (string, error) { 76 r.Lock() 77 defer r.Unlock() 78 79 for id, c := range r.Containers { 80 if c.SandboxID == sandboxID && c.Metadata.Name == name && c.Metadata.Attempt == attempt { 81 return id, nil 82 } 83 } 84 return "", fmt.Errorf("container (name, attempt, sandboxID)=(%q, %d, %q) not found", name, attempt, sandboxID) 85} 86 87// SetFakeSandboxes sets the fake sandboxes for the FakeRuntimeService. 88func (r *FakeRuntimeService) SetFakeSandboxes(sandboxes []*FakePodSandbox) { 89 r.Lock() 90 defer r.Unlock() 91 92 r.Sandboxes = make(map[string]*FakePodSandbox) 93 for _, sandbox := range sandboxes { 94 sandboxID := sandbox.Id 95 r.Sandboxes[sandboxID] = sandbox 96 } 97} 98 99// SetFakeContainers sets fake containers for the FakeRuntimeService. 100func (r *FakeRuntimeService) SetFakeContainers(containers []*FakeContainer) { 101 r.Lock() 102 defer r.Unlock() 103 104 r.Containers = make(map[string]*FakeContainer) 105 for _, c := range containers { 106 containerID := c.Id 107 r.Containers[containerID] = c 108 } 109 110} 111 112// AssertCalls validates whether specified calls were made to the FakeRuntimeService. 113func (r *FakeRuntimeService) AssertCalls(calls []string) error { 114 r.Lock() 115 defer r.Unlock() 116 117 if !reflect.DeepEqual(calls, r.Called) { 118 return fmt.Errorf("expected %#v, got %#v", calls, r.Called) 119 } 120 return nil 121} 122 123// GetCalls returns the list of calls made to the FakeRuntimeService. 124func (r *FakeRuntimeService) GetCalls() []string { 125 r.Lock() 126 defer r.Unlock() 127 return append([]string{}, r.Called...) 128} 129 130// InjectError inject the error to the next call to the FakeRuntimeService. 131func (r *FakeRuntimeService) InjectError(f string, err error) { 132 r.Lock() 133 defer r.Unlock() 134 r.Errors[f] = append(r.Errors[f], err) 135} 136 137// caller of popError must grab a lock. 138func (r *FakeRuntimeService) popError(f string) error { 139 if r.Errors == nil { 140 return nil 141 } 142 errs := r.Errors[f] 143 if len(errs) == 0 { 144 return nil 145 } 146 err, errs := errs[0], errs[1:] 147 r.Errors[f] = errs 148 return err 149} 150 151// NewFakeRuntimeService creates a new FakeRuntimeService. 152func NewFakeRuntimeService() *FakeRuntimeService { 153 return &FakeRuntimeService{ 154 Called: make([]string, 0), 155 Errors: make(map[string][]error), 156 Containers: make(map[string]*FakeContainer), 157 Sandboxes: make(map[string]*FakePodSandbox), 158 FakeContainerStats: make(map[string]*runtimeapi.ContainerStats), 159 } 160} 161 162// Version returns version information from the FakeRuntimeService. 163func (r *FakeRuntimeService) Version(apiVersion string) (*runtimeapi.VersionResponse, error) { 164 r.Lock() 165 defer r.Unlock() 166 167 r.Called = append(r.Called, "Version") 168 if err := r.popError("Version"); err != nil { 169 return nil, err 170 } 171 172 return &runtimeapi.VersionResponse{ 173 Version: FakeVersion, 174 RuntimeName: FakeRuntimeName, 175 RuntimeVersion: FakeVersion, 176 RuntimeApiVersion: FakeVersion, 177 }, nil 178} 179 180// Status returns runtime status of the FakeRuntimeService. 181func (r *FakeRuntimeService) Status() (*runtimeapi.RuntimeStatus, error) { 182 r.Lock() 183 defer r.Unlock() 184 185 r.Called = append(r.Called, "Status") 186 if err := r.popError("Status"); err != nil { 187 return nil, err 188 } 189 190 return r.FakeStatus, nil 191} 192 193// RunPodSandbox emulates the run of the pod sandbox in the FakeRuntimeService. 194func (r *FakeRuntimeService) RunPodSandbox(config *runtimeapi.PodSandboxConfig, runtimeHandler string) (string, error) { 195 r.Lock() 196 defer r.Unlock() 197 198 r.Called = append(r.Called, "RunPodSandbox") 199 if err := r.popError("RunPodSandbox"); err != nil { 200 return "", err 201 } 202 203 if r.ErrorOnSandboxCreate { 204 return "", fmt.Errorf("error on sandbox create") 205 } 206 207 // PodSandboxID should be randomized for real container runtime, but here just use 208 // fixed name from BuildSandboxName() for easily making fake sandboxes. 209 podSandboxID := BuildSandboxName(config.Metadata) 210 createdAt := time.Now().UnixNano() 211 r.Sandboxes[podSandboxID] = &FakePodSandbox{ 212 PodSandboxStatus: runtimeapi.PodSandboxStatus{ 213 Id: podSandboxID, 214 Metadata: config.Metadata, 215 State: runtimeapi.PodSandboxState_SANDBOX_READY, 216 CreatedAt: createdAt, 217 Network: &runtimeapi.PodSandboxNetworkStatus{ 218 Ip: FakePodSandboxIPs[0], 219 }, 220 // Without setting sandboxStatus's Linux.Namespaces.Options, kubeGenericRuntimeManager's podSandboxChanged will consider it as network 221 // namespace changed and always recreate sandbox which causes pod creation failed. 222 // Ref `sandboxStatus.GetLinux().GetNamespaces().GetOptions().GetNetwork() != networkNamespaceForPod(pod)` in podSandboxChanged function. 223 Linux: &runtimeapi.LinuxPodSandboxStatus{ 224 Namespaces: &runtimeapi.Namespace{ 225 Options: config.GetLinux().GetSecurityContext().GetNamespaceOptions(), 226 }, 227 }, 228 Labels: config.Labels, 229 Annotations: config.Annotations, 230 RuntimeHandler: runtimeHandler, 231 }, 232 RuntimeHandler: runtimeHandler, 233 } 234 // assign additional IPs 235 additionalIPs := FakePodSandboxIPs[1:] 236 additionalPodIPs := make([]*runtimeapi.PodIP, 0, len(additionalIPs)) 237 for _, ip := range additionalIPs { 238 additionalPodIPs = append(additionalPodIPs, &runtimeapi.PodIP{ 239 Ip: ip, 240 }) 241 } 242 r.Sandboxes[podSandboxID].PodSandboxStatus.Network.AdditionalIps = additionalPodIPs 243 return podSandboxID, nil 244} 245 246// StopPodSandbox emulates the stop of pod sandbox in the FakeRuntimeService. 247func (r *FakeRuntimeService) StopPodSandbox(podSandboxID string) error { 248 r.Lock() 249 defer r.Unlock() 250 251 r.Called = append(r.Called, "StopPodSandbox") 252 if err := r.popError("StopPodSandbox"); err != nil { 253 return err 254 } 255 256 if s, ok := r.Sandboxes[podSandboxID]; ok { 257 s.State = runtimeapi.PodSandboxState_SANDBOX_NOTREADY 258 } else { 259 return fmt.Errorf("pod sandbox %s not found", podSandboxID) 260 } 261 262 return nil 263} 264 265// RemovePodSandbox emulates removal of the pod sadbox in the FakeRuntimeService. 266func (r *FakeRuntimeService) RemovePodSandbox(podSandboxID string) error { 267 r.Lock() 268 defer r.Unlock() 269 270 r.Called = append(r.Called, "RemovePodSandbox") 271 if err := r.popError("RemovePodSandbox"); err != nil { 272 return err 273 } 274 275 // Remove the pod sandbox 276 delete(r.Sandboxes, podSandboxID) 277 278 return nil 279} 280 281// PodSandboxStatus returns pod sandbox status from the FakeRuntimeService. 282func (r *FakeRuntimeService) PodSandboxStatus(podSandboxID string) (*runtimeapi.PodSandboxStatus, error) { 283 r.Lock() 284 defer r.Unlock() 285 286 r.Called = append(r.Called, "PodSandboxStatus") 287 if err := r.popError("PodSandboxStatus"); err != nil { 288 return nil, err 289 } 290 291 s, ok := r.Sandboxes[podSandboxID] 292 if !ok { 293 return nil, fmt.Errorf("pod sandbox %q not found", podSandboxID) 294 } 295 296 status := s.PodSandboxStatus 297 return &status, nil 298} 299 300// ListPodSandbox returns the list of pod sandboxes in the FakeRuntimeService. 301func (r *FakeRuntimeService) ListPodSandbox(filter *runtimeapi.PodSandboxFilter) ([]*runtimeapi.PodSandbox, error) { 302 r.Lock() 303 defer r.Unlock() 304 305 r.Called = append(r.Called, "ListPodSandbox") 306 if err := r.popError("ListPodSandbox"); err != nil { 307 return nil, err 308 } 309 310 result := make([]*runtimeapi.PodSandbox, 0) 311 for id, s := range r.Sandboxes { 312 if filter != nil { 313 if filter.Id != "" && filter.Id != id { 314 continue 315 } 316 if filter.State != nil && filter.GetState().State != s.State { 317 continue 318 } 319 if filter.LabelSelector != nil && !filterInLabels(filter.LabelSelector, s.GetLabels()) { 320 continue 321 } 322 } 323 324 result = append(result, &runtimeapi.PodSandbox{ 325 Id: s.Id, 326 Metadata: s.Metadata, 327 State: s.State, 328 CreatedAt: s.CreatedAt, 329 Labels: s.Labels, 330 Annotations: s.Annotations, 331 RuntimeHandler: s.RuntimeHandler, 332 }) 333 } 334 335 return result, nil 336} 337 338// PortForward emulates the set up of port forward in the FakeRuntimeService. 339func (r *FakeRuntimeService) PortForward(*runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error) { 340 r.Lock() 341 defer r.Unlock() 342 343 r.Called = append(r.Called, "PortForward") 344 if err := r.popError("PortForward"); err != nil { 345 return nil, err 346 } 347 348 return &runtimeapi.PortForwardResponse{}, nil 349} 350 351// CreateContainer emulates container creation in the FakeRuntimeService. 352func (r *FakeRuntimeService) CreateContainer(podSandboxID string, config *runtimeapi.ContainerConfig, sandboxConfig *runtimeapi.PodSandboxConfig) (string, error) { 353 r.Lock() 354 defer r.Unlock() 355 356 r.Called = append(r.Called, "CreateContainer") 357 if err := r.popError("CreateContainer"); err != nil { 358 return "", err 359 } 360 361 // ContainerID should be randomized for real container runtime, but here just use 362 // fixed BuildContainerName() for easily making fake containers. 363 containerID := BuildContainerName(config.Metadata, podSandboxID) 364 createdAt := time.Now().UnixNano() 365 createdState := runtimeapi.ContainerState_CONTAINER_CREATED 366 imageRef := config.Image.Image 367 r.Containers[containerID] = &FakeContainer{ 368 ContainerStatus: runtimeapi.ContainerStatus{ 369 Id: containerID, 370 Metadata: config.Metadata, 371 Image: config.Image, 372 ImageRef: imageRef, 373 CreatedAt: createdAt, 374 State: createdState, 375 Labels: config.Labels, 376 Annotations: config.Annotations, 377 }, 378 SandboxID: podSandboxID, 379 LinuxResources: config.GetLinux().GetResources(), 380 } 381 382 return containerID, nil 383} 384 385// StartContainer emulates start of a container in the FakeRuntimeService. 386func (r *FakeRuntimeService) StartContainer(containerID string) error { 387 r.Lock() 388 defer r.Unlock() 389 390 r.Called = append(r.Called, "StartContainer") 391 if err := r.popError("StartContainer"); err != nil { 392 return err 393 } 394 395 c, ok := r.Containers[containerID] 396 if !ok { 397 return fmt.Errorf("container %s not found", containerID) 398 } 399 400 // Set container to running. 401 c.State = runtimeapi.ContainerState_CONTAINER_RUNNING 402 c.StartedAt = time.Now().UnixNano() 403 404 return nil 405} 406 407// StopContainer emulates stop of a container in the FakeRuntimeService. 408func (r *FakeRuntimeService) StopContainer(containerID string, timeout int64) error { 409 r.Lock() 410 defer r.Unlock() 411 412 r.Called = append(r.Called, "StopContainer") 413 if err := r.popError("StopContainer"); err != nil { 414 return err 415 } 416 417 c, ok := r.Containers[containerID] 418 if !ok { 419 return fmt.Errorf("container %q not found", containerID) 420 } 421 422 // Set container to exited state. 423 finishedAt := time.Now().UnixNano() 424 exitedState := runtimeapi.ContainerState_CONTAINER_EXITED 425 c.State = exitedState 426 c.FinishedAt = finishedAt 427 428 return nil 429} 430 431// RemoveContainer emulates remove of a container in the FakeRuntimeService. 432func (r *FakeRuntimeService) RemoveContainer(containerID string) error { 433 r.Lock() 434 defer r.Unlock() 435 436 r.Called = append(r.Called, "RemoveContainer") 437 if err := r.popError("RemoveContainer"); err != nil { 438 return err 439 } 440 441 // Remove the container 442 delete(r.Containers, containerID) 443 444 return nil 445} 446 447// ListContainers returns the list of containers in the FakeRuntimeService. 448func (r *FakeRuntimeService) ListContainers(filter *runtimeapi.ContainerFilter) ([]*runtimeapi.Container, error) { 449 r.Lock() 450 defer r.Unlock() 451 452 r.Called = append(r.Called, "ListContainers") 453 if err := r.popError("ListContainers"); err != nil { 454 return nil, err 455 } 456 457 result := make([]*runtimeapi.Container, 0) 458 for _, s := range r.Containers { 459 if filter != nil { 460 if filter.Id != "" && filter.Id != s.Id { 461 continue 462 } 463 if filter.PodSandboxId != "" && filter.PodSandboxId != s.SandboxID { 464 continue 465 } 466 if filter.State != nil && filter.GetState().State != s.State { 467 continue 468 } 469 if filter.LabelSelector != nil && !filterInLabels(filter.LabelSelector, s.GetLabels()) { 470 continue 471 } 472 } 473 474 result = append(result, &runtimeapi.Container{ 475 Id: s.Id, 476 CreatedAt: s.CreatedAt, 477 PodSandboxId: s.SandboxID, 478 Metadata: s.Metadata, 479 State: s.State, 480 Image: s.Image, 481 ImageRef: s.ImageRef, 482 Labels: s.Labels, 483 Annotations: s.Annotations, 484 }) 485 } 486 487 return result, nil 488} 489 490// ContainerStatus returns the container status given the container ID in FakeRuntimeService. 491func (r *FakeRuntimeService) ContainerStatus(containerID string) (*runtimeapi.ContainerStatus, error) { 492 r.Lock() 493 defer r.Unlock() 494 495 r.Called = append(r.Called, "ContainerStatus") 496 if err := r.popError("ContainerStatus"); err != nil { 497 return nil, err 498 } 499 500 c, ok := r.Containers[containerID] 501 if !ok { 502 return nil, fmt.Errorf("container %q not found", containerID) 503 } 504 505 status := c.ContainerStatus 506 return &status, nil 507} 508 509// UpdateContainerResources returns the container resource in the FakeRuntimeService. 510func (r *FakeRuntimeService) UpdateContainerResources(string, *runtimeapi.LinuxContainerResources) error { 511 r.Lock() 512 defer r.Unlock() 513 514 r.Called = append(r.Called, "UpdateContainerResources") 515 return r.popError("UpdateContainerResources") 516} 517 518// ExecSync emulates the sync execution of a command in a container in the FakeRuntimeService. 519func (r *FakeRuntimeService) ExecSync(containerID string, cmd []string, timeout time.Duration) (stdout []byte, stderr []byte, err error) { 520 r.Lock() 521 defer r.Unlock() 522 523 r.Called = append(r.Called, "ExecSync") 524 err = r.popError("ExecSync") 525 return 526} 527 528// Exec emulates the execution of a command in a container in the FakeRuntimeService. 529func (r *FakeRuntimeService) Exec(*runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error) { 530 r.Lock() 531 defer r.Unlock() 532 533 r.Called = append(r.Called, "Exec") 534 if err := r.popError("Exec"); err != nil { 535 return nil, err 536 } 537 538 return &runtimeapi.ExecResponse{}, nil 539} 540 541// Attach emulates the attach request in the FakeRuntimeService. 542func (r *FakeRuntimeService) Attach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error) { 543 r.Lock() 544 defer r.Unlock() 545 546 r.Called = append(r.Called, "Attach") 547 if err := r.popError("Attach"); err != nil { 548 return nil, err 549 } 550 551 return &runtimeapi.AttachResponse{}, nil 552} 553 554// UpdateRuntimeConfig emulates the update of a runtime config for the FakeRuntimeService. 555func (r *FakeRuntimeService) UpdateRuntimeConfig(runtimeCOnfig *runtimeapi.RuntimeConfig) error { 556 r.Lock() 557 defer r.Unlock() 558 559 r.Called = append(r.Called, "UpdateRuntimeConfig") 560 return r.popError("UpdateRuntimeConfig") 561} 562 563// SetFakeContainerStats sets the fake container stats in the FakeRuntimeService. 564func (r *FakeRuntimeService) SetFakeContainerStats(containerStats []*runtimeapi.ContainerStats) { 565 r.Lock() 566 defer r.Unlock() 567 568 r.FakeContainerStats = make(map[string]*runtimeapi.ContainerStats) 569 for _, s := range containerStats { 570 r.FakeContainerStats[s.Attributes.Id] = s 571 } 572} 573 574// ContainerStats returns the container stats in the FakeRuntimeService. 575func (r *FakeRuntimeService) ContainerStats(containerID string) (*runtimeapi.ContainerStats, error) { 576 r.Lock() 577 defer r.Unlock() 578 579 r.Called = append(r.Called, "ContainerStats") 580 if err := r.popError("ContainerStats"); err != nil { 581 return nil, err 582 } 583 584 s, found := r.FakeContainerStats[containerID] 585 if !found { 586 return nil, fmt.Errorf("no stats for container %q", containerID) 587 } 588 return s, nil 589} 590 591// ListContainerStats returns the list of all container stats given the filter in the FakeRuntimeService. 592func (r *FakeRuntimeService) ListContainerStats(filter *runtimeapi.ContainerStatsFilter) ([]*runtimeapi.ContainerStats, error) { 593 r.Lock() 594 defer r.Unlock() 595 596 r.Called = append(r.Called, "ListContainerStats") 597 if err := r.popError("ListContainerStats"); err != nil { 598 return nil, err 599 } 600 601 var result []*runtimeapi.ContainerStats 602 for _, c := range r.Containers { 603 if filter != nil { 604 if filter.Id != "" && filter.Id != c.Id { 605 continue 606 } 607 if filter.PodSandboxId != "" && filter.PodSandboxId != c.SandboxID { 608 continue 609 } 610 if filter.LabelSelector != nil && !filterInLabels(filter.LabelSelector, c.GetLabels()) { 611 continue 612 } 613 } 614 s, found := r.FakeContainerStats[c.Id] 615 if !found { 616 continue 617 } 618 result = append(result, s) 619 } 620 621 return result, nil 622} 623 624// ReopenContainerLog emulates call to the reopen container log in the FakeRuntimeService. 625func (r *FakeRuntimeService) ReopenContainerLog(containerID string) error { 626 r.Lock() 627 defer r.Unlock() 628 629 r.Called = append(r.Called, "ReopenContainerLog") 630 631 if err := r.popError("ReopenContainerLog"); err != nil { 632 return err 633 } 634 635 return nil 636} 637