X

Getting Started with Oracle Functions and Object Storage

Abhishek Gupta
Principal Product Manager

Oracle Functions, Oracle Cloud Infrastructure’s functions-as-a-service (FaaS) platform, is now generally available in all commercial regions.

This post shows you how to build and deploy a serverless Java function in Oracle Functions to list the objects of a bucket in Oracle Cloud Infrastructure Object Storage, in a secure manner with fine-grained access. It covers:

  • Code and concepts—overview of the logic along with relevant context
  • Configuration and deployment of the function

The function leverages the Object Storage APIs in the Oracle Cloud Infrastructure Java SDK to access the Object Storage bucket.

Before we begin, let’s look at the Oracle Cloud Infrastructure platform features that we'll use.

Oracle Functions

Oracle Functions is a fully managed, highly scalable, on-demand, FaaS platform. It’s a serverless offering that enables you to focus on writing code to meet business needs without worrying about the underlying infrastructure, and get billed only for the resources consumed during the execution.

Oracle Functions is powered by the Fn Project, which is an open source, container native, serverless platform that can be run anywhere — in any cloud or on premises. You can download and install the open source distribution of Fn Project, develop and test a function locally, and then use the same tooling to deploy that function to Oracle Functions.

Object Storage

The Object Storage service is an internet-scale, high-performance storage platform that offers reliable and cost-efficient data durability. It can store an unlimited amount of unstructured data of any content type, including analytic data and rich content, like images and videos.

Code Walkthrough

Now let’s walk through the code that makes this happen, to provide some context before you configure and deploy the function.

Authentication

The Resource Principal authentication provider included in the Oracle Cloud Infrastructure SDK is used to allow the function to access the Object Storage service. The ResourcePrincipalAuthenticationDetailsProvider object is used to create the ObjectStorageClient instance, which in turn is used to invoke the Object Storage service.


private ObjectStorage objStoreClient = null;

final ResourcePrincipalAuthenticationDetailsProvider provider
            = ResourcePrincipalAuthenticationDetailsProvider.builder().build();

    public ObjectStoreListFunction() {
        try {
        
            objStoreClient = new ObjectStorageClient(provider);

        } catch (Throwable ex) {
            System.err.println("Failed to instantiate ObjectStorage client - " + ex.getMessage());
        }
    }

For this to work, you configure Oracle Cloud Infrastructure to include the function in a dynamic group. Then, you create an Identity and Access Management (IAM) policy to grant the dynamic group access to the Object Storage service. This step is covered in a later section. You can find details in the documentation.

List Function

The handle method for the list objects function needs just the bucket name as the input. Then, it uses the API handle (listObjects method) to get all the bucket object names.


    public List<String> handle(String bucketName) {
        if (objStoreClient == null) {
            System.err.println("There was a problem creating the ObjectStorageClient object. Please check logs");
            return Collections.emptyList();
        }
        List<String> objNames = null;
        try {
            String nameSpace = System.getenv().get("NAMESPACE");
            ListObjectsRequest lor = ListObjectsRequest.builder()
                    .namespaceName(nameSpace)
                    .bucketName(bucketName)
                    .build();

            System.err.println("Listing objects from bucket " + bucketName);
            
            ListObjectsResponse response = objStoreClient.listObjects(lor);
            objNames = response.getListObjects().getObjects().stream()
                    .map((objSummary) -> objSummary.getName())
                    .collect(Collectors.toList());

            System.err.println("Got list of objects in bucket " + bucketName);
        } catch (Throwable e) {
            System.err.println("Error fetching object list from bucket " + e.getMessage());
        }
        return objNames;
    }

Prerequisites

Before you can configure and deploy the function, perform the following setup.

Object Storage

Create an Object Storage bucket. For details, see the documentation. Then, upload a few objects to the bucket (documentation).

Oracle Functions

Set up and configure Oracle Functions in your tenancy as described in the documentation.

Source Code

Create a directory structure for storing source code:


mkdir -p function-object-store-example/src/main/java/io/fnproject/example

cd function-object-store-example

Create a file named func.yaml with the following contents:


schema_version: 20180708
name: listobjects
version: 0.0.1
runtime: java
build_image: fnproject/fn-java-fdk-build:jdk11-1.0.98
run_image: fnproject/fn-java-fdk:jre11-1.0.98
cmd: io.fnproject.example.ObjectStoreListFunction::handle
timeout: 120

Create a file named pom.xml with the following contents:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <groupId>io.fnproject.example</groupId>
    <artifactId>list-objects</artifactId>
    <version>1.0.0</version>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.oracle.oci.sdk</groupId>
                <artifactId>oci-java-sdk-bom</artifactId>
                <version>1.5.14</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.oracle.oci.sdk</groupId>
            <artifactId>oci-java-sdk-objectstorage</artifactId>
        </dependency>
    </dependencies> 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.1</version>
                <configuration>
                    <useSystemClassLoader>false</useSystemClassLoader>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <name>list-objects</name>
