Creating, Building and Invoking a Function on OCI with Terraform image 54

Creating, Building and Invoking a Function on OCI with Terraform

Automation through infrastructure as code is the name of the game. And I am a player in that game. In the last few weeks I have spent a lot of time on creating resources on Oracle Cloud Infrastructure. Through the console, using Terraform and the Resource Manager and Stacks, with the CLI and with the OCI DevOps Build and Deployment Pipelines.

I am trying to use Terraform more and more. Using the console for the initial creation perhaps – then using Resource Discovery with the OCI Provider for Terraform (see for example this article) or the Stack export option in the OCI Console for create the Terraform plan files for the resources I just created and from that point on continuing with those plan files and the command line – typically OCI Cloud Shell with Terraform preinstalled and configured.

It seemed to me a good idea to start building a collection of Terraform plan files for common constellations of resources. And I am well aware that I am far from the first to create such a collection. I will happily use similar offerings from my friends and peers. And I will add some composites that are not yet available (or at least I did not find or like them).

In this article I will introduce the GitHub repo: https://github.com/lucasjellema/oci-terraform-composites in which I want to collect my handy reusable plan files.

And I am happy to share with you my first and so far my only OCI composite: Create, Build and Invoke a Function. Functions on OCI are based on the Project Fn framework. They are created as container images that are pushed to the OCI Container Registry. A function is created in an application with a reference to the container image that provides the implementation.

image

image

In my first little collection of plan files, a function’s container image is built from the sources included in the repository (a Python function that does very little). This is just an example of course and you can easily replace these sources with real ones. The container image is pushed to OCI Container Image Registry to a repository that is created by these plans specifically for the function. The function itself is subsequently created in the context of an application that already exists. The name of application as well as the name of the function are defined in the variables.tf file – as are the region, the tenancy_ocid, the target compartment ocid, the Docker Registry credentials and the test request with which the function is to be invoked.

Use this sample set of Terraform plans to create your own plans. Some “tricks” in these plans:

  • use data sources to query existing OCI resources, such as the application based on its name (in datasources.tf)
  • use local variables derived from data source results as well as input variables
  • use provisioner “local-exec” to execute Linux Bash Shell command on the server running Terraform (to interact with fn and terraform tools)
  • use Terraform function tomap() to create a key-value map structure (or JSON object) for one of the complex properties
  • use Terraform output to report on what is going on or went on (like a console.log or a dbms_output.put_line or System.out.println )
  • use resource “time_sleep” to introduce a little wait (10 seconds), in this case between creating and invoking the function (without the wait time, I ran into 404-NotAuthorizedOrNotFound  errors)

The most important Terraform resources in these plans are shown here:

### Repository in the Container Image Registry for the container images underpinning the function
resource "oci_artifacts_container_repository" "container_repository_for_function" {
# note: repository = store for all images versions of a specific container image - so it included the function name
compartment_id = var.compartment_ocid
display_name = "${local.ocir_repo_name}/${var.function_name}"
is_immutable = false
is_public = false
}
resource "null_resource" "Login2OCIR" {
depends_on = [ oci_artifacts_container_repository.container_repository_for_function]
provisioner "local-exec" {
command = "echo '${var.ocir_user_password}' | docker login ${local.ocir_docker_repository} --username ${local.ocir_namespace}/${var.ocir_user_name} --password-stdin"
}
}
### build the function into a container image and push that image to the repository in the OCI Container Image Registry
resource "null_resource" "FnPush2OCIR" {
depends_on = [null_resource.Login2OCIR, oci_artifacts_container_repository.container_repository_for_function]
# remove function image (if it exists) from local container registry
provisioner "local-exec" {
command = "image=$(docker images | grep ${local.app_name_lower} | awk -F ' ' '{print $3}') ; docker rmi -f $image &> /dev/null ; echo $image"
working_dir = "functions/fake-fun"
}
# remove fake-fun image (if it exists) from local container registry
provisioner "local-exec" {
command = "image=$(docker images | grep fake-fun | awk -F ' ' '{print $3}') ; docker rmi -f $image &> /dev/null ; echo $image"
working_dir = "functions/fake-fun"
}
# build the function; this results in an image called fake-fun (because of the name attribnute in the func.yaml file)
provisioner "local-exec" {
command = "fn build --verbose"
working_dir = "functions/fake-fun"
}
# tag the container image with the proper name - based on the actual name of the function
provisioner "local-exec" {
command = "image=$(docker images | grep fake-fun | awk -F ' ' '{print $3}') ; docker tag $image ${local.ocir_docker_repository}/${local.ocir_namespace}/${local.ocir_repo_name}/${var.function_name}:0.0.1"
working_dir = "functions/fake-fun"
}
# create a container image based on fake-fun (hello world), tagged for the designated function name
provisioner "local-exec" {
command = "docker push ${local.ocir_docker_repository}/${local.ocir_namespace}/${local.ocir_repo_name}/${var.function_name}:0.0.1"
working_dir = "functions/fake-fun"
}
}
resource "oci_functions_function" "new_function" {
depends_on = [null_resource.FnPush2OCIR]
application_id = "${local.application_id}"
display_name = "${var.function_name}"
image = "${local.ocir_docker_repository}/${local.ocir_namespace}/${local.ocir_repo_name}/${var.function_name}:0.0.1"
memory_in_mbs = "128"
config = tomap({
DUMMY_CONFIG_PARAM = "no value required"
})
}
### wait a little while before the function is ready to be invoked
## I got the following errors without this wait introduced into the plan
## Error: 404-NotAuthorizedOrNotFound
## Error Message: Authorization failed or requested resource not found
##│Suggestion: Either the resource has been deleted or service Functions Invoke Function need policy to access this resource.
resource "time_sleep" "wait_for_function_to_be_ready" {
depends_on = [oci_functions_function.new_function]
create_duration = "10s"
}
resource "oci_functions_invoke_function" "test_invoke_new_function" {
depends_on = [time_sleep.wait_for_function_to_be_ready]
#Required
function_id = oci_functions_function.new_function.id
#Optional
invoke_function_body = var.test_invoke_function_body
fn_intent = "httprequest"
fn_invoke_type = "sync"
base64_encode_content = false
}
output "function_response" {
value = oci_functions_invoke_function.test_invoke_new_function.content
}

The final output from running terraform apply:

image

It reports 7 resources added. That may be including a function call as a resource. The actual results from these plans that form the composite:

  • Container Image Registry Repository
  • Container Image for the new function (built from the Python sources)
  • Function based on Container Image added to existing application

A test call has been made to the newly created function, and it has successfully returned a response.

A next step would be to create as a Terraform composite the plans to create a DevOps Build and Deployment Pipeline for this function – in an existing DevOps project with an existing DevOps Code Repository that will trigger the Build Pipeline.

Resources

Oracle Terraform Modules – https://github.com/oracle-terraform-modules 

Terraform on OCI – articles by That Finnish Guy (Simo Vilmunen) : https://www.thatfinnishguy.blog/tag/terraform/ 

Terraform docs on Sleep – https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.