在 Python 中使用 API 创建 Google Cloud Function [英] Create Google Cloud Function using API in Python

查看:31
本文介绍了在 Python 中使用 API 创建 Google Cloud Function的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Python(3.6) & 开发一个项目Django(1.10),其中我需要使用 API 请求在 Google Cloud 上创建一个函数.

如何在创建该函数时以 zip 存档的形式上传代码?

这是我尝试过的:

来自views.py:

 def post(self, request, *args, **kwargs):如果 request.method == 'POST':post_data = request.POST.copy()post_data.update({'user': request.user.pk})form = forms.SlsForm(post_data, request.FILES)打印('获取发布请求')如果 form.is_valid():func_obj = 形式func_obj.user = request.userfunc_obj.project = form.cleaned_data['project']func_obj.fname = form.cleaned_data['fname']func_obj.fmemory = form.cleaned_data['fmemory']func_obj.entryPoint = form.cleaned_data['entryPoint']func_obj.sourceFile = form.cleaned_data['sourceFile']func_obj.sc_github = form.cleaned_data['sc_github']func_obj.sc_inline_index = form.cleaned_data['sc_inline_index']func_obj.sc_inline_package = form.cleaned_data['sc_inline_package']func_obj.bucket = form.cleaned_data['bucket']func_obj.save()service = discovery.build('cloudfunctions', 'v1', http=views.getauth(), cache_discovery=False)requ = service.projects().locations().functions().generateUploadUrl(parent='projects/' + func_obj.project + '/locations/us-central1', body={})resp = requ.execute()打印(响应)尝试:auth = views.getauth()# 准备请求正文req_body = {云函数":{名称":func_obj.fname,入口点":func_obj.entryPoint,"超时": '60s',availableMemoryMb":func_obj.fmemory,"sourceArchiveUrl": func_obj.sc_github,},"sourceUploadUrl": func_obj.bucket,}service = discovery.build('cloudfunctions', 'v1beta2', http=auth, cachce_dicovery=False)func_req = service.projects().locations().functions().create(location='projects/' + func_obj.project+ '/位置/-',身体=req_body)func_res = func_req.execute()打印(func_res)返回 HttpResponse('提交',)除了:返回 HttpResponse(状态=500)返回 HttpResponse('已发送!')

<块引用>

以下更新代码:

 如果 form.is_valid():func_obj = 形式func_obj.user = request.userfunc_obj.project = form.cleaned_data['project']func_obj.fname = form.cleaned_data['fname']func_obj.fmemory = form.cleaned_data['fmemory']func_obj.entryPoint = form.cleaned_data['entryPoint']func_obj.sourceFile = form.cleaned_data['sourceFile']func_obj.sc_github = form.cleaned_data['sc_github']func_obj.sc_inline_index = form.cleaned_data['sc_inline_index']func_obj.sc_inline_package = form.cleaned_data['sc_inline_package']func_obj.bucket = form.cleaned_data['bucket']func_obj.save()##################################################################### 使用存储桶创建函数的第一种方法####################################################################file_name = os.path.join(IGui.settings.BASE_DIR, 'media/archives/', func_obj.sourceFile.name)打印(文件名)service = discovery.build('cloudfunctions', 'v1')func_api = service.projects().locations().functions()url_svc_req = func_api.generateUploadUrl(parent='projects/'+ func_obj.project+ '/locations/us-central1',身体={})url_svc_res = url_svc_req.execute()打印(url_svc_res)upload_url = url_svc_res['uploadUrl']打印(上传网址)标题 = {'内容类型':'应用程序/zip',x-goog-content-length-range":0,104857600"}打印(requests.put(upload_url,headers=headers,data=func_obj.sourceFile.name))auth = views.getauth()# 准备请求正文name = "projects/{}/locations/us-central1/functions/{}".format(func_obj.project, func_obj.fname,)打印(名称)req_body = {名称":名称,入口点":func_obj.entryPoint,"超时": "3.5s",availableMemoryMb":func_obj.fmemory,sourceUploadUrl":upload_url,"httpsTrigger": {},}service = discovery.build('cloudfunctions', 'v1')func_api = service.projects().locations().functions()response = func_api.create(location='projects/' + func_obj.project + '/locations/us-central1',body=req_body).execute()pprint.pprint(响应)

现在这个函数已经创建成功了,但是因为源代码没有上传到存储桶而失败,可能是哪里出了问题:

upload_url = url_svc_res['uploadUrl']打印(上传网址)标题 = {'内容类型':'应用程序/zip',x-goog-content-length-range":0,104857600"}打印(requests.put(upload_url,headers=headers,data=func_obj.sourceFile.name))

解决方案

在请求正文中,请求中有一个字典CloudFunction".CloudFunction"的内容应直接在请求中.

