예제 #1
0
    def _ApplyNonSourceArgsToFunction(self, function, name, entry_point,
                                      timeout_sec, trigger_http,
                                      trigger_params, retry, memory):
        """Modifies a function object without touching in the sources properties.

    Args:
      function: message, function resource to be modified.
      name: str, name of the function (resource).
      entry_point: str, name of the function (in deployed code) to be executed.
      timeout_sec: int, maximum time allowed for function execution, in seconds.
      trigger_http: bool, indicates whether function should have a HTTPS
                    trigger; when truthy trigger_params argument is ignored.
      trigger_params: None or dict from str to str, the dict is assumed to
                      contain exactly the following keys: trigger_provider,
                      trigger_event, trigger_resource.
      retry: bool, indicates if function should retry.
      memory: int, memory limit for the function in bytes.
    """
        messages = util.GetApiMessagesModule()
        function.name = name
        if entry_point:
            function.entryPoint = entry_point
        if timeout_sec:
            function.timeout = str(timeout_sec) + 's'
        if trigger_http:
            function.httpsTrigger = messages.HTTPSTrigger()
        elif trigger_params:
            function.eventTrigger = self._EventTrigger(**trigger_params)
        if retry:
            function.eventTrigger.failurePolicy = messages.FailurePolicy()
            function.eventTrigger.failurePolicy.retry = messages.Retry()
        elif function.eventTrigger:
            function.eventTrigger.failurePolicy = None
        if memory:
            function.availableMemoryMb = utils.BytesToMb(memory)
예제 #2
0
 def _DeployFunction(self, name, location, args, deploy_method,
                     trigger_params):
     function = self._PrepareFunctionWithoutSources(name, args.entry_point,
                                                    args.timeout,
                                                    args.trigger_http,
                                                    trigger_params)
     if args.source:
         deploy_util.AddSourceToFunction(function, args.source,
                                         args.include_ignored_files,
                                         args.name, args.stage_bucket)
     elif args.source_url:
         messages = util.GetApiMessagesModule()
         source_path = args.source_path
         source_branch = args.source_branch or 'master'
         function.sourceRepository = messages.SourceRepository(
             tag=args.source_tag,
             branch=source_branch,
             revision=args.source_revision,
             repositoryUrl=args.source_url,
             sourcePath=source_path)
     else:
         function.sourceArchiveUrl = self._PrepareSourcesOnGcs(args)
     memory_mb = utils.BytesToMb(args.memory)
     if memory_mb:
         function.availableMemoryMb = memory_mb
     return deploy_method(location, function)
예제 #3
0
def _GetServiceConfig(args, messages):
    """Construct a ServiceConfig message from the command-line arguments."""
    env_var_flags = map_util.GetMapFlagsFromArgs('env-vars', args)
    env_vars = map_util.ApplyMapFlags({}, **env_var_flags)

    return messages.ServiceConfig(
        availableMemoryMb=utils.BytesToMb(args.memory)
        if args.memory else None,
        maxInstanceCount=args.max_instances,
        serviceAccountEmail=args.run_service_account or args.service_account,
        timeoutSeconds=args.timeout,
        environmentVariables=messages.ServiceConfig.EnvironmentVariablesValue(
            additionalProperties=[
                messages.ServiceConfig.EnvironmentVariablesValue.
                AdditionalProperty(key=key, value=value)
                for key, value in sorted(env_vars.items())
            ]))
예제 #4
0
 def _DeployFunction(self, name, location, args, deploy_method):
     function = self._PrepareFunctionWithoutSources(name, args)
     if args.source_url:
         messages = self.context['functions_messages']
         source_path = args.source or args.source_path
         function.sourceRepository = messages.SourceRepository(
             tag=args.source_tag,
             branch=args.source_branch,
             revision=args.source_revision,
             repositoryUrl=args.source_url,
             sourcePath=source_path)
     else:
         function.gcsUrl = self._PrepareSourcesOnGcs(args)
     memory_mb = utils.BytesToMb(args.memory)
     if memory_mb:
         function.availableMemoryMb = memory_mb
     return deploy_method(location, function)
예제 #5
0
    def _ApplyNonSourceArgsToFunction(self, function, function_ref,
                                      update_mask, messages, args,
                                      trigger_params):
        """Modifies a function object without touching in the sources properties.

    Args:
      function: message, function resource to be modified.
      function_ref: reference to function.
      update_mask: update mask to which modified fields will be added.
      messages: messages module.
      args: parsed commandline arguments.
      trigger_params: None or dict from str to str, the dict is assumed to
                      contain exactly the following keys: trigger_provider,
                      trigger_event, trigger_resource.
    """
        function.name = function_ref.RelativeName()
        if args.entry_point:
            function.entryPoint = args.entry_point
            update_mask.append('entryPoint')
        if args.timeout:
            function.timeout = '{}s'.format(args.timeout)
            update_mask.append('timeout')
        if args.trigger_http:
            deploy_util.CleanOldTriggerInfo(function, update_mask)
            function.httpsTrigger = messages.HttpsTrigger()
        if trigger_params:
            deploy_util.CleanOldTriggerInfo(function, update_mask)
            function.eventTrigger = self._EventTrigger(**trigger_params)
        if args.IsSpecified('retry'):
            update_mask.append('eventTrigger.failurePolicy')
            if args.retry:
                function.eventTrigger.failurePolicy = messages.FailurePolicy()
                function.eventTrigger.failurePolicy.retry = messages.Retry()
            else:
                function.eventTrigger.failurePolicy = None
        elif function.eventTrigger:
            function.eventTrigger.failurePolicy = None
        if args.memory:
            function.availableMemoryMb = utils.BytesToMb(args.memory)
            update_mask.append('memory')