</project>

Change to another directory:


cd src/main/java/io/fnproject/example/

Create a file named ObjectStoreListFunction.java with the following contents:


package io.fnproject.example;

import com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider;
import com.oracle.bmc.objectstorage.ObjectStorage;
import com.oracle.bmc.objectstorage.ObjectStorageClient;
import com.oracle.bmc.objectstorage.requests.ListObjectsRequest;
import com.oracle.bmc.objectstorage.responses.ListObjectsResponse;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class ObjectStoreListFunction {

    private ObjectStorage objStoreClient = null;

    final ResourcePrincipalAuthenticationDetailsProvider provider
            = ResourcePrincipalAuthenticationDetailsProvider.builder().build();

    public ObjectStoreListFunction() {
        try {

            //print env vars in Functions container
            System.err.println("OCI_RESOURCE_PRINCIPAL_VERSION " + System.getenv("OCI_RESOURCE_PRINCIPAL_VERSION"));
            System.err.println("OCI_RESOURCE_PRINCIPAL_REGION " + System.getenv("OCI_RESOURCE_PRINCIPAL_REGION"));
            System.err.println("OCI_RESOURCE_PRINCIPAL_RPST " + System.getenv("OCI_RESOURCE_PRINCIPAL_RPST"));
            System.err.println("OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM " + System.getenv("OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM"));

            objStoreClient = new ObjectStorageClient(provider);

        } catch (Throwable ex) {
            System.err.println("Failed to instantiate ObjectStorage client - " + ex.getMessage());
        }
    }

    public List<String> handle(String bucketName) {

        if (objStoreClient == null) {
            System.err.println("There was a problem creating the ObjectStorage Client object. Please check logs");
            return Collections.emptyList();
        }

        List<String> objNames = null;
        try {
            String nameSpace = System.getenv().get("NAMESPACE");

            ListObjectsRequest lor = ListObjectsRequest.builder()
                    .namespaceName(nameSpace)
                    .bucketName(bucketName)
                    .build();

            System.err.println("Listing objects from bucket " + bucketName);

            ListObjectsResponse response = objStoreClient.listObjects(lor);

            objNames = response.getListObjects().getObjects().stream()
                    .map((objSummary) -> objSummary.getName())
                    .collect(Collectors.toList());

            System.err.println("Got " + objNames.size() + " objects in bucket " + bucketName);

        } catch (Throwable e) {
            System.err.println("Error fetching object list from bucket " + e.getMessage());
        }

        return objNames;
    }
}

Deploy to Oracle Functions

Go back to the root of the folder:


cd ../../../../../..

Create the Application

Create an application with required configuration. All your functions will be a part of this application.


fn create app --annotation oracle.com/oci/subnetIds='["<OCI_SUBNET_OCIDs>"] --config NAMESPACE=<NAMESPACE> fn-object-store-app

The configuration parameters are defined as follows:

  • OCI_SUBNET_OCIDs: Specify one or more subnets in the compartment that is configured for Oracle Functions development.

  • NAMESPACE: Specify the namespace for Object Storage (documentation).

For example:


fn create app --annotation oracle.com/oci/subnetIds='["ocid1.subnet.oc1.phx.exampleuniqueid"]' --config NAMESPACE=foobar fn-object-store-app

Deploy the Function

Run the following command to deploy the function:


fn -v deploy --app fn-object-store-app

After the process is complete, run the following commands to confirm successful deployment:


//to check your app (and its config)
fn inspect app fn-object-store-app

//to check associated function
fn list functions fn-object-store-app

Configure the Oracle Functions Resource Principals

Now that the function has been deployed, configure the tenancy so that it can access Object Storage.

Get the function OCID by using this command:


fn inspect fn fn-object-store-app listobjects id

Create a dynamic group named fn-obj-store-list with the matching rule:


resource.id = '<FUNCTION_OCID>'

Create an IAM policy named fn-object-store-list-policy with the following statement. Replace <COMPARTMENT_NAME> and <BUCKET_NAME> with values specific to your tenancy:


allow dynamic-group fn-obj-store-list-dg to read objects in compartment <COMPARTMENT_NAME> where all{target.bucket.name=’<BUCKET_NAME>’}

Test the Function

It’s time to try out the function that you just deployed.

List all the files in your bucket:


echo -n '<object store bucket name>' | fn invoke fn-object-store-app listobjects

For example:


echo -n 'test' | fn invoke fn-object-store-app listobjects

You should see a response with a list of all the object names. For example:


[
 "file1.txt,
 "file2.txt"
]

Summary

Now you have an idea of how to develop functions to interact with the Oracle Cloud Infrastructure Object Storage. You know how to use the Java SDK in your functions’ setup, configuration, and deployment to the Oracle Functions platform.

Although this was a simple example, the concepts introduced in this post are generally applicable.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.