Exemplo n.º 1
0
 def testDelete_OneServiceMultiVersion(self):
   """Test deletion of two versions in single service."""
   self._ExpectListAllVersionsForServices(['service1'])
   versions = [version_util.Version(self.Project(), 'service1', 'v1'),
               version_util.Version(self.Project(), 'service1', 'v2')]
   self.ExpectDeleteServicesAndVersions(versions=versions)
   self.Run('app versions delete v1 v2 --service=service1')
   self.AssertVersionMessages(versions)
Exemplo n.º 2
0
 def testDelete_AllServicesOneVersionInMultiServices(self):
   """Test deletion of single version that exists in two services."""
   self._ExpectListAllServicesAndVersions()
   versions = [
       version_util.Version(self.Project(), 'service1', 'v2'),
       version_util.Version(self.Project(), 'service2', 'v2')
   ]
   self.ExpectDeleteServicesAndVersions(versions=versions)
   self.Run('app versions delete v2')
   self.AssertVersionMessages(versions)
Exemplo n.º 3
0
 def testDelete_AllServicesMultiVersions(self):
   """Test deleting multiple versions that exist in multiple services."""
   self._ExpectListAllServicesAndVersions()
   versions = [
       version_util.Version(self.Project(), 'service1', 'v2'),
       version_util.Version(self.Project(), 'service1', 'v4'),
       version_util.Version(self.Project(), 'service2', 'v2'),
   ]
   self.ExpectDeleteServicesAndVersions(versions=versions)
   self.Run('app versions delete v2 v4')
   self.AssertVersionMessages(versions)
Exemplo n.º 4
0
 def testDelete_MultiServiceAllVersions(self):
   """Same as above, with multiple services whose versions are all deleted."""
   self._ExpectListAllServicesAndVersions()
   services = [service_util.Service(self.Project(), 'service2'),
               service_util.Service(self.Project(), 'service3')]
   versions = [version_util.Version(self.Project(), 'service1', 'v1'),
               version_util.Version(self.Project(), 'service1', 'v2')]
   self.ExpectDeleteServicesAndVersions(services, versions)
   self.Run('app versions delete v1 v2 v5')
   self.AssertServiceMessages(services)
   self.AssertVersionMessages(versions)
Exemplo n.º 5
0
 def testDelete_DefaultServiceOneVersion(self):
   """Test delete single version from default service."""
   self._ExpectListAllVersionsForServices(['default'])
   versions = [version_util.Version(self.Project(), 'default', 'v6')]
   self.ExpectDeleteServicesAndVersions(versions=versions)
   self.Run('app versions delete v6 --service=default')
   self.AssertVersionMessages(versions)
Exemplo n.º 6
0
    def Run(self, args):
        api_client = appengine_api_client.GetApiClient()
        # Why do this? It lets us know if we're missing services up front (fail
        # fast), and we get to control the error messages
        all_services = api_client.ListServices()

        services = service_util.GetMatchingServices(all_services,
                                                    args.services)

        if args.version:
            console_io.PromptContinue(
                'Deleting version [{0}] of {1} [{2}].'.format(
                    args.version, text.Pluralize(len(services), 'service'),
                    ', '.join(map(str, services))),
                cancel_on_no=True)
            versions = [
                version_util.Version(api_client.project, s.id, args.version)
                for s in services
            ]
            version_util.DeleteVersions(api_client, versions)
        else:
            console_io.PromptContinue('Deleting {0} [{1}].'.format(
                text.Pluralize(len(services), 'service'),
                ', '.join(map(str, services))),
                                      cancel_on_no=True)
            service_util.DeleteServices(api_client, services)
Exemplo n.º 7
0
 def testDelete_AllServicesOneVersionInOneService(self):
   """Same as above but version only exists in one service."""
   self._ExpectListAllServicesAndVersions()
   versions = [
       version_util.Version(self.Project(), 'service1', 'v4')
   ]
   self.ExpectDeleteServicesAndVersions(versions=versions)
   self.Run('app versions delete v4')
   self.AssertVersionMessages(versions)
Exemplo n.º 8
0
 def testDelete_OneServiceOneVersion(self):
   """Ensure `delete` command deletes single version in single service."""
   self._ExpectListAllVersionsForServices(['service1'])
   versions = [
       version_util.Version(self.Project(), 'service1', 'v1')
   ]
   self.ExpectDeleteServicesAndVersions(versions=versions)
   self.Run('app versions delete v1 --service=service1')
   self.AssertVersionMessages(versions)
