o
    l                     @   s:  d Z ddlmZ ddlmZ ddl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mZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlZG dd dejZG dd deZG dd deZG dd deZdd ZG dd deZ G dd deZ!dd Z"dS )zManages the state of what is installed in the cloud SDK.

This tracks the installed modules along with the files they created.  It also
provides functionality like extracting tar files into the installation and
tracking when we check for updates.
    )absolute_import)division)unicode_literalsN)config)
exceptions)
console_io)
installers)	snapshots)encoding)filesc                   @   s   e Zd ZdZdS )Errorz*Base exception for the local_state module.N)__name__
__module____qualname____doc__ r   r   D/tmp/google-cloud-sdk/lib/googlecloudsdk/core/updater/local_state.pyr   /   s    r   c                           e Zd ZdZ fddZ  ZS )InvalidSDKRootErrorzGError for when the root of the Cloud SDK is invalid or cannot be found.c                       t t| d d S )NzThe components management action could not be performed because the installation root of the Cloud SDK could not be located. If you previously used the Cloud SDK installer, you could re-install the SDK and retry again.)superr   __init__self	__class__r   r   r   7      
zInvalidSDKRootError.__init__r   r   r   r   r   __classcell__r   r   r   r   r   4       r   c                       r   )InvalidDownloadErrorz9Exception for when the SDK that was download was invalid.c                    r   )Nz#The Cloud SDK download was invalid.)r   r    r   r   r   r   r   r   B   r   zInvalidDownloadError.__init__r   r   r   r   r   r    ?   r   r    c                       r   )PermissionsErrorzCError for when a file operation cannot complete due to permissions.c                    s   t t| dj||d dS )zInitialize a PermissionsError.

    Args:
      message: str, The message from the underlying error.
      path: str, The absolute path to a file or directory that needs to be
          operated on, but can't because of insufficient permissions.
    zh{message}: [{path}]

Ensure you have the permissions to access the file and that the file is not in use.messagepathN)r   r!   r   format)r   r#   r$   r   r   r   r   J   s
   
zPermissionsError.__init__r   r   r   r   r   r!   G   r   r!   c                    s    fdd}|S )a7  Use this decorator for functions that deal with files.

  If an exception indicating file permissions is raised, this decorator will
  raise a PermissionsError instead, so that the caller only has to watch for
  one type of exception.

  Args:
    func: The function to decorate.

  Returns:
    A decorator.
  c                     s   z | i |W S  t jy5 } z |jd d } | d dr0tt| d tj	| d d  d }~w t
tfy^ } z|jtjkrYttt|jttj	|jd  d }~ww )Nr      z
[Errno 13]r"   )shutilr   args
startswithr   reraiser!   osr$   abspathOSErrorIOErrorerrnoEACCESr
   Decodestrerrorfilename)r(   kwargsefuncr   r   _TryFuncf   s.   
z)_RaisesPermissionsError.<locals>._TryFuncr   )r7   r8   r   r6   r   _RaisesPermissionsErrorX   s   r9   c                   @   sJ  e Zd ZdZejjZdZdZ	dZ
dZdZedd Zd	d
 Zedd Zedd Zedd Zedd Zdd Zedd Zedd Zd:ddZed:ddZed:ddZed d! Zd"d# Zd$d% Zed:d&d'Zed:d(d)Z ed:d*d+Z!e		,d;d-d.Z"e	d:d/d0Z#ed:d1d2Z$ed3d4 Z%d5d6 Z&d<d8d9Z'dS )=InstallationStatez@The main class for checking / updating local installation state.z.backupz.trashz.stagingz.snapshot.json)zlib/third_party/grpcc                  C   s$   t  j} | s
t ttj| S )zGets the installation state for the SDK that this code is running in.

    Returns:
      InstallationState, The state for this area.

    Raises:
      InvalidSDKRootError: If this code is not running under a valid SDK.
    )r   Pathssdk_rootr   r:   r+   r$   realpath)r<   r   r   r   
ForCurrent   s   

