def Run(self, args): """Delete a Cloud Run Integration.""" integration_name = args.name conn_context = connection_context.GetConnectionContext( args, run_flags.Product.RUN_APPS, self.ReleaseTrack()) with run_apps_operations.Connect(conn_context) as client: with progress_tracker.StagedProgressTracker( 'Deleting Integration...', stages.IntegrationDeleteStages(), failure_message='Failed to delete integration.' ) as tracker: try: integration_type = client.DeleteIntegration( name=integration_name, tracker=tracker) except exceptions.IntegrationsOperationError as err: pretty_print.Info( messages_util.GetDeleteErrorMessage(integration_name)) raise err else: pretty_print.Info('') pretty_print.Success( messages_util.GetSuccessMessage( integration_type=integration_type, integration_name=integration_name, action='deleted'))
def Run(self, args): """Deploy an app, function or container to Cloud Run.""" source_ref = flags.GetSourceRef(args.source, args.image) config_changes = flags.GetConfigurationChanges(args) conn_context = connection_context.GetConnectionContext(args) service_ref = flags.GetService(args) function_entrypoint = flags.GetFunction(args.function) msg = ('Deploying {dep_type} to service [{{bold}}{{service}}{{reset}}]' ' in {ns_label} [{{bold}}{{ns}}{{reset}}]') msg += conn_context.location_label if function_entrypoint: pretty_print.Info(msg.format( ns_label=conn_context.ns_label, dep_type='function [{bold}{function}{reset}]'), function=function_entrypoint, service=service_ref.servicesId, ns=service_ref.namespacesId) elif source_ref.source_type is source_ref.SourceType.IMAGE: pretty_print.Info(msg.format(ns_label=conn_context.ns_label, dep_type='container'), service=service_ref.servicesId, ns=service_ref.namespacesId) else: pretty_print.Info(msg.format(ns_label=conn_context.ns_label, dep_type='app'), service=service_ref.servicesId, ns=service_ref.namespacesId) with serverless_operations.Connect(conn_context) as operations: if not (source_ref.source_type is source_ref.SourceType.IMAGE or operations.IsSourceBranch()): raise exceptions.SourceNotSupportedError() new_deployable = operations.Detect(service_ref.Parent(), source_ref, function_entrypoint) operations.Upload(new_deployable) changes = [new_deployable] if config_changes: changes.extend(config_changes) operations.ReleaseService(service_ref, changes, asyn=args. async) url = operations.GetServiceUrl(service_ref) conf = operations.GetConfiguration(service_ref) msg = ('{{bold}}Service [{serv}] revision [{rev}] has been deployed' ' and is serving traffic at{{reset}} {url}') msg = msg.format(serv=service_ref.servicesId, rev=conf.status.latestReadyRevisionName, url=url) pretty_print.Success(msg)
def Run(self, args): """Create or Update service from YAML.""" conn_context = connection_context.GetConnectionContext( args, self.ReleaseTrack()) with serverless_operations.Connect(conn_context) as client: new_service = service.Service( messages_util.DictToMessageWithErrorCheck( args.FILE, client.messages_module.Service), client.messages_module) # If managed, namespace must match project (or will default to project if # not specified). # If not managed, namespace simply must not conflict if specified in # multiple places (or will default to "default" if not specified). namespace = args.CONCEPTS.namespace.Parse().Name() # From flag or default if new_service.metadata.namespace is not None: if (args.IsSpecified('namespace') and namespace != new_service.metadata.namespace): raise exceptions.ConfigurationError( 'Namespace specified in file does not match passed flag.') namespace = new_service.metadata.namespace project = properties.VALUES.core.project.Get() if flags.IsManaged(args) and namespace != project: raise exceptions.ConfigurationError( 'Namespace must be [{}] for Cloud Run (fully managed).'.format( project)) new_service.metadata.namespace = namespace changes = [config_changes.ReplaceServiceChange(new_service)] service_ref = resources.REGISTRY.Parse( new_service.metadata.name, params={'namespacesId': namespace}, collection='run.namespaces.services') original_service = client.GetService(service_ref) pretty_print.Info(deploy.GetStartDeployMessage(conn_context, service_ref)) deployment_stages = stages.ServiceStages() header = ( 'Deploying...' if original_service else 'Deploying new service...') with progress_tracker.StagedProgressTracker( header, deployment_stages, failure_message='Deployment failed', suppress_output=args.async_) as tracker: client.ReleaseService( service_ref, changes, tracker, asyn=args.async_, allow_unauthenticated=None, for_replace=True) if args.async_: pretty_print.Success( 'Service [{{bold}}{serv}{{reset}}] is deploying ' 'asynchronously.'.format(serv=service_ref.servicesId)) else: pretty_print.Success(deploy.GetSuccessMessageForSynchronousDeploy( client, service_ref))
def GetAllowUnauthenticated(args, client=None, service_ref=None, prompt=False): """Return bool for the explicit intent to allow unauth invocations or None. If --[no-]allow-unauthenticated is set, return that value. If not set, prompt for value if desired. If prompting not necessary or doable, return None, indicating that no action needs to be taken. Args: args: Namespace, The args namespace client: from googlecloudsdk.command_lib.run import serverless_operations serverless_operations.ServerlessOperations object service_ref: service resource reference (e.g. args.CONCEPTS.service.Parse()) prompt: bool, whether to attempt to prompt. Returns: bool indicating whether to allow/unallow unauthenticated or None if N/A """ if getattr(args, 'allow_unauthenticated', None) is not None: return args.allow_unauthenticated if prompt: # Need to check if the user has permissions before we prompt assert client is not None and service_ref is not None if client.CanSetIamPolicyBinding(service_ref): return console_io.PromptContinue( prompt_string=('Allow unauthenticated invocations ' 'to [{}]'.format(service_ref.servicesId)), default=False) else: pretty_print.Info( 'This service will require authentication to be invoked.') return None
def GetAllowUnauthenticated(args, service_ref=None, prompt=False): """Return bool for the explicit intent to allow unauth invocations or None. If --[no-]allow-unauthenticated is set, return that value. If not set, prompt for value if desired. If prompting not necessary or doable, return None, indicating that no action needs to be taken. Args: args: Namespace, The args namespace service_ref: service resource reference (e.g. args.CONCEPTS.service.Parse()) prompt: bool, whether to attempt to prompt. Returns: bool indicating whether to allow/unallow unauthenticated or None if N/A """ if getattr(args, 'allow_unauthenticated', None) is not None: return args.allow_unauthenticated if prompt: project = properties.VALUES.core.project.Get(required=True) if resourcemanager_operations.CanSetProjectIamPolicyBinding(project): return console_io.PromptContinue( prompt_string=('Allow unauthenticated invocations ' 'to [{}]'.format(service_ref.servicesId)), default=False) else: pretty_print.Info( 'This service will require authentication to be invoked.') return None
def Run(self, args): """Creates a Cloud Run Integration.""" integration_type = args.type service = args.service input_name = args.name parameters = flags.GetParameters(args) flags.ValidateCreateParameters(integration_type, parameters) conn_context = connection_context.GetConnectionContext( args, run_flags.Product.RUN_APPS, self.ReleaseTrack()) with run_apps_operations.Connect(conn_context) as client: self._validateServiceNameAgainstIntegrations( client, integration_type=integration_type, service=service, integration_name=input_name) with progress_tracker.StagedProgressTracker( 'Creating new Integration...', stages.IntegrationStages(create=True), failure_message='Failed to create new integration.' ) as tracker: integration_name = client.CreateIntegration( tracker=tracker, integration_type=integration_type, parameters=parameters, service=service, name=input_name) resource_config = client.GetIntegration(integration_name) resource_status = client.GetIntegrationStatus(integration_name) pretty_print.Info('') pretty_print.Success( messages_util.GetSuccessMessage( integration_type=integration_type, integration_name=integration_name, action='created')) call_to_action = messages_util.GetCallToAction( integration_type, resource_config, resource_status) if call_to_action: pretty_print.Info('') pretty_print.Info(call_to_action) pretty_print.Info( messages_util.CheckStatusMessage(self.ReleaseTrack(), integration_name))
def Run(self, args): """Deploy a container to Cloud Run.""" service_ref = flags.GetService(args) image = args.image # Build an image from source if source specified. if flags.FlagIsExplicitlySet(args, 'source'): # Create a tag for the image creation if image is None and not args.IsSpecified('config'): image = 'gcr.io/{projectID}/cloud-run-source-deploy/{service}:{tag}'.format( projectID=properties.VALUES.core.project.Get(required=True), service=service_ref.servicesId, tag=uuid.uuid4().hex) messages = cloudbuild_util.GetMessagesModule() build_config = submit_util.CreateBuildConfig( image, args.no_cache, messages, args.substitutions, args.config, args.IsSpecified('source'), False, args.source, args.gcs_source_staging_dir, args.ignore_file, args.gcs_log_dir, args.machine_type, args.disk_size) submit_util.Build(messages, args.async_, build_config) # Deploy a container with an image conn_context = connection_context.GetConnectionContext( args, flags.Product.RUN, self.ReleaseTrack()) config_changes = flags.GetConfigurationChanges(args) with serverless_operations.Connect(conn_context) as operations: image_change = config_changes_mod.ImageChange(image) changes = [image_change] if config_changes: changes.extend(config_changes) service = operations.GetService(service_ref) allow_unauth = GetAllowUnauth(args, operations, service_ref, service) pretty_print.Info(GetStartDeployMessage(conn_context, service_ref)) has_latest = (service is None or traffic.LATEST_REVISION_KEY in service.spec_traffic) deployment_stages = stages.ServiceStages( include_iam_policy_set=allow_unauth is not None, include_route=has_latest) header = 'Deploying...' if service else 'Deploying new service...' with progress_tracker.StagedProgressTracker( header, deployment_stages, failure_message='Deployment failed', suppress_output=args.async_) as tracker: operations.ReleaseService( service_ref, changes, tracker, asyn=args.async_, allow_unauthenticated=allow_unauth, prefetch=service) if args.async_: pretty_print.Success( 'Service [{{bold}}{serv}{{reset}}] is deploying ' 'asynchronously.'.format(serv=service_ref.servicesId)) else: pretty_print.Success(GetSuccessMessageForSynchronousDeploy( operations, service_ref))
def Run(self, args): """Update a Cloud Run Integration.""" add_service = args.add_service remove_service = args.remove_service integration_name = args.name parameters = flags.GetParameters(args) conn_context = connection_context.GetConnectionContext( args, run_flags.Product.RUN_APPS, self.ReleaseTrack()) with run_apps_operations.Connect(conn_context) as client: with progress_tracker.StagedProgressTracker( 'Updating Integration...', stages.IntegrationStages(create=False), failure_message='Failed to update integration.' ) as tracker: client.UpdateIntegration(tracker=tracker, name=integration_name, parameters=parameters, add_service=add_service, remove_service=remove_service) resource_config = client.GetIntegration(integration_name) resource_status = client.GetIntegrationStatus(integration_name) resource_type = client.GetResourceTypeFromConfig(resource_config) integration_type = types_utils.GetIntegrationType(resource_type) pretty_print.Info('') pretty_print.Success( messages_util.GetSuccessMessage( integration_type=integration_type, integration_name=integration_name, action='updated')) call_to_action = messages_util.GetCallToAction( integration_type, resource_config, resource_status) if call_to_action: pretty_print.Info('') pretty_print.Info(call_to_action) pretty_print.Info( messages_util.CheckStatusMessage(self.ReleaseTrack(), integration_name))
def Run(self, args): """Create or Update service from YAML.""" conn_context = connection_context.GetConnectionContext(args) if conn_context.supports_one_platform: flags.VerifyOnePlatformFlags(args) else: flags.VerifyGKEFlags(args) with serverless_operations.Connect(conn_context) as client: message_dict = yaml.load_path(args.FILE) new_service = service.Service( messages_util.DictToMessageWithErrorCheck( message_dict, client.messages_module.Service), client.messages_module) changes = [config_changes.ReplaceServiceChange(new_service)] service_ref = resources.REGISTRY.Parse( new_service.metadata.name, params={'namespacesId': new_service.metadata.namespace}, collection='run.namespaces.services') original_service = client.GetService(service_ref) pretty_print.Info( deploy.GetStartDeployMessage(conn_context, service_ref)) deployment_stages = stages.ServiceStages() header = ('Deploying...' if original_service else 'Deploying new service...') with progress_tracker.StagedProgressTracker( header, deployment_stages, failure_message='Deployment failed', suppress_output=args. async) as tracker: client.ReleaseService(service_ref, changes, tracker, asyn=args. async, allow_unauthenticated=None, for_replace=True) if args. async: pretty_print.Success( 'Service [{{bold}}{serv}{{reset}}] is deploying ' 'asynchronously.'.format(serv=service_ref.servicesId)) else: pretty_print.Success( deploy.GetSuccessMessageForSynchronousDeploy( client, service_ref))
def Run(self, args): """Deploy a container to Cloud Run.""" image = args.image conn_context = connection_context.GetConnectionContext( args, product=connection_context.Product.RUN) config_changes = flags.GetConfigurationChanges(args) service_ref = flags.GetService(args) with serverless_operations.Connect(conn_context) as operations: image_change = config_changes_mod.ImageChange(image) changes = [image_change] if config_changes: changes.extend(config_changes) service = operations.GetService(service_ref) allow_unauth = GetAllowUnauth(args, operations, service_ref, service) pretty_print.Info(GetStartDeployMessage(conn_context, service_ref)) has_latest = (service is None or traffic.LATEST_REVISION_KEY in service.traffic) deployment_stages = stages.ServiceStages( include_iam_policy_set=allow_unauth is not None, include_route=has_latest) header = 'Deploying...' if service else 'Deploying new service...' with progress_tracker.StagedProgressTracker( header, deployment_stages, failure_message='Deployment failed', suppress_output=args.async_) as tracker: operations.ReleaseService(service_ref, changes, tracker, asyn=args.async_, allow_unauthenticated=allow_unauth, prefetch=service) if args.async_: pretty_print.Success( 'Service [{{bold}}{serv}{{reset}}] is deploying ' 'asynchronously.'.format(serv=service_ref.servicesId)) else: pretty_print.Success( GetSuccessMessageForSynchronousDeploy( operations, service_ref))
def Run(self, args): """Deploy a container to Cloud Run.""" job_ref = args.CONCEPTS.job.Parse() flags.ValidateResource(job_ref) conn_context = connection_context.GetConnectionContext( args, flags.Product.RUN, self.ReleaseTrack(), version_override='v1alpha1') changes = flags.GetConfigurationChanges(args) changes.append( config_changes.SetLaunchStageAnnotationChange(self.ReleaseTrack())) with serverless_operations.Connect(conn_context) as operations: pretty_print.Info( messages_util.GetStartDeployMessage(conn_context, job_ref, 'job')) header_msg = 'Creating and {} job...'.format( 'running' if args.wait_for_completion else 'starting') with progress_tracker.StagedProgressTracker( header_msg, stages.JobStages(include_completion=args.wait_for_completion), failure_message='Job failed', suppress_output=args.async_) as tracker: job = operations.CreateJob( job_ref, changes, args.wait_for_completion, tracker, asyn=args.async_) if args.async_: pretty_print.Success('Job [{{bold}}{job}{{reset}}] is being created ' 'asynchronously.'.format(job=job.name)) else: job = operations.GetJob(job_ref) pretty_print.Success( 'Job [{{bold}}{job}{{reset}}] has successfully ' '{operation}.'.format( job=job.name, operation=('completed' if args.wait_for_completion else 'started running'))) return job
def Run(self, args): """Deploy a container to Cloud Run.""" image = args.image conn_context = connection_context.GetConnectionContext( args, self.ReleaseTrack()) config_changes = flags.GetConfigurationChanges(args) service_ref = flags.GetService(args) with serverless_operations.Connect(conn_context) as operations: image_change = config_changes_mod.ImageChange(image) changes = [image_change] if config_changes: changes.extend(config_changes) exists = operations.GetService(service_ref) allow_unauth = GetAllowUnauth(args, operations, service_ref, exists) pretty_print.Info(GetStartDeployMessage(conn_context, service_ref)) deployment_stages = stages.ServiceStages(allow_unauth is not None) header = 'Deploying...' if exists else 'Deploying new service...' with progress_tracker.StagedProgressTracker( header, deployment_stages, failure_message='Deployment failed', suppress_output=args.async_) as tracker: operations.ReleaseService(service_ref, changes, tracker, asyn=args.async_, allow_unauthenticated=allow_unauth) if args.async_: pretty_print.Success( 'Service [{{bold}}{serv}{{reset}}] is deploying ' 'asynchronously.'.format(serv=service_ref.servicesId)) else: pretty_print.Success( GetSuccessMessageForSynchronousDeploy( operations, service_ref))
def Run(self, args): """Update a Job on Cloud Run.""" job_ref = args.CONCEPTS.job.Parse() flags.ValidateResource(job_ref) conn_context = connection_context.GetConnectionContext( args, flags.Product.RUN, self.ReleaseTrack()) changes = flags.GetJobConfigurationChanges(args) changes.append( config_changes.SetLaunchStageAnnotationChange(self.ReleaseTrack())) with serverless_operations.Connect(conn_context) as operations: pretty_print.Info( messages_util.GetStartDeployMessage(conn_context, job_ref, 'Updating', 'job')) header_msg = 'Updating job...' with progress_tracker.StagedProgressTracker( header_msg, stages.JobStages(), failure_message='Job failed to deploy', suppress_output=args.async_) as tracker: job = operations.UpdateJob(job_ref, changes, tracker, asyn=args.async_) if args.async_: pretty_print.Success( 'Job [{{bold}}{job}{{reset}}] is being updated ' 'asynchronously.'.format(job=job.name)) else: job = operations.GetJob(job_ref) pretty_print.Success( 'Job [{{bold}}{job}{{reset}}] has been successfully updated' .format(job=job.name)) log.status.Print( messages_util.GetRunJobMessage(self.ReleaseTrack(), job.name)) return job
def Run(self, args): """Create or Update service from YAML.""" run_messages = apis.GetMessagesModule( global_methods.SERVERLESS_API_NAME, global_methods.SERVERLESS_API_VERSION) service_dict = dict(args.FILE) # Clear the status to make migration from k8s deployments easier. # Since a Deployment status will have several fields that Cloud Run doesn't # support, trying to convert it to a message as-is will fail even though # status is ignored by the server. if 'status' in service_dict: del service_dict['status'] # For cases where YAML contains the project number as metadata.namespace, # preemptively convert them to a string to avoid validation failures. namespace = service_dict.get('metadata', {}).get('namespace', None) if namespace is not None and not isinstance(namespace, str): service_dict['metadata']['namespace'] = str(namespace) try: raw_service = messages_util.DictToMessageWithErrorCheck( service_dict, run_messages.Service) new_service = service.Service(raw_service, run_messages) except messages_util.ScalarTypeMismatchError as e: exceptions.MaybeRaiseCustomFieldMismatch( e, help_text= 'Please make sure that the YAML file matches the Knative ' 'service definition spec in https://kubernetes.io/docs/' 'reference/kubernetes-api/services-resources/service-v1/' '#Service.') # If managed, namespace must match project (or will default to project if # not specified). # If not managed, namespace simply must not conflict if specified in # multiple places (or will default to "default" if not specified). namespace = args.CONCEPTS.namespace.Parse().Name( ) # From flag or default if new_service.metadata.namespace is not None: if (args.IsSpecified('namespace') and namespace != new_service.metadata.namespace): raise exceptions.ConfigurationError( 'Namespace specified in file does not match passed flag.') namespace = new_service.metadata.namespace if platforms.GetPlatform() == platforms.PLATFORM_MANAGED: project = properties.VALUES.core.project.Get() project_number = projects_util.GetProjectNumber(project) if namespace != project and namespace != str(project_number): raise exceptions.ConfigurationError( 'Namespace must be project ID [{}] or quoted number [{}] for ' 'Cloud Run (fully managed).'.format( project, project_number)) new_service.metadata.namespace = namespace changes = [ config_changes.ReplaceServiceChange(new_service), config_changes.SetLaunchStageAnnotationChange(self.ReleaseTrack()) ] service_ref = resources.REGISTRY.Parse( new_service.metadata.name, params={'namespacesId': new_service.metadata.namespace}, collection='run.namespaces.services') region_label = new_service.region if new_service.is_managed else None conn_context = connection_context.GetConnectionContext( args, flags.Product.RUN, self.ReleaseTrack(), region_label=region_label) with serverless_operations.Connect(conn_context) as client: service_obj = client.GetService(service_ref) pretty_print.Info( run_messages_util.GetStartDeployMessage( conn_context, service_ref, operation='Applying new configuration to')) deployment_stages = stages.ServiceStages() header = ('Deploying...' if service_obj else 'Deploying new service...') with progress_tracker.StagedProgressTracker( header, deployment_stages, failure_message='Deployment failed', suppress_output=args.async_) as tracker: service_obj = client.ReleaseService(service_ref, changes, tracker, asyn=args.async_, allow_unauthenticated=None, for_replace=True) if args.async_: pretty_print.Success( 'New configuration for [{{bold}}{serv}{{reset}}] is being applied ' 'asynchronously.'.format(serv=service_obj.name)) else: service_obj = client.GetService(service_ref) pretty_print.Success( 'New configuration has been applied to service ' '[{{bold}}{serv}{{reset}}].\n' 'URL: {{bold}}{url}{{reset}}'.format( serv=service_obj.name, url=service_obj.domain)) return service_obj
def _UpdateOrCreateService(self, service_ref, config_changes, with_code, private_endpoint=None): """Apply config_changes to the service. Create it if necessary. Arguments: service_ref: Reference to the service to create or update config_changes: list of ConfigChanger to modify the service with with_code: bool, True if the config_changes contains code to deploy. We can't create the service if we're not deploying code. private_endpoint: bool, True if creating a new Service for Cloud Run on GKE that should only be addressable from within the cluster. False if it should be publicly addressable. None if its existing visibility should remain unchanged. Returns: The Service object we created or modified. """ nonce = _Nonce() config_changes = [_NewRevisionForcingChange(nonce)] + config_changes messages = self._messages_module # GET the Service serv = self.GetService(service_ref) try: if serv: if not with_code: # Avoid changing the running code by making the new revision by digest self._EnsureImageDigest(serv, config_changes) if private_endpoint is None: # Don't change the existing service visibility pass elif private_endpoint: serv.labels[service.ENDPOINT_VISIBILITY] = service.CLUSTER_LOCAL else: del serv.labels[service.ENDPOINT_VISIBILITY] # PUT the changed Service for config_change in config_changes: config_change.AdjustConfiguration(serv.configuration, serv.metadata) serv_name = service_ref.RelativeName() serv_update_req = ( messages.RunNamespacesServicesReplaceServiceRequest( service=serv.Message(), name=serv_name)) with metrics.record_duration(metrics.UPDATE_SERVICE): updated = self._client.namespaces_services.ReplaceService( serv_update_req) return service.Service(updated, messages) else: if not with_code: raise serverless_exceptions.ServiceNotFoundError( 'Service [{}] could not be found.'.format(service_ref.servicesId)) # POST a new Service new_serv = service.Service.New(self._client, service_ref.namespacesId, private_endpoint) new_serv.name = service_ref.servicesId pretty_print.Info('Creating new service [{bold}{service}{reset}]', service=new_serv.name) parent = service_ref.Parent().RelativeName() for config_change in config_changes: config_change.AdjustConfiguration(new_serv.configuration, new_serv.metadata) serv_create_req = ( messages.RunNamespacesServicesCreateRequest( service=new_serv.Message(), parent=parent)) with metrics.record_duration(metrics.CREATE_SERVICE): raw_service = self._client.namespaces_services.Create( serv_create_req) return service.Service(raw_service, messages) except api_exceptions.HttpBadRequestError as e: error_payload = exceptions_util.HttpErrorPayload(e) if error_payload.field_violations: if (serverless_exceptions.BadImageError.IMAGE_ERROR_FIELD in error_payload.field_violations): exceptions.reraise(serverless_exceptions.BadImageError(e)) exceptions.reraise(e) except api_exceptions.HttpNotFoundError as e: # TODO(b/118339293): List available regions to check whether provided # region is invalid or not. raise serverless_exceptions.DeploymentFailedError( 'Deployment endpoint was not found. Perhaps the provided ' 'region was invalid. Set the `run/region` property to a valid ' 'region and retry. Ex: `gcloud config set run/region us-central1`')
def Run(self, args): """Deploy a container to Cloud Run.""" flags.GetAndValidatePlatform(args, self.ReleaseTrack(), flags.Product.RUN) service_ref = args.CONCEPTS.service.Parse() flags.ValidateResource(service_ref) build_type = None image = None pack = None source = None include_build = flags.FlagIsExplicitlySet(args, 'source') operation_message = 'Deploying container' # Build an image from source if source specified if include_build: # Create a tag for the image creation source = args.source if not args.IsSpecified('image'): args.image = 'gcr.io/{projectID}/cloud-run-source-deploy/{service}:{tag}'.format( projectID=properties.VALUES.core.project.Get( required=True), service=service_ref.servicesId, tag=uuid.uuid4().hex) # Use GCP Buildpacks if Dockerfile doesn't exist docker_file = args.source + '/Dockerfile' if os.path.exists(docker_file): build_type = BuildType.DOCKERFILE else: pack = [{'image': args.image}] build_type = BuildType.BUILDPACKS image = None if pack else args.image operation_message = 'Building using {build_type} and deploying container'.format( build_type=build_type.value) elif not args.IsSpecified('image'): raise c_exceptions.RequiredArgumentException( '--image', 'Requires a container image to deploy (e.g. ' '`gcr.io/cloudrun/hello:latest`) if no build source is provided.' ) # Deploy a container with an image conn_context = connection_context.GetConnectionContext( args, flags.Product.RUN, self.ReleaseTrack()) changes = flags.GetConfigurationChanges(args) changes.insert( 0, config_changes.DeleteAnnotationChange( k8s_object.BINAUTHZ_BREAKGLASS_ANNOTATION)) changes.append( config_changes.SetLaunchStageAnnotationChange(self.ReleaseTrack())) with serverless_operations.Connect(conn_context) as operations: service = operations.GetService(service_ref) allow_unauth = GetAllowUnauth(args, operations, service_ref, service) resource_change_validators.ValidateClearVpcConnector(service, args) pretty_print.Info( messages_util.GetStartDeployMessage(conn_context, service_ref, operation_message)) has_latest = (service is None or traffic.LATEST_REVISION_KEY in service.spec_traffic) deployment_stages = stages.ServiceStages( include_iam_policy_set=allow_unauth is not None, include_route=has_latest, include_build=include_build) header = None if include_build: header = 'Building and deploying' else: header = 'Deploying' if service is None: header += ' new service' header += '...' with progress_tracker.StagedProgressTracker( header, deployment_stages, failure_message='Deployment failed', suppress_output=args.async_) as tracker: service = operations.ReleaseService( service_ref, changes, tracker, asyn=args.async_, allow_unauthenticated=allow_unauth, prefetch=service, build_image=image, build_pack=pack, build_source=source) if args.async_: pretty_print.Success( 'Service [{{bold}}{serv}{{reset}}] is deploying ' 'asynchronously.'.format(serv=service.name)) else: service = operations.GetService(service_ref) pretty_print.Success( messages_util.GetSuccessMessageForSynchronousDeploy( service)) return service
def Run(self, args): """Deploy an app, function or container to Cloud Run.""" source_ref = flags.GetSourceRef(args.source, args.image) config_changes = flags.GetConfigurationChanges(args) conn_context = connection_context.GetConnectionContext(args) # pylint: disable=protected-access if (not isinstance(conn_context, connection_context._GKEConnectionContext) and getattr(args, 'endpoint', None)): raise exceptions.ConfigurationError( 'The `--endpoint=[internal|external]` flag ' 'is only supported with Cloud Run on GKE.') # pylint: enable=protected-access service_ref = flags.GetService(args) function_entrypoint = flags.GetFunction(args.function) msg = ('Deploying {dep_type} to {operator} ' 'service [{{bold}}{service}{{reset}}]' ' in {ns_label} [{{bold}}{ns}{{reset}}]') msg += conn_context.location_label if function_entrypoint: dep_type = 'function [{{bold}}{}{{reset}}]'.format(function_entrypoint) pretty_print.Info(msg.format( operator=conn_context.operator, ns_label=conn_context.ns_label, dep_type=dep_type, function=function_entrypoint, service=service_ref.servicesId, ns=service_ref.namespacesId)) elif source_ref.source_type is source_ref.SourceType.IMAGE: pretty_print.Info(msg.format( operator=conn_context.operator, ns_label=conn_context.ns_label, dep_type='container', service=service_ref.servicesId, ns=service_ref.namespacesId)) else: pretty_print.Info(msg.format( operator=conn_context.operator, ns_label=conn_context.ns_label, dep_type='app', service=service_ref.servicesId, ns=service_ref.namespacesId)) with serverless_operations.Connect(conn_context) as operations: if not (source_ref.source_type is source_ref.SourceType.IMAGE or operations.IsSourceBranch()): raise exceptions.SourceNotSupportedError() new_deployable = operations.Detect(service_ref.Parent(), source_ref, function_entrypoint) operations.Upload(new_deployable) changes = [new_deployable] if config_changes: changes.extend(config_changes) if args.endpoint == 'internal': private_endpoint = True elif args.endpoint == 'external': private_endpoint = False else: private_endpoint = None operations.ReleaseService(service_ref, changes, asyn=args.async, private_endpoint=private_endpoint) url = operations.GetServiceUrl(service_ref) conf = operations.GetConfiguration(service_ref) msg = ( 'Service [{{bold}}{serv}{{reset}}] revision [{{bold}}{rev}{{reset}}] ' 'has been deployed and is serving traffic at {{bold}}{url}{{reset}}') msg = msg.format( serv=service_ref.servicesId, rev=conf.status.latestReadyRevisionName, url=url) pretty_print.Success(msg)
def Run(self, args): """Deploy a container to Cloud Run.""" image = args.image conn_context = connection_context.GetConnectionContext(args) config_changes = flags.GetConfigurationChanges(args) if conn_context.supports_one_platform: flags.VerifyOnePlatformFlags(args) else: flags.VerifyGKEFlags(args) service_ref = flags.GetService(args) with serverless_operations.Connect(conn_context) as operations: image_change = config_changes_mod.ImageChange(image) changes = [image_change] if config_changes: changes.extend(config_changes) if args.connectivity == 'internal': private_endpoint = True elif args.connectivity == 'external': private_endpoint = False else: private_endpoint = None exists = operations.GetService(service_ref) if (not exists and not args.allow_unauthenticated and conn_context.supports_one_platform): if operations.CanAddIamPolicyBinding(service_ref): allow_unauth = console_io.PromptContinue( prompt_string=( 'Allow unauthenticated invocations ' 'to new service [{}]?'.format( service_ref.servicesId)), default=False) else: allow_unauth = False pretty_print.Info( 'This new service will require authentication to be invoked.') else: allow_unauth = False msg = ('Deploying {dep_type} to {operator} ' 'service [{{bold}}{service}{{reset}}]' ' in {ns_label} [{{bold}}{ns}{{reset}}]') msg += conn_context.location_label pretty_print.Info(msg.format( operator=conn_context.operator, ns_label=conn_context.ns_label, dep_type='container', service=service_ref.servicesId, ns=service_ref.namespacesId)) deployment_stages = stages.ServiceStages( allow_unauth or args.allow_unauthenticated) header = 'Deploying...' if exists else 'Deploying new service...' with progress_tracker.StagedProgressTracker( header, deployment_stages, failure_message='Deployment failed', suppress_output=args.async) as tracker: operations.ReleaseService( service_ref, changes, tracker, asyn=args.async, private_endpoint=private_endpoint, allow_unauthenticated=allow_unauth or args.allow_unauthenticated) if args.async: pretty_print.Success( 'Service [{{bold}}{serv}{{reset}}] is deploying ' 'asynchronously.'.format(serv=service_ref.servicesId)) else: url = operations.GetServiceUrl(service_ref) conf = operations.GetConfiguration(service_ref) msg = ( 'Service [{{bold}}{serv}{{reset}}] ' 'revision [{{bold}}{rev}{{reset}}] ' 'has been deployed and is serving traffic at ' '{{bold}}{url}{{reset}}') msg = msg.format( serv=service_ref.servicesId, rev=conf.status.latestReadyRevisionName, url=url) pretty_print.Success(msg)
def Run(self, args): """Deploy an app, function or container to Cloud Run.""" source_ref = flags.GetSourceRef(args.source, args.image) config_changes = flags.GetConfigurationChanges(args) conn_context = connection_context.GetConnectionContext(args) if (conn_context.supports_one_platform and getattr(args, 'connectivity', None)): raise exceptions.ConfigurationError( 'The `--endpoint=[internal|external]` flag ' 'is only supported with Cloud Run on GKE.') if (not conn_context.supports_one_platform and getattr(args, 'allow_unauthenticated', None)): raise exceptions.ConfigurationError( 'The `--allow-unauthenticated` flag ' 'is not supported with Cloud Run on GKE.') service_ref = flags.GetService(args) function_entrypoint = flags.GetFunction(args.function) msg = ('Deploying {dep_type} to {operator} ' 'service [{{bold}}{service}{{reset}}]' ' in {ns_label} [{{bold}}{ns}{{reset}}]') msg += conn_context.location_label if function_entrypoint: dep_type = 'function [{{bold}}{}{{reset}}]'.format( function_entrypoint) pretty_print.Info( msg.format(operator=conn_context.operator, ns_label=conn_context.ns_label, dep_type=dep_type, function=function_entrypoint, service=service_ref.servicesId, ns=service_ref.namespacesId)) elif source_ref.source_type is source_ref.SourceType.IMAGE: pretty_print.Info( msg.format(operator=conn_context.operator, ns_label=conn_context.ns_label, dep_type='container', service=service_ref.servicesId, ns=service_ref.namespacesId)) else: pretty_print.Info( msg.format(operator=conn_context.operator, ns_label=conn_context.ns_label, dep_type='app', service=service_ref.servicesId, ns=service_ref.namespacesId)) with serverless_operations.Connect(conn_context) as operations: if not (source_ref.source_type is source_ref.SourceType.IMAGE or operations.IsSourceBranch()): raise exceptions.SourceNotSupportedError() new_deployable = operations.Detect(service_ref.Parent(), source_ref, function_entrypoint) operations.Upload(new_deployable) changes = [new_deployable] if config_changes: changes.extend(config_changes) if args.connectivity == 'internal': private_endpoint = True elif args.connectivity == 'external': private_endpoint = False else: private_endpoint = None deployment_stages = stages.ServiceStages() exists = operations.GetService(service_ref) if (not exists and not args.allow_unauthenticated and conn_context.supports_one_platform): if operations.CanAddIamPolicyBinding(service_ref): allow_unauth = console_io.PromptContinue( prompt_string=('Allow unauthenticated invocations ' 'to new service [{}]?'.format( service_ref.servicesId)), default=False) else: allow_unauth = False pretty_print.Info( 'This new service will require authentication to be invoked.' ) else: allow_unauth = False header = 'Deploying...' if exists else 'Deploying new service...' with progress_tracker.StagedProgressTracker( header, deployment_stages, failure_message='Deployment failed', suppress_output=args. async) as tracker: operations.ReleaseService(service_ref, changes, tracker, asyn=args. async, private_endpoint=private_endpoint, allow_unauthenticated=allow_unauth or args.allow_unauthenticated) if args. async: pretty_print.Success( 'Service [{{bold}}{serv}{{reset}}] is deploying ' 'asynchronously.'.format(serv=service_ref.servicesId)) else: url = operations.GetServiceUrl(service_ref) conf = operations.GetConfiguration(service_ref) msg = ('Service [{{bold}}{serv}{{reset}}] ' 'revision [{{bold}}{rev}{{reset}}] ' 'has been deployed and is serving traffic at ' '{{bold}}{url}{{reset}}') msg = msg.format(serv=service_ref.servicesId, rev=conf.status.latestReadyRevisionName, url=url) pretty_print.Success(msg)
def Run(self, args): """Deploy a container to Cloud Run.""" image = args.image conn_context = connection_context.GetConnectionContext(args) config_changes = flags.GetConfigurationChanges(args) if conn_context.supports_one_platform: flags.VerifyOnePlatformFlags(args) else: flags.VerifyGKEFlags(args) service_ref = flags.GetService(args) with serverless_operations.Connect(conn_context) as operations: image_change = config_changes_mod.ImageChange(image) changes = [image_change] if config_changes: changes.extend(config_changes) endpoint_visibility = flags.GetEndpointVisibility(args) exists = operations.GetService(service_ref) allow_unauth = None if conn_context.supports_one_platform: allow_unauth = flags.GetAllowUnauthenticated( args, operations, service_ref, not exists) # Don't try to remove a policy binding from a service that doesn't exist if not exists and not allow_unauth: allow_unauth = None msg = ('Deploying {dep_type} to {operator} ' 'service [{{bold}}{service}{{reset}}]' ' in {ns_label} [{{bold}}{ns}{{reset}}]') msg += conn_context.location_label pretty_print.Info( msg.format(operator=conn_context.operator, ns_label=conn_context.ns_label, dep_type='container', service=service_ref.servicesId, ns=service_ref.namespacesId)) deployment_stages = stages.ServiceStages(allow_unauth is not None) header = 'Deploying...' if exists else 'Deploying new service...' with progress_tracker.StagedProgressTracker( header, deployment_stages, failure_message='Deployment failed', suppress_output=args. async) as tracker: operations.ReleaseService(service_ref, changes, tracker, asyn=args. async, private_endpoint=endpoint_visibility, allow_unauthenticated=allow_unauth) if args. async: pretty_print.Success( 'Service [{{bold}}{serv}{{reset}}] is deploying ' 'asynchronously.'.format(serv=service_ref.servicesId)) else: url = operations.GetServiceUrl(service_ref) conf = operations.GetConfiguration(service_ref) msg = ('Service [{{bold}}{serv}{{reset}}] ' 'revision [{{bold}}{rev}{{reset}}] ' 'has been deployed and is serving traffic at ' '{{bold}}{url}{{reset}}') msg = msg.format(serv=service_ref.servicesId, rev=conf.status.latestReadyRevisionName, url=url) pretty_print.Success(msg)
def Run(self, args): """Deploy a container to Cloud Run.""" platform = flags.GetAndValidatePlatform(args, self.ReleaseTrack(), flags.Product.RUN) include_build = flags.FlagIsExplicitlySet(args, 'source') if not include_build and not args.IsSpecified('image'): if console_io.CanPrompt(): args.source = flags.PromptForDefaultSource() include_build = True else: raise c_exceptions.RequiredArgumentException( '--image', 'Requires a container image to deploy (e.g. ' '`gcr.io/cloudrun/hello:latest`) if no build source is provided.' ) service_ref = args.CONCEPTS.service.Parse() flags.ValidateResource(service_ref) # Obtaining the connection context prompts the user to select a region if # one hasn't been provided. We want to do this prior to preparing a source # deploy so that we can use that region for the Artifact Registry repo. conn_context = connection_context.GetConnectionContext( args, flags.Product.RUN, self.ReleaseTrack()) build_type = None image = None pack = None source = None operation_message = 'Deploying container to' repo_to_create = None # Build an image from source if source specified if include_build: source = args.source ar_repo = docker_util.DockerRepo( project_id=properties.VALUES.core.project.Get(required=True), location_id=artifact_registry.RepoRegion( args, cluster_location=(conn_context.cluster_location if platform == platforms.PLATFORM_GKE else None)), repo_id='cloud-run-source-deploy') if artifact_registry.ShouldCreateRepository(ar_repo): repo_to_create = ar_repo # The image is built with latest tag. After build, the image digest # from the build result will be added to the image of the service spec. args.image = '{repo}/{service}'.format( repo=ar_repo.GetDockerString(), service=service_ref.servicesId) # Use GCP Buildpacks if Dockerfile doesn't exist docker_file = source + '/Dockerfile' if os.path.exists(docker_file): build_type = BuildType.DOCKERFILE else: pack = [{'image': args.image}] build_type = BuildType.BUILDPACKS image = None if pack else args.image operation_message = ( 'Building using {build_type} and deploying container' ' to').format(build_type=build_type.value) pretty_print.Info( messages_util.GetBuildEquivalentForSourceRunMessage( service_ref.servicesId, pack, source)) # Deploy a container with an image changes = flags.GetServiceConfigurationChanges(args) changes.insert( 0, config_changes.DeleteAnnotationChange( k8s_object.BINAUTHZ_BREAKGLASS_ANNOTATION)) changes.append( config_changes.SetLaunchStageAnnotationChange(self.ReleaseTrack())) with serverless_operations.Connect(conn_context) as operations: service = operations.GetService(service_ref) allow_unauth = GetAllowUnauth(args, operations, service_ref, service) resource_change_validators.ValidateClearVpcConnector(service, args) pretty_print.Info( messages_util.GetStartDeployMessage(conn_context, service_ref, operation_message)) has_latest = (service is None or traffic.LATEST_REVISION_KEY in service.spec_traffic) deployment_stages = stages.ServiceStages( include_iam_policy_set=allow_unauth is not None, include_route=has_latest, include_build=include_build, include_create_repo=repo_to_create is not None, ) header = None if include_build: header = 'Building and deploying' else: header = 'Deploying' if service is None: header += ' new service' header += '...' with progress_tracker.StagedProgressTracker( header, deployment_stages, failure_message='Deployment failed', suppress_output=args.async_) as tracker: service = operations.ReleaseService( service_ref, changes, tracker, asyn=args.async_, allow_unauthenticated=allow_unauth, prefetch=service, build_image=image, build_pack=pack, build_source=source, repo_to_create=repo_to_create) if args.async_: pretty_print.Success( 'Service [{{bold}}{serv}{{reset}}] is deploying ' 'asynchronously.'.format(serv=service.name)) else: service = operations.GetService(service_ref) pretty_print.Success( messages_util.GetSuccessMessageForSynchronousDeploy( service)) return service
def _UpdateOrCreateService(self, service_ref, config_changes, with_code): """Apply config_changes to the service. Create it if necessary. Arguments: service_ref: Reference to the service to create or update config_changes: list of ConfigChanger to modify the service with with_code: boolean, True if the config_changes contains code to deploy. We can't create the service if we're not deploying code. Returns: The Service object we created or modified. """ nonce = _Nonce() config_changes = [_NewRevisionForcingChange(nonce)] + config_changes messages = self._messages_module # GET the Service serv = self.GetService(service_ref) try: if serv: if not with_code: # Avoid changing the running code by making the new revision by digest self._EnsureImageDigest(serv, config_changes) # PUT the changed Service for config_change in config_changes: config_change.AdjustConfiguration(serv.configuration, serv.metadata) serv_name = service_ref.RelativeName() serv_update_req = ( messages.ServerlessNamespacesServicesReplaceServiceRequest( service=serv.Message(), name=serv_name)) with metrics.record_duration(metrics.UPDATE_SERVICE): updated = self._client.namespaces_services.ReplaceService( serv_update_req) return service.Service(updated, messages) else: if not with_code: raise serverless_exceptions.ServiceNotFoundError( 'Service [{}] could not be found.'.format( service_ref.servicesId)) # POST a new Service new_serv = service.Service.New(self._client, service_ref.namespacesId) new_serv.name = service_ref.servicesId pretty_print.Info( 'Creating new service [{bold}{service}{reset}]', service=new_serv.name) parent = service_ref.Parent().RelativeName() for config_change in config_changes: config_change.AdjustConfiguration(new_serv.configuration, new_serv.metadata) serv_create_req = ( messages.ServerlessNamespacesServicesCreateRequest( service=new_serv.Message(), parent=parent)) with metrics.record_duration(metrics.CREATE_SERVICE): raw_service = self._client.namespaces_services.Create( serv_create_req) return service.Service(raw_service, messages) except api_exceptions.HttpBadRequestError as e: error_payload = exceptions_util.HttpErrorPayload(e) if error_payload.field_violations: if (serverless_exceptions.BadImageError.IMAGE_ERROR_FIELD in error_payload.field_violations): exceptions.reraise(serverless_exceptions.BadImageError(e)) exceptions.reraise(e)
def Run(self, args): """Deploy a Job to Cloud Run.""" job_ref = args.CONCEPTS.job.Parse() flags.ValidateResource(job_ref) conn_context = connection_context.GetConnectionContext( args, flags.Product.RUN, self.ReleaseTrack()) changes = flags.GetJobConfigurationChanges(args) changes.append( config_changes.SetLaunchStageAnnotationChange(self.ReleaseTrack())) execute_now = args.execute_now or args.wait execution = None with serverless_operations.Connect(conn_context) as operations: pretty_print.Info( messages_util.GetStartDeployMessage(conn_context, job_ref, 'Creating', 'job')) if execute_now: header_msg = 'Creating and running job...' else: header_msg = 'Creating job...' with progress_tracker.StagedProgressTracker( header_msg, stages.JobStages(execute_now=execute_now, include_completion=args.wait), failure_message='Job failed to deploy', suppress_output=args.async_) as tracker: job = operations.CreateJob(job_ref, changes, tracker, asyn=(args.async_ and not execute_now)) if execute_now: execution = operations.RunJob(job_ref, args.wait, tracker, args.async_) if args.async_ and not execute_now: pretty_print.Success( 'Job [{{bold}}{job}{{reset}}] is being created ' 'asynchronously.'.format(job=job.name)) else: job = operations.GetJob(job_ref) operation = 'been created' if args.wait: operation += ' and completed execution [{}]'.format( execution.name) elif execute_now: operation += ' and started running execution [{}]'.format( execution.name) pretty_print.Success( 'Job [{{bold}}{job}{{reset}}] has successfully ' '{operation}.'.format(job=job.name, operation=operation)) msg = '' if execute_now: msg += messages_util.GetExecutionCreatedMessage( self.ReleaseTrack(), execution) msg += '\n' msg += messages_util.GetRunJobMessage(self.ReleaseTrack(), job.name, repeat=execute_now) log.status.Print(msg) return job
def Run(self, args): """Create or Update service from YAML.""" conn_context = connection_context.GetConnectionContext( args, flags.Product.RUN, self.ReleaseTrack()) with serverless_operations.Connect(conn_context) as client: try: new_service = service.Service( messages_util.DictToMessageWithErrorCheck( args.FILE, client.messages_module.Service), client.messages_module) except messages_util.ScalarTypeMismatchError as e: exceptions.MaybeRaiseCustomFieldMismatch(e) # If managed, namespace must match project (or will default to project if # not specified). # If not managed, namespace simply must not conflict if specified in # multiple places (or will default to "default" if not specified). namespace = args.CONCEPTS.namespace.Parse().Name( ) # From flag or default if new_service.metadata.namespace is not None: if (args.IsSpecified('namespace') and namespace != new_service.metadata.namespace): raise exceptions.ConfigurationError( 'Namespace specified in file does not match passed flag.' ) namespace = new_service.metadata.namespace if flags.GetPlatform() == flags.PLATFORM_MANAGED: project = properties.VALUES.core.project.Get() project_number = projects_util.GetProjectNumber(project) if namespace != project and namespace != str( project_number): raise exceptions.ConfigurationError( 'Namespace must be project ID [{}] or quoted number [{}] for ' 'Cloud Run (fully managed).'.format( project, project_number)) new_service.metadata.namespace = namespace changes = [ config_changes.ReplaceServiceChange(new_service), config_changes.SetLaunchStageAnnotationChange( self.ReleaseTrack()) ] service_ref = resources.REGISTRY.Parse( new_service.metadata.name, params={'namespacesId': new_service.metadata.namespace}, collection='run.namespaces.services') service_obj = client.GetService(service_ref) pretty_print.Info( run_messages_util.GetStartDeployMessage( conn_context, service_ref, operation='Applying new configuration')) deployment_stages = stages.ServiceStages() header = ('Deploying...' if service_obj else 'Deploying new service...') with progress_tracker.StagedProgressTracker( header, deployment_stages, failure_message='Deployment failed', suppress_output=args.async_) as tracker: service_obj = client.ReleaseService(service_ref, changes, tracker, asyn=args.async_, allow_unauthenticated=None, for_replace=True) if args.async_: pretty_print.Success( 'New configuration for [{{bold}}{serv}{{reset}}] is being applied ' 'asynchronously.'.format(serv=service_obj.name)) else: service_obj = client.GetService(service_ref) pretty_print.Success( 'New configuration has been applied to service ' '[{{bold}}{serv}{{reset}}].\n' 'URL: {{bold}}{url}{{reset}}'.format( serv=service_obj.name, url=service_obj.domain)) return service_obj