o
    'Th+\                     @   s0  d dl Z d dlmZmZ d dlZd dlZd dlZd dlZd dlZd dl	Z	d dl
Z
d dlZd dlZd dlmZmZmZ d dlmZmZ d dlmZmZmZmZ d dlmZ d dlmZ d ZdZd	Zd
ZdZ G dd dZ!G dd dZ"e#ddgZ$G dd dZ%dddZ&dddZ'dd Z(dd Z)dd Z*dS )    N)ApiExceptionApiValueError)	urlencodeurlparse
urlunparse)StringIOBytesIO)	WebSocketABNFenableTrace"WebSocketConnectionClosedException)urlsafe_b64decode)should_bypass_proxies            c                   @   s   e Zd Zdd Zdd ZdS )
_IgnoredIOc                 C   s   d S N )self_xr   r   \/home/air/segue/gemini/back/venv/lib/python3.10/site-packages/kubernetes/stream/ws_client.pywrite,   s   z_IgnoredIO.writec                 C   s   t d)Nz_Tried to read_all() from a WSClient configured to not capture. Did you mean `capture_all=True`?)	TypeErrorr   r   r   r   getvalue/   s   z_IgnoredIO.getvalueN)__name__
__module____qualname__r   r   r   r   r   r   r   +   s    r   c                   @   s   e Zd Zd(ddZd)ddZd)ddZd*d
dZdd Zd)ddZd*ddZ	d*ddZ
d)ddZd*ddZd*ddZdd Zdd Zdd Zd)d d!Zd*d"d#Zed$d% Zd&d' Zd	S )+WSClientFc                 C   sb   d| _ i | _|| _| jsdnd| _|r| jst nt | _nt | _t|||| _	d| _ d| _
dS )a2  A websocket client with support for channels.

            Exec command uses different channels for different streams. for
        example, 0 is stdin, 1 is stdout and 2 is stderr. Some other API calls
        like port forwarding can forward different pods' streams to different
        channels.
        F
   
TN)
_connected	_channelsbinarynewliner   r   _allr   create_websocketsock_returncode)r   configurationurlheaderscapture_allr%   r   r   r   __init__4   s   
zWSClient.__init__r   c                 C   s$   | j |d || jv r| j| S dS )zLPeek a channel and return part of the input,
        empty string otherwise.timeout )updater$   )r   channelr1   r   r   r   peek_channelH   s   

zWSClient.peek_channelc                 C   s8   || j vr| ||}n| j | }|| j v r| j |= |S )zRead data from a channel.)r$   r5   )r   r4   r1   retr   r   r   read_channelP   s   


zWSClient.read_channelNc                 C   s   |du rt d}t }|  rdt | |k rf|| jv rJ| j| }| j|v rJ|| j}|d| }||d d }|rD|| j|< |S | j|= |S | j|t  | d |  rht | |k sdS dS dS dS )zRead a line from a channel.Ninfr   r0   )floattimeis_openr$   r&   findr3   )r   r4   r1   startdataindexr6   r   r   r   readline_channelZ   s"   



