
    G                     0   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Jr  SSKJ	r	  SSK
Jr  SS	KJr  SS
KJr  SrSrSR%                  \\S9rS\-   rS\-   r " S S\5      rS rS rS rS rS rS rS rS r " S S\5      r  " S S\5      r!S S jr"g)!zUtilities for deriving services and configs from paths.

Paths are typically given as positional params, like
`gcloud app deploy <path1> <path2>...`.
    )absolute_import)division)unicode_literalsN)env)yaml_parsing)
exceptions)log)fileszChttps://cloud.google.com/appengine/docs/standard/reference/app-yamlzChttps://cloud.google.com/appengine/docs/flexible/reference/app-yamlzusing the directions at {flex} (App Engine flexible environment) or {std} (App Engine standard environment) under the tab for your language.)flexstdz4As an alternative, create an app.yaml file yourself zAn app.yaml (or appengine-web.xml) file is required to deploy this directory as an App Engine application. Create an app.yaml file c                   N    \ rS rSrSrS r\S 5       r\S 5       r\	S 5       r
Srg)	Service5   a3  Represents data around a deployable service.

Attributes:
  descriptor: str, File path to the original deployment descriptor, which is
    either a `<service>.yaml` or an `appengine-web.xml`.
  source: str, Path to the original deployable artifact or directory, which
    is typically the original source directory, but could also be an artifact
    such as a fat JAR file.
  service_info: yaml_parsing.ServiceYamlInfo, Info parsed from the
    `<service>.yaml` file. Note that service_info.file may point to a
    file in a staged directory.
  upload_dir: str, Path to the source directory. If staging is required, this
    points to the staged directory.
  service_id: str, the service id.
  path: str, File path to the staged deployment `<service>.yaml` descriptor
    or to the original one, if no staging is used.
c                 4    Xl         X l        X0l        X@l        g N)
descriptorsourceservice_info
upload_dir)selfr   r   r   r   s        1lib/googlecloudsdk/command_lib/app/deployables.py__init__Service.__init__H   s     OK$ O    c                 .    U R                   R                  $ r   )r   moduler   s    r   
service_idService.service_idN   s    ###r   c                 .    U R                   R                  $ r   )r   filer   s    r   pathService.pathR   s    !!!r   c                 >    U H  nU" XU5      nU(       d  M  Us  $    g)a9  Return a Service from a path using staging if necessary.

Args:
  path: str, Unsanitized absolute path, may point to a directory or a file
      of any type. There is no guarantee that it exists.
  stager: staging.Stager, stager that will be invoked if there is a runtime
      and environment match.
  path_matchers: List[Function], ordered list of functions on the form
      fn(path, stager), where fn returns a Service or None if no match.
  appyaml: str or None, the app.yaml location to used for deployment.

Returns:
  Service, if one can be derived, else None.
N )clsr"   stagerpath_matchersappyamlmatcherservices          r   FromPathService.FromPathV   s)      !g.g	 ! r   )r   r   r   r   N)__name__
__module____qualname____firstlineno____doc__r   propertyr   r"   classmethodr,   __static_attributes__r%   r   r   r   r   5   sH    $! $ $ " "  r   r   c                    [         R                  R                  U 5      (       a  U O[         R                  R                  U S5      n[         R                  R	                  U5      u  pE[         R                  R                  U5      (       a  US;   a{  [         R                  R                  U5      n[        R                  R                  U5      nUR                  X6UR                  UR                  U5      n[        X6Xx=(       d    U5      $ g)a  Generate a Service from an <service>.yaml source path.

This function is a path matcher that returns if and only if:
- `path` points to either a `<service>.yaml` or `<app-dir>` where
  `<app-dir>/app.yaml` exists.
- the yaml-file is a valid <service>.yaml file.

If the runtime and environment match an entry in the stager, the service will
be staged into a directory.

Args:
  path: str, Unsanitized absolute path, may point to a directory or a file of
      any type. There is no guarantee that it exists.
  stager: staging.Stager, stager that will be invoked if there is a runtime
      and environment match.
  appyaml: str or None, the app.yaml location to used for deployment.

Raises:
  staging.StagingCommandFailedError, staging command failed.

Returns:
  Service, fully populated with entries that respect a potentially
      staged deployable service, or None if the path does not match the
      pattern described.
