blob: 95bb19c45d7a9dd713eaf2cbfbc2fdd4258ace2d [file] [log] [blame]
Alex9a4ad212020-10-01 18:04:25 -05001"""
2Module to handle interaction with Kube
3"""
4import base64
5import os
6import urllib3
7import yaml
8
9from kubernetes import client as kclient, config as kconfig
10from kubernetes.stream import stream
Alex7b0ee9a2021-09-21 17:16:17 -050011from kubernetes.client.rest import ApiException
Alex9a4ad212020-10-01 18:04:25 -050012
13from cfg_checker.common import logger, logger_cli
Alex7b0ee9a2021-09-21 17:16:17 -050014from cfg_checker.common.decorators import retry
Alex9a4ad212020-10-01 18:04:25 -050015from cfg_checker.common.exception import InvalidReturnException, KubeException
16from cfg_checker.common.file_utils import create_temp_file_with_content
17from cfg_checker.common.other import utils, shell
18from cfg_checker.common.ssh_utils import ssh_shell_p
Alex359e5752021-08-16 17:28:30 -050019from cfg_checker.common.const import ENV_LOCAL
Alex9a4ad212020-10-01 18:04:25 -050020
Alex7b0ee9a2021-09-21 17:16:17 -050021
Alex9a4ad212020-10-01 18:04:25 -050022urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
23
24
25def _init_kube_conf_local(config):
26 # Init kube library locally
Alex359e5752021-08-16 17:28:30 -050027 _path = "local:{}".format(config.kube_config_path)
Alex9a4ad212020-10-01 18:04:25 -050028 try:
Alexc4f59622021-08-27 13:42:00 -050029 kconfig.load_kube_config(config_file=config.kube_config_path)
Alex33747812021-04-07 10:11:39 -050030 if config.insecure:
31 kconfig.assert_hostname = False
32 kconfig.client_side_validation = False
Alex9a4ad212020-10-01 18:04:25 -050033 logger_cli.debug(
Alexc4f59622021-08-27 13:42:00 -050034 "... found Kube env: core, {}". format(
Alex9a4ad212020-10-01 18:04:25 -050035 ",".join(
36 kclient.CoreApi().get_api_versions().versions
37 )
38 )
39 )
Alexc4f59622021-08-27 13:42:00 -050040 return kconfig, kclient.ApiClient(), _path
Alex9a4ad212020-10-01 18:04:25 -050041 except Exception as e:
42 logger.warn("Failed to init local Kube client: {}".format(
43 str(e)
44 )
45 )
Alex359e5752021-08-16 17:28:30 -050046 return None, None, _path
Alex9a4ad212020-10-01 18:04:25 -050047
48
49def _init_kube_conf_remote(config):
50 # init remote client
51 # Preload Kube token
52 """
53 APISERVER=$(kubectl config view --minify |
54 grep server | cut -f 2- -d ":" | tr -d " ")
55 SECRET_NAME=$(kubectl get secrets |
56 grep ^default | cut -f1 -d ' ')
57 TOKEN=$(kubectl describe secret $SECRET_NAME |
58 grep -E '^token' | cut -f2 -d':' | tr -d " ")
59
60 echo "Detected API Server at: '${APISERVER}'"
61 echo "Got secret: '${SECRET_NAME}'"
62 echo "Loaded token: '${TOKEN}'"
63
64 curl $APISERVER/api
65 --header "Authorization: Bearer $TOKEN" --insecure
66 """
67 import yaml
Alex359e5752021-08-16 17:28:30 -050068 _path = ''
Alexc4f59622021-08-27 13:42:00 -050069 # Try to load remote config only if it was not detected already
70 if not config.kube_config_detected and not config.env_name == ENV_LOCAL:
Alex359e5752021-08-16 17:28:30 -050071 _path = "{}@{}:{}".format(
72 config.ssh_user,
73 config.ssh_host,
74 config.kube_config_path
75 )
Alex9d913532021-03-24 18:01:45 -050076 _c_data = ssh_shell_p(
Alexc4f59622021-08-27 13:42:00 -050077 "cat " + config.kube_config_path,
Alex9d913532021-03-24 18:01:45 -050078 config.ssh_host,
79 username=config.ssh_user,
80 keypath=config.ssh_key,
81 piped=False,
82 use_sudo=config.ssh_uses_sudo,
83 )
84 else:
Alex359e5752021-08-16 17:28:30 -050085 _path = "local:{}".format(config.kube_config_path)
Alex9d913532021-03-24 18:01:45 -050086 with open(config.kube_config_path, 'r') as ff:
87 _c_data = ff.read()
Alex9a4ad212020-10-01 18:04:25 -050088
Alex359e5752021-08-16 17:28:30 -050089 if len(_c_data) < 1:
90 return None, None, _path
91
Alex9a4ad212020-10-01 18:04:25 -050092 _conf = yaml.load(_c_data, Loader=yaml.SafeLoader)
93
94 _kube_conf = kclient.Configuration()
95 # A remote host configuration
96
97 # To work with remote cluster, we need to extract these
98 # keys = ['host', 'ssl_ca_cert', 'cert_file', 'key_file', 'verify_ssl']
99 # When v12 of the client will be release, we will use load_from_dict
100
101 _kube_conf.ssl_ca_cert = create_temp_file_with_content(
102 base64.standard_b64decode(
103 _conf['clusters'][0]['cluster']['certificate-authority-data']
104 )
105 )
106 _host = _conf['clusters'][0]['cluster']['server']
107 _kube_conf.cert_file = create_temp_file_with_content(
108 base64.standard_b64decode(
109 _conf['users'][0]['user']['client-certificate-data']
110 )
111 )
112 _kube_conf.key_file = create_temp_file_with_content(
113 base64.standard_b64decode(
114 _conf['users'][0]['user']['client-key-data']
115 )
116 )
117 if "http" not in _host or "443" not in _host:
118 logger_cli.error(
119 "Failed to extract Kube host: '{}'".format(_host)
120 )
121 else:
122 logger_cli.debug(
Alexc4f59622021-08-27 13:42:00 -0500123 "... 'context' host extracted: '{}' via SSH@{}".format(
Alex9a4ad212020-10-01 18:04:25 -0500124 _host,
125 config.ssh_host
126 )
127 )
128
129 # Substitute context host to ours
130 _tmp = _host.split(':')
131 _kube_conf.host = \
132 _tmp[0] + "://" + config.mcp_host + ":" + _tmp[2]
133 config.kube_port = _tmp[2]
134 logger_cli.debug(
Alexc4f59622021-08-27 13:42:00 -0500135 "... kube remote host updated to {}".format(
Alex9a4ad212020-10-01 18:04:25 -0500136 _kube_conf.host
137 )
138 )
139 _kube_conf.verify_ssl = False
140 _kube_conf.debug = config.debug
Alex33747812021-04-07 10:11:39 -0500141 if config.insecure:
142 _kube_conf.assert_hostname = False
143 _kube_conf.client_side_validation = False
144
Alex9a4ad212020-10-01 18:04:25 -0500145 # Nevertheless if you want to do it
146 # you can with these 2 parameters
147 # configuration.verify_ssl=True
148 # ssl_ca_cert is the filepath
149 # to the file that contains the certificate.
150 # configuration.ssl_ca_cert="certificate"
151
152 # _kube_conf.api_key = {
153 # "authorization": "Bearer " + config.kube_token
154 # }
155
156 # Create a ApiClient with our config
157 _kube_api = kclient.ApiClient(_kube_conf)
158
Alex359e5752021-08-16 17:28:30 -0500159 return _kube_conf, _kube_api, _path
Alex9a4ad212020-10-01 18:04:25 -0500160
161
162class KubeApi(object):
163 def __init__(self, config):
164 self.config = config
Alex359e5752021-08-16 17:28:30 -0500165 self.initialized = self._init_kclient()
Alex9a4ad212020-10-01 18:04:25 -0500166 self.last_response = None
167
168 def _init_kclient(self):
169 # if there is no password - try to get local, if this available
Alex359e5752021-08-16 17:28:30 -0500170 logger_cli.debug("... init kube config")
Alex9a4ad212020-10-01 18:04:25 -0500171 if self.config.env_name == "local":
Alex359e5752021-08-16 17:28:30 -0500172 self.kConf, self.kApi, self.kConfigPath = _init_kube_conf_local(
173 self.config
174 )
Alex9a4ad212020-10-01 18:04:25 -0500175 self.is_local = True
Alexc4f59622021-08-27 13:42:00 -0500176 # Try to load local config data
177 if self.config.kube_config_path and \
178 os.path.exists(self.config.kube_config_path):
179 _cmd = "cat " + self.config.kube_config_path
180 _c_data = shell(_cmd)
Alex9a4ad212020-10-01 18:04:25 -0500181 _conf = yaml.load(_c_data, Loader=yaml.SafeLoader)
182 self.user_keypath = create_temp_file_with_content(
183 base64.standard_b64decode(
184 _conf['users'][0]['user']['client-key-data']
185 )
186 )
187 self.yaml_conf = _c_data
188 else:
Alex359e5752021-08-16 17:28:30 -0500189 self.kConf, self.kApi, self.kConfigPath = _init_kube_conf_remote(
190 self.config
191 )
Alex9a4ad212020-10-01 18:04:25 -0500192 self.is_local = False
193
Alex359e5752021-08-16 17:28:30 -0500194 if self.kConf is None or self.kApi is None:
195 return False
196 else:
197 return True
198
Alex9a4ad212020-10-01 18:04:25 -0500199 def get_versions_api(self):
200 # client.CoreApi().get_api_versions().versions
201 return kclient.VersionApi(self.kApi)
202
203
204class KubeRemote(KubeApi):
205 def __init__(self, config):
206 super(KubeRemote, self).__init__(config)
207 self._coreV1 = None
Alex1f90e7b2021-09-03 15:31:28 -0500208 self._appsV1 = None
209 self._podV1 = None
Alexdcb792f2021-10-04 14:24:21 -0500210 self._custom = None
211
212 @property
213 def CustomObjects(self):
214 if not self._custom:
215 self._custom = kclient.CustomObjectsApi(self.kApi)
216 return self._custom
Alex9a4ad212020-10-01 18:04:25 -0500217
218 @property
219 def CoreV1(self):
220 if not self._coreV1:
Alex7b0ee9a2021-09-21 17:16:17 -0500221 if self.is_local:
222 self._coreV1 = kclient.CoreV1Api(kclient.ApiClient())
223 else:
224 self._coreV1 = kclient.CoreV1Api(kclient.ApiClient(self.kConf))
Alex9a4ad212020-10-01 18:04:25 -0500225 return self._coreV1
226
Alex1f90e7b2021-09-03 15:31:28 -0500227 @property
228 def AppsV1(self):
229 if not self._appsV1:
230 self._appsV1 = kclient.AppsV1Api(self.kApi)
231 return self._appsV1
232
233 @property
234 def PodsV1(self):
235 if not self._podsV1:
236 self._podsV1 = kclient.V1Pod(self.kApi)
237 return self._podsV1
238
Alex9a4ad212020-10-01 18:04:25 -0500239 @staticmethod
240 def _typed_list_to_dict(i_list):
241 _dict = {}
242 for _item in i_list:
243 _d = _item.to_dict()
244 _type = _d.pop("type")
245 _dict[_type.lower()] = _d
246
247 return _dict
248
249 @staticmethod
250 def _get_listed_attrs(items, _path):
251 _list = []
252 for _n in items:
253 _list.append(utils.rgetattr(_n, _path))
254
255 return _list
256
Alex1f90e7b2021-09-03 15:31:28 -0500257 @staticmethod
258 def safe_get_item_by_name(api_resource, _name):
259 for item in api_resource.items:
260 if item.metadata.name == _name:
261 return item
262
263 return None
264
Alex9a4ad212020-10-01 18:04:25 -0500265 def get_node_info(self, http=False):
266 # Query API for the nodes and do some presorting
267 _nodes = {}
268 if http:
269 _raw_nodes = self.CoreV1.list_node_with_http_info()
270 else:
271 _raw_nodes = self.CoreV1.list_node()
272
273 if not isinstance(_raw_nodes, kclient.models.v1_node_list.V1NodeList):
274 raise InvalidReturnException(
275 "Invalid return type: '{}'".format(type(_raw_nodes))
276 )
277
278 for _n in _raw_nodes.items:
279 _name = _n.metadata.name
280 _d = _n.to_dict()
281 # parse inner data classes as dicts
282 _d['addresses'] = self._typed_list_to_dict(_n.status.addresses)
283 _d['conditions'] = self._typed_list_to_dict(_n.status.conditions)
284 # Update 'status' type
285 if isinstance(_d['conditions']['ready']['status'], str):
286 _d['conditions']['ready']['status'] = utils.to_bool(
287 _d['conditions']['ready']['status']
288 )
289 # Parse image names?
290 # TODO: Here is the place where we can parse each node image names
291
292 # Parse roles
293 _d['labels'] = {}
294 for _label, _data in _d["metadata"]["labels"].items():
295 if _data.lower() in ["true", "false"]:
296 _d['labels'][_label] = utils.to_bool(_data)
297 else:
298 _d['labels'][_label] = _data
299
300 # Save
301 _nodes[_name] = _d
302
303 # debug report on how many nodes detected
304 logger_cli.debug("...node items returned '{}'".format(len(_nodes)))
305
306 return _nodes
307
Alexdcb792f2021-10-04 14:24:21 -0500308 def get_pod_names_by_partial_name(self, partial_name, ns):
309 logger_cli.debug('... searching for pods with {}'.format(partial_name))
310 _pods = self.CoreV1.list_namespaced_pod(ns)
311 _names = self._get_listed_attrs(_pods.items, "metadata.name")
312 _pnames = [n for n in _names if partial_name in n]
313 if len(_pnames) > 1:
314 logger_cli.debug(
315 "... more than one pod found for '{}': {}\n".format(
316 partial_name,
317 ", ".join(_pnames)
318 )
319 )
320 elif len(_pnames) < 1:
321 logger_cli.warning(
322 "WARNING: No pods found for '{}'".format(partial_name)
323 )
324
325 return _pnames
326
327 def get_pods_by_partial_name(self, partial_name, ns):
328 logger_cli.debug('... searching for pods with {}'.format(partial_name))
329 _all_pods = self.CoreV1.list_namespaced_pod(ns)
330 # _names = self._get_listed_attrs(_pods.items, "metadata.name")
331 _pods = [_pod for _pod in _all_pods.items
332 if partial_name in _pod.metadata.name]
333 if len(_pods) > 1:
334 logger_cli.debug(
335 "... more than one pod found for '{}': {}\n".format(
336 partial_name,
337 ", ".join(partial_name)
338 )
339 )
340 elif len(_pods) < 1:
341 logger_cli.warning(
342 "WARNING: No pods found for '{}'".format(partial_name)
343 )
344
345 return _pods
346
Alex9a4ad212020-10-01 18:04:25 -0500347 def exec_on_target_pod(
348 self,
349 cmd,
350 pod_name,
351 namespace,
352 strict=False,
353 _request_timeout=120,
Alexb78191f2021-11-02 16:35:46 -0500354 arguments=None,
Alex9a4ad212020-10-01 18:04:25 -0500355 **kwargs
356 ):
Alexdcb792f2021-10-04 14:24:21 -0500357 _pname = ""
Alex9a4ad212020-10-01 18:04:25 -0500358 if not strict:
Alex1f90e7b2021-09-03 15:31:28 -0500359 logger_cli.debug(
360 "... searching for pods with the name '{}'".format(pod_name)
361 )
362 _pods = {}
Alex7b0ee9a2021-09-21 17:16:17 -0500363 _pods = self.CoreV1.list_namespaced_pod(namespace)
Alex1f90e7b2021-09-03 15:31:28 -0500364 _names = self._get_listed_attrs(_pods.items, "metadata.name")
Alex33747812021-04-07 10:11:39 -0500365 _pnames = [n for n in _names if n.startswith(pod_name)]
366 if len(_pnames) > 1:
Alex9a4ad212020-10-01 18:04:25 -0500367 logger_cli.debug(
Alexc4f59622021-08-27 13:42:00 -0500368 "... more than one pod found for '{}': {}\n"
369 "... using first one".format(
Alex9a4ad212020-10-01 18:04:25 -0500370 pod_name,
Alex33747812021-04-07 10:11:39 -0500371 ", ".join(_pnames)
Alex9a4ad212020-10-01 18:04:25 -0500372 )
373 )
Alexdcb792f2021-10-04 14:24:21 -0500374 elif len(_pnames) < 1:
Alex9a4ad212020-10-01 18:04:25 -0500375 raise KubeException("No pods found for '{}'".format(pod_name))
Alexb78191f2021-11-02 16:35:46 -0500376 # in case of >1 and =1 we are taking 1st anyway
377 _pname = _pnames[0]
Alex9a4ad212020-10-01 18:04:25 -0500378 else:
379 _pname = pod_name
Alex33747812021-04-07 10:11:39 -0500380 logger_cli.debug(
Alexb78191f2021-11-02 16:35:46 -0500381 "... cmd: [CoreV1] exec {} -n {} -- {} '{}'".format(
Alex33747812021-04-07 10:11:39 -0500382 _pname,
383 namespace,
Alexb78191f2021-11-02 16:35:46 -0500384 cmd,
385 arguments
Alex33747812021-04-07 10:11:39 -0500386 )
387 )
Alex1f90e7b2021-09-03 15:31:28 -0500388 # Set preload_content to False to preserve JSON
389 # If not, output gets converted to str
390 # Which causes to change " to '
391 # After that json.loads(...) fail
Alex7b0ee9a2021-09-21 17:16:17 -0500392 cmd = cmd if isinstance(cmd, list) else cmd.split()
Alexb78191f2021-11-02 16:35:46 -0500393 if arguments:
394 cmd += [arguments]
Alex1f90e7b2021-09-03 15:31:28 -0500395 _pod_stream = stream(
Alex9a4ad212020-10-01 18:04:25 -0500396 self.CoreV1.connect_get_namespaced_pod_exec,
397 _pname,
398 namespace,
Alex7b0ee9a2021-09-21 17:16:17 -0500399 command=cmd,
Alex9a4ad212020-10-01 18:04:25 -0500400 stderr=True,
401 stdin=False,
402 stdout=True,
403 tty=False,
404 _request_timeout=_request_timeout,
Alex1f90e7b2021-09-03 15:31:28 -0500405 _preload_content=False,
Alex9a4ad212020-10-01 18:04:25 -0500406 **kwargs
407 )
Alex1f90e7b2021-09-03 15:31:28 -0500408 # run for timeout
409 _pod_stream.run_forever(timeout=_request_timeout)
410 # read the output
Alex7b0ee9a2021-09-21 17:16:17 -0500411 _output = _pod_stream.read_stdout()
Alexb78191f2021-11-02 16:35:46 -0500412 _error = _pod_stream.read_stderr()
413 if _error:
414 # copy error to output
415 logger_cli.warning(
416 "WARNING: cmd of '{}' returned error:\n{}\n".format(
417 " ".join(cmd),
418 _error
419 )
420 )
421 if not _output:
422 _output = _error
Alex7b0ee9a2021-09-21 17:16:17 -0500423 # Force recreate of api objects
424 self._coreV1 = None
425 # Send output
426 return _output
Alex9a4ad212020-10-01 18:04:25 -0500427
Alex1f90e7b2021-09-03 15:31:28 -0500428 def ensure_namespace(self, ns):
429 """
430 Ensure that given namespace exists
431 """
432 # list active namespaces
433 _v1NamespaceList = self.CoreV1.list_namespace()
434 _ns = self.safe_get_item_by_name(_v1NamespaceList, ns)
435
436 if _ns is None:
437 logger_cli.debug("... creating namespace '{}'".format(ns))
Alexdcb792f2021-10-04 14:24:21 -0500438 _new_ns = kclient.V1Namespace()
439 _new_ns.metadata = kclient.V1ObjectMeta(name=ns)
440 _r = self.CoreV1.create_namespace(_new_ns)
Alex1f90e7b2021-09-03 15:31:28 -0500441 # TODO: check return on fail
442 if not _r:
443 return False
444 else:
445 logger_cli.debug("... found existing namespace '{}'".format(ns))
446
447 return True
448
449 def get_daemon_set_by_name(self, ns, name):
450 return self.safe_get_item_by_name(
451 self.AppsV1.list_namespaced_daemon_set(ns),
452 name
453 )
454
455 def create_config_map(self, ns, name, source, recreate=True):
456 """
457 Creates/Overwrites ConfigMap in working namespace
458 """
459 # Prepare source
460 logger_cli.debug(
461 "... preparing config map '{}/{}' with files from '{}'".format(
462 ns,
463 name,
464 source
465 )
466 )
467 _data = {}
468 if os.path.isfile(source):
469 # populate data with one file
470 with open(source, 'rt') as fS:
471 _data[os.path.split(source)[1]] = fS.read()
472 elif os.path.isdir(source):
473 # walk dirs and populate all 'py' files
474 for path, dirs, files in os.walk(source):
475 _e = ('.py')
476 _subfiles = (_fl for _fl in files
477 if _fl.endswith(_e) and not _fl.startswith('.'))
478 for _file in _subfiles:
479 with open(os.path.join(path, _file), 'rt') as fS:
480 _data[_file] = fS.read()
481
482 _cm = kclient.V1ConfigMap()
483 _cm.metadata = kclient.V1ObjectMeta(name=name, namespace=ns)
484 _cm.data = _data
485 logger_cli.debug(
486 "... prepared config map with {} scripts".format(len(_data))
487 )
488 # Query existing configmap, delete if needed
489 _existing_cm = self.safe_get_item_by_name(
490 self.CoreV1.list_namespaced_config_map(namespace=ns),
491 name
492 )
493 if _existing_cm is not None:
494 self.CoreV1.replace_namespaced_config_map(
495 namespace=ns,
496 name=name,
497 body=_cm
498 )
499 logger_cli.debug(
500 "... replaced existing config map '{}/{}'".format(
501 ns,
502 name
503 )
504 )
505 else:
506 # Create it
507 self.CoreV1.create_namespaced_config_map(
508 namespace=ns,
509 body=_cm
510 )
511 logger_cli.debug("... created config map '{}/{}'".format(
512 ns,
513 name
514 ))
515
516 return _data.keys()
517
518 def prepare_daemonset_from_yaml(self, ns, ds_yaml):
519 _name = ds_yaml['metadata']['name']
520 _ds = self.get_daemon_set_by_name(ns, _name)
521
522 if _ds is not None:
523 logger_cli.debug(
524 "... found existing daemonset '{}'".format(_name)
525 )
526 _r = self.AppsV1.replace_namespaced_daemon_set(
527 _ds.metadata.name,
528 _ds.metadata.namespace,
529 body=ds_yaml
530 )
531 logger_cli.debug(
532 "... replacing existing daemonset '{}'".format(_name)
533 )
534 return _r
535 else:
536 logger_cli.debug(
537 "... creating daemonset '{}'".format(_name)
538 )
539 _r = self.AppsV1.create_namespaced_daemon_set(ns, body=ds_yaml)
540 return _r
541
542 def delete_daemon_set_by_name(self, ns, name):
543 return self.AppsV1.delete_namespaced_daemon_set(name, ns)
544
545 def exec_on_all_pods(self, pods):
546 """
547 Create multiple threads to execute script on all target pods
548 """
549 # Create map for threads: [[node_name, ns, pod_name]...]
550 _pod_list = []
551 for item in pods.items:
552 _pod_list.append(
553 [
554 item.spec.nodeName,
555 item.metadata.namespace,
556 item.metadata.name
557 ]
558 )
559
560 # map func and cmd
Alexdcb792f2021-10-04 14:24:21 -0500561 logger_cli.error("ERROR: 'exec_on_all_pods'is not implemented yet")
Alex1f90e7b2021-09-03 15:31:28 -0500562 # create result list
563
564 return []
Alex7b0ee9a2021-09-21 17:16:17 -0500565
566 @retry(ApiException)
567 def get_pods_for_daemonset(self, ds):
568 # get all pod names for daemonset
569 logger_cli.debug(
570 "... extracting pod names from daemonset '{}'".format(
571 ds.metadata.name
572 )
573 )
574 _ns = ds.metadata.namespace
575 _name = ds.metadata.name
576 _pods = self.CoreV1.list_namespaced_pod(
577 namespace=_ns,
578 label_selector='name={}'.format(_name)
579 )
580 return _pods
581
582 def put_string_buffer_to_pod_as_textfile(
583 self,
584 pod_name,
585 namespace,
586 buffer,
587 filepath,
588 _request_timeout=120,
589 **kwargs
590 ):
591 _command = ['/bin/sh']
592 response = stream(
593 self.CoreV1.connect_get_namespaced_pod_exec,
594 pod_name,
595 namespace,
596 command=_command,
597 stderr=True,
598 stdin=True,
599 stdout=True,
600 tty=False,
601 _request_timeout=_request_timeout,
602 _preload_content=False,
603 **kwargs
604 )
605
606 # if json
607 # buffer = json.dumps(_dict, indent=2).encode('utf-8')
608
609 commands = [
610 bytes("cat <<'EOF' >" + filepath + "\n", 'utf-8'),
611 buffer,
612 bytes("\n" + "EOF\n", 'utf-8')
613 ]
614
615 while response.is_open():
616 response.update(timeout=1)
617 if response.peek_stdout():
618 logger_cli.debug("... STDOUT: %s" % response.read_stdout())
619 if response.peek_stderr():
620 logger_cli.debug("... STDERR: %s" % response.read_stderr())
621 if commands:
622 c = commands.pop(0)
623 logger_cli.debug("... running command... {}\n".format(c))
624 response.write_stdin(str(c, encoding='utf-8'))
625 else:
626 break
627 response.close()
628
629 # Force recreate of Api objects
630 self._coreV1 = None
631
632 return
Alexdcb792f2021-10-04 14:24:21 -0500633
634 def get_custom_resource(self, group, version, plural):
635 # Get it
636 # Example:
637 # kubernetes.client.CustomObjectsApi().list_cluster_custom_object(
638 # group="networking.istio.io",
639 # version="v1alpha3",
640 # plural="serviceentries"
641 # )
642 return self.CustomObjects.list_cluster_custom_object(
643 group=group,
644 version=version,
645 plural=plural
646 )