U
    (f=                     @   s`  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	 d dl
mZmZmZmZmZ d dlmZmZ d dlmZ d dlmZ e Zeeddd	ZeeeZG d
d dejZG dd dej Z!dd Z"d\eeef eeef eeeef dddZ#dg feee edddZ$d]ee%e%e%dddZ&eeef ee%dddZ'ddd d!d"d#d$d%d&d'd(d)d*d+d!d,d-d.d/d0d1d2d3d4d5d6gZ(e(fee ed7d8d9Z)ee e%e%d:d;d<Z*ee j d=d>d?Z+eed@dAdBZ,dCdD Z-dEdF Z.dGdHdIdJdKdLZ/eedMdNdOZ0ee ee dPdQdRZ1e%dSdTdUZ2eeedVdWdXZ3ee dYdZd[Z4dS )^    Nwraps)AnyDictListOptionalUnion)
exceptionsmessages)CONFIG_FIELD_ENVVAR_ALLOWLIST)MessagingOperations)namereturnc                 C   s(   | dkrdS |  d}d|d< d|S )z1Replace the name of the root logger from __name__ .Z	ubuntupror   )splitjoin)r   names r   //usr/lib/python3/dist-packages/uaclient/util.pyreplace_top_level_logger_name   s
    
r   c                       s    e Zd ZdZ fddZ  ZS )DatetimeAwareJSONEncoderzBA json.JSONEncoder subclass that writes out isoformat'd datetimes.c                    s    t |tjr| S t |S N)
isinstancedatetimeZ	isoformatsuperdefault)selfo	__class__r   r   r   "   s    z DatetimeAwareJSONEncoder.default)__name__
__module____qualname____doc__r   __classcell__r   r   r   r   r      s   r   c                       s,   e Zd ZdZ fddZedd Z  ZS )DatetimeAwareJSONDecodera,  
    A JSONDecoder that parses some ISO datetime strings to datetime objects.

    Important note: the "some" is because we seem to only be able extend
    Python's json library in a way that lets us convert string values within
    JSON objects (e.g. '{"lastModified": "2019-07-25T14:35:51"}'). Strings
    outside of JSON objects (e.g. '"2019-07-25T14:35:51"') will not be passed
    through our decoder.

    (N.B. This will override any object_hook specified using arguments to it,
    or used in load or loads calls that specify this as the cls.)
    c                    s.   d|kr| d t j|d| ji| d S )Nobject_hook)popr   __init__r'   )r   argskwargsr   r   r   r)   6   s    
z!DatetimeAwareJSONDecoder.__init__c              	   C   sN   |   D ]@\}}t|trzt|}W n tk
r>   |}Y nX || |< q| S r   )itemsr   strparse_rfc3339_date
ValueError)r   keyvalue	new_valuer   r   r   r'   ;   s    


z$DatetimeAwareJSONDecoder.object_hook)r!   r"   r#   r$   r)   staticmethodr'   r%   r   r   r   r   r&   (   s   r&   c                    s    fdd}|S )a  Decorator to retry on exception for retry_sleeps.

    @param retry_sleeps: List of sleep lengths to apply between
       retries. Specifying a list of [0.5, 1] tells subp to retry twice
       on failure; sleeping half a second before the first retry and 1 second
       before the second retry.
    @param exception: The exception class to catch and retry for the provided
       retry_sleeps. Any other exception types will not be caught by the
       decorator.
    c                    s   t   fdd}|S )Nc               
      sl     }z| |W S   k
rd } z2|s.|tdt|t| t|d W 5 d }~X Y qX qd S )Nz%s: Retrying %d more times.r   )copyLOGdebugr-   lentimesleepr(   )r*   r+   Zsleepse)	exceptionfretry_sleepsr   r   	decoratorW   s      z)retry.<locals>.wrapper.<locals>.decoratorr   )r<   r>   r;   r=   )r<   r   wrapperV   s    zretry.<locals>.wrapperr   )r;   r=   r@   r   r?   r   retryJ   s    rA   r   )	orig_dictnew_dictpathr   c           	      C   s   i }|   D ]\}}||t}|s(|n
|d | }t|trp||krft||| |d}|rn|||< qt||< q||krtd|t| |||< q|  D ]\}}|| kr|||< q|S )z<Return a dictionary of delta between orig_dict and new_dict.r   )rD   z'Contract value for '%s' changed to '%s')	r,   getDROPPED_KEYr   dictget_dict_deltasr5   r6   r-   )	rB   rC   rD   Zdeltasr0   r1   r2   Zkey_pathZ	sub_deltar   r   r   rH   j   s2    
  



rH   )msgvalid_choicesr   c                 C   sd   ddl m} | }d}d|ddd |D }||  td }||krTq`|| q4|S )	aG  Interactive prompt message, returning a valid choice from msg.

    Expects a structured msg which designates choices with square brackets []
    around the characters which indicate a valid choice.

    Uppercase and lowercase responses are allowed. Loop on invalid choices.

    :return: Valid response character chosen.
    r   )event_loggerr   z{} is not one of: {}z, c                 S   s   g | ]}|  qS r   )upper).0Zchoicer   r   r   