Exemplo n.º 9
0
 def SetUp(self):
     self.service_mock = mock.MagicMock()
     self.fake_image = 'appengine.gcr.io/gcloud/1.default'
     fake_image_artifact = build.BuildArtifact.MakeImageArtifact(
         self.fake_image)
     self.fake_image_artifact = fake_image_artifact
     self.fake_version = version_util.Version(self.Project(), 'default',
                                              '1')
     deploy_options = deploy_util.DeployOptions.FromProperties(
         runtime_builders.RuntimeBuilderStrategy.NEVER,
         deploy_util.FlexImageBuildOptions.ON_CLIENT)
     self.deployer = deploy_util.ServiceDeployer(self.mock_client,
                                                 deploy_options)
Exemplo n.º 10
0
    def testVersionResourceNonexistent(self):
        """Promoting a version whose current status cannot be determined."""
        all_services = {}
        new_version = version_util.Version('prj1', 'svc1', 'v1')

        # Fail to lookup a Version resource, so the code doesn't know whether the
        # version is currently serving.
        self.api_client.GetVersionResource.return_value = None

        version_util.PromoteVersion(all_services,
                                    new_version,
                                    self.api_client,
                                    stop_previous_version=False)

        # Don't call StartVersion because we're not sure it's necessary.
        self.api_client.StartVersion.assert_not_called()

        self.api_client.SetDefaultVersion.assert_called_once_with('svc1', 'v1')
Exemplo n.º 11
0
    def testErrorFetchingVersionResource(self):
        """An error occurs fetching the Version resource."""
        all_services = {}
        new_version = version_util.Version('prj1', 'svc1', 'v1')

        # Fail to lookup a Version resource, so the code doesn't know whether the
        # version is currently serving.
        self.api_client.GetVersionResource.side_effect = (
            apitools_exceptions.CommunicationError('failed'))

        version_util.PromoteVersion(all_services,
                                    new_version,
                                    self.api_client,
                                    stop_previous_version=False)

        # Don't call StartVersion because we're not sure it's necessary.
        self.api_client.StartVersion.assert_not_called()

        self.api_client.SetDefaultVersion.assert_called_once_with('svc1', 'v1')
Exemplo n.º 12
0
    def testVersionAlreadyServing(self):
        """Promoting a version that is already serving."""
        all_services = {}
        new_version = version_util.Version('prj1', 'svc1', 'v1')

        # New version is already deployed and serving.
        version_resource = self.messages.Version()
        version_resource.servingStatus = (
            self.messages.Version.ServingStatusValueValuesEnum.SERVING)
        self.api_client.GetVersionResource.return_value = version_resource

        version_util.PromoteVersion(all_services,
                                    new_version,
                                    self.api_client,
                                    stop_previous_version=False)

        # Don't call StartVersion because it's already started.
        self.api_client.StartVersion.assert_not_called()

        self.api_client.SetDefaultVersion.assert_called_once_with('svc1', 'v1')
Exemplo n.º 13
0
    def testStartStoppedVersion(self):
        """Verifies that a stopped version is started before promoting it."""
        all_services = {}
        new_version = version_util.Version('prj1', 'svc1', 'v1')

        # New version is already deployed but is STOPPED.
        version_resource = self.messages.Version()
        version_resource.servingStatus = (
            self.messages.Version.ServingStatusValueValuesEnum.STOPPED)
        self.api_client.GetVersionResource.return_value = version_resource

        version_util.PromoteVersion(all_services,
                                    new_version,
                                    self.api_client,
                                    stop_previous_version=False)

        # Start the new version before promoting it.
        self.api_client.StartVersion.assert_called_once_with('svc1',
                                                             'v1',
                                                             block=True)

        self.api_client.SetDefaultVersion.assert_called_once_with('svc1', 'v1')
