我有一种情况,我试图创建两个云函数,即CF1&CF2和我有一个Cloud Scheduler。这两个云函数都启用了经过身份验证的调用。我的流程是Cloud Scheduler将触发CF1。在完成CF1时,CF1将触发CF2作为http调用。我提到了无法从GCP调度程序调用谷歌云功能来访问云调度程序中经过验证的CF1,并且能够访问CF1。但我在从CF1访问CF2时遇到了问题。CF1不触发CF2,也不给出任何错误消息。当我们从另一个已验证的云函数访问已验证的云和函数时,是否需要遵循任何其他技术。
CF1代码:
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.
注意:如果我对两个云功能都使用未经身份验证的访问权限,则相同的流也会起作用。
这里发生了两件事:
- 您没有完全正确地使用
request-futures
。由于请求是异步发出的,因此需要在函数隐式返回之前阻止结果,否则它可能会在HTTP请求完成之前返回(尽管在本例中可能是这样(:
session = FuturesSession()
future = session.get("{}".format(cf2_endpoint))
resp = future.result() # Block on the request completing
- 您向第二个函数发出的请求实际上不是经过身份验证的请求。默认情况下,来自云功能的出站请求不经过身份验证。如果你看看上面的实际反应,你会发现:
>>> 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-以运行
然而,更好的选择是将第二个函数设置为"后台"函数,并通过从第一个函数发布的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请求添加授权,还可以确保至少一次传递。
Google Cloud Function提供REST API接口,其中包括可在另一个Cloud Function HTTP调用中使用的调用方法。尽管文档中提到使用谷歌提供的客户端库,但Python上仍然没有云函数库。
相反,您需要使用通用的Google API客户端库。[这是python的一个].3
使用这种方法的主要困难可能是对身份验证过程的理解。通常,您需要提供两件事来构建客户端服务:凭据和作用域。
获取凭据的最简单方法是在应用程序默认凭据(ADC(库上中继。正确的文档是:
- https://cloud.google.com/docs/authentication/production
- https://github.com/googleapis/google-api-python-client/blob/master/docs/auth.md
获取作用域的位置是每个REST API函数文档页。类似,OAuth范围:https://www.googleapis.com/auth/cloud-platform
下面是调用"helloworld"clound函数的完整代码示例。运行前:
- 在项目中的GCP上创建默认的云函数
- 保留并注意要使用的默认服务帐户
- 保留默认实体
- 请注意部署函数的project_id、函数名称和位置
- 如果您将在Cloud function环境之外调用函数(例如本地(,请根据上述文档设置环境变量GOOGLE_APPLICATION_CREDENTIALS
- 如果您将从另一个云函数实际调用,则根本不需要配置凭据
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!'}