request_body = {名称":父级 + '/functions/' + 名称,入口点":入口点,sourceUploadUrl":upload_url,https触发器":{}}

我建议使用 试用此 API" 以发现 projects.locations.functions.create 的结构.

"sourceArchiveUrl""sourceUploadUrl" 不能一起出现.这在 Resorce Cloud Function<中有解释/a>:

//联合字段 source_code 只能是以下之一:sourceArchiveUrl":字符串,"sourceRepository": { object(SourceRepository) },sourceUploadUrl":字符串,//联合字段 source_code 的可能类型列表的结尾.

在其余的答案中,我假设您想使用 "sourceUploadUrl".它要求您将 .generateUploadUrl(...).execute() 返回给您的 URL 传递给它.请参阅文档:

<块引用>

sourceUploadUrl -> string

用于源上传的 Google Cloud Storage 签名 URL,由 [google.cloud.functions.v1.GenerateUploadUrl][]

生成

但在传递之前,您需要将 zip 文件上传到此 URL:

curl -X PUT "${URL}" -H 'content-type:application/zip' -H 'x-goog-content-length-range: 0,104857600' -T test.zip

或在python中:

 标头 = {'内容类型':'应用程序/zip','x-goog-content-length-range':'0,104857600'}打印(requests.put(upload_url,headers=headers,data=data))

这是最棘手的部分:

  • 大小写很重要,它应该是小写的.因为签名是根据哈希计算的(此处)

  • 您需要内容类型":应用程序/zip".我从逻辑上推断出这个,​​因为文档没有提到它.(此处)

  • x-goog-content-length-range: min,max 对于云存储的所有 PUT 请求都是强制性的,并且在这种情况下是隐式假设的.更多相关信息此处

  • 104857600,上一个条目中的最大值,是一个神奇的数字,我在任何地方都没有发现.

其中 data 是一个 FileLikeObject.

我还假设您想使用 httpsTrigger.对于云函数,您只能选择一个触发字段.这里 据说触发器是一个联合字段.但是,对于 httpsTrigger,您可以将其保留为空字典,因为其内容不会影响结果.截至目前.

request_body = {名称":父级 + '/functions/' + 名称,入口点":入口点,sourceUploadUrl":upload_url,https触发器":{}}

对于 .create(),您可以安全地使用 'v1' 而不是 'v1beta2'.

这是一个完整的工作示例.如果我将它作为代码的一部分提供给您,那会很复杂,但您可以轻松集成它.

导入pprint导入压缩文件进口请求从临时文件导入临时文件从 googleapiclient 导入发现project_id = 'your_project_id'地区 = 'us-central1'parent = 'projects/{}/locations/{}'.format(project_id, region)打印(父)name = 'ExampleFunctionFibonacci'entry_point = "斐波那契"service = discovery.build('cloudfunctions', 'v1')CloudFunctionsAPI = service.projects().locations().functions()upload_url = CloudFunctionsAPI.generateUploadUrl(parent=parent, body={}).execute()['uploadUrl']打印(上传网址)有效载荷 = """/*** 响应任何可以在正文中提供消息"字段的 HTTP 请求.** @param {Object} req 云函数请求上下文.* @param {Object} res 云函数响应上下文.*/export.""" + entry_point + """= 函数 """ + entry_point + """ (req, res) {如果(req.body.message === 未定义){//这是一个错误案例,因为消息"是必需的res.status(400).send('没有定义消息!');} 别的 {//一切都好控制台日志(req.body.message);res.status(200).end();}};"""使用 TemporaryFile() 作为数据:使用 zipfile.ZipFile(data, 'w', zipfile.ZIP_DEFLATED) 作为存档:archive.writestr('function.js', 有效载荷)数据搜索(0)标题 = {'内容类型':'应用程序/zip','x-goog-content-length-range':'0,104857600'}打印(requests.put(upload_url,headers=headers,data=data))# 准备请求正文# https://cloud.google.com/functions/docs/reference/rest/v1/projects.locations.functions#resource-cloudfunctionrequest_body = {名称":父级 + '/functions/' + 名称,入口点":入口点,sourceUploadUrl":upload_url,"httpsTrigger": {},运行时":'nodejs8'}print('https://{}-{}.cloudfunctions.net/{}'.format(region,project_id,name))response = CloudFunctionsAPI.create(location=parent, body=request_body).execute()pprint.pprint(响应)

打开并上传一个 zip 文件,如下所示:

file_name = os.path.join(IGui.settings.BASE_DIR, 'media/archives/', func_obj.sourceFile.name)标题 = {'内容类型':'应用程序/zip',x-goog-content-length-range":0,104857600"}使用 open(file_name, 'rb') 作为数据:打印(requests.put(upload_url,headers=headers,data=data))

I'm working on a project with Python(3.6) & Django(1.10) in which I need to create a function at Google cloud using API request.