Exemplo n.º 14
0
def RunDeploy(args,
              enable_endpoints=False,
              app_create=False,
              use_beta_stager=False):
    """Perform a deployment based on the given args.

  Args:
    args: argparse.Namespace, An object that contains the values for the
        arguments specified in the ArgsDeploy() function.
    enable_endpoints: Enable Cloud Endpoints for the deployed app.
    app_create: Offer to create an app if current GCP project is appless.
    use_beta_stager: Use the stager registry defined for the beta track rather
        than the default stager registry.

  Returns:
    A dict on the form `{'versions': new_versions, 'configs': updated_configs}`
    where new_versions is a list of version_util.Version, and updated_configs
    is a list of config file identifiers, see yaml_parsing.ConfigYamlInfo.
  """
    version_id = args.version or util.GenerateVersionId()
    flags.ValidateVersion(version_id)
    project = properties.VALUES.core.project.Get(required=True)
    deploy_options = DeployOptions.FromProperties(enable_endpoints, app_create)

    # Parse existing app.yamls or try to generate a new one if the directory is
    # empty.
    if not args.deployables:
        yaml_path = deploy_command_util.DEFAULT_DEPLOYABLE
        if not os.path.exists(deploy_command_util.DEFAULT_DEPLOYABLE):
            log.warning('Automatic app detection is currently in Beta')
            yaml_path = deploy_command_util.CreateAppYamlForAppDirectory(
                os.getcwd())
        app_config = yaml_parsing.AppConfigSet([yaml_path])
    else:
        app_config = yaml_parsing.AppConfigSet(args.deployables)

    # If applicable, sort services by order they were passed to the command.
    services = app_config.Services()

    if not args.skip_image_url_validation:
        flags.ValidateImageUrl(args.image_url, services)

    # The new API client.
    api_client = appengine_api_client.GetApiClient()
    # pylint: disable=protected-access
    log.debug('API endpoint: [{endpoint}], API version: [{version}]'.format(
        endpoint=api_client.client.url, version=api_client.client._VERSION))
    # The legacy admin console API client.
    # The Admin Console API existed long before the App Engine Admin API, and
    # isn't being improved. We're in the process of migrating all of the calls
    # over to the Admin API, but a few things (notably config deployments) haven't
    # been ported over yet.
    ac_client = appengine_client.AppengineClient(args.server,
                                                 args.ignore_bad_certs)

    app = _PossiblyCreateApp(api_client, project, deploy_options.app_create)

    if properties.VALUES.app.use_gsutil.GetBool():
        log.warning(
            'Your gcloud installation has a deprecated config property '
            'enabled: [app/use_gsutil], which will be removed in a '
            'future version.  Run `gcloud config unset app/use_gsutil` to '
            'switch to the recommended approach.  If you encounter any '
            'issues, please report using `gcloud feedback`.  To revert '
            'temporarily, run `gcloud config set app/use_gsutil True`.\n')

    # Tell the user what is going to happen, and ask them to confirm.
    deployed_urls = output_helpers.DisplayProposedDeployment(
        app, project, app_config, version_id, deploy_options.promote)
    console_io.PromptContinue(cancel_on_no=True)
    if services:
        # Do generic app setup if deploying any services.
        # All deployment paths for a service involve uploading source to GCS.
        code_bucket_ref = args.bucket or flags.GetCodeBucket(app, project)
        metrics.CustomTimedEvent(metric_names.GET_CODE_BUCKET)
        log.debug(
            'Using bucket [{b}].'.format(b=code_bucket_ref.ToBucketUrl()))

        # Prepare Flex if any service is going to deploy an image.
        if any([m.RequiresImage() for m in services.values()]):
            deploy_command_util.DoPrepareManagedVms(ac_client)

        all_services = dict([(s.id, s) for s in api_client.ListServices()])
    else:
        code_bucket_ref = None
        all_services = {}
    new_versions = []
    if args.skip_staging:
        stager = staging.GetNoopStager()
    elif use_beta_stager:
        stager = staging.GetBetaStager()
    else:
        stager = staging.GetStager()
    deployer = ServiceDeployer(api_client, stager, deploy_options)

    for name, service in services.iteritems():
        new_version = version_util.Version(project, name, version_id)
        deployer.Deploy(service, new_version, code_bucket_ref, args.image_url,
                        all_services)
        new_versions.append(new_version)
        log.status.Print('Deployed service [{0}] to [{1}]'.format(
            name, deployed_urls[name]))

    # Deploy config files.
    for (name, config) in app_config.Configs().iteritems():
        message = 'Updating config [{config}]'.format(config=name)
        with progress_tracker.ProgressTracker(message):
            ac_client.UpdateConfig(name, config.parsed)

    updated_configs = app_config.Configs().keys()

    PrintPostDeployHints(new_versions, updated_configs)

    # Return all the things that were deployed.
    return {'versions': new_versions, 'configs': updated_configs}
