
    G                     4   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mZ ddlm	Z	 ddl
mZ dd	lmZ dd
lmZ dZdZdj%                  ee      Zdez   Zdez   Z G d de      Zd Zd Zd Zd Zd Zd Zd Zd Z G d de      Z  G d de      Z!d dZ"y)!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                   F    e Zd ZdZd Zed        Zed        Zed        Z	y)ServiceaQ  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                 <    || _         || _        || _        || _        y N)
descriptorsourceservice_info
upload_dir)selfr   r   r   r   s        1lib/googlecloudsdk/command_lib/app/deployables.py__init__zService.__init__H   s     DODK$D DO    c                 .    | j                   j                  S r   )r   moduler   s    r   
service_idzService.service_idN   s    ###r   c                 .    | j                   j                  S r   )r   filer   s    r   pathzService.pathR   s    !!!r   c                 2    |D ]  } ||||      }|s|c S  y)ae  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   FromPathzService.FromPathV   s)      !fg.g	 ! r   N)
__name__
__module____qualname____doc__r   propertyr   r   classmethodr(   r!   r   r   r   r   5   sH    $! $ $ " "  r   r   c                    t         j                  j                  |       r| nt         j                  j                  | d      }t         j                  j	                  |      \  }}t         j                  j                  |      r}|dv ryt         j                  j                  |      }t        j                  j                  |      }|j                  |||j                  |j                  |      }t        ||||xs |      S y)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   ServiceYamlMatcherr?   m   s    4 ww~~d+td>H2J*77J'&!SWW^^JC+<$<ggooj)G//88DL,,z7L4H4H+//:K :wk6LWMM	r   c                    t         j                  j                  |       \  }}t         j                  j                  |       r|dv rt         j                  j	                  t         j                  j                  | t         j                              }| }|j                  ||dt        j                  |      }t         j                  j                  |d      }t        j                  j                  |      }	t        |||	|      S y)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-jarr0   N)r1   r   r4   r5   abspathr3   pardirr9   r   STANDARDr   r7   r8   r   )
jar_pathr#   r%   r;   r<   r=   r   r>   	yaml_pathr   s
             r   
JarMatcherrF      s    , 77H%&!SWW^^H#/ggoobggll8RYY?@GJ,,z7J&(K[*5I//88CL:wkBB	r   c                 *   t         j                  j                  |       r| nt         j                  j                  | d      }t         j                  j	                  |      }t         j                  j                  |      r|dk(  rt         j                  j                  |      }|j                  ||dt        j                  |      }t         j                  j                  |d      }t        j                  j                  |      }t        ||||      S y)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-projectr0   Nr1   r   r2   r3   basenamer5   r6   r9   r   rC   r   r7   r8   r   	r   r#   r%   r   filenamer=   r>   rE   r   s	            r   PomXmlMatcherrL      s    2 ww~~d+tdI1N*WWj)(WW^^JH	$9ggooj)G,,z74H"||W6K[*5I//88CL:wkBB	r   c                 *   t         j                  j                  |       r| nt         j                  j                  | d      }t         j                  j	                  |      }t         j                  j                  |      r|dk(  rt         j                  j                  |      }|j                  ||dt        j                  |      }t         j                  j                  |d      }t        j                  j                  |      }t        ||||      S y)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-projectr0   NrH   rJ   s	            r   BuildGradleMatcherrN      s    2 ww~~d+t
N2*WWj)(WW^^JH$>ggooj)G,,z74I"||W6K[*5I//88CL:wkBB	r   c                 p   t         j                  j                  t         j                  dd      }| j	                  |      r| dt        |        n| }t         j                  j                  |dd      }t         j                  j                  |      syt        j                  |      }d|v sd|v rt        j                  d       |j                  ||dt        j                  |      }|syt         j                  j                  |d      }t        j                  j!                  |      }	t#        |||	|      S )	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-xmlr0   )r1   r   r3   sependswithlenr2   r
   ReadFileContentsr	   warningr9   r   rC   r   r7   r8   r   )
