Deploying an HTTP Function container Image using Terraform

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

Prerequisite

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

Terraform Scripts

Terraform deployment scripts can be found in: samples-doc/container-http/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 = "your_tag_app_group_here"
}

variable "API_GATEWAY_INSTANCE_ID" {
  type = string
  default = "your_api_gateway_instance_id"
}

variables.tfvars

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

variables.tfvars
function_name = "go-doc-sample-container-http"
prefix        = "go"
tag_app_group = "go-doc-sample-container-http"
    
# change to your API Gateway instance ID
# set as env var TF_VAR_API_GATEWAY_INSTANCE_ID or uncomment and set here
#API_GATEWAY_INSTANCE_ID="YOUR_API_GATEWAY_INSTANCE_ID"

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 HTTP 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   = "http"

  custom_image {
    url = var.image_url
  }

  description      = "Sample on how use container with go and http"
  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
  }

}

In the function.tf script, the relevant part to create the HTTP Function using a custom container image is as follows:

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

custom_image {
  url = var.image_url
}

were var.image_url is the address of the container image in SWR. in the format:

swr.<REGION>.otc.t-systems.com/<SWR-ORGANIZATION>/<IMAGE_NAME>:<TAG>

api_trigger.tf

This script creates an API Gateway trigger and all relevant resources to invoke the HTTP Function:

  • API gateway group (opentelekomcloud_apigw_group_v2)

  • API resource (opentelekomcloud_apigw_api_v2) that connects this API to the FunctionGraph function

  • publish API using (opentelekomcloud_apigw_api_publishment_v2)

api_trigger.tf
locals {
  API_GATEWAY_INSTANCE_ID = var.API_GATEWAY_INSTANCE_ID
  ENV_NAME                = "RELEASE"
  ENV_ID                  = "DEFAULT_ENVIRONMENT_RELEASE_ID"
}

##########################################################
# opentelekomcloud_apigw_group_v2.group
##########################################################
resource "opentelekomcloud_apigw_group_v2" "group1" {
  # depends_on = [ opentelekomcloud_fgs_function_v2.MyFunction ]
  name        = replace(format("%s_%s_api_group", var.prefix, var.function_name), "-", "_")
  instance_id = local.API_GATEWAY_INSTANCE_ID
  description = format("API Group for %s, %s", var.prefix, var.function_name)
}

##########################################################
# opentelekomcloud_apigw_api_v2.api1
# This is used to create the API Gateway trigger for the
# FunctionGraph function.
##########################################################
resource "opentelekomcloud_apigw_api_v2" "api1" {

  # TRIGGER-INSTANCE_id
  gateway_id = local.API_GATEWAY_INSTANCE_ID

  # TRIGGER-GROUP_id
  group_id = opentelekomcloud_apigw_group_v2.group1.id

  # TRIGGER-Name
  name = replace(format("%s_%s_apig_trigger", var.prefix, var.function_name), "-", "_")

  # TRIGGER-TYPE:1
  type = "Public"

  # TRIGGER-PROTOCOL: HTTPS
  request_protocol = "HTTPS"

  # TRIGGER-REG_METHOD: HTTPS
  request_method = "ANY"

  # TRIGGER-PATH
  request_uri = "/"

  # TRIGGER-AUTH: NONE
  security_authentication_type = "NONE"

  # TRIGGER-MATCH_MODE: SWA
  match_mode       = "PREFIX"

  success_response = "Success response"
  failure_response = "Failed response"
  description      = format("Created by script for %s-%s", var.prefix, var.function_name)

  func_graph {
    function_urn    = opentelekomcloud_fgs_function_v2.MyFunction.urn
    version         = "latest"
    timeout         = 5000
    invocation_type = "sync"
    network_type    = "NON-VPC"
  }

}

##########################################################
# Publish API to specific environment
##########################################################
resource "opentelekomcloud_apigw_api_publishment_v2" "default" {
  gateway_id     = local.API_GATEWAY_INSTANCE_ID
  environment_id = local.ENV_ID
  api_id         = opentelekomcloud_apigw_api_v2.api1.id
  version_id     = opentelekomcloud_apigw_api_v2.api1.version
}

output "API_GATEWAY_TRIGGER_URL" {
  description = "The URL of the API Gateway triggering the FunctionGraph function"
  value       = format("https://%s.apic.%s.otc.t-systems.com", 
                opentelekomcloud_apigw_group_v2.group1.id , 
                opentelekomcloud_apigw_group_v2.group1.region)
  
}

Note

To have full control over resources being created this sample does not use opentelekomcloud_fgs_trigger_v2 for APIG Trigger creation,

testevent.tf

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

testevent.tf
##########################################################
# Create Test Event
##########################################################
resource "opentelekomcloud_fgs_event_v2" "test_event" {
  function_urn = opentelekomcloud_fgs_function_v2.MyFunction.urn
  name         = "TestEvent"
  content = filebase64("../resources/apig_post_index.json")
}

Deployment

MakefileTF

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

MakefileTF
# Makefile for Terraform deployment of the Container HTTP 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-http.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:
  # get the API Gateway URL from terraform output
  $(eval API_GATEWAY_TRIGGER_URL := $(shell terraform -chdir=terraform output -raw API_GATEWAY_TRIGGER_URL))
  @echo "Using API Gateway URL: $(API_GATEWAY_TRIGGER_URL)"
  # execute a curl request against the deployed function via API Gateway
  curl -X POST -H 'Content-Type: application/json' -d @./resources/apig_post_index.json $(API_GATEWAY_TRIGGER_URL)/index

.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-http folder:

make -f MakefileTF tf_init

Plan Terraform deployment

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

make -f MakefileTF tf_plan

Deploy using Terraform

To deploy the HTTP Function using Terraform, run following command in the container-http folder:

make -f MakefileTF tf_apply

Test deployed Function

To test the deployed HTTP Function using the API Gateway trigger, run following command in the container-http folder:

make -f MakefileTF test_deployed

Cleanup deployed resources

Note

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

make -f MakefileTF tf_destroy