def RunDeploy(unused_self, args, enable_endpoints=False, app_create=False):
    """Perform a deployment based on the given args."""
    version_id = args.version or util.GenerateVersionId()
    flags.ValidateVersion(version_id)
    project = properties.VALUES.core.project.Get(required=True)
    deploy_options = DeployOptions.FromProperties(enable_endpoints, app_create)

    # Parse existing app.yamls or try to generate a new one if the directory is
    # empty.
    if not args.deployables:
        yaml_path = deploy_command_util.DEFAULT_DEPLOYABLE
        if not os.path.exists(deploy_command_util.DEFAULT_DEPLOYABLE):
            log.warning('Automatic app detection is currently in Beta')
            yaml_path = deploy_command_util.CreateAppYamlForAppDirectory(
                os.getcwd())
        app_config = yaml_parsing.AppConfigSet([yaml_path])
    else:
        app_config = yaml_parsing.AppConfigSet(args.deployables)

    services = app_config.Services()

    if not args.skip_image_url_validation:
        flags.ValidateImageUrl(args.image_url, services)

    # The new API client.
    api_client = appengine_api_client.GetApiClient()
    # pylint: disable=protected-access
    log.debug('API endpoint: [{endpoint}], API version: [{version}]'.format(
        endpoint=api_client.client.url, version=api_client.client._VERSION))
    # The legacy admin console API client.
    # The Admin Console API existed long before the App Engine Admin API, and
    # isn't being improved. We're in the process of migrating all of the calls
    # over to the Admin API, but a few things (notably config deployments) haven't
    # been ported over yet.
    ac_client = appengine_client.AppengineClient(args.server,
                                                 args.ignore_bad_certs)

    app = _PossiblyCreateApp(api_client, project, deploy_options.app_create)

    # Tell the user what is going to happen, and ask them to confirm.
    deployed_urls = output_helpers.DisplayProposedDeployment(
        app, project, app_config, version_id, deploy_options.promote)
    console_io.PromptContinue(cancel_on_no=True)
    if services:
        # Do generic app setup if deploying any services.
        # All deployment paths for a service involve uploading source to GCS.
        code_bucket_ref = args.bucket or flags.GetCodeBucket(app, project)
        metrics.CustomTimedEvent(metric_names.GET_CODE_BUCKET)
        log.debug(
            'Using bucket [{b}].'.format(b=code_bucket_ref.ToBucketUrl()))

        # Prepare Flex if any service is going to deploy an image.
        if any([m.RequiresImage() for m in services.values()]):
            deploy_command_util.DoPrepareManagedVms(ac_client)

        all_services = dict([(s.id, s) for s in api_client.ListServices()])
    else:
        code_bucket_ref = None
        all_services = {}

    new_versions = []
    stager = staging.GetNoopStager(
    ) if args.skip_staging else staging.GetStager()
    deployer = ServiceDeployer(api_client, stager, deploy_options)
    for (name, service) in services.iteritems():
        new_version = version_util.Version(project, name, version_id)
        deployer.Deploy(service, new_version, code_bucket_ref, args.image_url,
                        all_services)
        new_versions.append(new_version)
        log.status.Print('Deployed service [{0}] to [{1}]'.format(
            name, deployed_urls[name]))

    # Deploy config files.
    for (name, config) in app_config.Configs().iteritems():
        message = 'Updating config [{config}]'.format(config=name)
        with progress_tracker.ProgressTracker(message):
            ac_client.UpdateConfig(name, config.parsed)

    updated_configs = app_config.Configs().keys()

    PrintPostDeployHints(new_versions, updated_configs)

    # Return all the things that were deployed.
    return {'versions': new_versions, 'configs': updated_configs}
    def Run(self, args):
        project = properties.VALUES.core.project.Get(required=True)
        version = args.version or util.GenerateVersionId()
        use_cloud_build = properties.VALUES.app.use_cloud_build.GetBool()

        config_cleanup = None
        if args.deployables:
            app_config = yaml_parsing.AppConfigSet(args.deployables)
        else:
            if not os.path.exists(DEFAULT_DEPLOYABLE):
                console_io.PromptContinue(
                    'Deployment to Google App Engine requires an app.yaml file. '
                    'This command will run `gcloud preview app gen-config` to generate '
                    'an app.yaml file for you in the current directory (if the current '
                    'directory does not contain an App Engine module, please answer '
                    '"no").',
                    cancel_on_no=True)
                # This generates the app.yaml AND the Dockerfile (and related files).
                params = ext_runtime.Params(deploy=True)
                configurator = fingerprinter.IdentifyDirectory(os.getcwd(),
                                                               params=params)
                if configurator is None:
                    raise NoAppIdentifiedError(
                        'Could not identify an app in the current directory.\n\n'
                        'Please prepare an app.yaml file for your application manually '
                        'and deploy again.')
                config_cleanup = configurator.GenerateConfigs()
                log.status.Print(
                    '\nCreated [{0}] in the current directory.\n'.format(
                        DEFAULT_DEPLOYABLE))
            app_config = yaml_parsing.AppConfigSet([DEFAULT_DEPLOYABLE])

        # If the app has enabled Endpoints API Management features, pass
        # control to the cloud_endpoints handler.
        for _, module in app_config.Modules().items():
            if module and module.parsed and module.parsed.beta_settings:
                bs = module.parsed.beta_settings
                use_endpoints = bs.get('use_endpoints_api_management',
                                       '').lower()
                if (use_endpoints in ('true', '1', 'yes')
                        and bs.get('endpoints_swagger_spec_file')):
                    cloud_endpoints.PushServiceConfig(
                        bs.get('endpoints_swagger_spec_file'), project,
                        apis.GetClientInstance('servicemanagement', 'v1',
                                               self.Http()),
                        apis.GetMessagesModule('servicemanagement', 'v1'))

        remote_build = True
        docker_build_property = properties.VALUES.app.docker_build.Get()
        if args.docker_build:
            remote_build = args.docker_build == 'remote'
        elif docker_build_property:
            remote_build = docker_build_property == 'remote'

        clients = _AppEngineClients(
            appengine_client.AppengineClient(args.server,
                                             args.ignore_bad_certs),
            appengine_api_client.GetApiClient(self.Http(timeout=None)))
        log.debug(
            'API endpoint: [{endpoint}], API version: [{version}]'.format(
                endpoint=clients.api.client.url,
                version=clients.api.api_version))
        cloudbuild_client = apis.GetClientInstance('cloudbuild', 'v1',
                                                   self.Http())
        storage_client = apis.GetClientInstance('storage', 'v1', self.Http())
        promote = properties.VALUES.app.promote_by_default.GetBool()
        deployed_urls = _DisplayProposedDeployment(project, app_config,
                                                   version, promote)
        if args.version or promote:
            # Prompt if there's a chance that you're overwriting something important:
            # If the version is set manually, you could be deploying over something.
            # If you're setting the new deployment to be the default version, you're
            # changing the target of the default URL.
            # Otherwise, all existing URLs will continue to work, so need to prompt.
            console_io.PromptContinue(default=True,
                                      throw_if_unattended=False,
                                      cancel_on_no=True)

        log.status.Print('Beginning deployment...')

        source_contexts = []
        if args.repo_info_file:
            if args.image_url:
                raise NoRepoInfoWithImageUrlError()

            try:
                with open(args.repo_info_file, 'r') as f:
                    source_contexts = json.load(f)
            except (ValueError, IOError) as ex:
                raise RepoInfoLoadError(args.repo_info_file, ex)
            if isinstance(source_contexts, dict):
                # This is an old-style source-context.json file. Convert to a new-
                # style array of extended contexts.
                source_contexts = [
                    context_util.ExtendContextDict(source_contexts)
                ]

        code_bucket_ref = None
        if use_cloud_build or app_config.NonHermeticModules():
            # If using Argo CloudBuild, we'll need to upload source to a GCS bucket.
            code_bucket_ref = self._GetCodeBucket(clients.api, args)
            metrics.CustomTimedEvent(metric_names.GET_CODE_BUCKET)
            log.debug('Using bucket [{b}].'.format(b=code_bucket_ref))

        modules = app_config.Modules()
        if any([m.RequiresImage() for m in modules.values()]):
            deploy_command_util.DoPrepareManagedVms(clients.gae)
        if args.image_url:
            if len(modules) != 1:
                raise MultiDeployError()
            for registry in constants.ALL_SUPPORTED_REGISTRIES:
                if args.image_url.startswith(registry):
                    break
            else:
                raise UnsupportedRegistryError(args.image_url)
            module = modules.keys()[0]
            images = {module: args.image_url}
        else:
            images = deploy_command_util.BuildAndPushDockerImages(
                modules, version, cloudbuild_client, storage_client,
                self.Http(), code_bucket_ref, self.cli, remote_build,
                source_contexts, config_cleanup)

        deployment_manifests = {}
        if app_config.NonHermeticModules():
            if properties.VALUES.app.use_gsutil.GetBool():
                copy_func = deploy_app_command_util.CopyFilesToCodeBucket
                metric_name = metric_names.COPY_APP_FILES
            else:
                copy_func = deploy_app_command_util.CopyFilesToCodeBucketNoGsUtil
                metric_name = metric_names.COPY_APP_FILES_NO_GSUTIL

            deployment_manifests = copy_func(
                app_config.NonHermeticModules().items(), code_bucket_ref,
                source_contexts, storage_client)
            metrics.CustomTimedEvent(metric_name)

        all_services = clients.api.ListServices()
        # Now do deployment.
        for (module, info) in app_config.Modules().iteritems():
            message = 'Updating module [{module}]'.format(module=module)
            with console_io.ProgressTracker(message):
                if args.force:
                    log.warning(
                        'The --force argument is deprecated and no longer '
                        'required. It will be removed in a future release.')

                clients.api.DeployModule(module, version, info,
                                         deployment_manifests.get(module),
                                         images.get(module))
                metrics.CustomTimedEvent(metric_names.DEPLOY_API)

                stop_previous_version = (
                    deploy_command_util.GetStopPreviousVersionFromArgs(args))
                if promote:
                    new_version = version_util.Version(project, module,
                                                       version)
                    _Promote(all_services, new_version, clients,
                             stop_previous_version)
                elif stop_previous_version:
                    log.info(
                        'Not stopping previous version because new version was not '
                        'promoted.')

        # Config files.
        for (c, info) in app_config.Configs().iteritems():
            message = 'Updating config [{config}]'.format(config=c)
            with console_io.ProgressTracker(message):
                clients.gae.UpdateConfig(c, info.parsed)
        return deployed_urls
