def login_via_pykube( *args: Any, logger: Union[logging.Logger, logging.LoggerAdapter], **kwargs: Any, ) -> Optional[credentials.ConnectionInfo]: try: import pykube except ImportError: return None # Read the pykube config either way for later interpretation. # DEPRECATED: Previously, in some cases, get_pykube_cfg() was monkey-patched # to inject custom authentication methods. Support these hacks if possible. config: pykube.KubeConfig try: with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) config = auth.get_pykube_cfg() logger.debug("Pykube is configured via monkey-patched get_pykube_cfg().") except NotImplementedError: try: config = pykube.KubeConfig.from_service_account() logger.debug("Pykube is configured in cluster with service account.") except FileNotFoundError: try: config = pykube.KubeConfig.from_file() logger.debug("Pykube is configured via kubeconfig file.") except (pykube.PyKubeError, FileNotFoundError): raise credentials.LoginError(f"Cannot authenticate pykube " f"neither in-cluster, nor via kubeconfig.") # We don't know how this token will be retrieved, we just get it afterwards. provider_token = None if config.user.get('auth-provider'): api = pykube.HTTPClient(config) api.get(version='', base='/') # ignore the response status provider_token = config.user.get('auth-provider', {}).get('config', {}).get('access-token') # Interpret the config object for our own minimalistic credentials. ca: Optional[pykube.config.BytesOrFile] = config.cluster.get('certificate-authority') cert: Optional[pykube.config.BytesOrFile] = config.user.get('client-certificate') pkey: Optional[pykube.config.BytesOrFile] = config.user.get('client-key') return credentials.ConnectionInfo( server=config.cluster.get('server'), ca_path=ca.filename() if ca else None, # can be a temporary file insecure=config.cluster.get('insecure-skip-tls-verify'), username=config.user.get('username'), password=config.user.get('password'), token=config.user.get('token') or provider_token, certificate_path=cert.filename() if cert else None, # can be a temporary file private_key_path=pkey.filename() if pkey else None, # can be a temporary file default_namespace=config.namespace, priority=PRIORITY_OF_PYKUBE, )
def login_via_client( *args: Any, logger: Union[logging.Logger, logging.LoggerAdapter], **kwargs: Any, ) -> Optional[credentials.ConnectionInfo]: try: import kubernetes.config except ImportError: return None try: kubernetes.config.load_incluster_config() # cluster env vars logger.debug("Client is configured in cluster with service account.") except kubernetes.config.ConfigException as e1: try: kubernetes.config.load_kube_config() # developer's config files logger.debug("Client is configured via kubeconfig file.") except kubernetes.config.ConfigException as e2: raise credentials.LoginError( f"Cannot authenticate client neither in-cluster, nor via kubeconfig." ) # We do not even try to understand how it works and why. Just load it, and extract the results. # For kubernetes client >= 12.0.0 use the new 'get_default_copy' method if callable( getattr(kubernetes.client.Configuration, 'get_default_copy', None)): config = kubernetes.client.Configuration.get_default_copy() else: config = kubernetes.client.Configuration() # For auth-providers, this method is monkey-patched with the auth-provider's one. # We need the actual auth-provider's token, so we call it instead of accessing api_key. # Other keys (token, tokenFile) also end up being retrieved via this method. header: Optional[str] = config.get_api_key_with_prefix('authorization') parts: Sequence[str] = header.split(' ', 1) if header else [] scheme, token = ((None, None) if len(parts) == 0 else (None, parts[0]) if len(parts) == 1 else (parts[0], parts[1])) # RFC-7235, Appendix C. # Interpret the config object for our own minimalistic credentials. # Note: kubernetes client has no concept of a "current" context's namespace. return credentials.ConnectionInfo( server=config.host, ca_path=config.ssl_ca_cert, # can be a temporary file insecure=not config.verify_ssl, username=config.username or None, # an empty string when not defined password=config.password or None, # an empty string when not defined scheme=scheme, token=token, certificate_path=config.cert_file, # can be a temporary file private_key_path=config.key_file, # can be a temporary file priority=PRIORITY_OF_CLIENT, )