Note:

Host Multi-Tier Backend Applications On Oracle Cloud Infrastructure Functions

Introduction

Oracle Cloud Infrastructure Functions (OCI Functions) is a fully managed, multi-tenant, highly scalable, on-demand, Functions-as-a-Service platform. It is built on enterprise-grade Oracle Cloud Infrastructure (OCI) and powered by the Fn Project open source engine. Use OCI Functions when you want to focus on writing code to meet business needs. The serverless and elastic architecture of OCI Functions means there is no infrastructure administration or software administration for you to perform. You do not provision or maintain compute instances, and operating system software patches and upgrades are applied automatically. OCI Functions simply ensures your app is highly-available, scalable, secure, and monitored. With OCI Functions, you can write code in Java, Python, Node, Go, Ruby, and C# (and for advanced use cases, bring your own Docker File, and Graal VM). You can then deploy your code, call it directly or trigger it in response to events, and get billed only for the resources consumed during the execution.

While working in OCI or any other cloud, there can be a requirement for hosting your applications backend code on a serverless architecture and OCI Functions can be used for it. For example, let’s assume your application has multiple backend components which are developed by multiple teams. One team develop its components in Java while another uses Python. You can move these multiple application components to OCI Functions and then call them using API endpoints.

Objectives

Prerequisites

Task 1: Create required Dynamic Groups and Policies

  1. Go to the OCI Console and navigate to Identity & Security, Identity, Dynamic Group and click Create Dynamic Group.

    Create Dynamic Group

  2. Enter details like Name, Description and Matching Rules for your Dynamic Group. Add rules details carefully as they will define what resource will get the access in which compartment.

    Dynamic Group Details

  3. Save the details for the Dynamic Group, and wait for the time the Dynamic Group is created with the details on OCI console. Once Dynamic Group is created, move ahead with creating the IAM policies for this newly created Dynamic Group.

  4. Navigate to Identity & Security, Identity, Policies and click Create Policy.

    Create Policy

  5. Enter details like Name, Description and Compartments for your Dynamic Group. Add policy details carefully as they will define what other resource your function can access in which compartment.

    Policy Details

    Policy Created

  6. Save the details for the Policies, and wait for the time the Policies are created with the details on OCI console. Once Policy is created, move ahead to create the application and function.

Task 2: Create Application and Functions with multiple programming languages

  1. Go to the OCI Console and navigate to Developer Services, Functions, Applications and click Create application.

    Create Application

  2. Enter details like Name, VCN, Subnet and Shape for your application. Select the VCN and Subnet by taking networking into account as they will make your application available to be invoked within the allowed networks.

    Application Details

  3. Save details for the Application, and wait for the time the Application is created with the details on OCI console. Once Application is created, move ahead to create the functions on it.

    Application Created

  4. Navigate to Resources and click Applications. You will find all the commands you need to connect with the created application and create function. You can select any option from Cloud Shell Setup or Local Setup, select Cloud Shell Setup for this tutorial.

    Function Getting Started

  5. To verify you have connected to OCI Functions, you should be able to connect and trigger the following command.

    fn list apps
    

    Function List

  6. Create a couple of functions in multiple programming languages like Java and Python and add the code as per your business requirements.

    fn init --runtime python getStreamData
    fn init --runtime java pushObjectStorageFile
    fn init --runtime java getADWSecret
    fn init --runtime python pushADWData
    
  7. We have created four different functions, two in Java and two in Python. Make sure you have deployed the code and verified the code is working as expected by invoking them manually.

    cd getStreamData
    fn -v deploy --app oci-app-demo
    fn invoke oci-app-demo getStreamData
    
    cd pushObjectStorageFile
    fn -v deploy --app oci-app-demo
    fn invoke oci-app-demo pushObjectStorageFile
    
    cd getADWSecret
    fn -v deploy --app oci-app-demo
    fn invoke oci-app-demo getADWSecret
    
    cd pushADWData
    fn -v deploy --app oci-app-demo
    fn invoke oci-app-demo pushADWData
    

The flow of this backend application is our primary application which will be producing some data in streaming and the data will be consumed by getStreamData function written in Python. Then we will push the data to OCI Object Storage as a file and get the secret password for Autonomous Data Warehouse. We will then push the data to Autonomous Data Warehouse for further processing and analytics.

In Task 3, we will see how we can add Java and Python based function invocation into our application and function code so that we can create a flow.

