Deploy FunctionGraph Event Function from OBS¶
This sample demonstrates how to deploy a simple event function to FunctionGraph with code from OBS using terraform.
This approach is used, if your unpacked FunctionGraph deployment package is less than 40MB.
Source code of this sample is available on GitHub.
Prerequisites¶
running on Linux / Windows Subsystem for Linux (WSL)
make installed
curl installed
Terraform installed and configured, see Terraform Setup.
What will be deployed¶
obs bucket and obs bucket object for function code
event function
lts log group and log stream
test event configuration
Creating deployment package¶
The deployment package for this sample is created with make create_package.
# Makefile for Terraform deployment
# Terraform backend configuration
BACKEND_CONFIG_BUCKET := "doc-samples-tf-backend"
BACKEND_CONFIG_KEY := "terraform_state/php/deploy-from-obs.tf"
BACKEND_CONFIG_REGION := "eu-de"
BACKEND_CONFIG_ENDPOINTS := "endpoints={s3=\"https://obs.eu-de.otc.t-systems.com\"}"
CURRENT_MAKEFILE := $(firstword $(MAKEFILE_LIST))
create_package:
# install production dependencies without development dependencies
composer install --no-dev --prefer-dist
# create a code.zip archive of the production dependencies
composer archive --format=zip --file=code
# reinstall all dependencies including development dependencies
composer install
tf_init:
terraform -chdir=terraform \
init \
-backend-config=$(BACKEND_CONFIG_ENDPOINTS) \
-backend-config="bucket=$(BACKEND_CONFIG_BUCKET)" \
-backend-config="key=$(BACKEND_CONFIG_KEY)" \
-backend-config="region=$(BACKEND_CONFIG_REGION)"
tf_plan:
if [ ! -f "terraform/.terraform.lock.hcl" ]; then \
$(MAKE) -f $(CURRENT_MAKEFILE) tf_init; \
fi
terraform -chdir=terraform \
plan \
-var-file="variables.tfvars"
tf_apply: create_package
if [ ! -f "terraform/.terraform.lock.hcl" ]; then \
$(MAKE) -f $(CURRENT_MAKEFILE) tf_init; \
fi
terraform -chdir=terraform \
apply -auto-approve \
-var-file="variables.tfvars"
tf_destroy:
terraform -chdir=terraform \
destroy -auto-approve \
-var-file="variables.tfvars"
test_deployed:
# getting Token for authentication from Username/Password...
$(eval OTC_X_AUTH_TOKEN := $(shell ../../utils/tokenFromUsername.sh))
# getting the Function URN from terraform output...
$(eval MY_FUNCTION_URN := $(shell terraform -chdir=terraform output -raw MY_FUNCTION_URN))
# calling the deployed function via FunctionGraph API...
@curl -X POST \
-H "Content-Type: application/json" \
-H "x-auth-token: $(OTC_X_AUTH_TOKEN)" \
-d '{"key":"Hello World of FunctionGraph"}' \
https://functiongraph.$(OTC_SDK_REGION).otc.t-systems.com/v2/$(OTC_SDK_PROJECTID)/fgs/functions/$(MY_FUNCTION_URN):latest/invocations
@echo ""
# finished
.PHONY: create_package tf_init tf_plan tf_apply tf_destroy test_deployed test_deployed_apig_post
This will create a code.zip file with the function code in the project root folder.
Terraform files¶
The terraform files for this sample are located in the samples-doc/deploy-from-obs/terraform folder:
provider.tf¶
The file provider.tf defines the provider configuration for this sample:
# ----------------------------------------------------------------------------
# Secret variables to be injected as envvar (capital letters for Windows systems)
# - no defaults
# - Declared as sensitive --> Not printed in console or log if used in resources
# ----------------------------------------------------------------------------
# set by environment variable TF_VAR_OTC_SDK_AK
variable "OTC_SDK_AK" {
description = "Personal access key"
type = string
sensitive = true
}
# set by environment variable TF_VAR_OTC_SDK_SK
variable "OTC_SDK_SK" {
description = "Personal secret key"
type = string
sensitive = true
}
# set by environment variable TF_VAR_OTC_SDK_DOMAIN_NAME
variable "OTC_SDK_DOMAIN_NAME" {
description = "Domain Name, eg. OTC-EU-DE-000000000010000XXXXX"
type = string
}
# set by environment variable TF_VAR_OTC_SDK_PROJECTID
variable "OTC_SDK_PROJECTID" {
description = "Project Id"
type = string
}
# set by environment variable TF_VAR_OTC_SDK_PROJECTNAME
variable "OTC_SDK_PROJECTNAME" {
description = "Project Name, eg. eu-de_MYPROJECT"
type = string
}
# set by environment variable TF_VAR_OTC_IAM_ENDPOINT
variable "OTC_IAM_ENDPOINT" {
description = "IAM Endpoint"
type = string
default = "https://iam.eu-de.otc.t-systems.com/v3"
}
variable "OTC_FGS_ENDPOINT" {
description = "FunctionGraph Endpoint"
type = string
default = "https://functiongraph.eu-de.otc.t-systems.com"
}
terraform {
required_providers {
# specifies required provider, source and version
# see https://registry.terraform.io/providers/opentelekomcloud/opentelekomcloud/latest
opentelekomcloud = {
source = "opentelekomcloud/opentelekomcloud"
version = ">= 1.36.70"
}
}
backend "s3" {
# See: https://registry.terraform.io/providers/opentelekomcloud/opentelekomcloud/latest/docs/guides/backends
# (Required) Specifies the endpoint for OpenTelekomCloud OBS.
# The value is https://obs.{{region}}.otc.t-systems.com.
# This can also be sourced from the AWS_S3_ENDPOINT environment variable
endpoints = {
s3 = "https://obs.eu-de.otc.t-systems.com"
}
# (Required) Specifies the bucket name where to store the state.
# Make sure to create it before.
bucket = "<your-bucket-name>"
# (Required) Specifies the path to the state file inside the bucket.
key = "<path/to/your/terraform.tfstate>"
# (Required) Specifies the region where the bucket is located.
# This can also be sourced from the AWS_DEFAULT_REGION and
# AWS_REGION environment variables.
region = "<your-region>"
# (Required) Skip credentials validation via the STS API.
# It's mandatory for OpenTelekomCloud.
skip_credentials_validation = true
# (Required) Skip validation of provided region name.
# It's mandatory for OpenTelekomCloud.
skip_region_validation = true
skip_requesting_account_id = true
# (Required) Skip usage of EC2 Metadata API.
# It's mandatory for OpenTelekomCloud.
skip_metadata_api_check = true
# (Optional) Do not include checksum when uploading S3 Objects.
# Useful for some S3-Compatible APIs.
skip_s3_checksum = true
# Although the terraform block does not accept variables or locals and
# all backend configuration values must be hardcoded, you can provide
# the credentials via the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
# environment variables to access OBS, respectively:
#
# export AWS_ACCESS_KEY_ID="your accesskey"
# export AWS_SECRET_ACCESS_KEY="your secretkey"
#
# secret_key set env var: AWS_ACCESS_KEY_ID
# access_key set env var: AWS_SECRET_ACCESS_KEY
}
}
# ----------------------------------------------------------------------------
# Providers settings --> OTC
# We use the AKSK auth scheme
# See https://registry.terraform.io/providers/opentelekomcloud/opentelekomcloud/latest/docs
# ----------------------------------------------------------------------------
#
provider "opentelekomcloud" {
auth_url = var.OTC_IAM_ENDPOINT
access_key = var.OTC_SDK_AK
secret_key = var.OTC_SDK_SK
domain_name = var.OTC_SDK_DOMAIN_NAME
tenant_name = var.OTC_SDK_PROJECTNAME
}
You might need to adapt the provider configuration to your needs, especially the provider version and backend configuration for terraform state.
For variables used in provider.tf, see Terraform Setup.
variables.tf¶
The file variables.tf defines the variables for this sample:
# prefix will be prepended to all resource names
variable "prefix" {
type = string
default = "set in variables.tfvars"
}
# description of the function
variable "description" {
type = string
default = "set in variables.tfvars"
}
# FunctionGraph: Function name
variable "function_name" {
type = string
default = "set in variables.tfvars"
}
variable "handler_name" {
type = string
default = "set in variables.tfvars"
}
variable "initializer_name" {
type = string
default = "set in variables.tfvars"
}
# name of zip file to deploy, generated by 'mvn package' command
variable "zip_file_name" {
type = string
default = "set in variables.tfvars"
}
# Resource tag:
variable "tag_app_group" {
type = string
default = "set in variables.tfvars"
}
These variables will set in the variables.tfvars file.
code_from_obs_bucket.tf¶
The file code_from_obs_bucket.tf defines the obs bucket and obs bucket object resource
###################################################################################
# Code bucket to store function code zip file
###################################################################################
resource "opentelekomcloud_obs_bucket" "codebucket" {
bucket = format("%s-%s-%s", var.prefix, "codebucket", var.tag_app_group)
acl = "private"
tags = {
"app_group" = var.tag_app_group
}
}
###################################################################################
# Code bucket object to upload function code zip file
###################################################################################
resource "opentelekomcloud_obs_bucket_object" "code_object" {
bucket = opentelekomcloud_obs_bucket.codebucket.bucket
key = format("%s/%s", "code", basename(var.zip_file_name))
source = var.zip_file_name
etag = filemd5(var.zip_file_name)
content_type = "application/zip"
}
function.tf¶
The file function.tf defines the function resource for this sample:
##########################################################
# Create php event function
##########################################################
resource "opentelekomcloud_fgs_function_v2" "MyFunction" {
name = format("%s_%s", var.prefix, var.function_name)
app = "default"
handler = var.handler_name
initializer_handler = var.initializer_name
initializer_timeout = 30
runtime = "PHP8.3"
###### relevant part for deploy function code from obs file ######
code_type = "obs"
code_url = format("https://%s/%s/%s",
opentelekomcloud_obs_bucket.codebucket.bucket_domain_name,
"code",
basename(var.zip_file_name)
)
# on change of the code object etag (hash) new code version will be deployed.
source_code_hash = opentelekomcloud_obs_bucket_object.code_object.etag
###### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ######
description = var.description
memory_size = 512
timeout = 30
max_instance_num = 1
log_group_id = opentelekomcloud_lts_group_v2.MyLogGroup.id
log_group_name = opentelekomcloud_lts_group_v2.MyLogGroup.group_name
log_topic_id = opentelekomcloud_lts_stream_v2.MyLogStream.id
log_topic_name = opentelekomcloud_lts_stream_v2.MyLogStream.stream_name
# set some environment variables
user_data = jsonencode({
"RUNTIME_LOG_LEVEL" : "DEBUG",
})
tags = {
"app_group" = var.tag_app_group
}
}
output "MY_FUNCTION_URN" {
value = opentelekomcloud_fgs_function_v2.MyFunction.urn
}
output "MY_FUNCTION_VERSION" {
value = opentelekomcloud_fgs_function_v2.MyFunction.version
}
The relevant part for deploying function code from zip file is:
###### relevant part for deploy function code from obs file ######
code_type = "obs"
code_url = format("https://%s/%s/%s",
opentelekomcloud_obs_bucket.codebucket.bucket_domain_name,
"code",
basename(var.zip_file_name)
)
# on change of the code object etag (hash) new code version will be deployed.
source_code_hash = opentelekomcloud_obs_bucket_object.code_object.etag
###### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ######
loggroup.tf¶
The file loggroup.tf defines the log group and log stream for this sample:
##########################################################
# Create Log Group
##########################################################
resource "opentelekomcloud_lts_group_v2" "MyLogGroup" {
group_name = format("%s_%s_%s", var.prefix, var.function_name, "log_group")
ttl_in_days = 1
tags = {
"app_group" = var.tag_app_group
}
}
##########################################################
# Create Log Stream
##########################################################
resource "opentelekomcloud_lts_stream_v2" "MyLogStream" {
group_id = opentelekomcloud_lts_group_v2.MyLogGroup.id
stream_name = format("%s_%s_%s", var.prefix, var.function_name, "log_stream")
tags = {
"app_group" = var.tag_app_group
}
}
testevent.tf¶
The file testevent.tf creates a test event configuration to be used in the
FunctionGraph console for testing the deployed function.
##########################################################
# Create Test Event
##########################################################
resource "opentelekomcloud_fgs_event_v2" "test_event_post" {
function_urn = opentelekomcloud_fgs_function_v2.MyFunction.urn
name = "TestEventPost"
content = filebase64("../resources/test_event.json")
}
The test event will have the following content:
{
"key": "Hello World of FunctionGraph PHP Runtime!"
}
Deploying using Terraform and make¶
Following makefile can be used to deploy the function to FunctionGraph using Terraform.
# Makefile for Terraform deployment
# Terraform backend configuration
BACKEND_CONFIG_BUCKET := "doc-samples-tf-backend"
BACKEND_CONFIG_KEY := "terraform_state/php/deploy-from-obs.tf"
BACKEND_CONFIG_REGION := "eu-de"
BACKEND_CONFIG_ENDPOINTS := "endpoints={s3=\"https://obs.eu-de.otc.t-systems.com\"}"
CURRENT_MAKEFILE := $(firstword $(MAKEFILE_LIST))
create_package:
# install production dependencies without development dependencies
composer install --no-dev --prefer-dist
# create a code.zip archive of the production dependencies
composer archive --format=zip --file=code
# reinstall all dependencies including development dependencies
composer install
tf_init:
terraform -chdir=terraform \
init \
-backend-config=$(BACKEND_CONFIG_ENDPOINTS) \
-backend-config="bucket=$(BACKEND_CONFIG_BUCKET)" \
-backend-config="key=$(BACKEND_CONFIG_KEY)" \
-backend-config="region=$(BACKEND_CONFIG_REGION)"
tf_plan:
if [ ! -f "terraform/.terraform.lock.hcl" ]; then \
$(MAKE) -f $(CURRENT_MAKEFILE) tf_init; \
fi
terraform -chdir=terraform \
plan \
-var-file="variables.tfvars"
tf_apply: create_package
if [ ! -f "terraform/.terraform.lock.hcl" ]; then \
$(MAKE) -f $(CURRENT_MAKEFILE) tf_init; \
fi
terraform -chdir=terraform \
apply -auto-approve \
-var-file="variables.tfvars"
tf_destroy:
terraform -chdir=terraform \
destroy -auto-approve \
-var-file="variables.tfvars"
test_deployed:
# getting Token for authentication from Username/Password...
$(eval OTC_X_AUTH_TOKEN := $(shell ../../utils/tokenFromUsername.sh))
# getting the Function URN from terraform output...
$(eval MY_FUNCTION_URN := $(shell terraform -chdir=terraform output -raw MY_FUNCTION_URN))
# calling the deployed function via FunctionGraph API...
@curl -X POST \
-H "Content-Type: application/json" \
-H "x-auth-token: $(OTC_X_AUTH_TOKEN)" \
-d '{"key":"Hello World of FunctionGraph"}' \
https://functiongraph.$(OTC_SDK_REGION).otc.t-systems.com/v2/$(OTC_SDK_PROJECTID)/fgs/functions/$(MY_FUNCTION_URN):latest/invocations
@echo ""
# finished
.PHONY: create_package tf_init tf_plan tf_apply tf_destroy test_deployed test_deployed_apig_post
Makefile targets:
create_package: creates the deployment package as zip file using`composer archive`.tf_init: initializes terraform, this will create the terraform state file in the defined backend.tf_plan: runs terraform plan to see which changes will be applied.tf_apply: runs terraform apply to deploy the function to FunctionGraph.tf_destroy: runs terraform destroy to remove the deployed infrastructure.
Adaptions¶
Adaptions in Makefile¶
Before running the targets in the makefile, make sure to
Adapt the BACKEND_CONFIG_* variables in the Makefile
Variable |
Description |
Example value |
|---|---|---|
BACKEND_CONFIG_BUCKET |
The name of the bucket where terraform state is stored |
doc-samples-tf-backend |
BACKEND_CONFIG_KEY |
The name of the object(key) where terraform state is stored |
terraform_state/php/deploy-from-obs.tf |
BACKEND_CONFIG_REGION |
The region where the bucket for terraform state is stored is located |
eu-de |
BACKEND_CONFIG_ENDPOINTS |
The OBS endpoints for the bucket where terraform state is stored |
endpoints={s3="https://obs.eu-de.otc.t-systems.com"} |
Adaptions in variables.tfvars¶
Adapt the variables in the variables.tfvars
# Terraform variables
# prefix of all resources
prefix = "php"
# description of the function
description = "deploy-from-obs sample"
# name of the function (will be prefixed)
function_name = "deploy-from-obs"
# handler function name defined in your code, e.g. "index.handler"
handler_name = "src/index.handler"
# initializer function name defined in your code, e.g. "index.initializer"
initializer_name = "src/index.initializer"
# name of zip file to deploy
zip_file_name = "../code.zip"
# resources will be tagged with this app_group tag
tag_app_group = "deploy-from-obs"
Variable |
Description |
Example value |
|---|---|---|
prefix |
The prefix for the generated resources, all generated resources will have this prefix in their name. |
php |
description |
The description for the deployed function. |
Sample deploy-from-obs |
function_name |
The name of the deployed function (will be prefixed). |
deploy-from-obs |
handler_name |
The handler name for the deployed |
src/index.handler |
initializer_name |
The initializer name for the deployed function. |
src/index.initializer |
zip_file_name |
The name (with relative path) of the zip file with the function
code which is created by |
code.zip |
tag_app_group |
The tag “app_group” with this value will be added to all created resources, where tagging is applicable. |
deploy-from-obs |
Deployment to Cloud¶
After the necessary adaptions are done, you can run the following command to deploy the function to FunctionGraph:
make tf_apply
Testing the deployed function¶
For testing the deployed function, you will need to set the following environment variables. These are used in “tokenFromUsername.sh” script to get Token for Token-based authentication when calling FunctionGraph API.
Name |
Description |
|---|---|
OTC_USER_NAME |
User name |
OTC_USER_PASSWORD |
User password |
Synchronous invocation¶
After the deployment is done, you can test the deployed function with the following command:
make test_deployed
This will call the deployed function with a test event in synchronous way and print the response.
# getting the Function URN from terraform output...
# calling the deployed function via FunctionGraph API...
{"statusCode":200,"headers":{"Content-Type":"application/json"},"isBase64Encoded":false,"body":"{\"key\":\"Hello World of FunctionGraph\"}"}
# finished
Destroy all deployed resources from Cloud¶
To destroy all deployed resources from the cloud, you can run the following command:
make tf_destroy