
    -                        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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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Z  ejB                  d      Z" G d dejF                        Z$ G d dejF                        Z%d Z&d Z'd Z(d Z) G d de*      Z+d Z,	 d"d Z-d! Z.y)#z/Utility methods used by the deploy_app command.    )absolute_import)division)unicode_literalsN)
exceptions)metric_names)storage_api)storage_util)context_util)storage_parallel)log)metrics)
propertiesencoding)files)times)map      c                        e Zd Z fdZ xZS )LargeFileErrorc                 P    t         t        |   dj                  |||             y )NzCannot upload file [{path}], which has size [{size}] (greater than maximum allowed size of [{max_size}]). Please delete the file or add to the skip_files entry in your application .yaml file and try again.)pathsizemax_size)superr   __init__format)selfr   r   r   	__class__s       9lib/googlecloudsdk/api_lib/app/deploy_app_command_util.pyr   zLargeFileError.__init__3   s*    	.$(
 &d&A	D    __name__
__module____qualname__r   __classcell__r    s   @r!   r   r   1   s    D Dr"   r   c                        e Zd Z fdZ xZS )
MultiErrorc                     t        |      dkD  rdj                  |      }ndj                  |      }dj                  t        t        |            }t
        t        j                  | #  ||z          || _	        y )Nr   zMultiple errors occurred {0}
zAn error occurred {0}
z

)
lenr   joinr   strr   core_exceptionsErrorr   errors)r   operation_descriptionr1   msgerrors_stringr    s        r!   r   zMultiError.__init__=   sf    
6{Q,334IJc%,,-BCcKKC 01M	/

/m0CDDKr"   r#   r(   s   @r!   r*   r*   ;   s     r"   r*   c                    i }dj                  |j                        }|D ]u  }t        j                  j	                  | |      }t
        j                  j                  |t        j                        }dj	                  ||g      }	|	|d|t        |      <   w t        j                  |d|       }
|
D ]  }t        j                  j                  |      }||v rt        j                  d       <t
        j                  j                  |t        j                        }dj	                  ||g      }	|	|d|t        |      <    |S )a  Builds a deployment manifest for use with the App Engine Admin API.

  Args:
    upload_dir: str, path to the service's upload directory
    source_files: [str], relative paths to upload.
    bucket_ref: The reference to the bucket files will be placed in.
    tmp_dir: A temp directory for storing generated files (currently just source
        context files).
  Returns:
    A deployment manifest (dict) for use with the Admin API.
  z"https://storage.googleapis.com/{0})	algorithm/)	sourceUrlsha1SumN)
source_dirz7Source context already exists. Using the existing file.)r   bucketosr   r-   
file_utilsChecksumHashSingleFilehashlibsha1_FormatForManifestr
   CreateContextFilesbasenamer   debug)
upload_dirsource_files
bucket_reftmp_dirmanifest
bucket_urlrel_path	full_path	sha1_hashmanifest_pathcontext_filescontext_files               r!   _BuildDeploymentManifestrR   G   s5    (3:::;L;LM* hZ2I##229=D\\ 3 KIHHj)45M".H)*  11t
,-#lww-H8	iiIJ%%44\?F|| 5 Mihh
I67m$0h!(+, $ 
/r"   c                 l   	 | j                   j                  j                  | j                  j	                  |j
                        | j                  j                  d            }|j                  sy|j                  j                  }|D cg c]a  }|j                  j                  |j                  j                  dk\  r/|j                  j                  dk(  r|j                  j                  c }}|rt        j                   t#        |            S dS # t        j                  $ r Y yw xY wc c}w )a^  Get the TTL of objects in days as specified by the lifecycle policy.

  Only "delete by age" policies are accounted for.

  Args:
    storage_client: storage_api.StorageClient, API client wrapper.
    bucket_ref: The GCS bucket reference.

  Returns:
    datetime.timedelta, TTL of objects in days, or None if no deletion
    policy on the bucket.
  )r;   	lifecycle)fields)requestglobal_paramsNr   Delete)clientbucketsGetmessagesStorageBucketsGetRequestr;   StandardQueryParametersapitools_exceptionsHttpForbiddenErrorrT   rule	conditionageactiontypedatetime	timedeltamin)storage_clientrH   r;   rulesra   agess         r!   _GetLifecycleDeletePolicyrl   v   s   ""**..''@@$$ A &$--EE F   / !F 
		




%%*
%*Tdnn.@.@.L
nnA$++"2"2h"> nnU  
 +/		CI	&8D8 
	/	/ 

s   A%D D1*A
D1D.-D.c                     | yt        j                  t         j                        }| t        z
  }||j                  z
  |k  S )a  Determines whether a GCS object is close to end-of-life.

  In order to reduce false negative rate (objects that are close to deletion but
  aren't marked as such) the returned filter is forward-adjusted with
  _TTL_MARGIN.

  Args:
    ttl: datetime.timedelta, TTL of objects, or None if no TTL.
    obj: storage object to check.

  Returns:
    True if the ojbect is safe or False if it is approaching end of life.
  T)r   NowUTC_TTL_MARGINtimeCreated)ttlobjnowdeltas       r!   
_IsTTLSaferv      s<     	[		%))#

%