Exemplo n.º 17
0
def RunDeploy(
        args,
        api_client,
        use_beta_stager=False,
        runtime_builder_strategy=runtime_builders.RuntimeBuilderStrategy.NEVER,
        parallel_build=True,
        flex_image_build_option=FlexImageBuildOptions.ON_CLIENT,
        disable_build_cache=False):
    """Perform a deployment based on the given args.

  Args:
    args: argparse.Namespace, An object that contains the values for the
        arguments specified in the ArgsDeploy() function.
    api_client: api_lib.app.appengine_api_client.AppengineClient, App Engine
        Admin API client.
    use_beta_stager: Use the stager registry defined for the beta track rather
        than the default stager registry.
    runtime_builder_strategy: runtime_builders.RuntimeBuilderStrategy, when to
      use the new CloudBuild-based runtime builders (alternative is old
      externalized runtimes).
    parallel_build: bool, whether to use parallel build and deployment path.
      Only supported in v1beta and v1alpha App Engine Admin API.
    flex_image_build_option: FlexImageBuildOptions, whether a flex deployment
      should upload files so that the server can build the image or build the
      image on client.
    disable_build_cache: bool, disable the build cache.

  Returns:
    A dict on the form `{'versions': new_versions, 'configs': updated_configs}`
    where new_versions is a list of version_util.Version, and updated_configs
    is a list of config file identifiers, see yaml_parsing.ConfigYamlInfo.
  """
    project = properties.VALUES.core.project.Get(required=True)
    deploy_options = DeployOptions.FromProperties(
        runtime_builder_strategy=runtime_builder_strategy,
        parallel_build=parallel_build,
        flex_image_build_option=flex_image_build_option)

    with files.TemporaryDirectory() as staging_area:
        stager = _MakeStager(args.skip_staging, use_beta_stager,
                             args.staging_command, staging_area)
        services, configs = deployables.GetDeployables(
            args.deployables, stager, deployables.GetPathMatchers())
        service_infos = [d.service_info for d in services]

        flags.ValidateImageUrl(args.image_url, service_infos)

        # pylint: disable=protected-access
        log.debug(
            'API endpoint: [{endpoint}], API version: [{version}]'.format(
                endpoint=api_client.client.url,
                version=api_client.client._VERSION))
        # The legacy admin console API client.
        # The Admin Console API existed long before the App Engine Admin API, and
        # isn't being improved. We're in the process of migrating all of the calls
        # over to the Admin API, but a few things (notably config deployments)
        # haven't been ported over yet.
        ac_client = appengine_client.AppengineClient(args.server,
                                                     args.ignore_bad_certs)

        app = _PossiblyCreateApp(api_client, project)
        _RaiseIfStopped(api_client, app)
        app = _PossiblyRepairApp(api_client, app)

        # Tell the user what is going to happen, and ask them to confirm.
        version_id = args.version or util.GenerateVersionId()
        deployed_urls = output_helpers.DisplayProposedDeployment(
            app, project, services, configs, version_id,
            deploy_options.promote)
        console_io.PromptContinue(cancel_on_no=True)
        if service_infos:
            # Do generic app setup if deploying any services.
            # All deployment paths for a service involve uploading source to GCS.
            metrics.CustomTimedEvent(metric_names.GET_CODE_BUCKET_START)
            code_bucket_ref = args.bucket or flags.GetCodeBucket(app, project)
            metrics.CustomTimedEvent(metric_names.GET_CODE_BUCKET)
            log.debug(
                'Using bucket [{b}].'.format(b=code_bucket_ref.ToBucketUrl()))

            # Prepare Flex if any service is going to deploy an image.
            if any([s.RequiresImage() for s in service_infos]):
                if deploy_options.use_service_management:
                    deploy_command_util.PossiblyEnableFlex(project)
                else:
                    deploy_command_util.DoPrepareManagedVms(ac_client)

            all_services = dict([(s.id, s) for s in api_client.ListServices()])
        else:
            code_bucket_ref = None
            all_services = {}
        new_versions = []
        deployer = ServiceDeployer(api_client, deploy_options)

        # Track whether a service has been deployed yet, for metrics.
        service_deployed = False
        for service in services:
            if not service_deployed:
                metrics.CustomTimedEvent(
                    metric_names.FIRST_SERVICE_DEPLOY_START)
            new_version = version_util.Version(project, service.service_id,
                                               version_id)
            deployer.Deploy(service,
                            new_version,
                            code_bucket_ref,
                            args.image_url,
                            all_services,
                            app.gcrDomain,
                            disable_build_cache=disable_build_cache,
                            flex_image_build_option=flex_image_build_option)
            new_versions.append(new_version)
            log.status.Print('Deployed service [{0}] to [{1}]'.format(
                service.service_id, deployed_urls[service.service_id]))
            if not service_deployed:
                metrics.CustomTimedEvent(metric_names.FIRST_SERVICE_DEPLOY)
            service_deployed = True

    # Deploy config files.
    if configs:
        metrics.CustomTimedEvent(metric_names.UPDATE_CONFIG_START)
        for config in configs:
            message = 'Updating config [{config}]'.format(config=config.name)
            with progress_tracker.ProgressTracker(message):
                ac_client.UpdateConfig(config.name, config.parsed)
        metrics.CustomTimedEvent(metric_names.UPDATE_CONFIG)

    updated_configs = [c.name for c in configs]

    PrintPostDeployHints(new_versions, updated_configs)

    # Return all the things that were deployed.
    return {'versions': new_versions, 'configs': updated_configs}
