无法执行云函数触发不允许未经身份验证的调用的HTTP触发的云函数?

2024-05-13 17:52:10 发布

您现在位置:Python中文网/ 问答频道 /正文

我尝试创建两个云函数,即CF1&;我和我有一个云调度器。两个云函数都启用了身份验证调用。我的流程是云调度器将触发CF1。完成CF1后,CF1将作为http调用触发CF2。我已经提到Cannot invoke Google Cloud Function from GCP Scheduler从云调度器访问经过身份验证的CF1,并且能够访问CF1。但当从CF1访问CF2时,我遇到了问题。CF1不会触发CF2,也不会给出任何错误消息。当从另一个经过身份验证的云函数访问经过身份验证的云函数时,我们是否需要遵循任何其他技术

代码:

import json
import logging
from requests_futures.sessions import FuturesSession


def main(request):
    # To read parameter values from request (url arguments or Json body).
    raw_request_data = request.data
    string_request_data = raw_request_data.decode("utf-8")
    request_json: dict = json.loads(string_request_data)

    request_args = request.args

    if request_json and 'cf2_endpoint' in request_json:
        cf2_endpoint = request_json['cf2_endpoint']
    elif request_args and 'cf2_endpoint' in request_args:
        cf2_endpoint = request_args['cf2_endpoint']
    else:
        cf2_endpoint = 'Invalid endpoint for CF2'

    logger = logging.getLogger('test')
    try:
        session = FuturesSession()
        session.get("{}".format(cf2_endpoint))
        logger.info("First cloud function executed successfully.")

    except RuntimeError:
        logger.error("Exception occurred {}".format(RuntimeError))

CF2代码:

import logging

def main(request):
    logger = logging.getLogger('test')
    logger.info("second cloud function executed successfully.")

当前输出日志:

First cloud function executed successfully.

预期输出日志:

First cloud function executed successfully.
second cloud function executed successfully.

注意:如果我对这两个云功能使用未经验证的访问,则相同的流程也可以工作


Tags: 函数身份验证jsonclouddatarequestargsfunction
2条回答

谷歌云函数提供了REST API interface什么包括调用方法,可以在另一个云函数HTTP调用中使用。 尽管文档mention using Google-provided client libraries仍然没有一个用于Python上的云函数

相反,您需要使用通用的GoogleAPI客户端库。[这是python版本]。3

使用这种方法的主要困难可能是理解身份验证过程。 通常,您需要提供两件事来构建客户端服务: 凭证ans作用域

获取凭据的最简单方法是中继应用程序默认凭据(ADC)库。关于这一点的rigth文档包括:

  1. https://cloud.google.com/docs/authentication/production
  2. https://github.com/googleapis/google-api-python-client/blob/master/docs/auth.md

获取范围的地方是each restapi函数文档页面。 比如OAuth scope: https://www.googleapis.com/auth/cloud-platform

下面是调用“hello world”clound function的完整代码示例。 运行前:

  1. 在项目中的GCP上创建默认云函数
  • 保留并通知要使用的默认服务帐户
  • 保留默认的主体
  1. 请注意部署函数的项目id函数名位置
  2. 如果要调用云函数环境外的函数(例如本地),请根据上述文档设置环境变量GOOGLE\u APPLICATION\u CREDENTIALS
  3. 如果您将实际从另一个云函数调用,则根本不需要配置凭据
from googleapiclient.discovery import build
from googleapiclient.discovery_cache.base import Cache
import google.auth

import pprint as pp

def get_cloud_function_api_service():
    class MemoryCache(Cache):
        _CACHE = {}

        def get(self, url):
            return MemoryCache._CACHE.get(url)

        def set(self, url, content):
            MemoryCache._CACHE[url] = content

    scopes = ['https://www.googleapis.com/auth/cloud-platform']

    # If the environment variable GOOGLE_APPLICATION_CREDENTIALS is set,
    # ADC uses the service account file that the variable points to.
    #
    # If the environment variable GOOGLE_APPLICATION_CREDENTIALS isn't set,
    # ADC uses the default service account that Compute Engine, Google Kubernetes Engine, App Engine, Cloud Run,
    # and Cloud Functions provide
    #
    # see more on https://cloud.google.com/docs/authentication/production
    credentials, project_id = google.auth.default(scopes)

    service = build('cloudfunctions', 'v1', credentials=credentials, cache=MemoryCache())
    return service


google_api_service = get_cloud_function_api_service()
name = 'projects/{project_id}/locations/us-central1/functions/function-1'
body = {
    'data': '{ "message": "It is awesome, you are develop on Stack Overflow language!"}' # json passed as a string
}
result_call = google_api_service.projects().locations().functions().call(name=name, body=body).execute()
pp.pprint(result_call)
# expected out out is:
# {'executionId': '3h4c8cb1kwe2', 'result': 'It is awesome, you are develop on Stack Overflow language!'}

这里发生了两件事:

  1. 您没有完全正确地使用request-futures。由于请求是异步发出的,因此需要在函数隐式返回之前阻止结果,否则它可能会在HTTP请求完成之前返回(尽管在本例中可能是这样):
session = FuturesSession()
future = session.get("{}".format(cf2_endpoint))
resp = future.result()  # Block on the request completing
  1. 您对第二个函数发出的请求实际上不是经过身份验证的请求。默认情况下,云函数的出站请求不经过身份验证。如果您查看上面的实际响应,您将看到:
>>> resp.status_code
403
>>> resp.content
b'\n<html><head>\n<meta http-equiv="content-type" content="text/html;charset=utf-8">\n<title>403 Forbidden</title>\n</head>\n<body text=#000000 bgcolor=#ffffff>\n<h1>Error: Forbidden</h1>\n<h2>Your client does not have permission to get URL <code>/function_two</code> from this server.</h2>\n<h2></h2>\n</body></html>\n'

您可以跳过许多障碍来正确验证此请求,如文档中所述:https://cloud.google.com/functions/docs/securing/authenticating#function-to-function

但是,更好的替代方法是将第二个函数设置为“后台”函数,并通过从第一个函数发布的PubSub消息调用它:

from google.cloud import pubsub

publisher = pubsub.PublisherClient()
topic_name = 'projects/{project_id}/topics/{topic}'.format(
    project_id=<your project id>,
    topic='MY_TOPIC_NAME',  # Set this to something appropriate.
)

def function_one(request):
    message = b'My first message!'
    publisher.publish(topic_name, message)

def function_two(event, context):
    message = event['data'].decode('utf-8')
    print(message)

只要您的函数具有发布PubSub消息的权限,就不需要向HTTP请求添加授权,并且还可以确保至少一次传递

相关问题 更多 >