(zWSClient.readline_channelc                 C   sX   t jo	t|t jk}|rtjntj}t|}|rt |d}|| }| jj	||d dS )zWrite data to a channel.ascii)opcodeN)
sixPY3typebinary_typer
   OPCODE_BINARYOPCODE_TEXTchrr)   send)r   r4   r>   r%   rB   channel_prefixpayloadr   r   r   write_channelm   s   zWSClient.write_channelc                 C      | j t|dS )z$Same as peek_channel with channel=1.r0   )r5   STDOUT_CHANNELr   r1   r   r   r   peek_stdoutz      zWSClient.peek_stdoutc                 C   rN   )z$Same as read_channel with channel=1.r0   )r7   rO   rP   r   r   r   read_stdout~   rR   zWSClient.read_stdoutc                 C   rN   )z(Same as readline_channel with channel=1.r0   )r@   rO   rP   r   r   r   readline_stdout   rR   zWSClient.readline_stdoutc                 C   rN   )z$Same as peek_channel with channel=2.r0   )r5   STDERR_CHANNELrP   r   r   r   peek_stderr   rR   zWSClient.peek_stderrc                 C   rN   )z$Same as read_channel with channel=2.r0   )r7   rU   rP   r   r   r   read_stderr   rR   zWSClient.read_stderrc                 C   rN   )z(Same as readline_channel with channel=2.r0   )r@   rU   rP   r   r   r   readline_stderr   rR   zWSClient.readline_stderrc                 C   s    | j  }| j  | _ i | _|S )a  Return buffered data received on stdout and stderr channels.
        This is useful for non-interactive call where a set of command passed
        to the API call and their result is needed after the call is concluded.
        Should be called after run_forever() or update()

        TODO: Maybe we can process this and return a more meaningful map with
        channels mapped for each input.
        )r'   r   	__class__r$   )r   outr   r   r   read_all   s   
	zWSClient.read_allc                 C   s   | j S )z&True if the connection is still alive.)r#   r   r   r   r   r;      s   zWSClient.is_openc                 C   s   |  t| dS )z)The same as write_channel with channel=0.N)rM   STDIN_CHANNEL)r   r>   r   r   r   write_stdin   s   zWSClient.write_stdinc           	      C   sp  |   sdS | jjsd| _dS ttdr6t }|| jjtj |dur)|d9 }||}|	| jj nt| jjfdd|\}}}|r| j
d\}}|tjkrXd| _dS |tjksb|tjkr|j}tjrq| jsq|dd}t|d	kr|d
 }tjr| jst|}|d	d }|r|ttfv r| j| || jvr|| j|< dS | j|  |7  < dS dS dS dS dS )z@Update channel buffers with at most one complete frame of input.NFpolli  r   Tzutf-8replacer   r   )r;   r)   	connectedr#   hasattrselectr^   registerPOLLIN
unregisterrecv_data_framer
   OPCODE_CLOSErG   rH   r>   rC   rD   r%   decodelenordrO   rU   r'   r   r$   )	r   r1   r^   r_op_codeframer>   r4   r   r   r   r3      sN   




zWSClient.updatec                 C   s   |r2t   }|  r,t   | |k r0| j|t    | d |  r.t   | |k sdS dS dS dS |  rB| jdd |  s6dS dS )zfWait till connection is closed or timeout reached. Buffer any input
        received during this time.r0   N)r:   r;   r3   )r   r1   r=   r   r   r   run_forever   s   (zWSClient.run_foreverc                 C   sb   |   rdS | jdu r.| t}t|}|d dkr!d| _| jS t|d d d d | _| jS )zi
        The return code, A None value indicates that the process hasn't
        terminated yet.
        NstatusSuccessr   detailscausesmessage)r;   r*   r7   ERROR_CHANNELyaml	safe_loadint)r   errr   r   r   
returncode   s   


zWSClient.returncodec                 K   s&   d| _ | jr| jjdi | dS dS )z-
        close websocket connection.
        FNr   )r#   r)   close)r   kwargsr   r   r   r{      s   zWSClient.close)F)r   r   )r   r   r   r/   r5   r7   r@   rM   rQ   rS   rT   rV   rW   rX   r[   r;   r]   r3   ro   propertyrz   r{   r   r   r   r   r    3   s(    












4

r    
WSResponser>   c                   @   sN   e Zd Zdd Zedd Zdd Zdd Zd	d
 ZG dd dZ	dd Z
dS )PortForwardc                 C   sf   || _ i | _t|D ]\}}| ||| j|< q
tjdddd |D  | jd}d|_|	  dS )a&  A websocket client with support for port forwarding.

        Port Forward command sends on 2 channels per port, a read/write
        data channel and a read only error channel. Both channels are sent an
        initial frame containing the port number that channel is associated with.
        z!Kubernetes port forward proxy: %sz, c                 S   s   g | ]}t |qS r   )str).0portr   r   r   