예제 #6
0
 def _DeployFunction(self, name, location, args, deploy_method,
                     trigger_params):
     function = self._PrepareFunctionWithoutSources(name, args.entry_point,
                                                    args.timeout,
                                                    args.trigger_http,
                                                    trigger_params)
     if args.source_url:
         messages = self.context['functions_messages']
         source_path = args.source_path
         source_branch = args.source_branch or 'master'
         function.sourceRepository = messages.SourceRepository(
             tag=args.source_tag,
             branch=source_branch,
             revision=args.source_revision,
             repositoryUrl=args.source_url,
             sourcePath=source_path)
     else:
         function.sourceArchiveUrl = self._PrepareSourcesOnGcs(args)
     memory_mb = utils.BytesToMb(args.memory)
     if memory_mb:
         function.availableMemoryMb = memory_mb
     return deploy_method(location, function)
예제 #7
0
def _Run(args,
         track=None,
         enable_runtime=True,
         enable_build_worker_pool=False):
    """Run a function deployment with the given args."""
    # Check for labels that start with `deployment`, which is not allowed.
    labels_util.CheckNoDeploymentLabels('--remove-labels', args.remove_labels)
    labels_util.CheckNoDeploymentLabels('--update-labels', args.update_labels)

    # Check that exactly one trigger type is specified properly.
    trigger_util.ValidateTriggerArgs(args.trigger_event, args.trigger_resource,
                                     args.IsSpecified('retry'),
                                     args.IsSpecified('trigger_http'))
    trigger_params = trigger_util.GetTriggerEventParams(
        args.trigger_http, args.trigger_bucket, args.trigger_topic,
        args.trigger_event, args.trigger_resource)

    function_ref = args.CONCEPTS.name.Parse()
    function_url = function_ref.RelativeName()

    messages = api_util.GetApiMessagesModule(track)

    # Get an existing function or create a new one.
    function = api_util.GetFunction(function_url)
    is_new_function = function is None
    had_vpc_connector = bool(
        function.vpcConnector) if not is_new_function else False
    if is_new_function:
        trigger_util.CheckTriggerSpecified(args)
        function = messages.CloudFunction()
        function.name = function_url
    elif trigger_params:
        # If the new deployment would implicitly change the trigger_event type
        # raise error
        trigger_util.CheckLegacyTriggerUpdate(function.eventTrigger,
                                              trigger_params['trigger_event'])

    # Keep track of which fields are updated in the case of patching.
    updated_fields = []

    # Populate function properties based on args.
    if args.entry_point:
        function.entryPoint = args.entry_point
        updated_fields.append('entryPoint')
    if args.timeout:
        function.timeout = '{}s'.format(args.timeout)
        updated_fields.append('timeout')
    if args.memory:
        function.availableMemoryMb = utils.BytesToMb(args.memory)
        updated_fields.append('availableMemoryMb')
    if args.service_account:
        function.serviceAccountEmail = args.service_account
        updated_fields.append('serviceAccountEmail')
    if (args.IsSpecified('max_instances')
            or args.IsSpecified('clear_max_instances')):
        max_instances = 0 if args.clear_max_instances else args.max_instances
        function.maxInstances = max_instances
        updated_fields.append('maxInstances')
    if enable_runtime:
        if args.IsSpecified('runtime'):
            function.runtime = args.runtime
            updated_fields.append('runtime')
            if args.runtime in ['nodejs6']:
                log.warning(
                    'The Node.js 6 runtime is deprecated on Cloud Functions. '
                    'Please migrate to Node.js 8 (--runtime=nodejs8) or Node.js 10 '
                    '(--runtime=nodejs10). '
                    'See https://cloud.google.com/functions/docs/migrating/nodejs-runtimes'
                )
        elif is_new_function:
            raise exceptions.RequiredArgumentException(
                'runtime', 'Flag `--runtime` is required for new functions.')
    if args.vpc_connector or args.clear_vpc_connector:
        function.vpcConnector = ('' if args.clear_vpc_connector else
                                 args.vpc_connector)
        updated_fields.append('vpcConnector')
    if args.IsSpecified('egress_settings'):
        will_have_vpc_connector = ((had_vpc_connector
                                    and not args.clear_vpc_connector)
                                   or args.vpc_connector)
        if not will_have_vpc_connector:
            raise exceptions.RequiredArgumentException(
                'vpc-connector', 'Flag `--vpc-connector` is '
                'required for setting `egress-settings`.')
        egress_settings_enum = arg_utils.ChoiceEnumMapper(
            arg_name='egress_settings',
            message_enum=function.VpcConnectorEgressSettingsValueValuesEnum,
            custom_mappings=flags.EGRESS_SETTINGS_MAPPING).GetEnumForChoice(
                args.egress_settings)
        function.vpcConnectorEgressSettings = egress_settings_enum
        updated_fields.append('vpcConnectorEgressSettings')
    if args.IsSpecified('ingress_settings'):
        ingress_settings_enum = arg_utils.ChoiceEnumMapper(
            arg_name='ingress_settings',
            message_enum=function.IngressSettingsValueValuesEnum,
            custom_mappings=flags.INGRESS_SETTINGS_MAPPING).GetEnumForChoice(
                args.ingress_settings)
        function.ingressSettings = ingress_settings_enum
        updated_fields.append('ingressSettings')
    if enable_build_worker_pool:
        if args.build_worker_pool or args.clear_build_worker_pool:
            function.buildWorkerPool = ('' if args.clear_build_worker_pool else
                                        args.build_worker_pool)
            updated_fields.append('buildWorkerPool')
    # Populate trigger properties of function based on trigger args.
    if args.trigger_http:
        function.httpsTrigger = messages.HttpsTrigger()
        function.eventTrigger = None
        updated_fields.extend(['eventTrigger', 'httpsTrigger'])
    if trigger_params:
        function.eventTrigger = trigger_util.CreateEventTrigger(
            **trigger_params)
        function.httpsTrigger = None
        updated_fields.extend(['eventTrigger', 'httpsTrigger'])
    if args.IsSpecified('retry'):
        updated_fields.append('eventTrigger.failurePolicy')
        if args.retry:
            function.eventTrigger.failurePolicy = messages.FailurePolicy()
            function.eventTrigger.failurePolicy.retry = messages.Retry()
        else:
            function.eventTrigger.failurePolicy = None
    elif function.eventTrigger:
        function.eventTrigger.failurePolicy = None

    # Populate source properties of function based on source args.
    # Only Add source to function if its explicitly provided, a new function,
    # using a stage bucket or deploy of an existing function that previously
    # used local source.
    if (args.source or args.stage_bucket or is_new_function
            or function.sourceUploadUrl):
        updated_fields.extend(
            source_util.SetFunctionSourceProps(function, function_ref,
                                               args.source, args.stage_bucket,
                                               args.ignore_file))

    # Apply label args to function
    if labels_util.SetFunctionLabels(function, args.update_labels,
                                     args.remove_labels, args.clear_labels):
        updated_fields.append('labels')

    # Apply environment variables args to function
    updated_fields.extend(_ApplyEnvVarsArgsToFunction(function, args))

    ensure_all_users_invoke = flags.ShouldEnsureAllUsersInvoke(args)
    deny_all_users_invoke = flags.ShouldDenyAllUsersInvoke(args)

    if is_new_function:
        if (not ensure_all_users_invoke and not deny_all_users_invoke and
                api_util.CanAddFunctionIamPolicyBinding(_GetProject(args))):
            ensure_all_users_invoke = console_io.PromptContinue(prompt_string=(
                'Allow unauthenticated invocations of new function [{}]?'.
                format(args.NAME)),
                                                                default=False)

        op = api_util.CreateFunction(function,
                                     function_ref.Parent().RelativeName())
        if (not ensure_all_users_invoke and not deny_all_users_invoke):
            template = ('Function created with limited-access IAM policy. '
                        'To enable unauthorized access consider "%s"')
            log.warning(template %
                        _CreateBindPolicyCommand(args.NAME, args.region))
            deny_all_users_invoke = True

    elif updated_fields:
        op = api_util.PatchFunction(function, updated_fields)

    else:
        op = None  # Nothing to wait for
        if not ensure_all_users_invoke and not deny_all_users_invoke:
            log.status.Print('Nothing to update.')
            return

    stop_trying_perm_set = [False]

    # The server asyncrhonously sets allUsers invoker permissions some time after
    # we create the function. That means, to remove it, we need do so after the
    # server adds it. We can remove this mess after the default changes.
    # TODO(b/139026575): Remove the "remove" path, only bother adding. Remove the
    # logic from the polling loop. Remove the ability to add logic like this to
    # the polling loop.
    def TryToSetInvokerPermission():
        """Try to make the invoker permission be what we said it should.

    This is for executing in the polling loop, and will stop trying as soon as
    it succeeds at making a change.
    """
        if stop_trying_perm_set[0]:
            return
        try:
            if ensure_all_users_invoke:
                api_util.AddFunctionIamPolicyBinding(function.name)
                stop_trying_perm_set[0] = True
            elif deny_all_users_invoke:
                stop_trying_perm_set[0] = (
                    api_util.RemoveFunctionIamPolicyBindingIfFound(
                        function.name))
        except exceptions.HttpException:
            stop_trying_perm_set[0] = True
            log.warning('Setting IAM policy failed, try "%s"' %
                        _CreateBindPolicyCommand(args.NAME, args.region))

    if op:
        api_util.WaitForFunctionUpdateOperation(
            op, do_every_poll=TryToSetInvokerPermission)
    return api_util.GetFunction(function.name)