How can upload code in the form of a zip archive while creating that function?

Here's what I have tried:

From views.py :

    def post(self, request, *args, **kwargs):
    if request.method == 'POST':
        post_data = request.POST.copy()
        post_data.update({'user': request.user.pk})
        form = forms.SlsForm(post_data, request.FILES)
        print('get post request')
        if form.is_valid():
            func_obj = form
            func_obj.user = request.user
            func_obj.project = form.cleaned_data['project']
            func_obj.fname = form.cleaned_data['fname']
            func_obj.fmemory = form.cleaned_data['fmemory']
            func_obj.entryPoint = form.cleaned_data['entryPoint']
            func_obj.sourceFile = form.cleaned_data['sourceFile']
            func_obj.sc_github = form.cleaned_data['sc_github']
            func_obj.sc_inline_index = form.cleaned_data['sc_inline_index']
            func_obj.sc_inline_package = form.cleaned_data['sc_inline_package']
            func_obj.bucket = form.cleaned_data['bucket']
            func_obj.save()
            service = discovery.build('cloudfunctions', 'v1', http=views.getauth(), cache_discovery=False)
            requ = service.projects().locations().functions().generateUploadUrl(parent='projects/' + func_obj.project + '/locations/us-central1', body={})
            resp = requ.execute()
            print(resp)
            try:
                auth = views.getauth()
                # Prepare Request Body
                req_body = {
                    "CloudFunction": {
                        "name": func_obj.fname,
                        "entryPoint": func_obj.entryPoint,
                        "timeout": '60s',
                        "availableMemoryMb": func_obj.fmemory,
                        "sourceArchiveUrl": func_obj.sc_github,
                    },
                    "sourceUploadUrl": func_obj.bucket,
                }
                service = discovery.build('cloudfunctions', 'v1beta2', http=auth, cachce_dicovery=False)
                func_req = service.projects().locations().functions().create(location='projects/' + func_obj.project
                                                                                      + '/locations/-',
                                                                             body=req_body)
                func_res = func_req.execute()
                print(func_res)
                return HttpResponse('Submitted',)
            except:
                return HttpResponse(status=500)

        return HttpResponse('Sent!')

Updated Code below:

            if form.is_valid():
            func_obj = form
            func_obj.user = request.user
            func_obj.project = form.cleaned_data['project']
            func_obj.fname = form.cleaned_data['fname']
            func_obj.fmemory = form.cleaned_data['fmemory']
            func_obj.entryPoint = form.cleaned_data['entryPoint']
            func_obj.sourceFile = form.cleaned_data['sourceFile']
            func_obj.sc_github = form.cleaned_data['sc_github']
            func_obj.sc_inline_index = form.cleaned_data['sc_inline_index']
            func_obj.sc_inline_package = form.cleaned_data['sc_inline_package']
            func_obj.bucket = form.cleaned_data['bucket']
            func_obj.save()

            #######################################################################
            # FIRST APPROACH FOR FUNCTION CREATION USING STORAGE BUCKET
            #######################################################################

            file_name = os.path.join(IGui.settings.BASE_DIR, 'media/archives/', func_obj.sourceFile.name)
            print(file_name)

            service = discovery.build('cloudfunctions', 'v1')
            func_api = service.projects().locations().functions()
            url_svc_req = func_api.generateUploadUrl(parent='projects/'
                                                            + func_obj.project
                                                            + '/locations/us-central1',
                                                     body={})
            url_svc_res = url_svc_req.execute()
            print(url_svc_res)

            upload_url = url_svc_res['uploadUrl']
            print(upload_url)
            headers = {
                'content-type': 'application/zip',
                'x-goog-content-length-range': '0,104857600'
            }
            print(requests.put(upload_url, headers=headers, data=func_obj.sourceFile.name))
            auth = views.getauth()
            # Prepare Request Body
            name = "projects/{}/locations/us-central1/functions/{}".format(func_obj.project, func_obj.fname,)
            print(name)
            req_body = {
              "name": name,
              "entryPoint": func_obj.entryPoint,
              "timeout": "3.5s",
              "availableMemoryMb": func_obj.fmemory,
              "sourceUploadUrl": upload_url,
              "httpsTrigger": {},
            }
            service = discovery.build('cloudfunctions', 'v1')
            func_api = service.projects().locations().functions()

            response = func_api.create(location='projects/' + func_obj.project + '/locations/us-central1',
                                                body=req_body).execute()

            pprint.pprint(response)

Now the function has been created successfully, but it fails because the source code doesn't upload to storage bucket, that's maybe something wrong at:

upload_url = url_svc_res['uploadUrl']
            print(upload_url)
            headers = {
                'content-type': 'application/zip',
                'x-goog-content-length-range': '0,104857600'
            }
            print(requests.put(upload_url, headers=headers, data=func_obj.sourceFile.name))

