codekingpro/portable-devtools
114k
1# Copyright 2018 The Kubernetes Authors.2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7# http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14 15import atexit16import base6417import copy18import datetime19import json20import logging21import os22import platform23import subprocess24import tempfile25import time26from collections import namedtuple27 28import oauthlib.oauth229import urllib330import yaml31from requests_oauthlib import OAuth2Session32from six import PY333 34from kubernetes.client import ApiClient, Configuration35from kubernetes.config.exec_provider import ExecProvider36 37from .config_exception import ConfigException38from .dateutil import UTC, format_rfc3339, parse_rfc333939 40try:41 import adal42except ImportError:43 pass44 45try:46 import google.auth47 import google.auth.transport.requests48 google_auth_available = True49except ImportError:50 google_auth_available = False51 52 53 54EXPIRY_SKEW_PREVENTION_DELAY = datetime.timedelta(minutes=5)55KUBE_CONFIG_DEFAULT_LOCATION = os.environ.get('KUBECONFIG', '~/.kube/config')56ENV_KUBECONFIG_PATH_SEPARATOR = ';' if platform.system() == 'Windows' else ':'57_temp_files = {}58 59 60def _cleanup_temp_files():61 global _temp_files62 for temp_file in _temp_files.values():63 try:64 os.remove(temp_file)65 except OSError:66 pass67 _temp_files = {}68 69 70def _create_temp_file_with_content(content, temp_file_path=None, force_recreate=False):71 if len(_temp_files) == 0:72 atexit.register(_cleanup_temp_files)73 # Because we may change context several times, try to remember files we74 # created and reuse them at a small memory cost.75 content_key = str(content)76 if not force_recreate and content_key in _temp_files:77 return _temp_files[content_key]78 if temp_file_path and not os.path.isdir(temp_file_path):79 os.makedirs(name=temp_file_path)80 fd, name = tempfile.mkstemp(dir=temp_file_path)81 os.close(fd)82 _temp_files[content_key] = name83 with open(name, 'wb') as fd:84 fd.write(content.encode() if isinstance(content, str) else content)85 return name86 87 88def _is_expired(expiry):89 return ((parse_rfc3339(expiry) - EXPIRY_SKEW_PREVENTION_DELAY) <=90 datetime.datetime.now(tz=UTC))91 92 93class FileOrData(object):94 """Utility class to read content of obj[%data_key_name] or file's95 content of obj[%file_key_name] and represent it as file or data.96 Note that the data is preferred. The obj[%file_key_name] will be used iff97 obj['%data_key_name'] is not set or empty. Assumption is file content is98 raw data and data field is base64 string. The assumption can be changed99 with base64_file_content flag. If set to False, the content of the file100 will assumed to be base64 and read as is. The default True value will101 result in base64 encode of the file content after read."""102 103 def __init__(self, obj, file_key_name, data_key_name=None,104 file_base_path="", base64_file_content=True,105 temp_file_path=None):106 if not data_key_name:107 data_key_name = file_key_name + "-data"108 self._file = None109 self._data = None110 self._base64_file_content = base64_file_content111 self._temp_file_path = temp_file_path112 if not obj:113 return114 if data_key_name in obj:115 self._data = obj[data_key_name]116 elif file_key_name in obj:117 self._file = os.path.normpath(118 os.path.join(file_base_path, obj[file_key_name]))119 120 def as_file(self):121 """If obj[%data_key_name] exists, return name of a file with base64122 decoded obj[%data_key_name] content otherwise obj[%file_key_name]."""123 use_data_if_no_file = not self._file and self._data124 if use_data_if_no_file:125 self._write_file()126 127 if self._file and not os.path.isfile(self._file):128 self._write_file(force_rewrite=True)129 if self._file and not os.path.isfile(self._file):130 raise ConfigException("File does not exist: %s" % self._file)131 return self._file132 133 def as_data(self):134 """If obj[%data_key_name] exists, Return obj[%data_key_name] otherwise135 base64 encoded string of obj[%file_key_name] file content."""136 use_file_if_no_data = not self._data and self._file137 if use_file_if_no_data:138 with open(self._file) as f:139 if self._base64_file_content:140 self._data = bytes.decode(141 base64.standard_b64encode(str.encode(f.read())))142 else:143 self._data = f.read()144 return self._data145 146 def _write_file(self, force_rewrite=False):147 if self._base64_file_content:148 if isinstance(self._data, str):149 content = self._data.encode()150 else:151 content = self._data152 self._file = _create_temp_file_with_content(153 base64.standard_b64decode(content), self._temp_file_path, force_recreate=force_rewrite)154 else:155 self._file = _create_temp_file_with_content(156 self._data, self._temp_file_path, force_recreate=force_rewrite)157 158 159class CommandTokenSource(object):160 def __init__(self, cmd, args, tokenKey, expiryKey):161 self._cmd = cmd162 self._args = args163 if not tokenKey:164 self._tokenKey = '{.access_token}'165 else:166 self._tokenKey = tokenKey167 if not expiryKey:168 self._expiryKey = '{.token_expiry}'169 else:170 self._expiryKey = expiryKey171 172 def token(self):173 fullCmd = self._cmd + (" ") + " ".join(self._args)174 process = subprocess.Popen(175 [self._cmd] + self._args,176 stdout=subprocess.PIPE,177 stderr=subprocess.PIPE,178 universal_newlines=True)179 (stdout, stderr) = process.communicate()180 exit_code = process.wait()181 if exit_code != 0:182 msg = 'cmd-path: process returned %d' % exit_code183 msg += "\nCmd: %s" % fullCmd184 stderr = stderr.strip()185 if stderr:186 msg += '\nStderr: %s' % stderr187 raise ConfigException(msg)188 try:189 data = json.loads(stdout)190 except ValueError as de:191 raise ConfigException(192 'exec: failed to decode process output: %s' % de)193 A = namedtuple('A', ['token', 'expiry'])194 return A(195 token=data['credential']['access_token'],196 expiry=parse_rfc3339(data['credential']['token_expiry']))197 198 199class KubeConfigLoader(object):200 201 def __init__(self, config_dict, active_context=None,202 get_google_credentials=None,203 config_base_path="",204 config_persister=None,205 temp_file_path=None):206 207 if config_dict is None:208 raise ConfigException(209 'Invalid kube-config. '210 'Expected config_dict to not be None.')211 elif isinstance(config_dict, ConfigNode):212 self._config = config_dict213 else:214 self._config = ConfigNode('kube-config', config_dict)215 216 self._current_context = None217 self._user = None218 self._cluster = None219 self.set_active_context(active_context)220 self._config_base_path = config_base_path221 self._config_persister = config_persister222 self._temp_file_path = temp_file_path223 224 def _refresh_credentials_with_cmd_path():225 config = self._user['auth-provider']['config']226 cmd = config['cmd-path']227 if len(cmd) == 0:228 raise ConfigException(229 'missing access token cmd '230 '(cmd-path is an empty string in your kubeconfig file)')231 if 'scopes' in config and config['scopes'] != "":232 raise ConfigException(233 'scopes can only be used '234 'when kubectl is using a gcp service account key')235 args = []236 if 'cmd-args' in config:237 args = config['cmd-args'].split()238 else:239 fields = config['cmd-path'].split()240 cmd = fields[0]241 args = fields[1:]242 243 commandTokenSource = CommandTokenSource(244 cmd, args,245 config.safe_get('token-key'),246 config.safe_get('expiry-key'))247 return commandTokenSource.token()248 249 def _refresh_credentials():250 # Refresh credentials using cmd-path251 if ('auth-provider' in self._user and252 'config' in self._user['auth-provider'] and253 'cmd-path' in self._user['auth-provider']['config']):254 return _refresh_credentials_with_cmd_path()255 256 # Make the Google auth block optional.257 if google_auth_available:258 credentials, project_id = google.auth.default(scopes=[259 'https://www.googleapis.com/auth/cloud-platform',260 'https://www.googleapis.com/auth/userinfo.email'261 ])262 request = google.auth.transport.requests.Request()263 credentials.refresh(request)264 return credentials265 else:266 return None267 268 if get_google_credentials:269 self._get_google_credentials = get_google_credentials270 else:271 self._get_google_credentials = _refresh_credentials272 273 def set_active_context(self, context_name=None):274 if context_name is None:275 context_name = self._config['current-context']276 self._current_context = self._config['contexts'].get_with_name(277 context_name)278 if (self._current_context['context'].safe_get('user') and279 self._config.safe_get('users')):280 user = self._config['users'].get_with_name(281 self._current_context['context']['user'], safe=True)282 if user:283 self._user = user['user']284 else:285 self._user = None286 else:287 self._user = None288 self._cluster = self._config['clusters'].get_with_name(289 self._current_context['context']['cluster'])['cluster']290 291 def _load_authentication(self):292 """Read authentication from kube-config user section if exists.293 294 This function goes through various authentication methods in user295 section of kube-config and stops if it finds a valid authentication296 method. The order of authentication methods is:297 298 1. auth-provider (gcp, azure, oidc)299 2. token field (point to a token file)300 3. exec provided plugin301 4. username/password302 """303 if not self._user:304 return305 if self._load_auth_provider_token():306 return307 if self._load_user_token():308 return309 if self._load_from_exec_plugin():310 return311 self._load_user_pass_token()312 313 def _load_auth_provider_token(self):314 if 'auth-provider' not in self._user:315 return316 provider = self._user['auth-provider']317 if 'name' not in provider:318 return319 if provider['name'] == 'gcp':320 return self._load_gcp_token(provider)321 if provider['name'] == 'azure':322 return self._load_azure_token(provider)323 if provider['name'] == 'oidc':324 return self._load_oid_token(provider)325 326 def _azure_is_expired(self, provider):327 expires_on = provider['config']['expires-on']328 if expires_on.isdigit():329 return int(expires_on) < time.time()330 else:331 exp_time = time.strptime(expires_on, '%Y-%m-%d %H:%M:%S.%f')332 return exp_time < time.gmtime()333 334 def _load_azure_token(self, provider):335 if 'config' not in provider:336 return337 if 'access-token' not in provider['config']:338 return339 if 'expires-on' in provider['config']:340 if self._azure_is_expired(provider):341 self._refresh_azure_token(provider['config'])342 self.token = 'Bearer %s' % provider['config']['access-token']343 return self.token344 345 def _refresh_azure_token(self, config):346 if 'adal' not in globals():347 raise ImportError('refresh token error, adal library not imported')348 349 tenant = config['tenant-id']350 authority = 'https://login.microsoftonline.com/{}'.format(tenant)351 context = adal.AuthenticationContext(352 authority, validate_authority=True, api_version='1.0'353 )354 refresh_token = config['refresh-token']355 client_id = config['client-id']356 apiserver_id = '00000002-0000-0000-c000-000000000000'357 try:358 apiserver_id = config['apiserver-id']359 except ConfigException:360 # We've already set a default above361 pass362 token_response = context.acquire_token_with_refresh_token(363 refresh_token, client_id, apiserver_id)364 365 provider = self._user['auth-provider']['config']366 provider.value['access-token'] = token_response['accessToken']367 provider.value['expires-on'] = token_response['expiresOn']368 if self._config_persister:369 self._config_persister()370 371 def _load_gcp_token(self, provider):372 if (('config' not in provider) or373 ('access-token' not in provider['config']) or374 ('expiry' in provider['config'] and375 _is_expired(provider['config']['expiry']))):376 # token is not available or expired, refresh it377 self._refresh_gcp_token()378 379 self.token = "Bearer %s" % provider['config']['access-token']380 if 'expiry' in provider['config']:381 self.expiry = parse_rfc3339(provider['config']['expiry'])382 return self.token383 384 def _refresh_gcp_token(self):385 if 'config' not in self._user['auth-provider']:386 self._user['auth-provider'].value['config'] = {}387 provider = self._user['auth-provider']['config']388 credentials = self._get_google_credentials()389 provider.value['access-token'] = credentials.token390 provider.value['expiry'] = format_rfc3339(credentials.expiry)391 if self._config_persister:392 self._config_persister()393 394 def _load_oid_token(self, provider):395 if 'config' not in provider:396 return397 398 reserved_characters = frozenset(["=", "+", "/"])399 token = provider['config']['id-token']400 401 if any(char in token for char in reserved_characters):402 # Invalid jwt, as it contains url-unsafe chars403 return404 405 parts = token.split('.')406 if len(parts) != 3: # Not a valid JWT407 return408 409 padding = (4 - len(parts[1]) % 4) * '='410 if len(padding) == 3:411 # According to spec, 3 padding characters cannot occur412 # in a valid jwt413 # https://tools.ietf.org/html/rfc7515#appendix-C414 return415 416 if PY3:417 jwt_attributes = json.loads(418 base64.urlsafe_b64decode(parts[1] + padding).decode('utf-8')419 )420 else:421 jwt_attributes = json.loads(422 base64.b64decode(parts[1] + padding)423 )424 425 expire = jwt_attributes.get('exp')426 427 if ((expire is not None) and428 (_is_expired(datetime.datetime.fromtimestamp(expire,429 tz=UTC)))):430 self._refresh_oidc(provider)431 432 if self._config_persister:433 self._config_persister()434 435 self.token = "Bearer %s" % provider['config']['id-token']436 437 return self.token438 439 def _refresh_oidc(self, provider):440 config = Configuration()441 442 if 'idp-certificate-authority-data' in provider['config']:443 ca_cert = tempfile.NamedTemporaryFile(delete=True)444 445 if PY3:446 cert = base64.b64decode(447 provider['config']['idp-certificate-authority-data']448 ).decode('utf-8')449 else:450 cert = base64.b64decode(451 provider['config']['idp-certificate-authority-data'] + "=="452 )453 454 with open(ca_cert.name, 'w') as fh:455 fh.write(cert)456 457 config.ssl_ca_cert = ca_cert.name458 459 elif 'idp-certificate-authority' in provider['config']:460 config.ssl_ca_cert = provider['config']['idp-certificate-authority']461 462 else:463 config.verify_ssl = False464 465 client = ApiClient(configuration=config)466 467 response = client.request(468 method="GET",469 url="%s/.well-known/openid-configuration"470 % provider['config']['idp-issuer-url']471 )472 473 if response.status != 200:474 return475 476 response = json.loads(response.data)477 478 request = OAuth2Session(479 client_id=provider['config']['client-id'],480 token=provider['config']['refresh-token'],481 auto_refresh_kwargs={482 'client_id': provider['config']['client-id'],483 'client_secret': provider['config']['client-secret']484 },485 auto_refresh_url=response['token_endpoint']486 )487 488 try:489 refresh = request.refresh_token(490 token_url=response['token_endpoint'],491 refresh_token=provider['config']['refresh-token'],492 auth=(provider['config']['client-id'],493 provider['config']['client-secret']),494 verify=config.ssl_ca_cert if config.verify_ssl else None495 )496 except oauthlib.oauth2.rfc6749.errors.InvalidClientIdError:497 return498 499 provider['config'].value['id-token'] = refresh['id_token']500 provider['config'].value['refresh-token'] = refresh['refresh_token']501 502 def _load_from_exec_plugin(self):503 if 'exec' not in self._user:504 return505 try:506 base_path = self._get_base_path(self._cluster.path)507 status = ExecProvider(self._user['exec'], base_path, self._cluster).run()508 if 'token' in status:509 self.token = "Bearer %s" % status['token']510 elif 'clientCertificateData' in status:511 # https://kubernetes.io/docs/reference/access-authn-authz/authentication/#input-and-output-formats512 # Plugin has provided certificates instead of a token.513 if 'clientKeyData' not in status:514 logging.error('exec: missing clientKeyData field in '515 'plugin output')516 return None517 self.cert_file = FileOrData(518 status, None,519 data_key_name='clientCertificateData',520 file_base_path=base_path,521 base64_file_content=False,522 temp_file_path=self._temp_file_path).as_file()523 self.key_file = FileOrData(524 status, None,525 data_key_name='clientKeyData',526 file_base_path=base_path,527 base64_file_content=False,528 temp_file_path=self._temp_file_path).as_file()529 else:530 logging.error('exec: missing token or clientCertificateData '531 'field in plugin output')532 return None533 if 'expirationTimestamp' in status:534 self.expiry = parse_rfc3339(status['expirationTimestamp'])535 return True536 except Exception as e:537 logging.error(str(e))538 539 def _load_user_token(self):540 base_path = self._get_base_path(self._user.path)541 token = FileOrData(542 self._user, 'tokenFile', 'token',543 file_base_path=base_path,544 base64_file_content=False,545 temp_file_path=self._temp_file_path).as_data()546 if token:547 self.token = "Bearer %s" % token548 return True549 550 def _load_user_pass_token(self):551 if 'username' in self._user and 'password' in self._user:552 self.token = urllib3.util.make_headers(553 basic_auth=(self._user['username'] + ':' +554 self._user['password'])).get('authorization')555 return True556 557 def _get_base_path(self, config_path):558 if self._config_base_path is not None:559 return self._config_base_path560 if config_path is not None:561 return os.path.abspath(os.path.dirname(config_path))562 return ""563 564 def _load_cluster_info(self):565 if 'server' in self._cluster:566 self.host = self._cluster['server'].rstrip('/')567 if self.host.startswith("https"):568 base_path = self._get_base_path(self._cluster.path)569 self.ssl_ca_cert = FileOrData(570 self._cluster, 'certificate-authority',571 file_base_path=base_path,572 temp_file_path=self._temp_file_path).as_file()573 if 'cert_file' not in self.__dict__:574 # cert_file could have been provided by575 # _load_from_exec_plugin; only load from the _user576 # section if we need it.577 self.cert_file = FileOrData(578 self._user, 'client-certificate',579 file_base_path=base_path,580 temp_file_path=self._temp_file_path).as_file()581 self.key_file = FileOrData(582 self._user, 'client-key',583 file_base_path=base_path,584 temp_file_path=self._temp_file_path).as_file()585 if 'insecure-skip-tls-verify' in self._cluster:586 self.verify_ssl = not self._cluster['insecure-skip-tls-verify']587 if 'tls-server-name' in self._cluster:588 self.tls_server_name = self._cluster['tls-server-name']589 590 def _set_config(self, client_configuration):591 if 'token' in self.__dict__:592 client_configuration.api_key['authorization'] = self.token593 594 def _refresh_api_key(client_configuration):595 if ('expiry' in self.__dict__ and _is_expired(self.expiry)):596 self._load_authentication()597 self._set_config(client_configuration)598 client_configuration.refresh_api_key_hook = _refresh_api_key599 # copy these keys directly from self to configuration object600 keys = ['host', 'ssl_ca_cert', 'cert_file', 'key_file', 'verify_ssl','tls_server_name']601 for key in keys:602 if key in self.__dict__:603 setattr(client_configuration, key, getattr(self, key))604 605 def load_and_set(self, client_configuration):606 self._load_authentication()607 self._load_cluster_info()608 self._set_config(client_configuration)609 610 def list_contexts(self):611 return [context.value for context in self._config['contexts']]612 613 @property614 def current_context(self):615 return self._current_context.value616 617 618class ConfigNode(object):619 """Remembers each config key's path and construct a relevant exception620 message in case of missing keys. The assumption is all access keys are621 present in a well-formed kube-config."""622 623 def __init__(self, name, value, path=None):624 self.name = name625 self.value = value626 self.path = path627 628 def __contains__(self, key):629 return key in self.value630 631 def __len__(self):632 return len(self.value)633 634 def safe_get(self, key):635 if (isinstance(self.value, list) and isinstance(key, int) or636 key in self.value):637 return self.value[key]638 639 def __getitem__(self, key):640 v = self.safe_get(key)641 if v is None:642 raise ConfigException(643 'Invalid kube-config file. Expected key %s in %s'644 % (key, self.name))645 if isinstance(v, dict) or isinstance(v, list):646 return ConfigNode('%s/%s' % (self.name, key), v, self.path)647 else:648 return v649 650 def get_with_name(self, name, safe=False):651 if not isinstance(self.value, list):652 raise ConfigException(653 'Invalid kube-config file. Expected %s to be a list'654 % self.name)655 result = None656 for v in self.value:657 if 'name' not in v:658 raise ConfigException(659 'Invalid kube-config file. '660 'Expected all values in %s list to have \'name\' key'661 % self.name)662 if v['name'] == name:663 if result is None:664 result = v665 else:666 raise ConfigException(667 'Invalid kube-config file. '668 'Expected only one object with name %s in %s list'669 % (name, self.name))670 if result is not None:671 if isinstance(result, ConfigNode):672 return result673 else:674 return ConfigNode(675 '%s[name=%s]' %676 (self.name, name), result, self.path)677 if safe:678 return None679 raise ConfigException(680 'Invalid kube-config file. '681 'Expected object with name %s in %s list' % (name, self.name))682 683 684class KubeConfigMerger:685 686 """Reads and merges configuration from one or more kube-config's.687 The property `config` can be passed to the KubeConfigLoader as config_dict.688 689 It uses a path attribute from ConfigNode to store the path to kubeconfig.690 This path is required to load certs from relative paths.691 692 A method `save_changes` updates changed kubeconfig's (it compares current693 state of dicts with).694 """695 696 def __init__(self, paths):697 self.paths = []698 self.config_files = {}699 self.config_merged = None700 if hasattr(paths, 'read'):701 self._load_config_from_file_like_object(paths)702 else:703 self._load_config_from_file_path(paths)704 705 @property706 def config(self):707 return self.config_merged708 709 def _load_config_from_file_like_object(self, string):710 if hasattr(string, 'getvalue'):711 config = yaml.safe_load(string.getvalue())712 else:713 config = yaml.safe_load(string.read())714 715 if config is None:716 raise ConfigException(717 'Invalid kube-config.')718 if self.config_merged is None:719 self.config_merged = copy.deepcopy(config)720 # doesn't need to do any further merging721 722 def _load_config_from_file_path(self, string):723 for path in string.split(ENV_KUBECONFIG_PATH_SEPARATOR):724 if path:725 path = os.path.expanduser(path)726 if os.path.exists(path):727 self.paths.append(path)728 self.load_config(path)729 self.config_saved = copy.deepcopy(self.config_files)730 731 def load_config(self, path):732 with open(path) as f:733 config = yaml.safe_load(f)734 735 if config is None:736 raise ConfigException(737 'Invalid kube-config. '738 '%s file is empty' % path)739 740 if self.config_merged is None:741 config_merged = copy.deepcopy(config)742 for item in ('clusters', 'contexts', 'users'):743 config_merged[item] = []744 self.config_merged = ConfigNode(path, config_merged, path)745 for item in ('clusters', 'contexts', 'users'):746 self._merge(item, config.get(item, []) or [], path)747 748 if 'current-context' in config:749 self.config_merged.value['current-context'] = config['current-context']750 751 self.config_files[path] = config752 753 def _merge(self, item, add_cfg, path):754 for new_item in add_cfg:755 for exists in self.config_merged.value[item]:756 if exists['name'] == new_item['name']:757 break758 else:759 self.config_merged.value[item].append(ConfigNode(760 '{}/{}'.format(path, new_item), new_item, path))761 762 def save_changes(self):763 for path in self.paths:764 if self.config_saved[path] != self.config_files[path]:765 self.save_config(path)766 self.config_saved = copy.deepcopy(self.config_files)767 768 def save_config(self, path):769 with open(path, 'w') as f:770 yaml.safe_dump(self.config_files[path], f,771 default_flow_style=False)772 773 774def _get_kube_config_loader_for_yaml_file(775 filename, persist_config=False, **kwargs):776 return _get_kube_config_loader(777 filename=filename,778 persist_config=persist_config,779 **kwargs)780 781 782def _get_kube_config_loader(783 filename=None,784 config_dict=None,785 persist_config=False,786 **kwargs):787 if config_dict is None:788 kcfg = KubeConfigMerger(filename)789 if persist_config and 'config_persister' not in kwargs:790 kwargs['config_persister'] = kcfg.save_changes791 792 if kcfg.config is None:793 raise ConfigException(794 'Invalid kube-config file. '795 'No configuration found.')796 return KubeConfigLoader(797 config_dict=kcfg.config,798 config_base_path=None,799 **kwargs)800 else:801 return KubeConfigLoader(802 config_dict=config_dict,803 config_base_path=None,804 **kwargs)805 806 807def list_kube_config_contexts(config_file=None):808 809 if config_file is None:810 config_file = KUBE_CONFIG_DEFAULT_LOCATION811 812 loader = _get_kube_config_loader(filename=config_file)813 return loader.list_contexts(), loader.current_context814 815 816def load_kube_config(config_file=None, context=None,817 client_configuration=None,818 persist_config=True,819 temp_file_path=None):820 """Loads authentication and cluster information from kube-config file821 and stores them in kubernetes.client.configuration.822 823 :param config_file: Name of the kube-config file.824 :param context: set the active context. If is set to None, current_context825 from config file will be used.826 :param client_configuration: The kubernetes.client.Configuration to827 set configs to.828 :param persist_config: If True, config file will be updated when changed829 (e.g GCP token refresh).830 :param temp_file_path: store temp files path.831 """832 833 if config_file is None:834 config_file = KUBE_CONFIG_DEFAULT_LOCATION835 836 loader = _get_kube_config_loader(837 filename=config_file, active_context=context,838 persist_config=persist_config,839 temp_file_path=temp_file_path)840 841 if client_configuration is None:842 config = type.__call__(Configuration)843 loader.load_and_set(config)844 Configuration.set_default(config)845 else:846 loader.load_and_set(client_configuration)847 848 849def load_kube_config_from_dict(config_dict, context=None,850 client_configuration=None,851 persist_config=True,852 temp_file_path=None):853 """Loads authentication and cluster information from config_dict file854 and stores them in kubernetes.client.configuration.855 856 :param config_dict: Takes the config file as a dict.857 :param context: set the active context. If is set to None, current_context858 from config file will be used.859 :param client_configuration: The kubernetes.client.Configuration to860 set configs to.861 :param persist_config: If True, config file will be updated when changed862 (e.g GCP token refresh).863 :param temp_file_path: store temp files path.864 """865 if config_dict is None:866 raise ConfigException(867 'Invalid kube-config dict. '868 'No configuration found.')869 870 loader = _get_kube_config_loader(871 config_dict=config_dict, active_context=context,872 persist_config=persist_config,873 temp_file_path=temp_file_path)874 875 if client_configuration is None:876 config = type.__call__(Configuration)877 loader.load_and_set(config)878 Configuration.set_default(config)879 else:880 loader.load_and_set(client_configuration)881 882 883def new_client_from_config(884 config_file=None,885 context=None,886 persist_config=True,887 client_configuration=None):888 """889 Loads configuration the same as load_kube_config but returns an ApiClient890 to be used with any API object. This will allow the caller to concurrently891 talk with multiple clusters.892 """893 if client_configuration is None:894 client_configuration = type.__call__(Configuration)895 load_kube_config(config_file=config_file, context=context,896 client_configuration=client_configuration,897 persist_config=persist_config)898 return ApiClient(configuration=client_configuration)899 900 901def new_client_from_config_dict(902 config_dict=None,903 context=None,904 persist_config=True,905 temp_file_path=None,906 client_configuration=None):907 """908 Loads configuration the same as load_kube_config_from_dict but returns an ApiClient909 to be used with any API object. This will allow the caller to concurrently910 talk with multiple clusters.911 """912 if client_configuration is None:913 client_configuration = type.__call__(Configuration)914 load_kube_config_from_dict(config_dict=config_dict, context=context,915 client_configuration=client_configuration,916 persist_config=persist_config,917 temp_file_path=temp_file_path)918 return ApiClient(configuration=client_configuration)919 