예제 #8
0
def _Run(args,
         track=None,
         enable_runtime=True,
         enable_max_instances=False,
         enable_connected_vpc=False):
    """Run a function deployment with the given args."""
    # Check for labels that start with `deployment`, which is not allowed.
    labels_util.CheckNoDeploymentLabels('--remove-labels', args.remove_labels)
    labels_util.CheckNoDeploymentLabels('--update-labels', args.update_labels)

    # Check that exactly one trigger type is specified properly.
    trigger_util.ValidateTriggerArgs(args.trigger_event, args.trigger_resource,
                                     args.IsSpecified('retry'),
                                     args.IsSpecified('trigger_http'))

    trigger_params = trigger_util.GetTriggerEventParams(
        args.trigger_http, args.trigger_bucket, args.trigger_topic,
        args.trigger_event, args.trigger_resource)

    function_ref = args.CONCEPTS.name.Parse()
    function_url = function_ref.RelativeName()

    messages = api_util.GetApiMessagesModule(track)

    # Get an existing function or create a new one.
    function = api_util.GetFunction(function_url)
    is_new_function = function is None
    if is_new_function:
        trigger_util.CheckTriggerSpecified(args)
        function = messages.CloudFunction()
        function.name = function_url
    elif trigger_params:
        # If the new deployment would implicitly change the trigger_event type
        # raise error
        trigger_util.CheckLegacyTriggerUpdate(function.eventTrigger,
                                              trigger_params['trigger_event'])

    # Keep track of which fields are updated in the case of patching.
    updated_fields = []

    # Populate function properties based on args.
    if args.entry_point:
        function.entryPoint = args.entry_point
        updated_fields.append('entryPoint')
    if args.timeout:
        function.timeout = '{}s'.format(args.timeout)
        updated_fields.append('timeout')
    if args.memory:
        function.availableMemoryMb = utils.BytesToMb(args.memory)
        updated_fields.append('availableMemoryMb')
    if args.service_account:
        function.serviceAccountEmail = args.service_account
        updated_fields.append('serviceAccountEmail')
    if enable_runtime:
        if args.IsSpecified('runtime'):
            function.runtime = args.runtime
            updated_fields.append('runtime')
        elif is_new_function:
            log.warning('Flag `--runtime` will become a required flag soon. '
                        'Please specify the value for this flag.')
    if enable_max_instances:
        if (args.IsSpecified('max_instances')
                or args.IsSpecified('clear_max_instances')):
            max_instances = 0 if args.clear_max_instances else args.max_instances
            function.maxInstances = max_instances
            updated_fields.append('maxInstances')
    if enable_connected_vpc:
        if args.connected_vpc:
            function.network = args.connected_vpc
            updated_fields.append('network')
        if args.vpc_connector:
            function.vpcConnector = args.vpc_connector
            updated_fields.append('vpcConnector')

    # Populate trigger properties of function based on trigger args.
    if args.trigger_http:
        function.httpsTrigger = messages.HttpsTrigger()
        function.eventTrigger = None
        updated_fields.extend(['eventTrigger', 'httpsTrigger'])
    if trigger_params:
        function.eventTrigger = trigger_util.CreateEventTrigger(
            **trigger_params)
        function.httpsTrigger = None
        updated_fields.extend(['eventTrigger', 'httpsTrigger'])
    if args.IsSpecified('retry'):
        updated_fields.append('eventTrigger.failurePolicy')
        if args.retry:
            function.eventTrigger.failurePolicy = messages.FailurePolicy()
            function.eventTrigger.failurePolicy.retry = messages.Retry()
        else:
            function.eventTrigger.failurePolicy = None
    elif function.eventTrigger:
        function.eventTrigger.failurePolicy = None

    # Populate source properties of function based on source args.
    # Only Add source to function if its explicitly provided, a new function,
    # using a stage bucket or deploy of an existing function that previously
    # used local source.
    if (args.source or args.stage_bucket or is_new_function
            or function.sourceUploadUrl):
        updated_fields.extend(
            source_util.SetFunctionSourceProps(function, function_ref,
                                               args.source, args.stage_bucket))

    # Apply label args to function
    if labels_util.SetFunctionLabels(function, args.update_labels,
                                     args.remove_labels, args.clear_labels):
        updated_fields.append('labels')

    # Apply environment variables args to function
    updated_fields.extend(_ApplyEnvVarsArgsToFunction(function, args))

    if is_new_function:
        return api_util.CreateFunction(function,
                                       function_ref.Parent().RelativeName())
    if updated_fields:
        return api_util.PatchFunction(function, updated_fields)
    log.status.Print('Nothing to update.')