zInstallationState.ForCurrentc                 C   s   |   sdS ttj| jS )zGets the installation state for the backup of this  state, if it exists.

    Returns:
      InstallationState, The state for this area or None if the backup does not
          exist.
    N)	HasBackupr:   r+   r$   r=   $_InstallationState__backup_directoryr   r   r   r   BackupInstallationState   s   z)InstallationState.BackupInstallationStatec              	   C   s:   zt  }t|j|  W S    td| t  Y dS )a4  Gets the version string for the given installed component.

    This function is to be used to get component versions for metrics reporting.
    If it fails in any way or if the component_id is unknown, it will return
    None.  This prevents errors from surfacing when the version is needed
    strictly for reporting purposes.

    Args:
      component_id: str, The component id of the component you want the version
        for.

    Returns:
      str, The installed version of the component, or None if it is not
        installed or if an error occurs.
    z8Failed to get installed version for component [%s]: [%s]N)	r:   r>   InstallationManifest_state_directoryVersionStringloggingdebugsysexc_info)component_idstater   r   r   VersionForInstalledComponent   s   z.InstallationState.VersionForInstalledComponentc                 C   s   t j|std|t|| _t j| jt	j
| _t j| jt	j| _t j| jt	j| _t j| jt	j | _t| j| j| _dS )zInitializes the installation state for the given sdk install.

    Args:
      sdk_root: str, The file path of the root of the SDK installation.

    Raises:
      ValueError: If the given SDK root does not exist.
    z.The given Cloud SDK root does not exist: [{0}]N)r+   r$   isdir
ValueErrorr%   r
   r1   _InstallationState__sdk_rootjoinr:   STATE_DIR_NAMErC   BACKUP_DIR_NAMEr@   TRASH_DIR_NAME#_InstallationState__trash_directorynormpathSTAGING_ROOT_SUFFIX$_InstallationState__sdk_staging_rootr   ComponentInstaller_component_installer)r   r<   r   r   r   r      s(   




zInstallationState.__init__c                 C   s"   t j| jst| j dS dS )z1Creates the state directory if it does not exist.N)r+   r$   rL   rC   
file_utilsMakeDirr   r   r   r   _CreateStateDir   s   z!InstallationState._CreateStateDirc                 C   s   | j S )zvGets the root of the SDK that this state corresponds to.

    Returns:
      str, the path to the root directory.
    )rN   r   r   r   r   r<      s   zInstallationState.sdk_rootc                    s6   t j js	g S t  j} fdd|D }|S )zReturns the files in the state directory that have the given suffix.

    Args:
      suffix: str, The file suffix to match on.

    Returns:
      list of str, The file names that match.
    c                    s2   g | ]}t jt j j|r|r|qS r   )r+   r$   isfilerO   rC   endswith).0fr   suffixr   r   
<listcomp>   s
    
