o
    (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dedefd	d
ZeeeZG dd dejZG dd dej Z!dd Z"	dJdeeef deeef dedeeef fddZ#dg fdedee defddZ$	dKdede%de%de%fddZ&d eeef d!ede%fd"d#Z'g d$Z(e(fd%ee defd&d'Z)d(ee de%de%fd)d*Z*d+ede j fd,d-Z+d.edefd/d0Z,d1d2 Z-d3d4 Z.d5d6d7d8d9d:Z/d;edefd<d=Z0d>ee dee fd?d@Z1de%fdAdBZ2dCedDedefdEdFZ3dGee fdHdIZ4dS )L    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__ .	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datetime	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|v 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   sL   |   D ]\}}t|tr#zt|}W n ty   |}Y nw || |< 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               
      sp     }	 z| i |W S   y6 } z|s|tdt|t| t|d W Y d }~nd }~ww q)NTz%s: Retrying %d more times.r   )copyLOGdebugr/   lentimesleepr*   )r,   r-   sleepse)	exceptionfretry_sleepsr   r   	decoratorW   s   z)retry.<locals>.wrapper.<locals>.decoratorr   )r?   rA   r>   r@   )r?   r   wrapperV   s   zretry.<locals>.wrapperr   )r>   r@   rC   r   rB   r   retryJ   s   rD   r   	orig_dictnew_dictpathc           	      C   s   i }|   D ]C\}}||t}|s|n|d | }t|tr8||v r3t||| |d}|r2|||< qt||< q||krItd|t| |||< q|  D ]\}}|| vrZ|||< qN|S )z<Return a dictionary of delta between orig_dict and new_dict.r   )rG   z'Contract value for '%s' changed to '%s')	r.   getDROPPED_KEYr   dictget_dict_deltasr7   r8   r/   )	rE   rF   rG   deltasr2   r3   r4   key_path	sub_deltar   r   r   rK   j   s4   


rK   msgvalid_choicesc                 C   sf   ddl m} | }d}d|ddd |D }	 ||  td	 }||v r-	 |S || q)
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).0choicer   r   r   
<listcomp>   s    z"prompt_choices.<locals>.<listcomp>Tz> )uaclientrQ   get_event_loggerformatr   infoinputlower)rO   rP   rQ   eventr3   	error_msgr   r   r   prompt_choices   s   


r^   F
assume_yesr   c                 C   s>   |rdS | s	t j} t|   }|dkr|S |dv 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   )yyesF)r
   PROMPT_YES_NOrZ   r[   strip)rO   r_   r   r3   r   r   r   prompt_for_confirmation   s   rd   configpath_to_valuec                 C   s   | }i }| d}|d }|D ]}||krd}t|tr#|||}q dS t|}| dkr2dS | dkr:dS tj|d|d)	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   falseFtrueTzboolean string: true or false)rf   expected_valuer3   )r   r   rJ   rH   r/   r[   r	   InvalidBooleanConfigValue)re   rf   r3   default_valuepaths
leaf_valuer2   	value_strr   r   r   is_config_value_true   s(   

rp   )z(Bearer )[^\']+z(\'attach\', \')[^\']+z(\'machineToken\': \')[^\']+(\'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: ).*rq   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_regexsc                 C   s    | }|D ]	}t |d|}q|S )z4Redact known sensitive information from log content.z\g<1><REDACTED>)resub)logrr   redacted_logredact_regexr   r   r   redact_sensitive_logs  s   rx   msg_opsc                 C   sN   | sdS | D ]}t |trt| q|\}}||d< |di |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.
    Tr_   FNr   )r   r/   print)ry   r_   msg_opfunctorr,   r   r   r   handle_message_operations  s   

r}   dt_strc                 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)rs   rt   replacer   strptime)r~   dt_str_without_subsecondsdt_str_with_zdt_str_without_zdt_str_with_pythonish_tzr   r   r   r0   &  s   	r0   messagec                 C   s`   t jjdu sdt jj v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encodingrR   r   r
   OKGREEN_CHECKFAIL_Xencodedecode)r   r   r   r   handle_unicode_characters]  s   r   c                   C   s   dd t j D S )Nc                 S   s4   i | ]\}}|  tv s|d s|dkr||qS )UA_FEATURESUA_CONFIG_FILE)r[   r   
startswith)rS   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 ]D\}}| |}t|tr#t|tr#t| | | qt|trHt|trHt|rBt|d trB|| | ||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 ]%}d}t| D ]\}}	|	 || |kr,t|	| d}q|s4|| q| | d S )Nr   typeselector)availableResourcesresourceEntitlements	overridesFT)rH   	enumeratedepth_first_merge_overlay_dictappendextend)
base_valuesoverlay_valuesr2   merge_id_key_mapvalues_to_appendid_keyoverlay_valuewas_replacedbase_value_idx
base_valuer   r   r   update_dict_list  s"   


z8depth_first_merge_overlay_dict.<locals>.update_dict_listr   )r2   N)r.   rH   r   rJ   r   listr9   )	base_dictoverlay_dictr   r2   r3   r   r   r   r   r     s   


r   amd64i386ppc64elarm64armhf)x86_64i686ppc64leaarch64armv7larchc                 C   s   |   }t||S r   )r[   ARCH_ALIASESrH   )r   
arch_lowerr   r   r   standardize_arch_name  s   r   archesc                 C   s*   t  }| D ]	}|t| qtt|S r   )setaddr   sortedr   )r   deduplicated_archesr   r   r   r   deduplicate_arches  s   r   c                   C   s   t  dkS )Nr   )r   getuidr   r   r   r   we_are_currently_root  s   r   filenamenew_extensionc                 C   s   t j| \}}|d | S )Nr   )r   rG   splitext)r   r   r   
_extensionr   r   r   set_filename_extension  s   r   package_listc              
   C   s&   d tjd | ddddddd S )N
r   P   Fz  )widthbreak_long_wordsbreak_on_hyphensinitial_indentsubsequent_indent)r   textwrapwrap)r   r   r   r   create_package_list_str  s   
r   )r   )r   FF)5r   jsonloggingr   rs   r   r   r:   	functoolsr   typingr   r   r   r   r   rV   r	   r
   uaclient.defaultsr   uaclient.typesr   objectrI   r/   r   	getLoggerr#   r7   JSONEncoderr   JSONDecoderr(   rD   rK   r^   boolrd   rp   REDACT_SENSITIVE_LOGSrx   r}   r0   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s    		"!



 
' 


7
5	