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}&timestamps=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