<listcomp>   s     z"prompt_choices.<locals>.<listcomp>z> )uaclientrK   Zget_event_loggerformatr   infoinputlower)rI   rJ   rK   Zeventr1   Z	error_msgr   r   r   prompt_choices   s    
 
rT   F)rI   
assume_yesr   r   c                 C   s>   |rdS | st j} t|   }|dkr.|S |dkr:dS dS )a  
    Display a confirmation prompt, returning a bool indicating the response

    :param msg: String custom prompt text to emit from input call.
    :param assume_yes: Boolean set True to skip confirmation input and return
        True.
    :param default: Boolean to return when user doesn't enter any text

    This function will only prompt a single time, and defaults to "no" (i.e. it
    returns False).
    Tr   )yZyesF)r
   ZPROMPT_YES_NOrR   rS   strip)rI   rU   r   r1   r   r   r   prompt_for_confirmation   s    rX   )configpath_to_valuer   c                 C   s   | }i }| d}|d }|D ].}||kr.d}t|trF|||}q dS qt|}| dkrfdS | dkrvdS tj|d|dd	S )
aJ  Check if value parameter can be translated into a boolean 'True' value.

    @param config: A config dict representing
                   /etc/ubuntu-advantange/uaclient.conf
    @param path_to_value: The path from where the value parameter was
                          extracted.
    @return: A boolean value indicating if the value paramater corresponds
             to a 'True' boolean value.
    @raises exceptions.UbuntuProError when the value provide by the
            path_to_value parameter can not be translated into either
            a 'False' or 'True' boolean value.
    r   ZfalseFtrueTzboolean string: true or false)rZ   Zexpected_valuer1   N)r   r   rG   rE   r-   rS   r	   ZInvalidBooleanConfigValue)rY   rZ   r1   Zdefault_valuepathsZ
leaf_valuer0   Z	value_strr   r   r   is_config_value_true   s(    

r^   z(Bearer )[^\']+z(\'attach\', \')[^\']+z(\'machineToken\': \')[^\']+z(\'token\': \')[^\']+z((\'X-aws-ec2-metadata-token\': \')[^\']+z*(.*\[PUT\] response.*api/token,.*data: ).*z(https://bearer:)[^\@]+z1(/snap/bin/canonical-livepatch\s+enable\s+)[^\s]+z>(Contract\s+value\s+for\s+'resourceToken'\s+changed\s+to\s+).*z(\'resourceToken\': \')[^\']+z(\'contractToken\': \')[^\']+zF(https://contracts.canonical.com/v1/resources/livepatch\?token=)[^\s]+z(\"identityToken\": \")[^\"]+zT(response:\s+http://metadata/computeMetadata/v1/instance/service-accounts.*data: ).*z(\'userCode\': \')[^\']+z(\'magic_token=)[^\']+z(--registration-key=\")[^\"]+z(--registration-key=\')[^\']+z(--registration-key=)[^ ]+z(--registration-key \")[^\"]+z(--registration-key \')[^\']+z(--registration-key )[^\s]+z(-p \")[^\"]+z(-p \')[^\']+z(-p )[^\s]+)redact_regexsr   c                 C   s    | }|D ]}t |d|}q|S )z4Redact known sensitive information from log content.z\g<1><REDACTED>)resub)logr_   Zredacted_logZredact_regexr   r   r   redact_sensitive_logs  s    rc   )msg_opsrU   r   c                 C   sJ   | sdS | D ]8}t |tr$t| q|\}}||d< |f |s dS qdS )ah  Emit messages to the console for user interaction

    :param msg_op: A list of strings or tuples. Any string items are printed.
        Any tuples will contain a callable and a dict of args to pass to the
        callable. Callables are expected to return True on success and
        False upon failure.

    :return: True upon success, False on failure.
    TrU   F)r   r-   print)rd   rU   Zmsg_opZfunctorr*   r   r   r   handle_message_operations  s    