E	))r"   c           	         i }t        j                         }t        ||      t        fd|j	                  |      D              }d\  }}	| D ]9  }
t
        j                  j                  ||
      }t
        j                  j                  t        j                  |d            s t
        j                  j                  ||
      }t
        j                  j                  t        j                  |d            }|r||kD  rt        |||      | |
   d   }|	|z  }	||v r+t        j                  dj                  |
             ||z  }n|||<   |	st        j                   dj                  t#        d	|z  |	z  d
                   < |S )a  Builds a map of files to upload, indexed by their hash.

  This skips already-uploaded files.

  Args:
    manifest: A dict containing the deployment manifest for a single service.
    source_dir: The relative source directory of the service.
    bucket_ref: The GCS bucket reference to upload files into.
    tmp_dir: The path to a temporary directory where generated files may be
      stored. If a file in the manifest is not found in the source directory,
      it will be retrieved from this directory instead.
    max_file_size: int, File size limit per individual file or None if no limit.

  Raises:
    LargeFileError: if one of the files to upload exceeds the maximum App Engine
    file size.

  Returns:
    A dict mapping hashes to file paths that should be uploaded.
  c              3   N   K   | ]  }t        |      r|j                    y wN)rv   name).0orr   s     r!   	<genexpr>z&_BuildFileUploadMap.<locals>.<genexpr>   s'      .'L!$S!, vv'Ls   "%)r   r   zutf-8r   r9   zSkipping upload of [{f}])fz)Incremental upload skipped {pct}% of datag      Y@   )pct)r   StorageClientrl   set
ListBucketr<   r   r-   existsr   Encodegetsizer   r   rE   r   inforound)rJ   r:   rH   rI   max_file_sizefiles_to_uploadri   existing_itemsskipped_size
total_sizerL   rM   r   rN   rr   s                 @r!   _BuildFileUploadMapr      se   , /,,..!.*=# .~'@'@'L . ..!,
hZ2I 77>>(//)gFG'',,w1i 77??8??9wGHD-9dM::"9-I$JN"	ii*11H1=>dl#,oi 	hh:AAEL(:5q9 B ; <+ . 
r"   c                       e Zd Zd Zy)FileUploadTaskc                 .    || _         || _        || _        y ry   )rN   r   rK   )r   rN   r   rK   s       r!   r   zFileUploadTask.__init__   s    DNDI DOr"   N)r$   r%   r&   r    r"   r!   r   r      s    !r"   r   c                    t         j                  j                  j                  j	                         xs t
        j                  }g }t        | j                               D ]L  \  }}t        j                  j                  ||      }t        j                  ||      }|j                  |       N t        j                  ||d       y)aE  Uploads files to App Engine Cloud Storage bucket using threads.

  Args:
    files_to_upload: dict {str: str}, map of checksum to local path
    bucket_ref: storage_api.BucketReference, the reference to the bucket files
      will be placed in.

  Raises:
    MultiError: if one or more errors occurred during file upload.
  T)num_threadsshow_progress_barN)r   VALUESappnum_file_upload_threadsGetIntr   DEFAULT_NUM_THREADSsorteditemsr	   ObjectReferenceFromBucketRefr   appendUploadFiles)r   rH   r   tasksrN   r   dest_obj_reftasks           r!   _UploadFilesThreadsr      s     ""&&>>EEG 6!55 
%   5 5 78oi//==j>GIL**4>D	LL	 9
 u+157r"   c                    t        j                  t        j                         t	        j
                         5 }t        | |||      }t        || |||      }t        ||       ddd       t        j                  j                  d       t        j                  dj                               t        j                  t        j                         |S # 1 sw Y   qxY w)a  Copies application files to the Google Cloud Storage code bucket.

  Use the Cloud Storage API using threads.

  Consider the following original structure:
    app/
      main.py
      tools/
        foo.py

   Assume main.py has SHA1 hash 123 and foo.py has SHA1 hash 456. The resultant
   GCS bucket will look like this:
     gs://$BUCKET/
       123
       456

   The resulting App Engine API manifest will be:
     {
       "app/main.py": {
         "sourceUrl": "https://storage.googleapis.com/staging-bucket/123",
         "sha1Sum": "123"
       },
       "app/tools/foo.py": {
         "sourceUrl": "https://storage.googleapis.com/staging-bucket/456",
         "sha1Sum": "456"
       }
     }

    A 'list' call of the bucket is made at the start, and files that hash to
    values already present in the bucket will not be uploaded again.

  Args:
    upload_dir: str, path to the service's upload directory
    source_files: [str], relative paths to upload.
    bucket_ref: The reference to the bucket files will be placed in.
    max_file_size: int, File size limit per individual file or None if no limit.

  Returns:
    A dictionary representing the manifest.
  NzFile upload done.zManifest: [{0}])r   CustomTimedEventr   COPY_APP_FILES_STARTr=   TemporaryDirectoryrR   r   r   r   statusPrintr   r   COPY_APP_FILES)rF   rG   rH   r   rI   rJ   r   s          r!   CopyFilesToCodeBucketr     s    T 
<<<= $$&''L*g7H)*j'=BO4 ' **&'((##H-.	<667	/ '&s   *CCc                 d    t         j                  j                  dk(  r| j                  dd      S | S )zHReformat a filename for the deployment manifest if it is Windows format.\r7   )r<   r   sepreplace)filenames    r!   rB   rB   :  s*    WW[[DD#&&	/r"   ry   )/__doc__
__future__r   r   r   rf   r@   r<   apitools.base.pyr   r_   googlecloudsdk.api_lib.appr   googlecloudsdk.api_lib.storager   r	   googlecloudsdk.appengine.toolsr
   "googlecloudsdk.command_lib.storager   googlecloudsdk.corer/   r   r   r   googlecloudsdk.core.utilr   r   r=   r   	six.movesr   _DEFAULT_NUM_THREADSrg   rp   r0   r   r*   rR   rl   rv   r   objectr   r   r   rB   r   r"   r!   <module>r      s     6 &  '   	 > 3 6 7 7 ? = # ' * - 8 *    !h  #D_** D	&& 	,^9>**3l!V !74 596rr"   