
    V4                         d Z ddlZddl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  G d	 d
      Zy)z'Utilities for app migrate gen1-to-gen2.    N)path)
exceptions)log)
properties)yaml)filesc                   f    e Zd ZdZdZdZdZdZdZdZ	dZ
d	Zd
 Zd Zd Zd Zd Zd Zd Zd Zd Zy)Gen1toGen2MigrationzCUtility class for migrating Gen 1 App Engine applications to Gen 2.zapp.yamlzmigration_progress.jsondefault)python27servicer   app_yamlprocessed_filesc                 J   t        j                  |       || _        t        j                         | _        |j                  r/t        j                  j                  |j                        | _	        nNt        j                  d       t        j                  j                  | j
                  | j                        | _	        t        j                  j                  |j                        | _        t        j                   j"                  j$                  j'                         | _        y)zInitializes the Gen1toGen2Migration utility class.

    Args:
      api_client: The AppEngine API client.
      args: The argparse arguments.
    z:appyaml not provided. Using app.yaml in current directory.N)r   debug
api_clientosgetcwd	input_dirappyamlr   relpathappyaml_pathinfojoinDEFAULT_APPYAMLabspath
output_dirr   VALUEScoreprojectGet)selfr   argss      4lib/googlecloudsdk/command_lib/app/migration_util.py__init__zGen1toGen2Migration.__init__+   s     IIdO DOYY[DN ||''//$,,7d	hhKL'',,t~~t7K7KLdggoodoo6DO$$))11557DL    c                 $   | j                         }|j                  | j                        r|j                  | j                        }n+| j                  }t        j
                  j                  d       t	        j                  dj                  |             | j                  j                  || j                        st        j                  | j                        | j                         }|r| j                  |       y| j!                  |       y)zStarts the migration process.

    Raises:
      MissingGen1ApplicationError: If the provided project does not contain an
      AppEngine version with a Gen1 runtime.
    z:Service name not found in app.yaml. Using default service.zservice_name: {0}N)ValidateAppyamlAndGetContentsgetSERVICE_FIELDDEFAULT_SERVICE_NAMEr   statusPrintr   formatr   CheckGen1AppIdr    r   MissingGen1ApplicationError)CheckOutputDirectoryAndGetMigrationStatusStartNewMigrationResumeMigration)r"   app_yaml_contentservice_nameis_new_migrations       r$   StartMigrationz"Gen1toGen2Migration.StartMigration?   s     99;D../%))$*<*<=l..l	jj
F HH ''56 ??)),E224<<@@ EEG
\*
<(r&   c                 N   t        j                  | j                        st        j                  | j                        t        j                  | j                        }|j                  d      | j                  vr*t        j                  | j                  | j                        |S )a  Validates the app.yaml file and returns its contents.

    Returns:
      The contents of the app.yaml file.

    Raises:
      FileNotFoundError: If the app.yaml file is not found.
      UnsupportedRuntimeError: If the runtime in app.yaml is not a valid Gen 1
      runtime.
    runtime)
r   existsr   r   FileNotFoundErrorr   	load_pathr)   SUPPORTED_GEN1_RUNTIMESUnsupportedRuntimeError)r"   appyaml_contents     r$   r(   z1Gen1toGen2Migration.ValidateAppyamlAndGetContents]   s     ;;t(()(():):;; nnT%6%67O9%T-I-II..