r   r#   r%   suffixr=   r   xml_filer>   rE   r   s
             r   AppengineWebMatcherrW     s    2 77<<	+>?&#'==#8D3v;,d'ww||GY0CD*	
	###J/( K8$;KK 8 9 Z*cll$&+	ggll;
3)--66yA,	WlK	@@r   c                     |rLt         j                  j                  |      }|j                  || d|j                  |      }t        || ||      S y)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   r7   r8   r9   r   r   )r   r#   r%   r   r>   s        r   ExplicitAppYamlMatcherrY   /  sP     //88AL,,wnl>N>N&(K7D,<<	r   c                 x    ~~t         j                  j                  |       rt        j                  t
               y)ai  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)r1   r   isdirr	   errorNO_YAML_ERROR)r   r#   r%   s      r   UnidentifiedDirMatcherr^   E  s(     gWW]]4IIm	r   c                  L    t         t        t        t        t        t
        t        gS )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.
  )r?   rW   rF   rL   rN   rY   r^   r!   r   r   GetPathMatchersr`   V  s!     -z=02H
 r   c                   $    e Zd ZdZddZd Zd Zy)Servicesz"Collection of deployable services.Nc                 l    t        j                         | _        |r|D ]  }| j                  |        yy)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   zServices.__init__f  s1     !,,.DN!  r   c                     | j                   j                  |j                        }|r5t        j                  |j
                  |j
                  |j                        || j                   |j                  <   y)zAdd a deployable service to the set.

    Args:
      service: Service, to add.

    Raises:
      DuplicateServiceError: Two or more services have the same service id.
    N)rf   getr   r   DuplicateServiceErrorr   )r   r'   existings      r   rg   zServices.Addu  s_     ~~!!'"4"45H,,X]]GLL-4-?-?A A)0DNN7%%&r   c                 H    t        | j                  j                               S )zyRetrieve the service info objects in the order they were added.

    Returns:
      List[Service], list of services.
    )listrf   valuesr   s    r   GetAllzServices.GetAll  s     %%'((r   r   r)   r*   r+   r,   r   rg   rq   r!   r   r   rb   rb   c  s    *1)r   rb   c                   "    e Zd ZdZd Zd Zd Zy)ConfigszCollection of config files.c                 6    t        j                         | _        y r   )rd   re   _configsr   s    r   r   zConfigs.__init__  s    ++-DMr   c                     |j                   }| j                  j                  |      }|r+t        j                  |j
                  |j
                  |      || j                  |<   y)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)configrv   rk   r   DuplicateConfigErrorr   )r   rx   config_typerm   s       r   rg   zConfigs.Add  sT     --K}}  -H++HMM6;;,79 9!'DMM+r   c                 H    t        | j                  j                               S )zRetreive the config file objects in the order they were added.

    Returns:
      List[ConfigYamlInfo], list of config file objects.
    )ro   rv   rp   r   s    r   rq   zConfigs.GetAll  s     $$&''r   Nrr   r!   r   r   rt   rt     s    #.( (r   rt   c                    | sdg} | D cg c]!  }t         j                  j                  |      # }}t               }t	               }|rt        |      dkD  rt        j                         t         j                  j                  t         j                  j                  |            s$t        j                  dj                  |            t         j                  j                  |d         st        j                  |d         |D ]  }t         j                  j                  |      st        j                  |      t        j                  j                  |      }	|	r|j                  |	       jt        j!                  ||||      }
|
r|j                  |
       t        j"                  |       |j%                         |j%                         fS c c}w )ac  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   )r1   r   rA   rt   rb   rR   r   MultiDeployErrorr5   FileNotFoundErrorformatr   ConfigYamlInfor8   rg   r   r(   UnknownSourceErrorrq   )argsr#   r$   r%   argpathsconfigsrh   r   rx   r'   s              r   GetDeployablesr     sp   J 
5D+/
04C277??34%
0I'Z(
5zA~''))77>>"''//'23(( *;;A6'?L L77>>%(#((q22d77>>$((..((11$7Fkk&tV]GDGll7

'
'
--  
	GNN,	,,1 1s   &Gr   )#r,   
__future__r   r   r   rd   r1   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_WARNINGr]   objectr   r?   rF   rL   rN   rW   rY   r^   r`   rb   rt   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   