<listcomp>  s    z(PortForward.__init__.<locals>.<listcomp>)nametargetTN)
	websocketlocal_ports	enumerate_Port	threadingThreadjoin_proxydaemonr=   )r   r   portsixport_numberproxyr   r   r   r/     s   zPortForward.__init__c                 C   s   | j jS r   )r   r`   r   r   r   r   r`     s   zPortForward.connectedc                 C      || j vr	td| j | jS NzInvalid port number)r   
ValueErrorsocketr   r   r   r   r   r        
zPortForward.socketc                 C   r   r   )r   r   errorr   r   r   r   r   $  r   zPortForward.errorc                 C   s   | j  D ]}|j  qd S r   )r   valuesr   r{   )r   r   r   r   r   r{   )  s   zPortForward.closec                   @   s"   e Zd Zdd ZG dd dZdS )zPortForward._Portc                 C   s@   || _ t|d | _t \}| _| || _d| _d | _	d S )Nr       )
r   rC   int2byter4   r   
socketpairpython_Socketr>   r   )r   r   r   sr   r   r   r/   .  s   
zPortForward._Port.__init__c                   @   s$   e Zd Zdd Zdd Zdd ZdS )zPortForward._Port._Socketc                 C   s
   || _ d S r   )_socket)r   r   r   r   r   r/   D  s   
z"PortForward._Port._Socket.__init__c                 C   s   t | j|S r   )getattrr   )r   r   r   r   r   __getattr__G  s   z%PortForward._Port._Socket.__getattr__c                 C   s,   |t jkr|t jkrd S | j||| d S r   )r   IPPROTO_TCPTCP_NODELAYr   
setsockopt)r   leveloptnamevaluer   r   r   r   J  s   z$PortForward._Port._Socket.setsockoptN)r   r   r   r/   r   r   r   r   r   r   r   C  s    r   N)r   r   r   r/   r   r   r   r   r   r   -  s    r   c              	   C   s  g }g }i }| j  D ]!}|| |d || |d |jd |||j< qd}	 g }g }| jjrF|| j |rF|| j d}| j  D ]0}|j dkr}| jjrl||j |jri||j d}qM|jrx||j d}qM|j	  qM|r| jjr|s| j	  d S t

||g \}	}
}|	D ]}|| jkryd}|rxz
| jd\}}W n ty   | j  D ]}|j	  qY  d S w |tjkrS|jstdt|j}|t|krtd| || }|| r |d r|jd u rd|_| j|jd	d   7  _|j	  nQ| j|jd	d  7  _nDt|jd
kr,tdt|jd	d t|jdd
 d  }||jkrNtd| d||< n|tjtjtjfvrdtd| t| jjtjrt| jj svd}|sq|| }|j dkr|jd}|r|t|j | tj! 7 }q|j	  q|
D ]3}|| jkr| jj"|}||d  }q|| }|j dkr|j"|j}|j|d  |_qq0)NFTr   zUnexpected frame data sizezUnexpected channel number: %sr   r2   r   r   z*Unexpected initial channel frame data size   z3Unexpected port number in initial channel frame: %szUnexpected websocket opcode: %si   )#r   r   appendr   setblockingr   r`   filenor>   r{   rb   rf   r   r
   rG   RuntimeErrorrC   byte2intri   r   rh   r   OPCODE_PINGOPCODE_PONGrg   
isinstancer)   ssl	SSLSocketpendingrecvcreate_framer4   formatrJ   )r   channel_portschannel_initializedr   r   kubernetes_datarlistwlistlocal_all_closedrk   wrl   r)   r   rB   rn   r4   r   r>   sentr   r   r   r   R  s   









,
 &
