在 Python 中使用 API 创建 Google Cloud Function

Abd*_*man 3 python google-cloud-storage google-cloud-platform

我正在使用 Python(3.6) 和 Django(1.10) 开发一个项目,在该项目中我需要使用 API 请求在 Google 云上创建一个函数。

在创建该功能时,如何以 zip 存档的形式上传代码?

这是我尝试过的:

从 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!')
Run Code Online (Sandbox Code Playgroud)

更新代码如下:

            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)
Run Code Online (Sandbox Code Playgroud)

现在该函数已经创建成功,但是由于源代码没有上传到存储桶而失败,这可能是错误的:

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))
Run Code Online (Sandbox Code Playgroud)

A.Q*_*eue 6

在请求正文中,请求中有一个字典“CloudFunction”。“CloudFunction”的内容应直接在请求中。

request_body = {
    "name": parent + '/functions/' + name,
    "entryPoint": entry_point,
    "sourceUploadUrl": upload_url,
    "httpsTrigger": {}
}
Run Code Online (Sandbox Code Playgroud)

我建议使用“尝试这个 API”来发现 projects.locations.functions.create 的结构。

"sourceArchiveUrl"并且"sourceUploadUrl"不能一起出现。这在资源云功能中有解释:

// 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.
Run Code Online (Sandbox Code Playgroud)

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

sourceUploadUrl ->字符串

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

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

curl -X PUT "${URL}" -H 'content-type:application/zip' -H 'x-goog-content-length-range: 0,104857600'  -T test.zip
Run Code Online (Sandbox Code Playgroud)

或在python中:

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

这是最棘手的部分:

  • 大小写很重要,它应该是小写的。因为签名是根据哈希计算的(这里

  • 您需要“内容类型”:“应用程序/zip”。我从逻辑上推断出这一点,因为文档没有提到它。(这里

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

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

dataFileLikeObject在哪里。

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

request_body = {
    "name": parent + '/functions/' + name,
    "entryPoint": entry_point,
    "sourceUploadUrl": upload_url,
    "httpsTrigger": {}
}
Run Code Online (Sandbox Code Playgroud)

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

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

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)
Run Code Online (Sandbox Code Playgroud)

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

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))
Run Code Online (Sandbox Code Playgroud)