app.yaml)z.yamlz.ymlN)osr"   isfilejoinsplitextexistsdirnamer   ServiceYamlInfoFromFileStageruntimer   r   )	r"   r'   r)   r   _extapp_dirr   staging_dirs	            r   ServiceYamlMatcherrF   m   s    4 ww~~d++td>H2J*77J'&!WW^^JC+<$<ggooj)G//88DL,,zL4H4H+//:K :6LWMM	r   c                    [         R                  R                  U 5      u  p4[         R                  R                  U 5      (       a  US;   a  [         R                  R	                  [         R                  R                  U [         R                  5      5      nU nUR                  XeS[        R                  U5      n[         R                  R                  US5      n[        R                  R                  U5      n	[        XeX5      $ g)a  Generate a Service from a Java fatjar path.

This function is a path matcher that returns if and only if:
- `jar_path` points to  a jar file .

The service will be staged according to the stager as a jar runtime,
which is defined in staging.py.

Args:
  jar_path: str, Unsanitized absolute path pointing to a file of jar type.
  stager: staging.Stager, stager that will be invoked if there is a runtime
    and environment match.
  appyaml: str or None, the app.yaml location to used for deployment.

Raises:
  staging.StagingCommandFailedError, staging command failed.

Returns:
  Service, fully populated with entries that respect a staged deployable
      service, or None if the path does not match the pattern described.
)z.jarzjava-jarr7   N)r8   r"   r;   r<   abspathr:   pardirr@   r   STANDARDr   r>   r?   r   )
jar_pathr'   r)   rB   rC   rD   r   rE   	yaml_pathr   s
             r   
JarMatcherrM      s    , 77H%&!WW^^H#/ggoobggll8RYY?@GJ,,zJ&(K[*5I//88CL:BB	r   c                 :   [         R                  R                  U 5      (       a  U O[         R                  R                  U S5      n[         R                  R	                  U5      n[         R                  R                  U5      (       a  US:X  a  [         R                  R                  U5      nUR                  X5S[        R                  U5      n[         R                  R                  US5      n[        R                  R                  U5      n[        X5X5      $ g)a  Generate a Service from an Maven project source path.

This function is a path matcher that returns true if and only if:
- `path` points to either a Maven `pom.xml` or `<maven=project-dir>` where
  `<maven-project-dir>/pom.xml` exists.

If the runtime and environment match an entry in the stager, the service will
be staged into a directory.

Args:
  path: str, Unsanitized absolute path, may point to a directory or a file of
    any type. There is no guarantee that it exists.
  stager: staging.Stager, stager that will be invoked if there is a runtime
    and environment match.
  appyaml: str or None, the app.yaml location to used for deployment.

Raises:
  staging.StagingCommandFailedError, staging command failed.

Returns:
  Service, fully populated with entries that respect a potentially
      staged deployable service, or None if the path does not match the
      pattern described.
zpom.xmlzjava-maven-projectr7   Nr8   r"   r9   r:   basenamer<   r=   r@   r   rJ   r   r>   r?   r   	r"   r'   r)   r   filenamerD   rE   rL   r   s	            r   PomXmlMatcherrS      s    2 ww~~d++tdI1N*WWj)(WW^^JH	$9ggooj)G,,z4H"||W6K[*5I//88CL:BB	r   c                 :   [         R                  R                  U 5      (       a  U O[         R                  R                  U S5      n[         R                  R	                  U5      n[         R                  R                  U5      (       a  US:X  a  [         R                  R                  U5      nUR                  X5S[        R                  U5      n[         R                  R                  US5      n[        R                  R                  U5      n[        X5X5      $ g)a  Generate a Service from an Gradle project source path.

This function is a path matcher that returns true if and only if:
- `path` points to either a Gradle `build.gradle` or `<gradle-project-dir>`
where `<gradle-project-dir>/build.gradle` exists.

If the runtime and environment match an entry in the stager, the service will
be staged into a directory.

Args:
  path: str, Unsanitized absolute path, may point to a directory or a file of
    any type. There is no guarantee that it exists.
  stager: staging.Stager, stager that will be invoked if there is a runtime
    and environment match.
  appyaml: str or None, the app.yaml location to used for deployment.

Raises:
  staging.StagingCommandFailedError, staging command failed.

Returns:
  Service, fully populated with entries that respect a potentially
      staged deployable service, or None if the path does not match the
      pattern described.