zPortForward._proxyN)r   r   r   r/   r}   r`   r   r   r{   r   r   r   r   r   r   r     s    
%r   c                 C   s   t | }t|}|jdkrd|d< n	|jdkrd|d< |rKg }|D ]!\}}|dkr;t|tr;|D ]	}|||f q0q!|||f q!|rKt||d< t|S )Nhttpwsr   httpswsscommandr   )r   listschemer   r   r   r   )r,   query_params
parsed_urlpartsquerykeyr   r   r   r   r   get_websocket_url  s"   


r   c                 C   s  t d g }|rd|v r|d|d   |r%d|v r%|d|d   n|d |drH| jrHtj| jp:t d}| j	d urG| j	|d	< nd
tj
i}| jrU| j|d< | jr]| j|d< | jre| j|d< t|dd}d|i}| jsu| jr|t|| ||}|j|fi | |S )NFauthorizationzauthorization: %szsec-websocket-protocolzsec-websocket-protocol: %sz)sec-websocket-protocol: v4.channel.k8s.iozwss://)	cert_reqsca_certscheck_hostnamer   certfilekeyfileserver_hostname)ssloptskip_utf8_validationheader)r   r   
startswith
verify_sslr   CERT_REQUIREDssl_ca_certcertifiwhereassert_hostname	CERT_NONE	cert_filekey_filetls_server_namer	   r   proxy_headerswebsocket_proxycareconnect)r+   r,   r-   r   ssl_optsr   connect_optr   r   r   r(     s<   






r(   c           	      C   s   |j r| d|j di |jr t|j}| |j|jd |jrR|j D ])\}}|dkrQ|	drQ| d }t
| d}| d|d	 |d fi q(| S )
zc An internal function to be called in api-client when a websocket
        create is requested.
    http_no_proxy,)http_proxy_hosthttp_proxy_portzproxy-authorizationBasicr   :http_proxy_authr   )no_proxyr3   splitr   r   hostnamer   r   itemsr   r   rh   )	r   r+   r,   r-   	proxy_urlr   r   b64valueauthr   r   r   r     s   
r   c              
   K   s   t ||d}|d}|dd}|dd}|dd}|dd	}z)t| ||||d
}	|s4|	W S |	j|d |	 }
|rEt|
W S tdd|
 W S  ttt	fye } zt
dt|dd}~ww )zAn internal function to be called in api-client when a websocket
    connection is required. method, url, and kwargs are the parameters of
    apiClient.request method.r   r-   _request_timeout<   _preload_contentTr.   r%   F)r%   r0   z%sr2   r   rp   reasonN)r   getr    ro   r[   r~   r   	ExceptionKeyboardInterrupt
SystemExitr   r   )r+   _methodr,   r|   r-   r   r   r.   r%   clientaller   r   r   websocket_call	  s&   

r  c              
   K   s  | d}g }|D ]D\}}|dkrM|dD ]6}zt|}	W n ty+   td| w d|	  k r6dk s=n td| |	|v rGtd| ||	 qq	|sTtd	t||}| d
}
zt| ||
}t||W S  t	t
tfy } ztdt|dd}~ww )zAn internal function to be called in api-client when a websocket
    connection is required for port forwarding. args and kwargs are the
    parameters of apiClient.request method.r   r   r   zInvalid port number: %sr   i   z+Port number must be between 0 and 65536: %szDuplicate port numbers: %sz"Missing required parameter `ports`r-   r   N)r   r   rx   r   r   r   r   r(   r   r   r   r   r   r   )r+   r   r,   r|   r   r   paramr   r   r   r-   r   r  r   r   r   portforward_call"  s6   


r  r   )+syskubernetes.client.restr   r   r   collectionsrb   r   r   r   r:   rC   rv   six.moves.urllib.parser   r   r   r   r   r   r	   r
   r   r   base64r   requests.utilsr   r\   rO   rU   ru   RESIZE_CHANNELr   r    
namedtupler~   r   r   r(   r   r  r  r   r   r   r   <module>   s@    O 
5
*