1# frozen_string_literal: true 2 3module KubernetesHelpers 4 include Gitlab::Kubernetes 5 6 NODE_NAME = "gke-cluster-applications-default-pool-49b7f225-v527" 7 8 def kube_response(body) 9 { body: body.to_json } 10 end 11 12 def kube_pods_response 13 kube_response(kube_pods_body) 14 end 15 16 def nodes_response 17 kube_response(nodes_body) 18 end 19 20 def nodes_metrics_response 21 kube_response(nodes_metrics_body) 22 end 23 24 def kube_pod_response 25 kube_response(kube_pod) 26 end 27 28 def kube_logs_response 29 { body: kube_logs_body } 30 end 31 32 def kube_deployments_response 33 kube_response(kube_deployments_body) 34 end 35 36 def kube_ingresses_response(with_canary: false) 37 kube_response(kube_ingresses_body(with_canary: with_canary)) 38 end 39 40 def stub_kubeclient_discover_base(api_url) 41 WebMock.stub_request(:get, api_url + '/api/v1').to_return(kube_response(kube_v1_discovery_body)) 42 WebMock 43 .stub_request(:get, api_url + '/apis/extensions/v1beta1') 44 .to_return(kube_response(kube_extensions_v1beta1_discovery_body)) 45 WebMock 46 .stub_request(:get, api_url + '/apis/apps/v1') 47 .to_return(kube_response(kube_apps_v1_discovery_body)) 48 WebMock 49 .stub_request(:get, api_url + '/apis/rbac.authorization.k8s.io/v1') 50 .to_return(kube_response(kube_v1_rbac_authorization_discovery_body)) 51 WebMock 52 .stub_request(:get, api_url + '/apis/metrics.k8s.io/v1beta1') 53 .to_return(kube_response(kube_metrics_v1beta1_discovery_body)) 54 end 55 56 def stub_kubeclient_discover_istio(api_url) 57 stub_kubeclient_discover_base(api_url) 58 59 WebMock 60 .stub_request(:get, api_url + '/apis/networking.istio.io/v1alpha3') 61 .to_return(kube_response(kube_istio_discovery_body)) 62 end 63 64 def stub_kubeclient_discover(api_url) 65 stub_kubeclient_discover_base(api_url) 66 67 WebMock 68 .stub_request(:get, api_url + '/apis/serving.knative.dev/v1alpha1') 69 .to_return(kube_response(kube_v1alpha1_serving_knative_discovery_body)) 70 WebMock 71 .stub_request(:get, api_url + '/apis/networking.k8s.io/v1') 72 .to_return(kube_response(kube_v1_networking_discovery_body)) 73 end 74 75 def stub_kubeclient_discover_knative_not_found(api_url) 76 stub_kubeclient_discover_base(api_url) 77 78 WebMock 79 .stub_request(:get, api_url + '/apis/serving.knative.dev/v1alpha1') 80 .to_return(status: [404, "Resource Not Found"]) 81 end 82 83 def stub_kubeclient_discover_knative_found(api_url) 84 WebMock 85 .stub_request(:get, api_url + '/apis/serving.knative.dev/v1alpha1') 86 .to_return(kube_response(kube_knative_discovery_body)) 87 end 88 89 def stub_kubeclient_service_pods(response = nil, options = {}) 90 stub_kubeclient_discover(service.api_url) 91 92 namespace_path = options[:namespace].present? ? "namespaces/#{options[:namespace]}/" : "" 93 94 pods_url = service.api_url + "/api/v1/#{namespace_path}pods" 95 96 WebMock.stub_request(:get, pods_url).to_return(response || kube_pods_response) 97 end 98 99 def stub_kubeclient_nodes(api_url) 100 stub_kubeclient_discover_base(api_url) 101 102 nodes_url = api_url + "/api/v1/nodes" 103 104 WebMock.stub_request(:get, nodes_url).to_return(nodes_response) 105 end 106 107 def stub_kubeclient_nodes_and_nodes_metrics(api_url) 108 stub_kubeclient_nodes(api_url) 109 110 nodes_url = api_url + "/apis/metrics.k8s.io/v1beta1/nodes" 111 112 WebMock.stub_request(:get, nodes_url).to_return(nodes_metrics_response) 113 end 114 115 def stub_kubeclient_pods(namespace, status: nil) 116 stub_kubeclient_discover(service.api_url) 117 pods_url = service.api_url + "/api/v1/namespaces/#{namespace}/pods" 118 response = { status: status } if status 119 120 WebMock.stub_request(:get, pods_url).to_return(response || kube_pods_response) 121 end 122 123 def stub_kubeclient_pod_details(pod, namespace, status: nil) 124 stub_kubeclient_discover(service.api_url) 125 126 pod_url = service.api_url + "/api/v1/namespaces/#{namespace}/pods/#{pod}" 127 response = { status: status } if status 128 129 WebMock.stub_request(:get, pod_url).to_return(response || kube_pod_response) 130 end 131 132 def stub_kubeclient_logs(pod_name, namespace, container: nil, status: nil, message: nil) 133 stub_kubeclient_discover(service.api_url) 134 135 if container 136 container_query_param = "container=#{container}&" 137 end 138 139 logs_url = service.api_url + "/api/v1/namespaces/#{namespace}/pods/#{pod_name}" \ 140 "/log?#{container_query_param}tailLines=#{::PodLogs::KubernetesService::LOGS_LIMIT}×tamps=true" 141 142 if status 143 response = { status: status } 144 response[:body] = { message: message }.to_json if message 145 end 146 147 WebMock.stub_request(:get, logs_url).to_return(response || kube_logs_response) 148 end 149 150 def stub_kubeclient_deployments(namespace, status: nil) 151 stub_kubeclient_discover(service.api_url) 152 deployments_url = service.api_url + "/apis/extensions/v1beta1/namespaces/#{namespace}/deployments" 153 response = { status: status } if status 154 155 WebMock.stub_request(:get, deployments_url).to_return(response || kube_deployments_response) 156 end 157 158 def stub_kubeclient_ingresses(namespace, status: nil, method: :get, resource_path: "", response: kube_ingresses_response) 159 stub_kubeclient_discover(service.api_url) 160 ingresses_url = service.api_url + "/apis/extensions/v1beta1/namespaces/#{namespace}/ingresses#{resource_path}" 161 response = { status: status } if status 162 163 WebMock.stub_request(method, ingresses_url).to_return(response) 164 end 165 166 def stub_kubeclient_knative_services(options = {}) 167 namespace_path = options[:namespace].present? ? "namespaces/#{options[:namespace]}/" : "" 168 169 options[:name] ||= "kubetest" 170 options[:domain] ||= "example.com" 171 options[:response] ||= kube_response(kube_knative_services_body(**options)) 172 173 stub_kubeclient_discover(service.api_url) 174 175 knative_url = service.api_url + "/apis/serving.knative.dev/v1alpha1/#{namespace_path}services" 176 177 WebMock.stub_request(:get, knative_url).to_return(options[:response]) 178 end 179 180 def stub_kubeclient_get_secret(api_url, **options) 181 options[:metadata_name] ||= "default-token-1" 182 options[:namespace] ||= "default" 183 184 WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{options[:namespace]}/secrets/#{options[:metadata_name]}") 185 .to_return(kube_response(kube_v1_secret_body(options))) 186 end 187 188 def stub_kubeclient_get_secret_error(api_url, name, namespace: 'default', status: 404) 189 WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/secrets/#{name}") 190 .to_return(status: [status, "Internal Server Error"]) 191 end 192 193 def stub_kubeclient_get_secret_not_found_then_found(api_url, **options) 194 options[:metadata_name] ||= "default-token-1" 195 options[:namespace] ||= "default" 196 197 WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{options[:namespace]}/secrets/#{options[:metadata_name]}") 198 .to_return(status: [404, "Not Found"]) 199 .then 200 .to_return(kube_response(kube_v1_secret_body(options))) 201 end 202 203 def stub_kubeclient_get_secret_missing_token_then_with_token(api_url, **options) 204 options[:metadata_name] ||= "default-token-1" 205 options[:namespace] ||= "default" 206 207 WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{options[:namespace]}/secrets/#{options[:metadata_name]}") 208 .to_return(kube_response(kube_v1_secret_body(options.merge(token: nil)))) 209 .then 210 .to_return(kube_response(kube_v1_secret_body(options))) 211 end 212 213 def stub_kubeclient_get_service_account(api_url, name, namespace: 'default') 214 WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts/#{name}") 215 .to_return(kube_response({})) 216 end 217 218 def stub_kubeclient_get_service_account_error(api_url, name, namespace: 'default', status: 404) 219 WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts/#{name}") 220 .to_return(status: [status, "Internal Server Error"]) 221 end 222 223 def stub_kubeclient_create_service_account(api_url, namespace: 'default') 224 WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts") 225 .to_return(kube_response({})) 226 end 227 228 def stub_kubeclient_create_service_account_error(api_url, namespace: 'default') 229 WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts") 230 .to_return(status: [500, "Internal Server Error"]) 231 end 232 233 def stub_kubeclient_put_service_account(api_url, name, namespace: 'default') 234 WebMock.stub_request(:put, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts/#{name}") 235 .to_return(kube_response({})) 236 end 237 238 def stub_kubeclient_create_secret(api_url, namespace: 'default') 239 WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/secrets") 240 .to_return(kube_response({})) 241 end 242 243 def stub_kubeclient_put_secret(api_url, name, namespace: 'default') 244 WebMock.stub_request(:put, api_url + "/api/v1/namespaces/#{namespace}/secrets/#{name}") 245 .to_return(kube_response({})) 246 end 247 248 def stub_kubeclient_put_cluster_role_binding(api_url, name) 249 WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings/#{name}") 250 .to_return(kube_response({})) 251 end 252 253 def stub_kubeclient_delete_role_binding(api_url, name, namespace: 'default') 254 WebMock.stub_request(:delete, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{name}") 255 .to_return(kube_response({})) 256 end 257 258 def stub_kubeclient_put_role_binding(api_url, name, namespace: 'default') 259 WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{name}") 260 .to_return(kube_response({})) 261 end 262 263 def stub_kubeclient_create_namespace(api_url) 264 WebMock.stub_request(:post, api_url + "/api/v1/namespaces") 265 .to_return(kube_response({})) 266 end 267 268 def stub_kubeclient_get_namespace(api_url, namespace: 'default') 269 WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}") 270 .to_return(kube_response({})) 271 end 272 273 def stub_kubeclient_put_role(api_url, name, namespace: 'default') 274 WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/roles/#{name}") 275 .to_return(kube_response({})) 276 end 277 278 def stub_kubeclient_get_gateway(api_url, name, namespace: 'default') 279 WebMock.stub_request(:get, api_url + "/apis/networking.istio.io/v1alpha3/namespaces/#{namespace}/gateways/#{name}") 280 .to_return(kube_response(kube_istio_gateway_body(name, namespace))) 281 end 282 283 def stub_kubeclient_put_gateway(api_url, name, namespace: 'default') 284 WebMock.stub_request(:put, api_url + "/apis/networking.istio.io/v1alpha3/namespaces/#{namespace}/gateways/#{name}") 285 .to_return(kube_response({})) 286 end 287 288 def kube_v1_secret_body(options) 289 { 290 "kind" => "SecretList", 291 "apiVersion": "v1", 292 "metadata": { 293 "name": options.fetch(:metadata_name, "default-token-1"), 294 "namespace": "kube-system" 295 }, 296 "data": { 297 "token": options.fetch(:token, Base64.encode64('token-sample-123')) 298 } 299 } 300 end 301 302 def kube_v1_discovery_body 303 { 304 "kind" => "APIResourceList", 305 "resources" => [ 306 { "name" => "nodes", "namespaced" => false, "kind" => "Node" }, 307 { "name" => "pods", "namespaced" => true, "kind" => "Pod" }, 308 { "name" => "deployments", "namespaced" => true, "kind" => "Deployment" }, 309 { "name" => "secrets", "namespaced" => true, "kind" => "Secret" }, 310 { "name" => "serviceaccounts", "namespaced" => true, "kind" => "ServiceAccount" }, 311 { "name" => "services", "namespaced" => true, "kind" => "Service" }, 312 { "name" => "namespaces", "namespaced" => true, "kind" => "Namespace" } 313 ] 314 } 315 end 316 317 # From Kubernetes 1.16+ Deployments are no longer served from apis/extensions 318 def kube_1_16_extensions_v1beta1_discovery_body 319 { 320 "kind" => "APIResourceList", 321 "resources" => [ 322 { "name" => "ingresses", "namespaced" => true, "kind" => "Deployment" } 323 ] 324 } 325 end 326 327 # From Kubernetes 1.22+ Ingresses are no longer served from apis/extensions 328 def kube_1_22_extensions_v1beta1_discovery_body 329 { 330 "kind" => "APIResourceList", 331 "resources" => [] 332 } 333 end 334 335 def kube_knative_discovery_body 336 { 337 "kind" => "APIResourceList", 338 "resources" => [] 339 } 340 end 341 342 def kube_extensions_v1beta1_discovery_body 343 { 344 "kind" => "APIResourceList", 345 "resources" => [ 346 { "name" => "deployments", "namespaced" => true, "kind" => "Deployment" }, 347 { "name" => "ingresses", "namespaced" => true, "kind" => "Ingress" } 348 ] 349 } 350 end 351 352 # Yes, deployments are defined in both apis/extensions/v1beta1 and apis/v1 353 # (for Kubernetes < 1.16). This matches what Kubenetes API server returns. 354 def kube_apps_v1_discovery_body 355 { 356 "kind" => "APIResourceList", 357 "resources" => [ 358 { "name" => "deployments", "namespaced" => true, "kind" => "Deployment" } 359 ] 360 } 361 end 362 363 def kube_v1_rbac_authorization_discovery_body 364 { 365 "kind" => "APIResourceList", 366 "resources" => [ 367 { "name" => "clusterrolebindings", "namespaced" => false, "kind" => "ClusterRoleBinding" }, 368 { "name" => "clusterroles", "namespaced" => false, "kind" => "ClusterRole" }, 369 { "name" => "rolebindings", "namespaced" => true, "kind" => "RoleBinding" }, 370 { "name" => "roles", "namespaced" => true, "kind" => "Role" } 371 ] 372 } 373 end 374 375 def kube_metrics_v1beta1_discovery_body 376 { 377 "kind" => "APIResourceList", 378 "resources" => [ 379 { "name" => "nodes", "namespaced" => false, "kind" => "NodeMetrics" }, 380 { "name" => "pods", "namespaced" => true, "kind" => "PodMetrics" } 381 ] 382 } 383 end 384 385 def kube_istio_discovery_body 386 { 387 "kind" => "APIResourceList", 388 "apiVersion" => "v1", 389 "groupVersion" => "networking.istio.io/v1alpha3", 390 "resources" => [ 391 { 392 "name" => "gateways", 393 "singularName" => "gateway", 394 "namespaced" => true, 395 "kind" => "Gateway", 396 "verbs" => %w[delete deletecollection get list patch create update watch], 397 "shortNames" => %w[gw], 398 "categories" => %w[istio-io networking-istio-io] 399 }, 400 { 401 "name" => "serviceentries", 402 "singularName" => "serviceentry", 403 "namespaced" => true, 404 "kind" => "ServiceEntry", 405 "verbs" => %w[delete deletecollection get list patch create update watch], 406 "shortNames" => %w[se], 407 "categories" => %w[istio-io networking-istio-io] 408 }, 409 { 410 "name" => "destinationrules", 411 "singularName" => "destinationrule", 412 "namespaced" => true, 413 "kind" => "DestinationRule", 414 "verbs" => %w[delete deletecollection get list patch create update watch], 415 "shortNames" => %w[dr], 416 "categories" => %w[istio-io networking-istio-io] 417 }, 418 { 419 "name" => "envoyfilters", 420 "singularName" => "envoyfilter", 421 "namespaced" => true, 422 "kind" => "EnvoyFilter", 423 "verbs" => %w[delete deletecollection get list patch create update watch], 424 "categories" => %w[istio-io networking-istio-io] 425 }, 426 { 427 "name" => "sidecars", 428 "singularName" => "sidecar", 429 "namespaced" => true, 430 "kind" => "Sidecar", 431 "verbs" => %w[delete deletecollection get list patch create update watch], 432 "categories" => %w[istio-io networking-istio-io] 433 }, 434 { 435 "name" => "virtualservices", 436 "singularName" => "virtualservice", 437 "namespaced" => true, 438 "kind" => "VirtualService", 439 "verbs" => %w[delete deletecollection get list patch create update watch], 440 "shortNames" => %w[vs], 441 "categories" => %w[istio-io networking-istio-io] 442 } 443 ] 444 } 445 end 446 447 def kube_v1_networking_discovery_body 448 { 449 "kind" => "APIResourceList", 450 "apiVersion" => "v1", 451 "groupVersion" => "networking.k8s.io/v1", 452 "resources" => [ 453 { "name" => "ingresses", "namespaced" => true, "kind" => "Ingress" } 454 ] 455 } 456 end 457 458 def kube_istio_gateway_body(name, namespace) 459 { 460 "apiVersion" => "networking.istio.io/v1alpha3", 461 "kind" => "Gateway", 462 "metadata" => { 463 "generation" => 1, 464 "labels" => { 465 "networking.knative.dev/ingress-provider" => "istio", 466 "serving.knative.dev/release" => "v0.7.0" 467 }, 468 "name" => name, 469 "namespace" => namespace, 470 "selfLink" => "/apis/networking.istio.io/v1alpha3/namespaces/#{namespace}/gateways/#{name}" 471 }, 472 "spec" => { 473 "selector" => { 474 "istio" => "ingressgateway" 475 }, 476 "servers" => [ 477 { 478 "hosts" => [ 479 "*" 480 ], 481 "port" => { 482 "name" => "http", 483 "number" => 80, 484 "protocol" => "HTTP" 485 } 486 }, 487 { 488 "hosts" => [ 489 "*" 490 ], 491 "port" => { 492 "name" => "https", 493 "number" => 443, 494 "protocol" => "HTTPS" 495 }, 496 "tls" => { 497 "mode" => "PASSTHROUGH" 498 } 499 } 500 ] 501 } 502 } 503 end 504 505 def kube_v1alpha1_serving_knative_discovery_body 506 { 507 "kind" => "APIResourceList", 508 "resources" => [ 509 { "name" => "revisions", "namespaced" => true, "kind" => "Revision" }, 510 { "name" => "services", "namespaced" => true, "kind" => "Service" }, 511 { "name" => "configurations", "namespaced" => true, "kind" => "Configuration" }, 512 { "name" => "routes", "namespaced" => true, "kind" => "Route" } 513 ] 514 } 515 end 516 517 def kube_pods_body 518 { 519 "kind" => "PodList", 520 "items" => [kube_pod] 521 } 522 end 523 524 def nodes_body 525 { 526 "kind" => "NodeList", 527 "items" => [kube_node] 528 } 529 end 530 531 def nodes_metrics_body 532 { 533 "kind" => "List", 534 "items" => [kube_node_metrics] 535 } 536 end 537 538 def kube_logs_body 539 "2019-12-13T14:04:22.123456Z Log 1\n2019-12-13T14:04:23.123456Z Log 2\n2019-12-13T14:04:24.123456Z Log 3" 540 end 541 542 def kube_deployments_body 543 { 544 "kind" => "DeploymentList", 545 "items" => [kube_deployment] 546 } 547 end 548 549 def kube_ingresses_body(with_canary: false) 550 items = with_canary ? [kube_ingress, kube_ingress(track: :canary)] : [kube_ingress] 551 552 { 553 "kind" => "List", 554 "items" => items 555 } 556 end 557 558 def kube_knative_pods_body(name, namespace) 559 { 560 "kind" => "PodList", 561 "items" => [kube_knative_pod(name: name, namespace: namespace)] 562 } 563 end 564 565 def kube_knative_services_body(**options) 566 { 567 "kind" => "List", 568 "items" => [knative_09_service(**options)] 569 } 570 end 571 572 # This is a partial response, it will have many more elements in reality but 573 # these are the ones we care about at the moment 574 def kube_pod(name: "kube-pod", container_name: "container-0", environment_slug: "production", namespace: "project-namespace", project_slug: "project-path-slug", status: "Running", track: nil) 575 { 576 "metadata" => { 577 "name" => name, 578 "namespace" => namespace, 579 "generateName" => "generated-name-with-suffix", 580 "creationTimestamp" => "2016-11-25T19:55:19Z", 581 "annotations" => { 582 "app.gitlab.com/env" => environment_slug, 583 "app.gitlab.com/app" => project_slug 584 }, 585 "labels" => { 586 "track" => track 587 }.compact 588 }, 589 "spec" => { 590 "containers" => [ 591 { "name" => "#{container_name}" }, 592 { "name" => "#{container_name}-1" } 593 ] 594 }, 595 "status" => { "phase" => status } 596 } 597 end 598 599 def kube_ingress(track: :stable) 600 additional_annotations = 601 if track == :canary 602 { 603 "nginx.ingress.kubernetes.io/canary" => "true", 604 "nginx.ingress.kubernetes.io/canary-by-header" => "canary", 605 "nginx.ingress.kubernetes.io/canary-weight" => "50" 606 } 607 else 608 {} 609 end 610 611 { 612 "metadata" => { 613 "name" => "production-auto-deploy", 614 "labels" => { 615 "app" => "production", 616 "app.kubernetes.io/managed-by" => "Helm", 617 "chart" => "auto-deploy-app-2.0.0-beta.2", 618 "heritage" => "Helm", 619 "release" => "production" 620 }, 621 "annotations" => { 622 "kubernetes.io/ingress.class" => "nginx", 623 "kubernetes.io/tls-acme" => "true", 624 "meta.helm.sh/release-name" => "production", 625 "meta.helm.sh/release-namespace" => "awesome-app-1-production" 626 }.merge(additional_annotations) 627 } 628 } 629 end 630 631 # This is a partial response, it will have many more elements in reality but 632 # these are the ones we care about at the moment 633 def kube_node 634 { 635 "metadata" => { 636 "name" => NODE_NAME 637 }, 638 "status" => { 639 "capacity" => { 640 "cpu" => "2", 641 "memory" => "7657228Ki" 642 }, 643 "allocatable" => { 644 "cpu" => "1930m", 645 "memory" => "5777164Ki" 646 } 647 } 648 } 649 end 650 651 # This is a partial response, it will have many more elements in reality but 652 # these are the ones we care about at the moment 653 def kube_node_metrics 654 { 655 "metadata" => { 656 "name" => NODE_NAME 657 }, 658 "usage" => { 659 "cpu" => "144208668n", 660 "memory" => "1789048Ki" 661 } 662 } 663 end 664 665 # Similar to a kube_pod, but should contain a running service 666 def kube_knative_pod(name: "kube-pod", namespace: "default", status: "Running") 667 { 668 "metadata" => { 669 "name" => name, 670 "namespace" => namespace, 671 "generateName" => "generated-name-with-suffix", 672 "creationTimestamp" => "2016-11-25T19:55:19Z", 673 "labels" => { 674 "serving.knative.dev/service" => name 675 } 676 }, 677 "spec" => { 678 "containers" => [ 679 { "name" => "container-0" }, 680 { "name" => "container-1" } 681 ] 682 }, 683 "status" => { "phase" => status } 684 } 685 end 686 687 def kube_deployment(name: "kube-deployment", environment_slug: "production", project_slug: "project-path-slug", track: nil, replicas: 3) 688 { 689 "metadata" => { 690 "name" => name, 691 "generation" => 4, 692 "annotations" => { 693 "app.gitlab.com/env" => environment_slug, 694 "app.gitlab.com/app" => project_slug 695 }, 696 "labels" => { 697 "track" => track 698 }.compact 699 }, 700 "spec" => { "replicas" => replicas }, 701 "status" => { 702 "observedGeneration" => 4 703 } 704 } 705 end 706 707 # noinspection RubyStringKeysInHashInspection 708 def knative_06_service(name: 'kubetest', namespace: 'default', domain: 'example.com', description: 'a knative service', environment: 'production', cluster_id: 9) 709 { "apiVersion" => "serving.knative.dev/v1alpha1", 710 "kind" => "Service", 711 "metadata" => 712 { "annotations" => 713 { "serving.knative.dev/creator" => "system:serviceaccount:#{namespace}:#{namespace}-service-account", 714 "serving.knative.dev/lastModifier" => "system:serviceaccount:#{namespace}:#{namespace}-service-account" }, 715 "creationTimestamp" => "2019-10-22T21:19:20Z", 716 "generation" => 1, 717 "labels" => { "service" => name }, 718 "name" => name, 719 "namespace" => namespace, 720 "resourceVersion" => "6042", 721 "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}", 722 "uid" => "9c7f63d0-f511-11e9-8815-42010a80002f" }, 723 "spec" => { 724 "runLatest" => { 725 "configuration" => { 726 "revisionTemplate" => { 727 "metadata" => { 728 "annotations" => { "Description" => description }, 729 "creationTimestamp" => "2019-10-22T21:19:20Z", 730 "labels" => { "service" => name } 731 }, 732 "spec" => { 733 "container" => { 734 "env" => [{ "name" => "timestamp", "value" => "2019-10-22 21:19:20" }], 735 "image" => "image_name", 736 "name" => "", 737 "resources" => {} 738 }, 739 "timeoutSeconds" => 300 740 } 741 } 742 } 743 } 744 }, 745 "status" => { 746 "address" => { 747 "hostname" => "#{name}.#{namespace}.svc.cluster.local", 748 "url" => "http://#{name}.#{namespace}.svc.cluster.local" 749 }, 750 "conditions" => 751 [{ "lastTransitionTime" => "2019-10-22T21:20:25Z", "status" => "True", "type" => "ConfigurationsReady" }, 752 { "lastTransitionTime" => "2019-10-22T21:20:25Z", "status" => "True", "type" => "Ready" }, 753 { "lastTransitionTime" => "2019-10-22T21:20:25Z", "status" => "True", "type" => "RoutesReady" }], 754 "domain" => "#{name}.#{namespace}.#{domain}", 755 "domainInternal" => "#{name}.#{namespace}.svc.cluster.local", 756 "latestCreatedRevisionName" => "#{name}-bskx6", 757 "latestReadyRevisionName" => "#{name}-bskx6", 758 "observedGeneration" => 1, 759 "traffic" => [{ "latestRevision" => true, "percent" => 100, "revisionName" => "#{name}-bskx6" }], 760 "url" => "http://#{name}.#{namespace}.#{domain}" 761 }, 762 "environment_scope" => environment, 763 "cluster_id" => cluster_id, 764 "podcount" => 0 } 765 end 766 767 # noinspection RubyStringKeysInHashInspection 768 def knative_07_service(name: 'kubetest', namespace: 'default', domain: 'example.com', description: 'a knative service', environment: 'production', cluster_id: 5) 769 { "apiVersion" => "serving.knative.dev/v1alpha1", 770 "kind" => "Service", 771 "metadata" => 772 { "annotations" => 773 { "serving.knative.dev/creator" => "system:serviceaccount:#{namespace}:#{namespace}-service-account", 774 "serving.knative.dev/lastModifier" => "system:serviceaccount:#{namespace}:#{namespace}-service-account" }, 775 "creationTimestamp" => "2019-10-22T21:19:13Z", 776 "generation" => 1, 777 "labels" => { "service" => name }, 778 "name" => name, 779 "namespace" => namespace, 780 "resourceVersion" => "289726", 781 "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}", 782 "uid" => "988349fa-f511-11e9-9ea1-42010a80005e" }, 783 "spec" => { 784 "template" => { 785 "metadata" => { 786 "annotations" => { "Description" => description }, 787 "creationTimestamp" => "2019-10-22T21:19:12Z", 788 "labels" => { "service" => name } 789 }, 790 "spec" => { 791 "containers" => [{ 792 "env" => 793 [{ "name" => "timestamp", "value" => "2019-10-22 21:19:12" }], 794 "image" => "image_name", 795 "name" => "user-container", 796 "resources" => {} 797 }], 798 "timeoutSeconds" => 300 799 } 800 }, 801 "traffic" => [{ "latestRevision" => true, "percent" => 100 }] 802 }, 803 "status" => 804 { "address" => { "url" => "http://#{name}.#{namespace}.svc.cluster.local" }, 805 "conditions" => 806 [{ "lastTransitionTime" => "2019-10-22T21:20:15Z", "status" => "True", "type" => "ConfigurationsReady" }, 807 { "lastTransitionTime" => "2019-10-22T21:20:15Z", "status" => "True", "type" => "Ready" }, 808 { "lastTransitionTime" => "2019-10-22T21:20:15Z", "status" => "True", "type" => "RoutesReady" }], 809 "latestCreatedRevisionName" => "#{name}-92tsj", 810 "latestReadyRevisionName" => "#{name}-92tsj", 811 "observedGeneration" => 1, 812 "traffic" => [{ "latestRevision" => true, "percent" => 100, "revisionName" => "#{name}-92tsj" }], 813 "url" => "http://#{name}.#{namespace}.#{domain}" }, 814 "environment_scope" => environment, 815 "cluster_id" => cluster_id, 816 "podcount" => 0 } 817 end 818 819 # noinspection RubyStringKeysInHashInspection 820 def knative_09_service(name: 'kubetest', namespace: 'default', domain: 'example.com', description: 'a knative service', environment: 'production', cluster_id: 5) 821 { "apiVersion" => "serving.knative.dev/v1alpha1", 822 "kind" => "Service", 823 "metadata" => 824 { "annotations" => 825 { "serving.knative.dev/creator" => "system:serviceaccount:#{namespace}:#{namespace}-service-account", 826 "serving.knative.dev/lastModifier" => "system:serviceaccount:#{namespace}:#{namespace}-service-account" }, 827 "creationTimestamp" => "2019-10-22T21:19:13Z", 828 "generation" => 1, 829 "labels" => { "service" => name }, 830 "name" => name, 831 "namespace" => namespace, 832 "resourceVersion" => "289726", 833 "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}", 834 "uid" => "988349fa-f511-11e9-9ea1-42010a80005e" }, 835 "spec" => { 836 "template" => { 837 "metadata" => { 838 "annotations" => { "Description" => description }, 839 "creationTimestamp" => "2019-10-22T21:19:12Z", 840 "labels" => { "service" => name } 841 }, 842 "spec" => { 843 "containers" => [{ 844 "env" => 845 [{ "name" => "timestamp", "value" => "2019-10-22 21:19:12" }], 846 "image" => "image_name", 847 "name" => "user-container", 848 "resources" => {} 849 }], 850 "timeoutSeconds" => 300 851 } 852 }, 853 "traffic" => [{ "latestRevision" => true, "percent" => 100 }] 854 }, 855 "status" => 856 { "address" => { "url" => "http://#{name}.#{namespace}.svc.cluster.local" }, 857 "conditions" => 858 [{ "lastTransitionTime" => "2019-10-22T21:20:15Z", "status" => "True", "type" => "ConfigurationsReady" }, 859 { "lastTransitionTime" => "2019-10-22T21:20:15Z", "status" => "True", "type" => "Ready" }, 860 { "lastTransitionTime" => "2019-10-22T21:20:15Z", "status" => "True", "type" => "RoutesReady" }], 861 "latestCreatedRevisionName" => "#{name}-92tsj", 862 "latestReadyRevisionName" => "#{name}-92tsj", 863 "observedGeneration" => 1, 864 "traffic" => [{ "latestRevision" => true, "percent" => 100, "revisionName" => "#{name}-92tsj" }], 865 "url" => "http://#{name}.#{namespace}.#{domain}" }, 866 "environment_scope" => environment, 867 "cluster_id" => cluster_id, 868 "podcount" => 0 } 869 end 870 871 # noinspection RubyStringKeysInHashInspection 872 def knative_05_service(name: 'kubetest', namespace: 'default', domain: 'example.com', description: 'a knative service', environment: 'production', cluster_id: 8) 873 { "apiVersion" => "serving.knative.dev/v1alpha1", 874 "kind" => "Service", 875 "metadata" => 876 { "annotations" => 877 { "serving.knative.dev/creator" => "system:serviceaccount:#{namespace}:#{namespace}-service-account", 878 "serving.knative.dev/lastModifier" => "system:serviceaccount:#{namespace}:#{namespace}-service-account" }, 879 "creationTimestamp" => "2019-10-22T21:19:19Z", 880 "generation" => 1, 881 "labels" => { "service" => name }, 882 "name" => name, 883 "namespace" => namespace, 884 "resourceVersion" => "330390", 885 "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}", 886 "uid" => "9c710da6-f511-11e9-9ba0-42010a800161" }, 887 "spec" => { 888 "runLatest" => { 889 "configuration" => { 890 "revisionTemplate" => { 891 "metadata" => { 892 "annotations" => { "Description" => description }, 893 "creationTimestamp" => "2019-10-22T21:19:19Z", 894 "labels" => { "service" => name } 895 }, 896 "spec" => { 897 "container" => { 898 "env" => [{ "name" => "timestamp", "value" => "2019-10-22 21:19:19" }], 899 "image" => "image_name", 900 "name" => "", 901 "resources" => { "requests" => { "cpu" => "400m" } } 902 }, 903 "timeoutSeconds" => 300 904 } 905 } 906 } 907 } 908 }, 909 "status" => 910 { "address" => { "hostname" => "#{name}.#{namespace}.svc.cluster.local" }, 911 "conditions" => 912 [{ "lastTransitionTime" => "2019-10-22T21:20:24Z", "status" => "True", "type" => "ConfigurationsReady" }, 913 { "lastTransitionTime" => "2019-10-22T21:20:24Z", "status" => "True", "type" => "Ready" }, 914 { "lastTransitionTime" => "2019-10-22T21:20:24Z", "status" => "True", "type" => "RoutesReady" }], 915 "domain" => "#{name}.#{namespace}.#{domain}", 916 "domainInternal" => "#{name}.#{namespace}.svc.cluster.local", 917 "latestCreatedRevisionName" => "#{name}-58qgr", 918 "latestReadyRevisionName" => "#{name}-58qgr", 919 "observedGeneration" => 1, 920 "traffic" => [{ "percent" => 100, "revisionName" => "#{name}-58qgr" }] }, 921 "environment_scope" => environment, 922 "cluster_id" => cluster_id, 923 "podcount" => 0 } 924 end 925 926 def kube_terminals(service, pod) 927 pod_name = pod['metadata']['name'] 928 pod_namespace = pod['metadata']['namespace'] 929 containers = pod['spec']['containers'] 930 931 containers.map do |container| 932 terminal = { 933 selectors: { pod: pod_name, container: container['name'] }, 934 url: container_exec_url(service.api_url, pod_namespace, pod_name, container['name']), 935 subprotocols: ['channel.k8s.io'], 936 headers: { 'Authorization' => ["Bearer #{service.token}"] }, 937 created_at: DateTime.parse(pod['metadata']['creationTimestamp']), 938 max_session_time: 0 939 } 940 terminal[:ca_pem] = service.ca_pem if service.ca_pem.present? 941 terminal 942 end 943 end 944 945 def kube_deployment_rollout_status(ingresses: []) 946 ::Gitlab::Kubernetes::RolloutStatus.from_deployments(kube_deployment, ingresses: ingresses) 947 end 948 949 def empty_deployment_rollout_status 950 ::Gitlab::Kubernetes::RolloutStatus.from_deployments 951 end 952end 953