
    -                        S r SSKJr  SSKJr  SSKJr  SSKrSSKrSSKrSSKJ	r
  SSKJr  SSKJr  SS	KJr  SS
KJr  SSKJr  SSKJ	r  SSKJr  SSKJr  SSKJr  SSKJr  SSKJr  SSKJr  SSKJr  Sr \RB                  " S5      r" " S S\RF                  5      r$ " S S\RF                  5      r%S r&S r'S r(S r) " S S\*5      r+S r, S"S  jr-S! r.g)#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                   (   ^  \ rS rSrU 4S jrSrU =r$ )LargeFileError1   c                 H   > [         [        U ]  SR                  XUS95        g )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   LargeFileError.__init__3   s(    	.$(
 &d&A	D     __name__
__module____qualname____firstlineno__r   __static_attributes____classcell__r!   s   @r"   r   r   1   s    D Dr$   r   c                   (   ^  \ rS rSrU 4S jrSrU =r$ )
MultiError;   c                    > [        U5      S:  a  SR                  U5      nOSR                  U5      nSR                  [        [        U5      5      n[
        [        R                  U ]#  X4-   5        X l	        g )Nr   zMultiple errors occurred {0}
zAn error occurred {0}
z

)
lenr   joinr   strr   core_exceptionsErrorr   errors)r    operation_descriptionr7   msgerrors_stringr!   s        r"   r   MultiError.__init__=   sb    
6{Q,334IJc%,,-BCcKKC 01M	/

/0CDKr$   )r7   r&   r-   s   @r"   r/   r/   ;   s     r$   r/   c                    0 nSR                  UR                  5      nU Hq  n[        R                  R	                  X5      n[
        R                  R                  U[        R                  S9nSR	                  XX/5      n	U	US.U[        U5      '   Ms     [        R                  " USU S9n
U
 H  n[        R                  R                  U5      nXd;   a  [        R                  " S5        M?  [
        R                  R                  U[        R                  S9nSR	                  XX/5      n	U	US.U[        U5      '   M     U$ )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   r3   
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"   _BuildDeploymentManifestrY   G   s-    (3:::;L;LM* hZ2I##229=D\\ 3 KIHHj45M".H)*  11t
,-#lww-H	iiIJ%%44\?F|| 5 Mihh
67m$0h!(+, $ 
/r$   c                     U R                   R                  R                  U R                  R	                  UR
                  S9U R                  R                  SS9S9nUR                  (       d  gUR                  R                  nU Vs/ s Hi  oDR                  R                  c  M  UR                  R                  S:  d  M7  UR                  R                  S:X  d  MS  UR                  R                  PMk     nnU(       a  [        R                   " [#        U5      5      $ S$ ! [        R                   a     gf = fs  snf )aN  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.
)rB   	lifecycle)fields)requestglobal_paramsNr   Delete)clientbucketsGetmessagesStorageBucketsGetRequestrB   StandardQueryParametersapitools_exceptionsHttpForbiddenErrorr[   rule	conditionageactiontypedatetime	timedeltamin)storage_clientrO   rB   rulesrh   agess         r"   _GetLifecycleDeletePolicyrs   v   s$   ""**..''@@$$ A &$--EE F   / !F 
		




%%*
%*Tnn.@.@
nnA "&++"2"2h"> dnnU  
 +/		CI	&8D8 
	/	/ 

s*   AD$ 
D>(D>D> D>$D;:D;c                     U c  g[         R                  " [         R                  5      nU [        -
  nX!R                  -
  U:*  $ )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"   
_IsTTLSafer}      s:     	[		%))#

%

E	))r$   c           	      "  ^ 0 n[         R                  " 5       n[        Xb5      m[        U4S jUR	                  U5       5       5      nSu  pU  GH<  n
[
        R                  R                  X5      n[
        R                  R                  [        R                  " USS95      (       d  [
        R                  R                  X:5      n[
        R                  R                  [        R                  " USS95      nU(       a  X:  a  [        XU5      eX
   S   nX-  n	X;   a(  [        R                  " SR                  U
S95        X-  nOXU'   U	(       d  GM	  [        R                   " SR                  [#        S	U-  U	-  S
5      S95        GM?     U$ )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   b   >#    U  H$  n[        TU5      (       d  M  UR                  v   M&     g 7fN)r}   name).0ory   s     r"   	<genexpr>&_BuildFileUploadMap.<locals>.<genexpr>   s(      .'L!$S!, qvv'Ls   //)r   r   zutf-8r   r@   zSkipping upload of [{f}])fz)Incremental upload skipped {pct}% of datag      Y@   )pct)r   StorageClientrs   set
ListBucketrC   r   r3   existsr   Encodegetsizer   r   rL   r   inforound)rQ   rA   rO   rP   max_file_sizefiles_to_uploadrp   existing_itemsskipped_size
total_sizerS   rT   r   rU   ry   s                 @r"   _BuildFileUploadMapr      sT   , /,,..!.=# .~'@'@'L . ..!,hZ2I 77>>(//)gFGG'',,w1i 77??8??9wGHD-9M::"9-IJ"	ii*11H1=>l#,i z	hh:AAEL(:5q9 B ; <+ . 
r$   c                       \ rS rSrS rSrg)FileUploadTask   c                 (    Xl         X l        X0l        g r   )rU   r   rR   )r    rU   r   rR   s       r"   r   FileUploadTask.__init__   s    NI Or$   )rR   r   rU   N)r'   r(   r)   r*   r   r+   r%   r$   r"   r   r      s    !r$   r   c                    [         R                  R                  R                  R	                  5       =(       d    [
        R                  n/ n[        U R                  5       5       HL  u  pE[        R                  R                  UU5      n[
        R                  " XV5      nUR                  U5        MN     [
        R                  " X2SS9  g)a7  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   rO   r   tasksrU   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
 u157r$   c                    [         R                  " [        R                  5        [        R
                  " 5        n[        XX$5      n[        XPX$U5      n[        Xb5        SSS5        [        R                  R                  S5        [        R                  " SR                  W5      5        [         R                  " [        R                  5        U$ ! , (       d  f       Nx= f)aZ  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_STARTrD   TemporaryDirectoryrY   r   r   r   statusPrintr   r   COPY_APP_FILES)rM   rN   rO   r   rP   rQ   r   s          r"   CopyFilesToCodeBucketr     s    T 
<<<= $$&''*7H)j=BO4 ' **&'((##H-.	<667	/ '&s   %C
Cc                 f    [         R                  R                  S:X  a  U R                  SS5      $ U $ )zHReformat a filename for the deployment manifest if it is Windows format.\r>   )rC   r   sepreplace)filenames    r"   rI   rI   :  s*    WW[[DD#&&	/r$   r   )/__doc__
__future__r   r   r   rm   rG   rC   apitools.base.pyr   rf   googlecloudsdk.api_lib.appr   googlecloudsdk.api_lib.storager   r	   googlecloudsdk.appengine.toolsr
   "googlecloudsdk.command_lib.storager   googlecloudsdk.corer5   r   r   r   googlecloudsdk.core.utilr   r   rD   r   	six.movesr   _DEFAULT_NUM_THREADSrn   rw   r6   r   r/   rY   rs   r}   r   objectr   r   r   rI   r%   r$   r"   <module>r      s     6 &  '   	 > 3 6 7 7 ? = # ' * - 8 *      #D_** D	&& 	,^9>**3l!V !74 596rr$   