def Run(args, track=None, enable_runtime=True):
    """Run a function deployment with the given args."""
    flags.ValidateV1TimeoutFlag(args)

    # Check for labels that start with `deployment`, which is not allowed.
    labels_util.CheckNoDeploymentLabels('--remove-labels', args.remove_labels)
    labels_util.CheckNoDeploymentLabels('--update-labels', args.update_labels)

    # Check that exactly one trigger type is specified properly.
    trigger_util.ValidateTriggerArgs(args.trigger_event, args.trigger_resource,
                                     args.IsSpecified('retry'),
                                     args.IsSpecified('trigger_http'))
    trigger_params = trigger_util.GetTriggerEventParams(
        args.trigger_http, args.trigger_bucket, args.trigger_topic,
        args.trigger_event, args.trigger_resource)

    function_ref = args.CONCEPTS.name.Parse()
    function_url = function_ref.RelativeName()

    messages = api_util.GetApiMessagesModule(track)

    # Get an existing function or create a new one.
    function = api_util.GetFunction(function_url)

    is_new_function = function is None
    had_vpc_connector = bool(
        function.vpcConnector) if not is_new_function else False
    had_http_trigger = bool(
        function.httpsTrigger) if not is_new_function else False
    if is_new_function:
        trigger_util.CheckTriggerSpecified(args)
        function = messages.CloudFunction()
        function.name = function_url
    elif trigger_params:
        # If the new deployment would implicitly change the trigger_event type
        # raise error
        trigger_util.CheckLegacyTriggerUpdate(function.eventTrigger,
                                              trigger_params['trigger_event'])

    # Keep track of which fields are updated in the case of patching.
    updated_fields = []

    # Populate function properties based on args.
    if args.entry_point:
        function.entryPoint = args.entry_point
        updated_fields.append('entryPoint')
    if args.timeout:
        function.timeout = '{}s'.format(args.timeout)
        updated_fields.append('timeout')
    if args.memory:
        function.availableMemoryMb = utils.BytesToMb(args.memory)
        updated_fields.append('availableMemoryMb')
    if args.service_account:
        function.serviceAccountEmail = args.service_account
        updated_fields.append('serviceAccountEmail')
    if (args.IsSpecified('max_instances')
            or args.IsSpecified('clear_max_instances')):
        max_instances = 0 if args.clear_max_instances else args.max_instances
        function.maxInstances = max_instances
        updated_fields.append('maxInstances')
    if (args.IsSpecified('min_instances')
            or args.IsSpecified('clear_min_instances')):
        min_instances = 0 if args.clear_min_instances else args.min_instances
        function.minInstances = min_instances
        updated_fields.append('minInstances')
    if enable_runtime:
        if args.IsSpecified('runtime'):
            function.runtime = args.runtime
            updated_fields.append('runtime')

            warning = api_util.ValidateRuntime(args.runtime)
            if warning:
                log.warning(warning)

        elif is_new_function:
            raise calliope_exceptions.RequiredArgumentException(
                'runtime', 'Flag `--runtime` is required for new functions.')
    if args.vpc_connector or args.clear_vpc_connector:
        function.vpcConnector = ('' if args.clear_vpc_connector else
                                 args.vpc_connector)
        updated_fields.append('vpcConnector')
    if args.IsSpecified('egress_settings'):
        will_have_vpc_connector = ((had_vpc_connector
                                    and not args.clear_vpc_connector)
                                   or args.vpc_connector)
        if not will_have_vpc_connector:
            raise calliope_exceptions.RequiredArgumentException(
                'vpc-connector', 'Flag `--vpc-connector` is '
                'required for setting `egress-settings`.')
        egress_settings_enum = arg_utils.ChoiceEnumMapper(
            arg_name='egress_settings',
            message_enum=function.VpcConnectorEgressSettingsValueValuesEnum,
            custom_mappings=flags.EGRESS_SETTINGS_MAPPING).GetEnumForChoice(
                args.egress_settings)
        function.vpcConnectorEgressSettings = egress_settings_enum
        updated_fields.append('vpcConnectorEgressSettings')
    if args.IsSpecified('ingress_settings'):
        ingress_settings_enum = arg_utils.ChoiceEnumMapper(
            arg_name='ingress_settings',
            message_enum=function.IngressSettingsValueValuesEnum,
            custom_mappings=flags.INGRESS_SETTINGS_MAPPING).GetEnumForChoice(
                args.ingress_settings)
        function.ingressSettings = ingress_settings_enum
        updated_fields.append('ingressSettings')
    if args.build_worker_pool or args.clear_build_worker_pool:
        function.buildWorkerPool = ('' if args.clear_build_worker_pool else
                                    args.build_worker_pool)
        updated_fields.append('buildWorkerPool')
    # Populate trigger properties of function based on trigger args.
    if args.trigger_http:
        function.httpsTrigger = messages.HttpsTrigger()
        function.eventTrigger = None
        updated_fields.extend(['eventTrigger', 'httpsTrigger'])
    if trigger_params:
        function.eventTrigger = trigger_util.CreateEventTrigger(
            **trigger_params)
        function.httpsTrigger = None
        updated_fields.extend(['eventTrigger', 'httpsTrigger'])
    if args.IsSpecified('retry'):
        updated_fields.append('eventTrigger.failurePolicy')
        if args.retry:
            function.eventTrigger.failurePolicy = messages.FailurePolicy()
            function.eventTrigger.failurePolicy.retry = messages.Retry()
        else:
            function.eventTrigger.failurePolicy = None
    elif function.eventTrigger:
        function.eventTrigger.failurePolicy = None

    will_have_http_trigger = had_http_trigger or args.trigger_http

    if args.IsSpecified('security_level') or (will_have_http_trigger
                                              and is_new_function):
        if not will_have_http_trigger:
            raise calliope_exceptions.RequiredArgumentException(
                'trigger-http',
                'Flag `--trigger-http` is required for setting `security-level`.'
            )

        # SecurityLevelValueValuesEnum('SECURE_ALWAYS' | 'SECURE_OPTIONAL')
        security_level_enum = arg_utils.ChoiceEnumMapper(
            arg_name='security_level',
            message_enum=function.httpsTrigger.SecurityLevelValueValuesEnum,
            custom_mappings=flags.SECURITY_LEVEL_MAPPING).GetEnumForChoice(
                args.security_level)
        function.httpsTrigger.securityLevel = security_level_enum
        updated_fields.append('httpsTrigger.securityLevel')

    kms_key = _GetActiveKMSKey(function, args)

    # Populate source properties of function based on source args.
    # Only Add source to function if its explicitly provided, a new function,
    # using a stage bucket or deploy of an existing function that previously
    # used local source.
    if (args.source or args.stage_bucket or is_new_function
            or function.sourceUploadUrl):
        updated_fields.extend(
            source_util.SetFunctionSourceProps(function, function_ref,
                                               args.source, args.stage_bucket,
                                               args.ignore_file, kms_key))

    # Apply label args to function
    if labels_util.SetFunctionLabels(function, args.update_labels,
                                     args.remove_labels, args.clear_labels):
        updated_fields.append('labels')

    # Apply build environment variables args to function
    updated_fields.extend(_ApplyBuildEnvVarsArgsToFunction(function, args))

    # Apply environment variables args to function
    updated_fields.extend(_ApplyEnvVarsArgsToFunction(function, args))

    ensure_all_users_invoke = flags.ShouldEnsureAllUsersInvoke(args)
    deny_all_users_invoke = flags.ShouldDenyAllUsersInvoke(args)

    # Applies secrets args to function
    updated_fields.extend(_ApplySecretsArgsToFunction(function, args))

    # Applies CMEK args to function
    updated_fields.extend(
        _ApplyCMEKArgsToFunction(function_ref, function, args))

    # Applies remaining Artifact Registry args to the function. Note that one of
    # them, docker_repository, was already added as part of CMEK
    updated_fields.extend(
        _ApplyDockerRegistryArgsToFunction(function, args, track))

    if is_new_function:
        if (function.httpsTrigger and not ensure_all_users_invoke
                and not deny_all_users_invoke
                and api_util.CanAddFunctionIamPolicyBinding(_GetProject())):
            ensure_all_users_invoke = console_io.PromptContinue(prompt_string=(
                'Allow unauthenticated invocations of new function [{}]?'.
                format(args.NAME)),
                                                                default=False)

        op = api_util.CreateFunction(function,
                                     function_ref.Parent().RelativeName())
        if (function.httpsTrigger and not ensure_all_users_invoke
                and not deny_all_users_invoke):
            template = ('Function created with limited-access IAM policy. '
                        'To enable unauthorized access consider `%s`')
            log.warning(template % _CreateBindPolicyCommand(function_ref))
            deny_all_users_invoke = True

    elif updated_fields:
        op = api_util.PatchFunction(function, updated_fields)

    else:
        op = None  # Nothing to wait for
        if not ensure_all_users_invoke and not deny_all_users_invoke:
            log.status.Print('Nothing to update.')
            return

    stop_trying_perm_set = [False]

    # The server asyncrhonously sets allUsers invoker permissions some time after
    # we create the function. That means, to remove it, we need do so after the
    # server adds it. We can remove this mess after the default changes.
    # TODO(b/130604453): Remove the "remove" path, only bother adding. Remove the
    # logic from the polling loop. Remove the ability to add logic like this to
    # the polling loop.
    # Because of the DRS policy restrictions, private-by-default behavior is not
    # guaranteed for all projects and we need this hack until IAM deny is
    # implemented and all projects have private-by-default.
    def TryToSetInvokerPermission():
        """Try to make the invoker permission be what we said it should.

    This is for executing in the polling loop, and will stop trying as soon as
    it succeeds at making a change.
    """
        if stop_trying_perm_set[0]:
            return
        try:
            if ensure_all_users_invoke:
                api_util.AddFunctionIamPolicyBinding(function.name)
                stop_trying_perm_set[0] = True
            elif deny_all_users_invoke:
                stop_trying_perm_set[0] = (
                    api_util.RemoveFunctionIamPolicyBindingIfFound(
                        function.name))
        except calliope_exceptions.HttpException:
            stop_trying_perm_set[0] = True
            log.warning('Setting IAM policy failed, try `%s`' %
                        _CreateBindPolicyCommand(function_ref))

    log_stackdriver_url = [True]

    def TryToLogStackdriverURL(op):
        """Logs stackdriver URL.

    This is for executing in the polling loop, and will stop trying as soon as
    it succeeds at making a change.

    Args:
      op: the operation
    """
        if log_stackdriver_url[0] and op.metadata:
            metadata = encoding.PyValueToMessage(
                messages.OperationMetadataV1,
                encoding.MessageToPyValue(op.metadata))
            if metadata.buildName and _BUILD_NAME_REGEX.match(
                    metadata.buildName):
                log.status.Print('\nFor Cloud Build Logs, visit: %s' %
                                 _CreateCloudBuildLogURL(metadata.buildName))
                log_stackdriver_url[0] = False
            elif metadata.buildId:
                sd_info_template = '\nFor Cloud Build Stackdriver Logs, visit: %s'
                log.status.Print(sd_info_template %
                                 _CreateStackdriverURLforBuildLogs(
                                     metadata.buildId, _GetProject()))
                log_stackdriver_url[0] = False

    if op:
        try_set_invoker = None
        if function.httpsTrigger:
            try_set_invoker = TryToSetInvokerPermission
        api_util.WaitForFunctionUpdateOperation(
            op,
            try_set_invoker=try_set_invoker,
            on_every_poll=[TryToLogStackdriverURL])
    return api_util.GetFunction(function.name)