T99  r&   c                 ~   t         j                  j                  | j                        sNt        j                  | j                         t        j                  dj                  | j                               yt        j                  | j                        s/t        j                  dj                  | j                               y| j                  t        j                  | j                        v r/t        j                  dj                  | j                               yt        j                  | j                        )a  Check if output directory exists and decide the migration status.

    If an output directory does not exist, we create one and decide that it is a
    new migration.

    Returns:
      Boolean: True for new migration, False for resuming migration.

    Raises:
      InvalidOutputDirectoryError: If the output directory is not empty and does
      not contain a migration_progress.json file.
    zCreating directory: {0}TzOutput directory {0} is empty.z8Output directory {0} is not empty. Resuming migration...F)r   r   r:   r   makedirsr   r   r.   listdirMIGRATION_PROGRESS_FILEwarningr   InvalidOutputDirectoryError)r"   s    r$   r1   z=Gen1toGen2Migration.CheckOutputDirectoryAndGetMigrationStatusu   s     77>>$//*kk$//"	hh(//@A ::doo&	hh/66tGH ##rzz$//'BB	kk
D
K
Koo
  
0
0
AAr&   c                 `   t        j                  dj                  | j                               t        j
                  j                  | j                        }t        j                  | j                  | j                  t        j                  d|t        j                  | j                        j                        d       t         j                  j!                  d       t        j
                  j#                  | j                  | j$                        }i }| j'                  |t        j
                  j#                  | j                  |      ||       t        j
                  j#                  | j                  d      }| j)                  ||||       t         j                  j!                  d       y)	zXFlow for starting a new migration.

    Args:
      service_name: The service name.
    input_dir: {0}z*.pyT)ignoredirs_exist_okz!Copying files to output directoryrequirements.txtMigration completed.N)r   r   r.   r   r   r   basenamer   shutilcopytreer   ignore_patternspathlibPurePathnamer,   r-   r   rC   WriteMigratedYamlWriteMigratedCode)r"   r5   appyaml_filenameprogress_filemigration_progressrequirements_files         r$   r2   z%Gen1toGen2Migration.StartNewMigration   s<    HH$$T^^45ww''(9(9: OO%%$g&6&6t&G&L&L
  JJ89 GGLL$2N2NOM 	
T__&67	 T__6HI(-9J JJ+,r&   c           	      `   t        j                  dj                  | j                               t        j
                  j                  | j                  | j                        }t        j                  |      5 }t        j                  |      }ddd       | j                  j                  dd      vrt        j                  dj                  | j                               | j                  |t        j
                  j                  | j                  t        j
                  j!                  | j                              ||       t        j
                  j                  | j                  d      }| j#                  ||||       t         j$                  j'                  d       y# 1 sw Y   xY w)zFlow for a resumed migration.

    Args:
      service_name: The service name.

    Raises:
      InvalidOutputDirectoryError: If the output directory is not empty and does
      not contain a migration_progress.json file.
    rG   Nr    z8{0} not present in migration_progress. Will be migrated.rJ   rK   )r   r   r.   r   r   r   r   r   rC   r   
FileReaderjsonloadr   r)   rS   rL   rT   r,   r-   )r"   r5   rV   pfrW   rX   s         r$   r3   z#Gen1toGen2Migration.ResumeMigration   s;    HH$$T^^45 GGLL$2N2NOM			-	(B99R= 
)  2 6 6z2 FF	hh
D
K
K
 

'',,t(8(89J9J(K
L

	 T__6HI(-9J JJ+,- 
)	(s   8F##F-c                    t        j                  | j                        }t        j                  j                  | j                        }| j                  j                  | j                  || j                  |      }t        j                  |j                        }t        j                  |      5 }	t        j                  ||	       ddd       | j                  || j                  <   t        j                  |d      5 }
t!        j                  ||
d       ddd       t"        j$                  j'                  dj)                  |             y# 1 sw Y   xY w# 1 sw Y   DxY w)a  Writes the migrated app.yaml to the output directory.

    Args:
      service_name: The service name.
      output_path: The path to the output directory.
      migration_progress: The migration progress dictionary.
      progress_file: The path to the migration progress file.
    Nw   indentz$Config modifications applied to {0}.)r   ReadFileContentsr   r   r   rL   r   MigrateConfigYamlr    PYTHON_GEN1_RUNTIMEr   r]   configAsString
FileWriterdumpAPP_YAML_FIELDr\   r   r,   r-   r.   )r"   r5   output_pathrW   rV   r?   rU   responsemigrated_yaml_contentsfr^   s              r$   rS   z%Gen1toGen2Migration.WriteMigratedYaml   s    ,,T->->?Oww''(9(9:00ot'?'?H "YYx'>'>?			+	&!
ii&* 
' /3.?.?t**+			-	-
ii"Bq1 
.JJ.556FG 
'	&
 
.	-s   /E=EEE"c           	         t        j                  | j                        D ]3  \  }}}|D cg c]0  }|t        j                  | j
                        j                  k7  r|2 c}|dd |D ]  }	t         j                  j                  ||	      }
