[英]Create Google Cloud Function using API in Python
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.我正在使用 Python(3.6) 和 Django(1.10) 开发一个项目,在该项目中我需要使用 API 请求在 Google 云上创建一个函数。
How can upload code in the form of a zip archive while creating that function?在创建该函数时,如何以 zip 存档的形式上传代码?
Here's what I have tried:这是我尝试过的:
From views.py :从 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.在请求正文中,请求中有一个字典“CloudFunction”。 The content of "CloudFunction" should be directly in request. “CloudFunction”的内容应直接在请求中。
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 .我建议使用“尝试这个 API”来发现 projects.locations.functions.create 的结构。
"sourceArchiveUrl"
and "sourceUploadUrl"
can't appear together. "sourceArchiveUrl"
和"sourceUploadUrl"
不能一起出现。 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"
.在其余的答案中,我假设您想使用"sourceUploadUrl"
。 It requires you to pass it a URL returned to you by .generateUploadUrl(...).execute()
.它要求您将.generateUploadUrl(...).execute()
返回给您的 URL 传递给它。 See documentation :请参阅文档:
sourceUploadUrl -> string sourceUploadUrl ->字符串
The Google Cloud Storage signed URL used for source uploading, generated by [google.cloud.functions.v1.用于源上传的 Google Cloud Storage 签名 URL,由 [google.cloud.functions.v1. GenerateUploadUrl ][]生成上传网址][]
But before passing it you need to upload a zip file to this URL:但在传递它之前,您需要将 zip 文件上传到此 URL:
curl -X PUT "${URL}" -H 'content-type:application/zip' -H 'x-goog-content-length-range: 0,104857600' -T test.zip
or in python:或在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'.您需要“内容类型”:“应用程序/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. x-goog-content-length-range: min,max
对于云存储的所有PUT
请求都是必需的,在这种情况下是隐式假设的。 More on it here更多关于这里
104857600, the max in previous entry, is a magical number which I didn't found mentioned anywhere. 104857600,上一个条目中的最大值,是一个神奇的数字,我在任何地方都没有发现它被提及。
where data
is a FileLikeObject.其中data
是一个 FileLikeObject。
I also assume that you want to use the httpsTrigger
.我还假设您想使用httpsTrigger
。 For a cloud function you can only choose one trigger field.对于云函数,您只能选择一个触发字段。 Here it's said that trigger is a Union field. 这里说 trigger 是一个 Union 字段。 For httpsTrigger however that you can just leave it to be an empty dictionary, as its content do not affect the outcome.但是,对于 httpsTrigger,您可以将其保留为空字典,因为其内容不会影响结果。 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()
.对于.create()
您可以安全地使用 'v1' 而不是 'v1beta2' 。
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:打开并上传一个 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))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.