예제 #10
0
def _Run(args,
         track=None,
         enable_runtime=True,
         enable_max_instances=False,
         enable_vpc_connector=False,
         enable_traffic_control=False,
         enable_allow_unauthenticated=False):
    """Run a function deployment with the given args."""
    # Check for labels that start with `deployment`, which is not allowed.
    labels_util.CheckNoDeploymentLabels('--remove-labels', args.remove_labels)
    labels_util.CheckNoDeploymentLabels('--update-labels', args.update_labels)

    # Check that exactly one trigger type is specified properly.
    trigger_util.ValidateTriggerArgs(args.trigger_event, args.trigger_resource,
                                     args.IsSpecified('retry'),
                                     args.IsSpecified('trigger_http'))

    trigger_params = trigger_util.GetTriggerEventParams(
        args.trigger_http, args.trigger_bucket, args.trigger_topic,
        args.trigger_event, args.trigger_resource)

    function_ref = args.CONCEPTS.name.Parse()
    function_url = function_ref.RelativeName()

    messages = api_util.GetApiMessagesModule(track)

    # Get an existing function or create a new one.
    function = api_util.GetFunction(function_url)
    is_new_function = function is None
    if is_new_function:
        trigger_util.CheckTriggerSpecified(args)
        function = messages.CloudFunction()
        function.name = function_url
    elif trigger_params:
        # If the new deployment would implicitly change the trigger_event type
        # raise error
        trigger_util.CheckLegacyTriggerUpdate(function.eventTrigger,
                                              trigger_params['trigger_event'])

    # Keep track of which fields are updated in the case of patching.
    updated_fields = []

    # Populate function properties based on args.
    if args.entry_point:
        function.entryPoint = args.entry_point
        updated_fields.append('entryPoint')
    if args.timeout:
        function.timeout = '{}s'.format(args.timeout)
        updated_fields.append('timeout')
    if args.memory:
        function.availableMemoryMb = utils.BytesToMb(args.memory)
        updated_fields.append('availableMemoryMb')
    if args.service_account:
        function.serviceAccountEmail = args.service_account
        updated_fields.append('serviceAccountEmail')
    if enable_runtime:
        if args.IsSpecified('runtime'):
            function.runtime = args.runtime
            updated_fields.append('runtime')
            if args.runtime in ['nodejs',
                                'nodejs6']:  # nodejs is nodejs6 alias
                log.warning(
                    'The Node.js 6 runtime is deprecated on Cloud Functions. '
                    'Please migrate to Node.js 8 (--runtime=nodejs8) or Node.js 10 '
                    '(--runtime=nodejs10). '
                    'See https://cloud.google.com/functions/docs/migrating/nodejs-runtimes'
                )
        elif is_new_function:
            raise exceptions.RequiredArgumentException(
                'runtime', 'Flag `--runtime` is required for new functions.')
    if enable_max_instances:
        if (args.IsSpecified('max_instances')
                or args.IsSpecified('clear_max_instances')):
            max_instances = 0 if args.clear_max_instances else args.max_instances
            function.maxInstances = max_instances
            updated_fields.append('maxInstances')
    if enable_vpc_connector:
        if args.IsSpecified('vpc_connector'):
            function.vpcConnector = args.vpc_connector
            updated_fields.append('vpcConnector')
            if enable_traffic_control:
                if args.IsSpecified('egress_settings'):
                    egress_settings_enum = \
                        arg_utils.ChoiceEnumMapper(
                            arg_name='egress_settings',
                            message_enum=function.VpcConnectorEgressSettingsValueValuesEnum,
                            custom_mappings=flags.EGRESS_SETTINGS_MAPPING,).GetEnumForChoice(
                                args.egress_settings)
                    function.vpcConnectorEgressSettings = egress_settings_enum
                    updated_fields.append('vpcConnectorEgressSettings')
                if (args.IsSpecified('egress_settings')
                        and not args.IsSpecified('vpc_connector')):
                    raise exceptions.RequiredArgumentException(
                        'vpc-connector', 'Flag `--vpc-connector` is '
                        'required for setting egress_settings.')
    if enable_traffic_control:
        if args.IsSpecified('ingress_settings'):
            ingress_settings_enum = \
              arg_utils.ChoiceEnumMapper(
                  arg_name='ingress_settings',
                  message_enum=function.IngressSettingsValueValuesEnum,
                  custom_mappings=flags.INGRESS_SETTINGS_MAPPING,).GetEnumForChoice(
                      args.ingress_settings)
            function.ingressSettings = ingress_settings_enum
            updated_fields.append('ingressSettings')
    # Populate trigger properties of function based on trigger args.
    if args.trigger_http:
        function.httpsTrigger = messages.HttpsTrigger()
        function.eventTrigger = None
        updated_fields.extend(['eventTrigger', 'httpsTrigger'])
    if trigger_params:
        function.eventTrigger = trigger_util.CreateEventTrigger(
            **trigger_params)
        function.httpsTrigger = None
        updated_fields.extend(['eventTrigger', 'httpsTrigger'])
    if args.IsSpecified('retry'):
        updated_fields.append('eventTrigger.failurePolicy')
        if args.retry:
            function.eventTrigger.failurePolicy = messages.FailurePolicy()
            function.eventTrigger.failurePolicy.retry = messages.Retry()
        else:
            function.eventTrigger.failurePolicy = None
    elif function.eventTrigger:
        function.eventTrigger.failurePolicy = None

    # Populate source properties of function based on source args.
    # Only Add source to function if its explicitly provided, a new function,
    # using a stage bucket or deploy of an existing function that previously
    # used local source.
    if (args.source or args.stage_bucket or is_new_function
            or function.sourceUploadUrl):
        updated_fields.extend(
            source_util.SetFunctionSourceProps(function, function_ref,
                                               args.source, args.stage_bucket,
                                               args.ignore_file))

    # Apply label args to function
    if labels_util.SetFunctionLabels(function, args.update_labels,
                                     args.remove_labels, args.clear_labels):
        updated_fields.append('labels')

    # Apply environment variables args to function
    updated_fields.extend(_ApplyEnvVarsArgsToFunction(function, args))

    ensure_all_users_invoke = (enable_allow_unauthenticated
                               and flags.ShouldEnsureAllUsersInvoke(args))
    deny_all_users_invoke = (enable_allow_unauthenticated
                             and flags.ShouldDenyAllUsersInvoke(args))

    if is_new_function:
        if (enable_allow_unauthenticated and not ensure_all_users_invoke
                and not deny_all_users_invoke and
                api_util.CanAddFunctionIamPolicyBinding(_GetProject(args))):
            ensure_all_users_invoke = console_io.PromptContinue(prompt_string=(
                'Allow unauthenticated invocations of new function [{}]?'.
                format(args.NAME)),
                                                                default=False)

        op = api_util.CreateFunction(function,
                                     function_ref.Parent().RelativeName())
        if (enable_allow_unauthenticated and not ensure_all_users_invoke
                and not deny_all_users_invoke):
            template = ('Function created with default IAM policy. '
                        'To enable unauthorized access consider "%s"')
            log.warning(template %
                        _CreateBindPolicyCommand(args.NAME, args.region))

    elif updated_fields:
        op = api_util.PatchFunction(function, updated_fields)

    else:
        op = None  # Nothing to wait for
        if not ensure_all_users_invoke and not deny_all_users_invoke:
            log.status.Print('Nothing to update.')
            return

    try:
        if ensure_all_users_invoke:
            api_util.AddFunctionIamPolicyBinding(function.name)
        elif deny_all_users_invoke:
            api_util.RemoveFunctionIamPolicyBindingIfFound(function.name)
    except exceptions.HttpException:
        log.warning('Setting IAM policy failed, try "%s"' %
                    _CreateBindPolicyCommand(args.NAME, args.region))

    if op:
        api_util.WaitForFunctionUpdateOperation(op)
    return api_util.GetFunction(function.name)
