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, )
async def post_event(*, obj=None, ref=None, type, reason, message=''): """ Issue an event for the object. This is where they can also be accumulated, aggregated, grouped, and where the rate-limits should be maintained. It can (and should) be done by the client library, as it is done in the Go client. """ # Object reference - similar to the owner reference, but different. if obj is not None and ref is not None: raise TypeError( "Only one of obj= and ref= is allowed for a posted event. Got both." ) if obj is None and ref is None: raise TypeError( "One of obj= and ref= is required for a posted event. Got none.") if ref is None: ref = hierarchies.build_object_reference(obj) now = datetime.datetime.utcnow() # See #164. For cluster-scoped objects, use the current namespace from the current context. # It could be "default", but in some systems, we are limited to one specific namespace only. namespace = ref.get('namespace') or auth.get_pykube_cfg().namespace if not ref.get('namespace'): ref = dict(ref, namespace=namespace) # Prevent a common case of event posting errors but shortening the message. if len(message) > MAX_MESSAGE_LENGTH: infix = CUT_MESSAGE_INFIX prefix = message[:MAX_MESSAGE_LENGTH // 2 - (len(infix) // 2)] suffix = message[-MAX_MESSAGE_LENGTH // 2 + (len(infix) - len(infix) // 2):] message = f'{prefix}{infix}{suffix}' body = { 'metadata': { 'namespace': namespace, 'generateName': 'kopf-event-', }, 'action': 'Action?', 'type': type, 'reason': reason, 'message': message, 'reportingComponent': 'kopf', 'reportingInstance': 'dev', 'source': { 'component': 'kopf' }, # used in the "From" column in `kubectl describe`. 'involvedObject': ref, 'firstTimestamp': now.isoformat() + 'Z', # '2019-01-28T18:25:03.000000Z' -- seen in `kubectl describe ...` 'lastTimestamp': now.isoformat() + 'Z', # '2019-01-28T18:25:03.000000Z' - seen in `kubectl get events` 'eventTime': now.isoformat() + 'Z', # '2019-01-28T18:25:03.000000Z' } try: api = auth.get_pykube_api() obj = pykube.Event(api, body) loop = asyncio.get_running_loop() await loop.run_in_executor(config.WorkersConfig.get_syn_executor(), obj.create) except (requests.exceptions.HTTPError, pykube.exceptions.HTTPError) as e: # Events are helpful but auxiliary, they should not fail the handling cycle. # Yet we want to notice that something went wrong (in logs). logger.warning( "Failed to post an event. Ignoring and continuing. " f"Error: {e!r}. " f"Event: type={type!r}, reason={reason!r}, message={message!r}.")