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}
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 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 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
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}