zbuild.gradlezjava-gradle-projectr7   NrO   rQ   s	            r   BuildGradleMatcherrU      s    2 ww~~d++t
N2*WWj)(WW^^JH$>ggooj)G,,z4I"||W6K[*5I//88CL:BB	r   c                    [         R                  R                  [         R                  SS5      nU R	                  U5      (       a  U S[        U5      *  OU n[         R                  R                  USS5      n[         R                  R                  U5      (       d  g[        R                  " U5      nSU;   d  SU;   a  [        R                  " S5        UR                  XTS[        R                  U5      nU(       d  g[         R                  R                  US5      n[        R                  R!                  U5      n	[#        XTX5      $ )	a  Generate a Service from an appengine-web.xml source path.

This function is a path matcher that returns if and only if:
- `path` points to either `.../WEB-INF/appengine-web.xml` or `<app-dir>` where
  `<app-dir>/WEB-INF/appengine-web.xml` exists.
- the xml-file is a valid appengine-web.xml file according to the Java stager.

The service will be staged according to the stager as a java-xml runtime,
which is defined in staging.py.

Args:
  path: str, Unsanitized absolute path, may point to a directory or a file of
      any type. There is no guarantee that it exists.
  stager: staging.Stager, stager that will be invoked if there is a runtime
      and environment match.
  appyaml: str or None, the app.yaml location to used for deployment.

Raises:
  staging.StagingCommandFailedError, staging command failed.

Returns:
  Service, fully populated with entries that respect a staged deployable
      service, or None if the path does not match the pattern described.
zWEB-INFzappengine-web.xmlNz<application>z	<version>zM<application> and <version> elements in `appengine-web.xml` are not respectedzjava-xmlr7   )r8   r"   r:   sependswithlenr9   r
   ReadFileContentsr	   warningr@   r   rJ   r   r>   r?   r   )
r"   r'   r)   suffixrD   r   xml_filerE   rL   r   s
             r   AppengineWebMatcherr^     s    2 77<<	+>?&#'==#8#8D3v;,d'ww||GY0CD*	
	#	###J/( K8$;KK 8 9 Z*cll$&+	ggll;
3)--66yA,	l	@@r   c                     U(       aI  [         R                  R                  U5      nUR                  X SUR                  U5      n[        X X45      $ g)a  Use optional app.yaml with a directory or a file the user wants to deploy.

Args:
  path: str, Unsanitized absolute path, may point to a directory or a file of
    any type. There is no guarantee that it exists.
  stager: staging.Stager, stager that will not be invoked.
  appyaml: str or None, the app.yaml location to used for deployment.

Returns:
  Service, fully populated with entries that respect a staged deployable
      service, or None if there is no optional --appyaml flag usage.
zgeneric-copyN)r   r>   r?   r@   r   r   )r"   r'   r)   r   rE   s        r   ExplicitAppYamlMatcherr`   /  sJ     //88AL,,wnl>N>N&(K7,<<	r   c                     AA[         R                  R                  U 5      (       a  [        R                  " [
        5        g)aY  Points out to the user that they need an app.yaml to deploy.

Args:
  path: str, Unsanitized absolute path, may point to a directory or a file of
      any type. There is no guarantee that it exists.
  stager: staging.Stager, stager that will not be invoked.
  appyaml: str or None, the app.yaml location to used for deployment.
Returns:
  None
N)r8   r"   isdirr	   errorNO_YAML_ERROR)r"   r'   r)   s      r   UnidentifiedDirMatcherre   E  s+     gWW]]4IIm	r   c                  L    [         [        [        [        [        [
        [        /$ )zGet list of path matchers ordered by descending precedence.