예제 #11
0
def _Run(args, enable_runtime=False):
    """Run a function deployment with the given args."""
    # Check for labels that start with `deployment`, which is not allowed.
    labels_util.CheckNoDeploymentLabels('--remove-labels', args.remove_labels)
    labels_util.CheckNoDeploymentLabels('--update-labels', args.update_labels)

    # Check that exactly one trigger type is specified properly.
    trigger_util.ValidateTriggerArgs(args.trigger_event, args.trigger_resource,
                                     args.IsSpecified('retry'),
                                     args.IsSpecified('trigger_http'))

    trigger_params = trigger_util.GetTriggerEventParams(
        args.trigger_http, args.trigger_bucket, args.trigger_topic,
        args.trigger_event, args.trigger_resource)

    function_ref = api_util.GetFunctionRef(args.name)
    function_url = function_ref.RelativeName()

    messages = api_util.GetApiMessagesModule()

    # Get an existing function or create a new one.
    function = api_util.GetFunction(function_url)
    is_new_function = function is None
    if is_new_function:
        trigger_util.CheckTriggerSpecified(args)
        function = messages.CloudFunction()
        function.name = function_url
    elif trigger_params:
        # If the new deployment would implicitly change the trigger_event type
        # raise error
        trigger_util.CheckLegacyTriggerUpdate(function.eventTrigger,
                                              trigger_params['trigger_event'])

    # Keep track of which fields are updated in the case of patching.
    updated_fields = []

    # Populate function properties based on args.
    if args.entry_point:
        function.entryPoint = args.entry_point
        updated_fields.append('entryPoint')
    if args.timeout:
        function.timeout = '{}s'.format(args.timeout)
        updated_fields.append('timeout')
    if args.memory:
        function.availableMemoryMb = utils.BytesToMb(args.memory)
        updated_fields.append('availableMemoryMb')
    if enable_runtime:
        if args.IsSpecified('runtime'):
            function.runtime = args.runtime
            updated_fields.append('runtime')

    # Populate trigger properties of function based on trigger args.
    if args.trigger_http:
        function.httpsTrigger = messages.HttpsTrigger()
        function.eventTrigger = None
        updated_fields.extend(['eventTrigger', 'httpsTrigger'])
    if trigger_params:
        function.eventTrigger = trigger_util.CreateEventTrigger(
            **trigger_params)
        function.httpsTrigger = None
        updated_fields.extend(['eventTrigger', 'httpsTrigger'])
    if args.IsSpecified('retry'):
        updated_fields.append('eventTrigger.failurePolicy')
        if args.retry:
            function.eventTrigger.failurePolicy = messages.FailurePolicy()
            function.eventTrigger.failurePolicy.retry = messages.Retry()
        else:
            function.eventTrigger.failurePolicy = None
    elif function.eventTrigger:
        function.eventTrigger.failurePolicy = None

    # Populate source properties of function based on source args.
    # Only Add source to function if its explicitly provided, a new function,
    # using a stage budget or deploy of an existing function that previously
    # used local source.
    if (args.source or args.stage_bucket or is_new_function
            or function.sourceUploadUrl):
        updated_fields.extend(
            source_util.SetFunctionSourceProps(function, function_ref,
                                               args.source, args.stage_bucket))

    # Apply label args to function
    if labels_util.SetFunctionLabels(function, args.update_labels,
                                     args.remove_labels, args.clear_labels):
        updated_fields.append('labels')

    if is_new_function:
        return api_util.CreateFunction(function)
    if updated_fields:
        return api_util.PatchFunction(function, updated_fields)
    log.status.Print('Nothing to update.')