Exemplo n.º 18
0
def RunDeploy(
        args,
        enable_endpoints=False,
        use_beta_stager=False,
        runtime_builder_strategy=runtime_builders.RuntimeBuilderStrategy.NEVER,
        use_service_management=False,
        check_for_stopped=False):
    """Perform a deployment based on the given args.

  Args:
    args: argparse.Namespace, An object that contains the values for the
        arguments specified in the ArgsDeploy() function.
    enable_endpoints: Enable Cloud Endpoints for the deployed app.
    use_beta_stager: Use the stager registry defined for the beta track rather
        than the default stager registry.
    runtime_builder_strategy: runtime_builders.RuntimeBuilderStrategy, when to
      use the new CloudBuild-based runtime builders (alternative is old
      externalized runtimes).
    use_service_management: bool, whether to use servicemanagement API to
      enable the Appengine Flexible API for a Flexible deployment.
    check_for_stopped: bool, whether to check if the app is stopped before
      deploying.

  Returns:
    A dict on the form `{'versions': new_versions, 'configs': updated_configs}`
    where new_versions is a list of version_util.Version, and updated_configs
    is a list of config file identifiers, see yaml_parsing.ConfigYamlInfo.
  """
    project = properties.VALUES.core.project.Get(required=True)
    deploy_options = DeployOptions.FromProperties(
        enable_endpoints, runtime_builder_strategy=runtime_builder_strategy)

    # Parse existing app.yamls or try to generate a new one if the directory is
    # empty.
    if not args.deployables:
        yaml_path = deploy_command_util.DEFAULT_DEPLOYABLE
        if not os.path.exists(deploy_command_util.DEFAULT_DEPLOYABLE):
            log.warning('Automatic app detection is currently in Beta')
            yaml_path = deploy_command_util.CreateAppYamlForAppDirectory(
                os.getcwd())
        app_config = yaml_parsing.AppConfigSet([yaml_path])
    else:
        app_config = yaml_parsing.AppConfigSet(args.deployables)

    # If applicable, sort services by order they were passed to the command.
    services = app_config.Services()

    if not args.skip_image_url_validation:
        flags.ValidateImageUrl(args.image_url, services)

    # The new API client.
    api_client = appengine_api_client.GetApiClient()
    # pylint: disable=protected-access
    log.debug('API endpoint: [{endpoint}], API version: [{version}]'.format(
        endpoint=api_client.client.url, version=api_client.client._VERSION))
    # The legacy admin console API client.
    # The Admin Console API existed long before the App Engine Admin API, and
    # isn't being improved. We're in the process of migrating all of the calls
    # over to the Admin API, but a few things (notably config deployments) haven't
    # been ported over yet.
    ac_client = appengine_client.AppengineClient(args.server,
                                                 args.ignore_bad_certs)

    app = _PossiblyCreateApp(api_client, project)
    if check_for_stopped:
        _RaiseIfStopped(api_client, app)
    app = _PossiblyRepairApp(api_client, app)

    # Tell the user what is going to happen, and ask them to confirm.
    version_id = args.version or util.GenerateVersionId()
    deployed_urls = output_helpers.DisplayProposedDeployment(
        app, project, app_config, version_id, deploy_options.promote)
    console_io.PromptContinue(cancel_on_no=True)
    if services:
        # Do generic app setup if deploying any services.
        # All deployment paths for a service involve uploading source to GCS.
        metrics.CustomTimedEvent(metric_names.GET_CODE_BUCKET_START)
        code_bucket_ref = args.bucket or flags.GetCodeBucket(app, project)
        metrics.CustomTimedEvent(metric_names.GET_CODE_BUCKET)
        log.debug(
            'Using bucket [{b}].'.format(b=code_bucket_ref.ToBucketUrl()))

        # Prepare Flex if any service is going to deploy an image.
        if any([m.RequiresImage() for m in services.values()]):
            if use_service_management:
                deploy_command_util.PossiblyEnableFlex(project)
            else:
                deploy_command_util.DoPrepareManagedVms(ac_client)

        all_services = dict([(s.id, s) for s in api_client.ListServices()])
    else:
        code_bucket_ref = None
        all_services = {}
    new_versions = []
    if args.skip_staging:
        stager = staging.GetNoopStager()
    elif use_beta_stager:
        stager = staging.GetBetaStager()
    else:
        stager = staging.GetStager()
    deployer = ServiceDeployer(api_client, stager, deploy_options)

    # Track whether a service has been deployed yet, for metrics.
    service_deployed = False
    for name, service in services.iteritems():
        if not service_deployed:
            metrics.CustomTimedEvent(metric_names.FIRST_SERVICE_DEPLOY_START)
        new_version = version_util.Version(project, name, version_id)
        deployer.Deploy(service, new_version, code_bucket_ref, args.image_url,
                        all_services, app.gcrDomain)
        new_versions.append(new_version)
        log.status.Print('Deployed service [{0}] to [{1}]'.format(
            name, deployed_urls[name]))
        if not service_deployed:
            metrics.CustomTimedEvent(metric_names.FIRST_SERVICE_DEPLOY)
        service_deployed = True

    # Deploy config files.
    if app_config.Configs():
        metrics.CustomTimedEvent(metric_names.UPDATE_CONFIG_START)
    for (name, config) in app_config.Configs().iteritems():
        message = 'Updating config [{config}]'.format(config=name)
        with progress_tracker.ProgressTracker(message):
            ac_client.UpdateConfig(name, config.parsed)
    if app_config.Configs():
        metrics.CustomTimedEvent(metric_names.UPDATE_CONFIG)

    updated_configs = app_config.Configs().keys()

    PrintPostDeployHints(new_versions, updated_configs)

    # Return all the things that were deployed.
    return {'versions': new_versions, 'configs': updated_configs}