Deploying an Event Function container Image using Terraform

This section describes on how to deploy an Event Function packed in a container image using Terraform.

Prerequisite

Full sample can be found in samples-doc/container-event.

Terraform Scripts

Terraform deployment scripts can be found in: samples-doc/container-event/terraform

provider.tf

This script configures the OpenTelekomCloud provider for Terraform.

provider.tf
# ----------------------------------------------------------------------------
# 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"
}


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.57"
    }
  }
  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

}

variables.tf

This script defines the variables used in the Terraform scripts.

variables.tf
# prefix will be prepended to all resource names
variable "prefix" {
  type    = string
  default = "sample"
}

# FunctionGraph: Function name
variable "function_name" {
  type    = string
  default = "function_name"
}

variable "image_url" {
  type    = string
  default = "your_image_url_here"
}

# Resource tag:
variable "tag_app_group" {
  type    = string
  default = "go-doc-sample-container-event"
}

variables.tfvars

This file provides values for the variables defined in variables.tf.

variables.tfvars
function_name = "go-doc-sample-container-event"
prefix        = "go"
tag_app_group = "go-doc-sample-container-event"
    

agency.tf

This script creates an agency with required permissions to allow FunctionGraph to pull images from SWR.

agency.tf
##########################################################
# Agency for FunctionGraph
# Attention: Creating agency will take some time.
# Calls to function after creating agency will fail until
# agency is set up.
##########################################################
resource "opentelekomcloud_identity_agency_v3" "agency" {
  delegated_domain_name = "op_svc_cff"

  name        = format("%s-%s-agency", var.prefix, var.function_name)
  description = "Agency for FunctionGraph to access SWR"

  project_role {
    all_projects = true
    roles = [
      "SWR Administrator",
    ]
  }

}

loggroup.tf

This script creates a log group and log stream in LTS.

loggroup.tf
##########################################################
# 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
  }
}

function.tf

This script creates the Event Function using the container image.

function.tf
##########################################################
# Create event function using container image 
##########################################################
resource "opentelekomcloud_fgs_function_v2" "MyFunction" {

  name   = format("%s_%s", var.prefix, var.function_name)
  app    = "default"
  agency = opentelekomcloud_identity_agency_v3.agency.name

  handler   = "-"
  code_type = "Custom-Image-Swr"
  runtime   = "Custom Image"

  custom_image {
    url = var.image_url
  }

  description      = "Sample on how use container with go"
  memory_size      = 512
  timeout          = 30
  max_instance_num = 1

  # if you need security access key, security secret key and security token
  # to access other OTC services, set enable_auth_in_header to true
  enable_auth_in_header = false

  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
  }

}

testevent.tf

This script creates a test event to test the Event Function.

testevent.tf
##########################################################
# Create Test Event
##########################################################
resource "opentelekomcloud_fgs_event_v2" "test_event" {
  function_urn = opentelekomcloud_fgs_function_v2.MyFunction.urn
  name         = "TestEvent"
  content = base64encode(jsonencode({
    "key" = "Hello World of FunctionGraph!"
  }))
}

Deployment

MakefileTF

To simplify the development and testing process, see MakefileTF in the container-event folder:

MakefileTF
# Makefile for Terraform deployment of the Container Event Function sample

# Include Makefile for build and docker tasks
include ./Makefile 

# Terraform backend configuration
BACKEND_CONFIG_BUCKET := "doc-samples-tf-backend"
BACKEND_CONFIG_KEY := "terraform_state/go/go-container-sample.tf"
BACKEND_CONFIG_REGION := "eu-de"
BACKEND_CONFIG_ENDPOINTS := "endpoints={s3=\"https://obs.eu-de.otc.t-systems.com\"}"

CURRENT_MAKEFILE := $(firstword $(MAKEFILE_LIST))

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) initTerraform; \
  fi
  terraform -chdir=terraform \
    plan \
    -var-file="variables.tfvars" \
    -var="image_url=$(OTC_SWR_ENDPOINT)/$(OTC_SWR_ORGANIZATION)/$(IMAGE_NAME):latest"