Task 3: Add code to invoke one function from another function

You can call or invoke one function from another function using OCI SDKs. The example snippets are written in Java and Python and can be used as per your business logic.

  1. Add the following code snippet for Java Software Development Kit (SDK).

    package com.example.fn;
    
    import com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider;
    import com.oracle.bmc.objectstorage.ObjectStorage;
    import com.oracle.bmc.objectstorage.ObjectStorageClient;
    import com.oracle.bmc.objectstorage.requests.GetObjectRequest;
    import com.oracle.bmc.objectstorage.responses.GetObjectResponse;
    import com.oracle.bmc.Region;
    import com.oracle.bmc.functions.FunctionsInvokeClient;
    import com.oracle.bmc.functions.FunctionsManagementClient;
    import com.oracle.bmc.functions.model.FunctionSummary;
    import com.oracle.bmc.functions.model.ApplicationSummary;
    import com.oracle.bmc.functions.requests.ListApplicationsRequest;
    import com.oracle.bmc.functions.responses.ListApplicationsResponse;
    import com.oracle.bmc.functions.requests.ListFunctionsRequest;
    import com.oracle.bmc.functions.responses.ListFunctionsResponse;
    import com.oracle.bmc.functions.requests.InvokeFunctionRequest;
    import com.oracle.bmc.functions.responses.InvokeFunctionResponse;
    import com.oracle.bmc.util.StreamUtils;
    
    import java.nio.charset.StandardCharsets;
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.util.stream.Collectors;
    
    public class HelloFunction {
    
       private ObjectStorage objStoreClient = null;
       final ResourcePrincipalAuthenticationDetailsProvider provider
                = ResourcePrincipalAuthenticationDetailsProvider.builder().build();
    
       public HelloFunction() {
          try {
                objStoreClient = new ObjectStorageClient(provider);
          } catch (Throwable ex) {
                System.err.println("Failed to instantiate ObjectStorage client - " + ex.getMessage());
          }
       }
       public static class GetObjectInfo {
    
          private String bucketName;
          private String name;
    
          public String getBucketName() {
                return bucketName;
          }
    
          public void setBucketName(String bucketName) {
                this.bucketName = bucketName;
          }
    
          public String getName() {
                return name;
          }
    
          public void setName(String name) {
                this.name = name;
          }
    
       }
       public String handleRequest(GetObjectInfo objectInfo) {
    
          String result = "FAILED";
          final ResourcePrincipalAuthenticationDetailsProvider provider = ResourcePrincipalAuthenticationDetailsProvider.builder().build();
          final Region region = Region.US_PHOENIX_1;
          final String compartmentId = "ocid1.compartment.oc1..<your_compartment_ocid>";
          final String name = "oci-java-sdk-function";
          final String payload = "Hii";
    
          if (objStoreClient == null) {
                System.err.println("There was a problem creating the ObjectStorage Client object. Please check logs");
                return result;
          }
          try {
    
                String nameSpace = System.getenv().get("NAMESPACE");
    
                GetObjectRequest gor = GetObjectRequest.builder()
                      .namespaceName(nameSpace)
                      .bucketName(objectInfo.getBucketName())
                      .objectName(objectInfo.getName())
                      .build();
                System.err.println("Getting content for object " + objectInfo.getName() + " from bucket " + objectInfo.getBucketName());
    
                GetObjectResponse response = objStoreClient.getObject(gor);
                result = new BufferedReader(new InputStreamReader(response.getInputStream()))
                      .lines().collect(Collectors.joining("\n"));
    
                System.err.println("Finished reading content for object " + objectInfo.getName());
                invokeFunction(provider, region, compartmentId, name, payload);
    
          } catch (Throwable e) {
                System.err.println("Error fetching object " + e.getMessage());
                result = "Error fetching object " + e.getMessage();
          }
    
          //invokeFunction(provider, region, compartmentId, name, payload);
    
          return result;
    
       }
    
       public static FunctionSummary getUniqueFunctionByName(
                final FunctionsManagementClient fnManagementClient,
                final String compartmentId,
                final String applicationDisplayName,
                final String functionDisplayName)
                throws Exception {
          final ApplicationSummary application =
                   getUniqueApplicationByName(
                            fnManagementClient, compartmentId, applicationDisplayName);
          return getUniqueFunctionByName(
                   fnManagementClient, application.getId(), functionDisplayName);
       }
    
       public static FunctionSummary getUniqueFunctionByName(
                final FunctionsManagementClient fnManagementClient,
                final String applicationId,
                final String functionDisplayName)
                throws Exception {
    
          final ListFunctionsRequest listFunctionsRequest =
                   ListFunctionsRequest.builder()
                            .applicationId(applicationId)
                            .displayName(functionDisplayName)
                            .build();
    
          final ListFunctionsResponse listFunctionsResponse =
                   fnManagementClient.listFunctions(listFunctionsRequest);
    
          if (listFunctionsResponse.getItems().size() != 1) {
                throw new Exception(
                      "Could not find function with name "
                               + functionDisplayName
                               + " in application "
                               + applicationId);
          }
    
          return listFunctionsResponse.getItems().get(0);
       }
    
       public static ApplicationSummary getUniqueApplicationByName(
                final FunctionsManagementClient fnManagementClient,
                final String compartmentId,
                final String applicationDisplayName)
                throws Exception {
          final ListApplicationsRequest listApplicationsRequest =
                   ListApplicationsRequest.builder()
                            .displayName(applicationDisplayName)
                            .compartmentId(compartmentId)
                            .build();
    
          final ListApplicationsResponse resp =
                   fnManagementClient.listApplications(listApplicationsRequest);
    
          if (resp.getItems().size() != 1) {
                throw new Exception(
                      "Could not find unique application with name "
                               + applicationDisplayName
                               + " in compartment "
                               + compartmentId);
          }
    
          final ApplicationSummary application = resp.getItems().get(0);
          return application;
       }
    
       private static String invokeFunction(
                final FunctionsInvokeClient fnInvokeClient,
                final FunctionSummary function,
                final String payload)
                throws Exception {
          String response;
          try {
                System.err.println("Invoking function endpoint - " + function.getInvokeEndpoint());
    
                fnInvokeClient.setEndpoint(function.getInvokeEndpoint());
                final InvokeFunctionRequest invokeFunctionRequest =
                      InvokeFunctionRequest.builder()
                               .functionId(function.getId())
                               .invokeFunctionBody(
                                        StreamUtils.createByteArrayInputStream(payload.getBytes()))
                               .build();
    
                final InvokeFunctionResponse invokeFunctionResponse =
                      fnInvokeClient.invokeFunction(invokeFunctionRequest);
    
                response = "Done executing func1...";
                      //StreamUtils.toString(
                               //invokeFunctionResponse.getInputStream(), StandardCharsets.UTF_8);
    
          } catch (final Exception e) {
                e.printStackTrace();
                System.err.println("Failed to invoke function: " + e);
                throw e;
          }
    
          return response;
       }
    
       public static void invokeFunction(
          final ResourcePrincipalAuthenticationDetailsProvider provider,
          final Region region,
          final String compartmentId,
          final String name,
          final String payload)
          throws Exception {
    
          final FunctionsManagementClient fnManagementClient =
                   FunctionsManagementClient.builder().region(region).build(provider);
    
          final FunctionsInvokeClient fnInvokeClient =
                   FunctionsInvokeClient.builder().build(provider);
    
          try {
             final String appName = "e2e-function-demo";
             final String fnName = "java_func2";
             final FunctionSummary fn =
                      getUniqueFunctionByName(fnManagementClient, compartmentId, appName, fnName);
    
             final String response = invokeFunction(fnInvokeClient, fn, payload);
             if (response != null) {
                   System.out.println("Response from function:  " + response);
             }
          } finally {
             fnInvokeClient.close();
             fnManagementClient.close();
          }
       }
    }
    
    
  2. Add the following code snippet for Python SDK.

    def trigger_function():
       logging.getLogger().info("Reached python function func1...")
       function_endpoint = "https://932jksmn9.us-ashburn-1.functions.oci.oraclecloud.com"
       function_ocid = "ocid1.fnfunc.oc1.iad.your_function_ocid"
       function_body = "Hii"
       signer = oci.auth.signers.get_resource_principals_signer()
       client = oci.functions.FunctionsInvokeClient(config={}, signer=signer, service_endpoint=function_endpoint)
       resp = client.invoke_function(function_id=function_ocid, invoke_function_body=function_body)
       logging.getLogger().info("Completed calling func2")
    

Acknowledgments

More Learning Resources

Explore other labs on docs.oracle.com/learn or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit education.oracle.com/learning-explorer to become an Oracle Learning Explorer.

For product documentation, visit Oracle Help Center.