1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
module Gitlab
# Helper methods to do with Kubernetes network services & resources
module Kubernetes
# This is the comand that is run to start a terminal session. Kubernetes
# expects `command=foo&command=bar, not `command[]=foo&command[]=bar`
EXEC_COMMAND = URI.encode_www_form(
['sh', '-c', 'bash || sh'].map { |value| ['command', value] }
)
# Filters an array of pods (as returned by the kubernetes API) by their labels
def filter_by_label(items, labels = {})
items.select do |item|
metadata = item.fetch("metadata", {})
item_labels = metadata.fetch("labels", nil)
next unless item_labels
labels.all? { |k, v| item_labels[k.to_s] == v }
end
end
# Converts a pod (as returned by the kubernetes API) into a terminal
def terminals_for_pod(api_url, namespace, pod)
metadata = pod.fetch("metadata", {})
status = pod.fetch("status", {})
spec = pod.fetch("spec", {})
containers = spec["containers"]
pod_name = metadata["name"]
phase = status["phase"]
return unless containers.present? && pod_name.present? && phase == "Running"
created_at = DateTime.parse(metadata["creationTimestamp"]) rescue nil
containers.map do |container|
{
selectors: { pod: pod_name, container: container["name"] },
url: container_exec_url(api_url, namespace, pod_name, container["name"]),
subprotocols: ['channel.k8s.io'],
headers: Hash.new { |h, k| h[k] = [] },
created_at: created_at
}
end
end
def add_terminal_auth(terminal, token:, max_session_time:, ca_pem: nil)
terminal[:headers]['Authorization'] << "Bearer #{token}"
terminal[:max_session_time] = max_session_time
terminal[:ca_pem] = ca_pem if ca_pem.present?
end
def container_exec_url(api_url, namespace, pod_name, container_name)
url = URI.parse(api_url)
url.path = [
url.path.sub(%r{/+\z}, ''),
'api', 'v1',
'namespaces', ERB::Util.url_encode(namespace),
'pods', ERB::Util.url_encode(pod_name),
'exec'
].join('/')
url.query = {
container: container_name,
tty: true,
stdin: true,
stdout: true,
stderr: true
}.to_query + '&' + EXEC_COMMAND
case url.scheme
when 'http'
url.scheme = 'ws'
when 'https'
url.scheme = 'wss'
end
url.to_s
end
def to_kubeconfig(url:, namespace:, token:, ca_pem: nil)
config = {
apiVersion: 'v1',
clusters: [
name: 'gitlab-deploy',
cluster: {
server: url
}
],
contexts: [
name: 'gitlab-deploy',
context: {
cluster: 'gitlab-deploy',
namespace: namespace,
user: 'gitlab-deploy'
}
],
'current-context': 'gitlab-deploy',
kind: 'Config',
users: [
{
name: 'gitlab-deploy',
user: { token: token }
}
]
}
kubeconfig_embed_ca_pem(config, ca_pem) if ca_pem
config.deep_stringify_keys
end
private
def kubeconfig_embed_ca_pem(config, ca_pem)
cluster = config.dig(:clusters, 0, :cluster)
cluster[:'certificate-authority-data'] = Base64.encode64(ca_pem)
end
end
end