tf_apply: docker_push
  if [ ! -f "terraform/.terraform.lock.hcl" ]; then \
    $(MAKE) -f $(CURRENT_MAKEFILE) initTerraform; \
  fi
  terraform -chdir=terraform \
    apply -auto-approve \
    -var-file="variables.tfvars" \
    -var="image_url=$(OTC_SWR_ENDPOINT)/$(OTC_SWR_ORGANIZATION)/$(IMAGE_NAME):latest"

tf_destroy:
  terraform -chdir=terraform \
    destroy -auto-approve \
    -var-file="variables.tfvars" \
    -var="image_url=$(OTC_SWR_ENDPOINT)/$(OTC_SWR_ORGANIZATION)/$(IMAGE_NAME):latest"


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: tf_init tf_plan tf_apply tf_destroy test_deployed

This MakefileTF imports all targets from the build Makefile and provides additional targets for Terraform deployment.

Initialize Terraform

To initialize Terraform, run following command in the container-event folder:

make -f MakefileTF tf_init

Plan Terraform deployment

To plan the Terraform deployment, run following command in the container-event folder:

make -f MakefileTF tf_plan

Deploy using Terraform

To deploy the Event Function using Terraform, run following command in the container-event folder:

make -f MakefileTF tf_apply

Test deployed Function

To test the deployed Event Function using the Invokation API, run following command in the container-event folder:

make -f MakefileTF test_deployed

This target makes use of the script tokenFromUsername.sh to obtain an authentication token using username and password for the API call.

tokenFromUsername.sh
#!/bin/bash
# Script to get an authentication token 
# from OTC IAM using username and password
# to be passed as x-auth-token header in API requests.
# Script outputs the token to 
# - stdout
# - and to the environment variable OTC_X_AUTH_TOKEN (if called using current shell "source ...")

# see: https://docs.otc.t-systems.com/identity-access-management/api-ref/calling_apis/authentication.html#iam-02-0510
# see: https://docs.otc.t-systems.com/identity-access-management/api-ref/apis/token_management/obtaining_a_user_token_through_password_authentication.html

# Following environment variables must be set:
# OTC_USER_NAME
# OTC_USER_PASSWORD
# OTC_DOMAIN_NAME
# OTC_SDK_PROJECTNAME
# OTC_SDK_PROJECTID
# OTC_IAM_ENDPOINT e.g. https://iam.eu-de.otc.t-systems.com/v3

# If DEBUG is not set in the environment, it defaults to 0 (off)
DEBUG=${DEBUG:-0}

payload=$(cat <<EOF
{
  "auth": {
    "identity": {
      "methods": [
        "password"
      ],
      "password": {
        "user": {
          "name": "${OTC_USER_NAME}",
          "password": "${OTC_USER_PASSWORD}",
          "domain": { "name": "${OTC_DOMAIN_NAME}" }
        }
      }
    },
    "scope": {
      "project": {
        "id": "${OTC_SDK_PROJECTID}",
        "domain": { "name": "${OTC_DOMAIN_NAME}" }
      }
    }
  }
}
EOF
)

if [ "$DEBUG" -eq 1 ]; then
  # for debugging, print the payload to stderr
  echo "################# Payload for token request: #################" >&2
  echo ${payload} >&2
  echo "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" >&2
fi

# Make the API call 
# and extract the X-Subject-Token from the response headers
token=$(curl -i -s \
-H 'Content-Type: application/json' \
-d "${payload}" \
-o /dev/null \
--dump-header /dev/stdout \
${OTC_IAM_ENDPOINT}/auth/tokens?nocatalog=true \
| grep -i ^X-Subject-Token: | cut -d' ' -f2)

if [ "$DEBUG" -eq 1 ]; then
  # for debugging, print the token to stderr
  echo "################# Token #################" >&2
  echo "${token}" >&2
  echo "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" >&2
fi

# export the token to environment variable OTC_X_AUTH_TOKEN
export OTC_X_AUTH_TOKEN=${token}

# print the token to stdout
echo ${token}

This file needs execution permissions, e.g. set using:

chmod +x tokenFromUsername.sh

Cleanup deployed resources

Note

To destroy the deployed resources, run following command in the container-event folder:

make -f MakefileTF tf_destroy