z5InstallationState._FilesForSuffix.<locals>.<listcomp>)r+   r$   rL   rC   listdir)r   ra   r   matchingr   r`   r   _FilesForSuffix   s
   	z!InstallationState._FilesForSuffixc                 C   sB   |  tj}i }|D ]}|dttj  }t| j|||< q
|S )zGets all the components that are currently installed.

    Returns:
      A dictionary of component id string to InstallationManifest.
    N)re   r:   COMPONENT_SNAPSHOT_FILE_SUFFIXlenrB   rC   )r   snapshot_files	manifestsr_   rI   r   r   r   InstalledComponents   s   
z%InstallationState.InstalledComponentsc                 C   s   t j| S )zFGenerates a ComponentSnapshot from the currently installed components.)r	   ComponentSnapshotFromInstallStater   r   r   r   Snapshot	  s   zInstallationState.SnapshotNc                 C   s   |   j||dS )az  Generates a ComponentSnapshotDiff from current state and the given state.

    Args:
      latest_snapshot:  snapshots.ComponentSnapshot, The current state of the
        world to diff against.
      platform_filter: platforms.Platform, A platform that components must
        match in order to be considered for any operations.

    Returns:
      A ComponentSnapshotDiff.
    )platform_filter)rm   
CreateDiff)r   latest_snapshotrn   r   r   r   DiffCurrentState  s   
z"InstallationState.DiffCurrentStatec                 C   s   |    t O}tj|d}tj|d}t|ddg\}}tj	|||dd}tj
|||d t|}	t|	dkr@t tj||	d }
t|
| j W d   n1 sZw   Y  t| j}|  | | |S )	a  Creates a new staging area from a fresh download of the Cloud SDK.

    Args:
      url: str, The url to download the new SDK from.
      progress_callback: f(float), A function to call with the fraction of
        completeness.

    Returns:
      An InstallationState object for the new install.

    Raises:
      installers.URLFetchError: If the new SDK could not be downloaded.
      InvalidDownloadError: If the new SDK was malformed.
    z	.downloadz.extract   zcomponents.reinstallprogress_callbackcommand_pathrt   r   N)_ClearStagingrY   TemporaryDirectoryr+   r$   rO   r   SplitProgressBarr   DownloadTar
ExtractTarrc   rg   r    MoveDirrV   r:   r[   CopyMachinePropertiesTo)r   urlrt   tdownload_dirextract_dirdownload_callbackextract_callbackdownloaded_tarr   r<   staging_sdkr   r   r   CreateStagingFromDownload  s.   



z+InstallationState.CreateStagingFromDownloadc                 C   sh   |    |   |   |   |  t| j|j |r"|d t|j| j |r2|d dS dS )a  Replaces this installation with the given other installation.

    This moves the current installation to the backup directory of the other
    installation.  Then, it moves the entire second installation to replace
    this one on the file system.  The result is that the other installation
    completely replaces the current one, but the current one is snapshotted and
    stored as a backup under the new one (and can be restored later).

    Args:
      other_install_state: InstallationState, The other state with which to
        replace this one.
      progress_callback: f(float), A function to call with the fraction of
        completeness.
    g      ?g      ?N)r[   ClearBackup
ClearTrashrY   r|   rN   r@   )r   other_install_statert   r   r   r   ReplaceWithE  s   zInstallationState.ReplaceWithc                 C   sb   |   sdS |   t| j| j t| j}|  |  t| j	|j
 t|j	| j	 dS )aj  Restore the backup from this install state if it exists.

    If this installation has a backup stored in it (created by and update that
    used ReplaceWith(), above), it replaces this installation with the backup,
    using a temporary staging area.  This installation is moved to the trash
    directory under the installation that exists after this is done.  The trash
    directory can be removed at any point in the future.  We just don't want to
    delete code that is running since some platforms have a problem with that.

    Returns:
      bool, True if there was a backup to restore, False otherwise.
    FT)r?   rw   rY   r|   r@   rV   r:   r[   r   rN   rS   )r   staging_stater   r   r   RestoreBackupc  s   
zInstallationState.RestoreBackupc                 C      t j| jS )zDetermines if this install has a valid backup that can be restored.

    Returns:
      bool, True if there is a backup, False otherwise.
    )r+   r$   rL   r@   r   r   r   r   r?        zInstallationState.HasBackupc                 C   s   |   r| jS dS )zGets the backup directory of this installation if it exists.

    Returns:
      str, The path to the backup directory or None if it does not exist.
    N)r?   r@   r   r   r   r   BackupDirectory  s   z!InstallationState.BackupDirectoryc                 C   .   t j| jrt| j |r|d dS dS )zDeletes the current staging directory if it exists.

    Args:
      progress_callback: f(float), A function to call with the fraction of
        completeness.
    rr   N)r+   r$   existsrV   rY   RmTreer   rt   r   r   r   rw     
   zInstallationState._ClearStagingc                 C   r   )zDeletes the current backup if it exists.

    Args:
      progress_callback: f(float), A function to call with the fraction of
        completeness.
    rr   N)r+   r$   rL   r@   rY   r   r   r   r   r   r     r   zInstallationState.ClearBackupc                 C   r   )zDeletes the current trash directory if it exists.

    Args:
      progress_callback: f(float), A function to call with the fraction of
        completeness.
    rr   N)r+   r$   rL   rS   rY   r   r   r   r   r   r     r   zInstallationState.ClearTrashunknownc                 C   s(   |    ||}| jj|||d}|S )a  Downloads the given component based on the given snapshot.

    Args:
      snapshot: snapshots.ComponentSnapshot, The snapshot that describes the
        component to install.
      component_id: str, The component to install from the given snapshot.
      progress_callback: f(float), A function to call with the fraction of
        completeness.
      command_path: the command path to include in the User-Agent header if the
        URL is HTTP

    Returns:
      Optional[str], The path of the downloaded archive, or None if the
        component has no actual sources.

    Raises:
      installers.URLFetchError: If the component associated with the provided
        component ID has a URL that is not fetched correctly.
    rs   )r[   ComponentFromIdrX   Download)r   snapshotrI   rt   ru   	componentdownloaded_archiver   r   r   r     s   
zInstallationState.Downloadc                 C   s,   | j j||d}t| j|}||| dS )a  Installs the archive previously downloaded from self.Download().

    Args:
      snapshot: snapshots.ComponentSnapshot, The snapshot that describes the
        component to install.
      component_id: str, The component to install from the given snapshot.
      downloaded_archive: Optional[str], The path to the archive downloaded
        previously.
      progress_callback: f(float), A function to call with the fraction of
        completeness.
    rv   N)rX   ExtractrB   rC   MarkInstalled)r   r   rI   r   rt   r   manifestr   r   r   Install  s
   zInstallationState.Installc                 C   s  t | j|}| }t|}| j}t }t }t|ddD ]q\}	}
tj	||
}tj
|s4tj|rwt| tjtj|
}|
drb|d }tj
|rWt| |tj	||d |rv|tj	|| tj|}|sdntj|r|tj| |r||	|  q|D ]}tj|rtj|st| qt|tddD ]}tj|rtj|st|st| q|  dS )	a,  Uninstalls the given component.

    Deletes all the files for this component and marks it as no longer being
    installed.

    Args:
      component_id: str, The id of the component to uninstall.
      progress_callback: f(float), A function to call with the fraction of
        completeness.
    rr   )startz.pyc__pycache__T)keyreverseN)rB   rC   InstalledPathsrg   rN   set	enumerater+   r$   rO   r\   islinkremovedirnamerT   r]   addrL   rY   r   sortedrc   rmdirMarkUninstalled)r   rI   rt   r   pathstotal_pathsrootdirs_to_removepycache_dirsnumpr$   dir_pathpyc_pathdr   r   r   	Uninstall  sF   



"
zInstallationState.Uninstallc                 C   s6   | j D ]}tj| j|}tj|rt| qdS )z=Clear deprecated directories that were not removed correctly.N)DEPRECATED_DIRSr+   r$   rO   r<   rL   rY   r   )r   r   r$   r   r   r   ClearDeprecatedDirs"  s   

z%InstallationState.ClearDeprecatedDirsc                 C   sH   t j| jtjj}t j|jtjj}t j|sdS t	|| dS )aN  Copy this state's properties file to another state.

    This is primarily intended to be used to maintain the machine properties
    file during a schema-change-induced reinstall.

    Args:
      other_state: InstallationState, The installation state of the fresh
          Cloud SDK that needs the properties file mirrored in.
    N)
r+   r$   rO   r<   r   r;   CLOUDSDK_PROPERTIES_NAMEr   r'   copyfile)r   other_statemy_propertiesother_propertiesr   r   r   r}   *  s   


z)InstallationState.CopyMachinePropertiesToFc              	   C   s   t jr	td}ntjd dkrtd}ntjd dkr#td}nd}t| j5 t	j
dd	t	j
d
dddg}|du rGtt	 dn|}|D ]}tj||d||d qKW d   dS 1 scw   Y  dS )aj  Attempts to compile all the python files into .pyc files.

    Args:
      force: boolean, passed to force option of compileall.compiledir,
      workers: int, can be used to explicitly set number of worker processes;
        otherwise we determine it automatically. Only set for testing.

    This does not raise exceptions if compiling a given file fails.
    a(  (httplib2/python3|typing/python3|platform/bq/third_party/yaml/lib3|third_party/google/api_core|third_party/google/auth|third_party/google/oauth2|third_party/overrides|third_party/proto|dulwich|gapic|pubsublite|pubsub/lite_subscriptions.py|logging_v2|platform/bundledpythonunix|pubsub_v1/services)rr      z.*   a
  (kubernetes/utils/create_from_yaml.py|platform/google_appengine|gslib/vendored/boto/boto/iam/connection.py|gslib/vendored/boto/tests/|third_party/.*/python2/|third_party/yaml/[a-z]*.py|third_party/yaml/lib2/|third_party/antlr3/|appengine/|google/cloud/appengine_|google/cloud/bigquery_logging_v1|third_party/fancy_urllib/|platform/bq/third_party/gflags|platform/ext-runtime/nodejs/test/|platform/gsutil/third_party/apitools/ez_setup|platform/gsutil/third_party/pyparsing|platform/gsutil/third_party/crcmod_osx/crcmod/test)Nbinbootstrappingdataclilibplatform   r&   )rxquietforceworkers)sixPY2recompilerG   version_inforY   ChDirr<   r+   r$   rO   min	cpu_count
compileallcompile_dir)r   r   r   regex_exclusion
to_compilenum_workersr   r   r   r   CompilePythonFiles<  s,   
"z$InstallationState.CompilePythonFiles)N)Nr   )FN)(r   r   r   r   r   r;   CLOUDSDK_STATE_DIRrP   rQ   rR   rU   rf   r   staticmethodr>   rA   rK   r9   r   r[   propertyr<   re   rj   rm   rq   r   r   r   r?   r   rw   r   r   r   r   r   r   r}   r   r   r   r   r   r:   }   sh    







'

8
r:   c                   @   sL   e Zd ZdZdZdd Zdd Zdd Zd	d
 Zdd Z	dd Z
dd ZdS )rB   zDClass to encapsulate the data stored in installation manifest files.z	.manifestc                 C   s@   || _ || _tj| j |tj | _tj| j |tj	 | _
dS )zCreates a new InstallationManifest.

    Args:
      state_dir: str, The directory path where install state is stored.
      component_id: str, The component id that you want to get the manifest for.
    N)	state_diridr+   r$   rO   r:   rf   snapshot_filerB   MANIFEST_SUFFIXmanifest_file)r   r   rI   r   r   r   r     s   
zInstallationManifest.__init__c                 C   s^   t | j}t|D ]	}||d  qW d   n1 sw   Y  |j| j| jd dS )az  Marks this component as installed with the given snapshot and files.

    This saves the ComponentSnapshot and writes the installed files to a
    manifest so they can be removed later.

    Args:
      snapshot: snapshots.ComponentSnapshot, The snapshot that was the source
        of the install.
      files: list of str, The files that were created by the installation.
    
N)rI   )rY   
FileWriterr   _NormalizeFileListwriteWriteToFiler   r   )r   r   r   fpr_   r   r   r   r     s   z"InstallationManifest.MarkInstalledc                 C   s,   | j | jfD ]}tj|rt| qdS )zMarks this component as no longer being installed.

    This does not actually uninstall the component, but rather just removes the
    snapshot and manifest.
    N)r   r   r+   r$   r\   r   )r   r_   r   r   r   r     s
   
z$InstallationManifest.MarkUninstalledc                 C   r   )zLoads the local ComponentSnapshot for this component.

    Returns:
      The snapshots.ComponentSnapshot for this component.
    )r	   rk   FromFiler   r   r   r   r   rk     r   z&InstallationManifest.ComponentSnapshotc                 C   s   |   | jS )zLoads the ComponentSnapshot and get the schemas.Component this component.

    Returns:
      The schemas.Component for this component.
    )rk   r   r   r   r   r   r   ComponentDefinition  s   z(InstallationManifest.ComponentDefinitionc                 C   s   |   jjS )zGets the version string of this component as it was installed.

    Returns:
      str, The installed version of this component.
    )r   versionversion_stringr   r   r   r   rD     s   z"InstallationManifest.VersionStringc                 C   s@   t | j}dd |D }W d   |S 1 sw   Y  |S )zGets the list of files and dirs created by installing this component.

    Returns:
      list of str, The files and directories installed by this component.
    c                 S   s   g | ]}|  qS r   )rstrip)r^   liner   r   r   rb     s    z7InstallationManifest.InstalledPaths.<locals>.<listcomp>N)rY   
FileReaderr   )r   r_   r   r   r   r   r     s   
z#InstallationManifest.InstalledPathsN)r   r   r   r   r   r   r   r   rk   r   rD   r   r   r   r   r   rB     s    
rB   c                 C   s   t g }t g }t g }| D ]0}t|}|dr"||d  n|| tj|}|r>||d  tj|}|s/qt|| |B S )z=Removes non-empty directory entries and sorts resulting list./)	r   	posixpathrT   r]   r   r+   r$   r   r   )	file_listparent_directoriesdirectoriesr   r_   norm_file_pathr   r   r   r     s   


r   )#r   
__future__r   r   r   r   r/   rE   r+   r   r   r'   rG   googlecloudsdk.corer   r   googlecloudsdk.core.consoler   googlecloudsdk.core.updaterr   r	   googlecloudsdk.core.utilr
   r   rY   r   r   r   r    r!   r9   objectr:   rB   r   r   r   r   r   <module>   s@   %    R