解决方案

In the request body you have a dictionary "CloudFunction" inside the request. The content of "CloudFunction" should be directly in request.

request_body = {
    "name": parent + '/functions/' + name,
    "entryPoint": entry_point,
    "sourceUploadUrl": upload_url,
    "httpsTrigger": {}
}

I recomend using "Try this API" to discover the structure of projects.locations.functions.create .

"sourceArchiveUrl" and "sourceUploadUrl" can't appear together. This is explained in Resorce Cloud Function:

// Union field source_code can be only one of the following:
"sourceArchiveUrl": string,
"sourceRepository": { object(SourceRepository) },
"sourceUploadUrl": string,
// End of list of possible types for union field source_code.

In the rest of the answer I assume that you want to use "sourceUploadUrl". It requires you to pass it a URL returned to you by .generateUploadUrl(...).execute(). See documentation:

sourceUploadUrl -> string

The Google Cloud Storage signed URL used for source uploading, generated by [google.cloud.functions.v1.GenerateUploadUrl][]

But before passing it you need to upload a zip file to this URL:

curl -X PUT "${URL}" -H 'content-type:application/zip' -H 'x-goog-content-length-range: 0,104857600'  -T test.zip

or in python:

    headers = {
        'content-type':'application/zip',
        'x-goog-content-length-range':'0,104857600'
    }
    print(requests.put(upload_url, headers=headers, data=data))

This is the trickiest part:

  • the case matters and it should be lowercase. Because the signature is calculated from a hash (here)

  • you need 'content-type':'application/zip'. I deduced this one logically, because documentation doesn't mention it. (here)

  • x-goog-content-length-range: min,max is obligatory for all PUT requests for cloud storage and is assumed implicitly in this case. More on it here

  • 104857600, the max in previous entry, is a magical number which I didn't found mentioned anywhere.

where data is a FileLikeObject.

I also assume that you want to use the httpsTrigger. For a cloud function you can only choose one trigger field. Here it's said that trigger is a Union field. For httpsTrigger however that you can just leave it to be an empty dictionary, as its content do not affect the outcome. As of now.

request_body = {
    "name": parent + '/functions/' + name,
    "entryPoint": entry_point,
    "sourceUploadUrl": upload_url,
    "httpsTrigger": {}
}

You can safely use 'v1' instead of 'v1beta2' for .create().

Here is a full working example. It would be to complicated if I presented it to you as part of your code, but you can easily integrate it.

import pprint
import zipfile
import requests
from tempfile import TemporaryFile
from googleapiclient import discovery

project_id = 'your_project_id'
region = 'us-central1'
parent = 'projects/{}/locations/{}'.format(project_id, region)
print(parent)
name = 'ExampleFunctionFibonacci'
entry_point = "fibonacci"

service = discovery.build('cloudfunctions', 'v1')
CloudFunctionsAPI = service.projects().locations().functions()
upload_url = CloudFunctionsAPI.generateUploadUrl(parent=parent, body={}).execute()['uploadUrl']
print(upload_url)


payload = """/**
 * Responds to any HTTP request that can provide a "message" field in the body.
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.""" + entry_point + """= function """ + entry_point + """ (req, res) {
  if (req.body.message === undefined) {
    // This is an error case, as "message" is required
    res.status(400).send('No message defined!');
  } else {
    // Everything is ok
    console.log(req.body.message);
    res.status(200).end();
  }
};"""


with TemporaryFile() as data:
    with zipfile.ZipFile(data, 'w', zipfile.ZIP_DEFLATED) as archive:
        archive.writestr('function.js', payload)

    data.seek(0)
    headers = {
        'content-type':'application/zip',
        'x-goog-content-length-range':'0,104857600'
    }
    print(requests.put(upload_url, headers=headers, data=data))

# Prepare Request Body
# https://cloud.google.com/functions/docs/reference/rest/v1/projects.locations.functions#resource-cloudfunction

request_body = {
    "name": parent + '/functions/' + name,
    "entryPoint": entry_point,
    "sourceUploadUrl": upload_url,
    "httpsTrigger": {},
    "runtime": 'nodejs8'
}

print('https://{}-{}.cloudfunctions.net/{}'.format(region,project_id,name))
response = CloudFunctionsAPI.create(location=parent, body=request_body).execute()

pprint.pprint(response)

Open and upload a zip file like following:

file_name = os.path.join(IGui.settings.BASE_DIR, 'media/archives/', func_obj.sourceFile.name)
headers = {
    'content-type': 'application/zip',
    'x-goog-content-length-range': '0,104857600'
}

with open(file_name, 'rb') as data:
    print(requests.put(upload_url, headers=headers, data=data))

这篇关于在 Python 中使用 API 创建 Google Cloud Function的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