rf   )dt_strr   c                 C   sD   t dd| }t dd|}|dd}t dd|}tj|d	S )
aT  
    Parse a datestring in rfc3339 format. Originally written for compatibility
    with golang's time.MarshalJSON function. Also handles output of pythons
    isoformat datetime method.

    This drops subseconds.

    :param dt_str: a date string in rfc3339 format

    :return: datetime.datetime object of time represented by dt_str
    z(\d{2}:\d{2}:\d{2})\.\d+z\g<1>z(\d{2}:\d{2}:\d{2})$z\g<1>ZZz+00:00z(-|\+)(\d{2}):(\d{2})$z\g<1>\g<2>\g<3>z%Y-%m-%dT%H:%M:%S%z)r`   ra   replacer   strptime)rg   Zdt_str_without_subsecondsZdt_str_with_zZdt_str_without_zZdt_str_with_pythonish_tzr   r   r   r.   &  s(        	   r.   )messager   c                 C   s`   t jjdksdt jj kr\| dd} | tjd d} | tjd d} | dd	 } | S )	z}
    Verify if the system can output unicode characters and if not,
    remove those characters from the message string.
    NzUTF-8u   —- r   asciiignore)
sysstdoutencodingrL   ri   r
   ZOKGREEN_CHECKZFAIL_Xencodedecode)rk   r   r   r   handle_unicode_characters]  s    
ru   c                   C   s   dd t j D S )Nc                 S   s4   i | ],\}}|  tks*|d s*|dkr||qS )ZUA_FEATURESZUA_CONFIG_FILE)rS   r   
startswith)rM   kvr   r   r   
<dictcomp>x  s   
 z'get_pro_environment.<locals>.<dictcomp>)osenvironr,   r   r   r   r   get_pro_environmentw  s    r|   c                 C   s   dd }|  D ]\}}| |}t|trFt|trFt| | | qt|trt|trt|rt|d tr|| | ||d q|| |< q|| |< qdS )a  Merge the contents of overlay dict into base_dict not only on top-level
    keys, but on all on the depths of the overlay_dict object. For example,
    using these values as entries for the function:

    base_dict = {"a": 1, "b": {"c": 2, "d": 3}}
    overlay_dict = {"b": {"c": 10}}

    Should update base_dict into:

    {"a": 1, "b": {"c": 10, "d": 3}}

    @param base_dict: The dict to be updated
    @param overlay_dict: The dict with information to be added into base_dict
    c           
      S   sx   dddd}g }| |}|D ]J}d}t| D ]*\}}	|	 || |kr.t|	| d}q.|s|| q| | d S )Nr   typeZselector)ZavailableResourcesZresourceEntitlementsZ	overridesFT)rE   	enumeratedepth_first_merge_overlay_dictappendextend)
Zbase_valuesZoverlay_valuesr0   Zmerge_id_key_mapZvalues_to_appendZid_keyZoverlay_valueZwas_replacedZbase_value_idx
base_valuer   r   r   update_dict_list  s    

z8depth_first_merge_overlay_dict.<locals>.update_dict_listr   )r0   N)r,   rE   r   rG   r   listr7   )Z	base_dictZoverlay_dictr   r0   r1   r   r   r   r   r     s    

r   Zamd64Zi386Zppc64elZarm64Zarmhf)Zx86_64Zi686Zppc64leZaarch64Zarmv7l)archr   c                 C   s   |   }t||S r   )rS   ARCH_ALIASESrE   )r   Z
arch_lowerr   r   r   standardize_arch_name  s    r   )archesr   c                 C   s*   t  }| D ]}|t| q
tt|S r   )setaddr   sortedr   )r   Zdeduplicated_archesr   r   r   r   deduplicate_arches  s    r   )r   c                   C   s   t  dkS )Nr   )rz   getuidr   r   r   r   we_are_currently_root  s    r   )filenamenew_extensionr   c                 C   s   t j| \}}|d | S )Nr   )rz   rD   splitext)r   r   r   Z
_extensionr   r   r   set_filename_extension  s    r   Zpackage_listc              
   C   s&   d tjd | ddddddd S )N
rm   P   Fz  )widthZbreak_long_wordsZbreak_on_hyphensZinitial_indentZsubsequent_indent)r   textwrapZwrapr   r   r   r   create_package_list_str  s    
r   )r   )r   FF)5r   ZjsonZloggingrz   r`   rp   r   r8   	functoolsr   typingr   r   r   r   r   rO   r	   r
   Zuaclient.defaultsr   Zuaclient.typesr   objectrF   r-   r   Z	getLoggerr!   r5   ZJSONEncoderr   ZJSONDecoderr&   rA   rH   rT   boolrX   r^   ZREDACT_SENSITIVE_LOGSrc   rf   r.   ru   r|   r   r   r   r   r   r   r   r   r   r   r   <module>   s   		"! 
 
 
       (  7
5	