Returns:
  List[Function], ordered list of functions on the form fn(path, stager),
  where fn returns a Service or None if no match.
)rF   r^   rM   rS   rU   r`   re   r%   r   r   GetPathMatchersrg   V  s!     -z=02H
 r   c                   .    \ rS rSrSrSS jrS rS rSrg)	Servicesic  z"Collection of deployable services.Nc                 |    [         R                  " 5       U l        U(       a  U H  nU R                  U5        M     gg)zInstantiate a set of deployable services.

Args:
  services: List[Service], optional list of services for quick
      initialization.

Raises:
  DuplicateServiceError: Two or more services have the same service id.
N)collectionsOrderedDict	_servicesAdd)r   servicesds      r   r   Services.__init__f  s1     !,,.DN!  r   c                     U R                   R                  UR                  5      nU(       a6  [        R                  " UR
                  UR
                  UR                  5      eXR                   UR                  '   g)zAdd a deployable service to the set.

Args:
  service: Service, to add.

Raises:
  DuplicateServiceError: Two or more services have the same service id.
N)rm   getr   r   DuplicateServiceErrorr"   )r   r+   existings      r   rn   Services.Addu  s]     ~~!!'"4"45H,,X]]GLL-4-?-?A A)0NN7%%&r   c                 H    [        U R                  R                  5       5      $ )zmRetrieve the service info objects in the order they were added.

Returns:
  List[Service], list of services.
)listrm   valuesr   s    r   GetAllServices.GetAll  s     %%'((r   )rm   r   	r.   r/   r0   r1   r2   r   rn   rz   r5   r%   r   r   ri   ri   c  s    *1)r   ri   c                   *    \ rS rSrSrS rS rS rSrg)Configsi  zCollection of config files.c                 8    [         R                  " 5       U l        g r   )rk   rl   _configsr   s    r   r   Configs.__init__  s    ++-DMr   c                     UR                   nU R                  R                  U5      nU(       a,  [        R                  " UR
                  UR
                  U5      eXR                  U'   g)zAdd a ConfigYamlInfo to the set of configs.

Args:
  config: ConfigYamlInfo, the config to add.

Raises:
  exceptions.DuplicateConfigError, the config type is already in the set.
N)configr   rs   r   DuplicateConfigErrorr!   )r   r   config_typeru   s       r   rn   Configs.Add  sR     --K}}  -H++HMM6;;,79 9!'MM+r   c                 H    [        U R                  R                  5       5      $ )z~Retreive the config file objects in the order they were added.

Returns:
  List[ConfigYamlInfo], list of config file objects.
)rx   r   ry   r   s    r   rz   Configs.GetAll  s     $$&''r   )r   Nr|   r%   r   r   r~   r~     s    #.( (r   r~   c                    U (       d  S/n U  Vs/ s H"  n[         R                  R                  U5      PM$     nn[        5       n[	        5       nU(       a  [        U5      S:  a  [        R                  " 5       e[         R                  R                  [         R                  R                  U5      5      (       d%  [        R                  " SR                  U5      5      e[         R                  R                  US   5      (       d  [        R                  " US   5      eU H  n[         R                  R                  U5      (       d  [        R                  " U5      e[        R                  R                  U5      n	U	(       a  UR                  U	5        Mv  [        R!                  XX#5      n
U
(       a  UR                  U
5        M  [        R"                  " U5      e   UR%                  5       UR%                  5       4$ s  snf )a)  Given a list of args, infer the deployable services and configs.

Given a deploy command, e.g. `gcloud app deploy ./dir other/service.yaml
cron.yaml WEB-INF/appengine-web.xml`, the deployables can be on multiple
forms. This method pre-processes and infers yaml descriptors from the
various formats accepted. The rules are as following:

This function is a context manager, and should be used in conjunction with
the `with` keyword.

1. If `args` is an empty list, add the current directory to it.
2. For each arg:
  - If arg refers to a config file, add it to the configs set.
  - Else match the arg against the path matchers. The first match will win.
    The match will be added to the services set. Matchers may run staging.

Args:
  args: List[str], positional args as given on the command-line.
  stager: staging.Stager, stager that will be invoked on sources that have
      entries in the stager's registry.
  path_matchers: List[Function], list of functions on the form
      fn(path, stager) ordered by descending precedence, where fn returns
      a Service or None if no match.
  appyaml: str or None, the app.yaml location to used for deployment.

Raises:
  FileNotFoundError: One or more argument does not point to an existing file
      or directory.
  UnknownSourceError: Could not infer a config or service from an arg.
  DuplicateConfigError: Two or more config files have the same type.
  DuplicateServiceError: Two or more services have the same service id.

Returns:
  Tuple[List[Service], List[ConfigYamlInfo]], lists of deployable services
  and configs.
.   z0File {0} referenced by --appyaml does not exist.r   )r8   r"   rH   r~   ri   rY   r   MultiDeployErrorr<   FileNotFoundErrorformatr   ConfigYamlInfor?   rn   r   r,   UnknownSourceErrorrz   )argsr'   r(   r)   argpathsconfigsro   r"   r   r+   s              r   GetDeployablesr     su   J 
5D+/
04C277??34%
0I'Z(
5zA~''))77>>"''//'233(( *;;A6'?L L77>>%(##((q22d77>>$((..((11$7Fkk&t]DGll7

'
'
--  
	GNN,	,,1 1s   )G?r   )#r2   
__future__r   r   r   rk   r8   googlecloudsdk.api_lib.appr   r   googlecloudsdk.command_lib.appr   googlecloudsdk.corer	   googlecloudsdk.core.utilr
   _STANDARD_APP_YAML_URL_FLEXIBLE_APP_YAML_URLr   APP_YAML_INSTRUCTIONSFINGERPRINTING_WARNINGrd   objectr   rF   rM   rS   rU   r^   r`   re   rg   ri   r~   r   r%   r   r   <module>r      s    '  '  	 * 3 5 # * J  J I&$*@&A  ; G 5f 5p$ND"J#L+A\,"
')v ')T(f (>?-r   