def Run(self, args): """Update the service resource. Different from `deploy` in that it can only update the service spec but no IAM or Cloud build changes. Args: args: Args! Returns: googlecloudsdk.api_lib.run.Service, the updated service """ changes = flags.GetServiceConfigurationChanges(args) if not changes or (len(changes) == 1 and isinstance( changes[0], config_changes.SetClientNameAndVersionAnnotationChange)): raise exceptions.NoConfigurationChangeError( 'No configuration change requested. ' 'Did you mean to include the flags `--update-env-vars`, ' '`--memory`, `--concurrency`, `--timeout`, `--connectivity`, ' '`--image`?') changes.insert( 0, config_changes.DeleteAnnotationChange( k8s_object.BINAUTHZ_BREAKGLASS_ANNOTATION)) changes.append( config_changes.SetLaunchStageAnnotationChange(self.ReleaseTrack())) conn_context = connection_context.GetConnectionContext( args, flags.Product.RUN, self.ReleaseTrack()) service_ref = args.CONCEPTS.service.Parse() flags.ValidateResource(service_ref) with serverless_operations.Connect(conn_context) as client: service = client.GetService(service_ref) resource_change_validators.ValidateClearVpcConnector(service, args) has_latest = (service is None or traffic.LATEST_REVISION_KEY in service.spec_traffic) deployment_stages = stages.ServiceStages( include_iam_policy_set=False, include_route=has_latest) with progress_tracker.StagedProgressTracker( 'Deploying...', deployment_stages, failure_message='Deployment failed', suppress_output=args.async_) as tracker: service = client.ReleaseService(service_ref, changes, tracker, asyn=args.async_, prefetch=service) if args.async_: pretty_print.Success( 'Service [{{bold}}{serv}{{reset}}] is deploying ' 'asynchronously.'.format(serv=service.name)) else: service = client.GetService(service_ref) pretty_print.Success( messages_util.GetSuccessMessageForSynchronousDeploy( service)) return service
def testClearVpcConnectorFalse_VpcEgressSet_DoesNothing(self, vpc_egress): args = self.arg_parser.parse_args(['--no-clear-vpc-connector']) self.service.template_annotations[ revision.EGRESS_SETTINGS_ANNOTATION] = vpc_egress resource_change_validators.ValidateClearVpcConnector(self.service, args) self.prompt_continue.assert_not_called()
def testClearVpcConnectorTrue_ServicePrivateRangesOnly_DoesNothing(self): args = self.arg_parser.parse_args(['--clear-vpc-connector']) self.service.template_annotations[ revision. EGRESS_SETTINGS_ANNOTATION] = revision.EGRESS_SETTINGS_PRIVATE_RANGES_ONLY resource_change_validators.ValidateClearVpcConnector(self.service, args) self.prompt_continue.assert_not_called()
def testClearVpcConnectorTrue_ServiceAll_CanPrompt_Prompts_NoConfirmThrows( self): self.can_prompt.return_value = True self.prompt_continue.side_effect = console_io.OperationCancelledError args = self.arg_parser.parse_args(['--clear-vpc-connector']) self.service.template_annotations[ revision.EGRESS_SETTINGS_ANNOTATION] = revision.EGRESS_SETTINGS_ALL with self.assertRaises(console_io.OperationCancelledError): resource_change_validators.ValidateClearVpcConnector(self.service, args)
def testClearVpcConnectorTrue_ServiceAll_NoPrompt_Throws(self): self.can_prompt.return_value = False args = self.arg_parser.parse_args(['--clear-vpc-connector']) self.service.template_annotations[ revision.EGRESS_SETTINGS_ANNOTATION] = revision.EGRESS_SETTINGS_ALL with self.assertRaisesRegex( exceptions.ConfigurationError, _CONFIGURATION_ERROR_MESSAGE): resource_change_validators.ValidateClearVpcConnector(self.service, args) self.prompt_continue.assert_not_called()
def testClearVpcConnectorTrue_ServiceAll_CanPrompt_Prompts_ConfirmSucceeds( self): self.can_prompt.return_value = True self.prompt_continue.return_value = True args = self.arg_parser.parse_args(['--clear-vpc-connector']) self.service.template_annotations[ revision.EGRESS_SETTINGS_ANNOTATION] = revision.EGRESS_SETTINGS_ALL resource_change_validators.ValidateClearVpcConnector(self.service, args) self.prompt_continue.assert_called_once_with( message=_PROMPT_MESSAGE, default=False, cancel_on_no=True)
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 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 testClearVpcConnectorTrue_NoVpcEgress_DoesNothing(self): args = self.arg_parser.parse_args(['--clear-vpc-connector']) resource_change_validators.ValidateClearVpcConnector(self.service, args) self.prompt_continue.assert_not_called()