t        j                  |
      j                  dk(  sG| j                  |v r6|
|| j                     v r%t        j                  dj                  |
             t        j                  j!                  dj                  |
             t#        j$                  |
      }| j'                  ||      \  }}t         j                  j                  | j
                  t         j                  j)                  |
| j                              }g }t         j                  j+                  |      r(t#        j$                  |      }|r|j-                  d      }|D ]  }||vs|j/                  |        t#        j0                  |dj                  |             t         j                  j+                  |      rt         j                  j3                  |      d   dz   t5        t7        j6                               j-                  d      d   z   dz   }t        j8                  d	j                  |
|             |}t#        j0                  ||d
       | j                  |vrg || j                  <   || j                     j/                  |
       t#        j:                  |d      5 }t=        j>                  ||d       ddd        6 yc c}w # 1 sw Y   xY w)a  Writes the migrated code to the output directory.

    Args:
      service_name: The service name.
      migration_progress: The migration progress dictionary.
      progress_file: The path to the migration progress file.
      requirements_file: The path to the requirements file.
    Nz.pyz)File {0} already exists. Will be skipped.zCurrently on file: {0}
r   _.z0File {0} already exists. Will be renamed to {1}.F)	overwriter`   ra   rb   ) r   walkr   rP   rQ   r   rR   r   r   PathsuffixPROCESSED_FILES_FIELDr   r   r.   r,   r-   r   rd   GetMigratedCoder   r:   splitappendWriteFileContentssplitextstrtimerD   rh   r\   ri   )r"   r5   rW   rV   rX   dirpathdirname	filenamesdfilename	file_pathfile_contenttransformed_coderequirements_listrk   existing_requirementsrequirements_file_contentsrequirementnew_output_pathr^   s                       r$   rT   z%Gen1toGen2Migration.WriteMigratedCode  s    (*wwt~~'>#) a'""4??3888 gaj
  (GGLL(3	<<	"))U2 ((,>>1$2L2LMMHH;BB9M 
**

3::9E
F//	:,040D0DL1
-
- oorwwy$..I+ #%
WW^^-.).)?)?!*& *&@&F&Ft&L# /k"77#**;7 / 
!
!+@!A WW^^K(  -a0diik"((-a01   KKBII
 *K

!
!+u
 ''/AA=?t99:
T77
8
?
?	
Js3rII("Q7 43{   (?D 43s   5M0MM'c                    | j                   j                  | j                  || j                  |      }d}g }|j                  j
                  }|D ]  }|j                  dk(  r|j                  j                  }|j                  dk(  s8|j                  j                  j                  }|D ]+  }	|j                  |	j                  j                                -  ||fS )aB  Calls MigrateCodeFile and gets the migrated code for a python file.

    Args:
      file_content: The contents of the python file.
      service_name: The service name.

    Returns:
      transformed_code: The migrated code for the python file.
      requirements_list: The list of requirements for the python file.
    rZ   codeAsStringpython3Requirements)r   MigrateCodeFiler    rf   rl   additionalPropertieskeyvaluestring_valuearray_valueentriesrz   strip)
r"   r   r5   	operationr   r   operation_responseproprequirementsentrys
             r$   rx   z#Gen1toGen2Migration.GetMigratedCodeV  s     //lD$<$<lI "++@@"	^	#::22	*	*zz--55!E

"
"5#5#5#;#;#=
> " # ...r&   N)__name__
__module____qualname____doc__r   rC   r+   r=   r*   rf   rj   rw   r%   r7   r(   r1   r2   r3   rS   rT   rx    r&   r$   r
   r
      se    K/5")-".+8()<0!BF'-R%-N8P8d/r&   r
   )r   r\   r   r   rP   rM   r~   googlecloudsdk.command_lib.appr   googlecloudsdk.corer   r   r   googlecloudsdk.core.utilr   r
   r   r&   r$   <module>r      s7    .  	     5 # * $ *Q/ Q/r&   