/info/alibaba/devops

Alibaba Cloud DevOps Cookbook

Day #4 - Function Compute and API Gateway

Date Created: May 25, 2018
Last Update: May 30, 2018

[Editor's Note: This is the second article of a three-part series on DirectMail, Function Compute, API Gateway and calling DirectMail using the REST API including creating the REST API Signature.]

Table of Contents

Alibaba Documentation
Introduction
Function Compute Interfaces
Function definition when called by an HTTP trigger
Function definition when called by the invoke API or by API Gateway
Example program that will return parameter "environ" in HTML
Example program that will return parameter "context" in HTML
Function Compute Access Keys

Alibaba Documentation

Function Compute:
Alibaba Function Compute Product Page
Alibaba Function Compute Documentation
Alibaba Function Compute Triggers
Alibaba Function Entry Definition
API Gateway:
Alibaba API Gateway Product Page
Alibaba API Gateway Documentation
Installing API Gateway SSL Certificates

Introduction

Alibaba Cloud Function Compute is an event driven, serverless computing platform that enables developers to build and deploy their services without the need to manage any infrastructure. It seamlessly handles the resource management, auto scaling, and load balancing so you can focus on your business logic and increase your development velocity. You can also setup various event sources from other Alibaba services to automatically trigger your code to run. The best part is you only pay for the resources your code actually consumes to the nearest 100 milliseconds.

API Gateway is a managed service that makes it easy to publish secure APIs at scale. API Gateway interfaces with a number services such as ECS, Function Compute and web applications to name a few. API Gateway also integrates with DNS services to provide domain-name based APIs.

In today's article we will use API Gateway, Function Compute and Direct Mail to provide a contact form suitable for a static website hosted on OSS.

Function Compute Interfaces

There are several methods to run your code in Function Compute. You can use "invoke" your function thru an SDK, via API Gateway or by Triggers. The function definition is different depending on what service will be invoking your function. We will cover two different styles of invocation in this article. The first method by an HTTP trigger and the second method by API Gateway. Note that the function definitions will be for Python. You can read more about Function Entry Definition.

For the HTTP Trigger function definition, we need to know what an HTTP Trigger is. An HTTP Trigger is just a URL that you assign to your function in the Function Compute Console so that your function can be called. The URL will look like this:

https://1234567891234567.ap-southeast-1.fc.aliyuncs.com/2016-08-15/proxy/service_name/function_name/

Function definition when called by an HTTP trigger

def my_handler(environ, start_response):
	context = environ['fc.context']
	request_uri = environ['fc.request_uri']
	for k, v in environ.items():
		if k.startswith("HTTP_"):
			# process custom request headers
			pass

	# do something here

	status = '200 OK'
	response_headers = [('Content-type', 'text/plain')]
	start_response(status, response_headers)
	return ["Hello World"]

Item Description and Usage
Function name my_handler must correspond to the "Handler" field of the created function. For example, if the Handler is set to main.my_handler upon the function creation, Function Compute automatically loads the my_handler function defined in main.py.
environ The environ parameter is a python dictionary that holds all client-related information. For details, refer to the environ parameter page.
start_response The start_response parameter is a callable. For details, see start_response parameter. This is provided by the FC runtime. It accepts two necessary position parameters and an optional parameter.
Return value When the function returns, it needs to return the result to the calling server. The HTTP response needs to include HTTP status, HTTP headers, and HTTP body. Therefore, before the function returns the HTML body as return value, you need to call start_response() to return the contents of the HTTP status and HTTP headers to the server. This function returns the HTML body.

Function definition when called by the invoke API or by API Gateway

def my_handler(event, context):
	body = 'hello world'
	res = {
		'isBase64Encoded': False,
		'statusCode': 200,
		'headers': {
			'content-type' : 'text/plain'
		},
		'body': body
	}

	return json.dumps(res)

Item Description and Usage
Function name my_handler must correspond to the "Handler" field of the created function. For example, if the Handler is set to main.my_handler upon the function creation, Function Compute automatically loads the my_handler function defined in main.py.
event parameter The event parameter is the data passed to function invocation call. Function Compute doesn't do any transformation but just passes through the value to user function. The parameter type in Python2.7 is str and in Python3 is bytes.
context parameter The context parameter contains the operation information of the function (such as the request ID and temporary Accesskeys). The parameter is of the FCContext type. The Python guide section describes the context structure and usage.
Return value The return value of a function is returned to you as the result of invoking the function. It can be of any type. For a simple type, Function Compute converts the result to a string before returning it. For a complicated type, Function Compute converts the result to a JSON string before returning it.

Example program that will return parameter "environ" in HTML

This example is for a function that is called by an HTTP Trigger (HTTP URL).

Download example program to upload to Function Compute.

View the output that this program generates. Note that some of the data has been masked for security reasons.

############################################################
# Version 1.01
# Date Created: 2018-05-25
# Last Update:  2018-05-29
# https://www.neoprime.io
# Copyright (c) 2018, NeoPrime, LLC
############################################################

""" Alibaba Cloud Function Compute Example """
""" This program will return the environment back to the caller as an HTML table """
""" This is useful to learn about the running environment in Function Compute """
""" This code is designed to be called thru HTTP URL via an HTTP Trigger """

def handler(environ, start_response):
    body = '<!DOCTYPE html>\n<html lang="en"><head>\n'
    body = body + '<style>\n'
    body = body + 'table, th, td {border: 1px solid black;}\n'
    body = body + 'table {border-collapse: collapse;}\n'
    body = body + 'th, td {border: 1px solid #ddd; padding 8px;}\n'
    body = body + 'th {padding-top: 12px; padding-bottom: 12px;\n'
    body = body + 'text-align: left; background-color: #4CAF50;color: white;}\n'
    body = body + 'tr:nth-child(even){background-color: #f2f2f2;}\n'
    body = body + 'tr:hover {background-color: #ddd;}\n'
    body = body + '</style>\n'
    body = body + '</head>\n<body><table width="%100%">\n'
    body = body + "<thead>\n<tr><th>Name</th><th>Type</th><th>Value</th></tr>\n</thead>\n"
    body = body + "<tbody>\n"

    for k, v in environ.items():
      body = body + "<tr>"
      body = body + "<td>" + k + "</td>"
      body = body + "<td>" + type(v).__name__ + "</td>"
      if isinstance(v, bool):
        body = body + "<td>" + str(v) + "</td>"
      if isinstance(v, str):
        body = body + "<td>" + v + "</td>"
      if isinstance(v, tuple):
        body = body + "<td>" + str(v) + "</td>"
      if isinstance(v, type):
        body = body + "<td>" + str(v) + "</td>"
      body = body + "</tr>\n"

    body = body + "</tbody>\n</table>\n</body></html>"
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    return [bytes(body, "utf-8")]

Example program that will return parameter "context" in HTML

This example is for a function that is called by API Gateway.

Download example program to upload to Function Compute.

View the output that this program generates. Note that some of the data has been masked for security reasons.

############################################################
# Version 1.01
# Date Created: 2018-05-25
# Last Update:  2018-05-29
# https://www.neoprime.io
# Copyright (c) 2018, NeoPrime, LLC
############################################################

""" Alibaba Cloud Function Compute Example """
""" This program will return the environment back to the caller as an HTML table """
""" This is useful to learn about the running environment in Function Compute """
""" This code is designed to be called thru API Gateway using an HTTP URL """

import json

# Add a row to the HTML table
def add_row(body, name, value):
	""" body - the current HTML body string that we append to """
	""" name - the name of the item. Added to the first column """
	""" value - the value to add. The type is added to the second column, value to the third """
	""" returns body """

	# begin a new table row
	body = body + '<tr>\n'
	body = body + '<td>' + name + '</td>\n'
	body = body + '<td>' + type(value).__name__ + '</td>\n'

	if isinstance(value, str):
		# Keep the string length less than 85 characters
		v = (value[:85] + ' ...') if len(value) > 85 else value
		body = body + '<td>' + v + '</td>\n'
	else:
		body = body + '<td>' + str(value) + '</td>\n'

	return body + '</tr>\n'

# Add a "dict" item to the table by parsing thru each item in the dictionary
def add_dict(body, name, item):
	""" body - the current HTML body string that we append to """
	""" name - the name of the item. """
	""" item - The item to process """
	for k in item:
		if isinstance(item[k], dict):
			body = add_dict(body, name + "." + k, item[k])
		else:
			body = add_row(body, name + "." + k, item[k])
	return body

def add_event(body, event):
	j = json.loads(event.decode("utf-8"))
	return add_dict(body, "event", j)

def add_context(body, context):
	body = add_row(body, 'context.request_id', context.request_id)
	body = add_row(body, 'context.region', context.region)
	body = add_row(body, 'context.account_id', context.account_id)
	body = add_row(body, 'context.function.handler', context.function.handler)
	body = add_row(body, 'context.function.memory', context.function.memory)
	body = add_row(body, 'context.function.name', context.function.name)
	body = add_row(body, 'context.function.timeout', context.function.timeout)
	body = add_row(body, 'context.credentials.accessKeyId', context.credentials.accessKeyId)
	body = add_row(body, 'context.credentials.accessKeySecret', context.credentials.accessKeySecret)
	body = add_row(body, 'context.credentials.access_key_id', context.credentials.access_key_id)
	body = add_row(body, 'context.credentials.access_key_secret', context.credentials.access_key_secret)
	body = add_row(body, 'context.credentials.securityToken', context.credentials.securityToken)
	body = add_row(body, 'context.credentials.security_token', context.credentials.security_token)
	body = add_row(body, 'context.service.log_project', context.service.log_project)
	body = add_row(body, 'context.service.log_store', context.service.log_store)

	return body

def build_html(body, event, context):
	body = '<!DOCTYPE html>\n<html lang="en"><head>\n'
	body = body + '<style>\n'
	body = body + 'table, th, td {border: 1px solid black;}\n'
	body = body + 'table {border-collapse: collapse;}\n'
	body = body + 'th, td {border: 1px solid #ddd; padding 8px;}\n'
	body = body + 'th {padding-top: 12px; padding-bottom: 12px;\n'
	body = body + 'text-align: left; background-color: #4CAF50;color: white;}\n'
	body = body + 'tr:nth-child(even){background-color: #f2f2f2;}\n'
	body = body + 'tr:hover {background-color: #ddd;}\n'
	body = body + '</style>\n'
	body = body + '</head>\n<body><table width="100%">\n'
	body = body + '<thead>\n<tr><th>Name</th><th width="5%">Type</th><th>Value</th></tr>\n</thead>\n'
	body = body + '<tbody>\n'

	body = add_event(body, event)
	body = add_context(body, context)

	body = body + '</tbody>\n'
	body = body + '</table>\n</body></html>'

	return body

def handler(event, context):
	body = ""
	body = build_html(body, event, context)

	res = {
		'isBase64Encoded': False,
		'statusCode': 200,
		'headers': {
			'content-type' : 'text/html'
		},
		'body': body
	}

	return json.dumps(res)

With the example programs, you will be able to display the function's environment and any parameters that you pass in the function call (either thru HTTP or API Gateway).

In my next articel we will create code for DirectMail using the REST API with signing. In a following article we will integrate an HTML contact form, API Gateway, Function Compute and DirectMail into a serverless system to provide a contact page for a website. The website can be either a dynamic or a static website on OSS.


Function Compute Access Keys

Let's discuss a safe and secure method of managing access keys for Function Compute: RAM Roles. Roles allow you to create access keys via a role in the Alibaba Console and assign the role to your Function Compute service. This prevents keys from being required in your source code. The role credentials are made available to your function via the context.credentials parameter.

In the Resource Access Management console, create a role with the desired permissions. Then assign this role to your Function Compute service. Each function located under this service with them have this role assigned.




15220 Main Street, Bellevue, WA 98007
T: 425-528-8500 - F: 425-528-8550 - E: neoprime@neoprime.io

Copyright 2018 NeoPrime LLC