The first rule of serverless is to keep your functions light and simple. Part of keeping a function light is avoiding unnecessary external dependencies such as SDKs. But what happens when you need to communicate with an external service or perform some other task that would normally require a dependency on the Oracle Cloud Infrastructure (OCI) SDK? Or, what happens when there isn't an SDK option available for you at the moment (as is currently the case with Node.JS)? Luckily, there's another option: the OCI REST APIs. Just about anything that you can do with any of our SDKs can be done via the OCI REST APIs and as an added bonus, you don't need to include too much extra weight to your functions to get it done.
Normally, you'd use your OCI credentials to sign your requests to the OCI REST endpoints. I'll admit, it can be a tricky process at first, but once you get comfortable with it you'll find it rather easy to do. However, in the case of a serverless function, there is not an easy way to get your credentials into the function environment (Docker container) so we're left looking at other options. That's why our brilliant engineers have given us the ability to use Resource Principal (RP) authentication and have added support to most of our SDKs for RP auth. See the docs for how to configure RP auth for your functions, but the basic premise is that you need to create a dynamic group and assign the appropriate IAM policies to that dynamic group. Once that's done, the function can utilize RP auth via the normal SDKs.
But Wait! Didn't you just say above that we're trying to keep our function light and avoid including an SDK? How can we use Resource Principal authentication if we're using the REST endpoints and not an SDK?
That's a fantastic question! The answer is: we'll manually sign our requests to the REST endpoints with our Resource Principal credentials instead of including our own credentials in the function. The process is outlined below and it's based heavily on the Node.JS request signing example in our documentation. Let's Get Started!
New to serverless on the Oracle Cloud? These two videos will help you get started: Serverless On Oracle Cloud Getting Your Tenancy Configured For Oracle Functions & Creating Your First Serverless Function On The Oracle Cloud.
First, create your serverless application. You can do this via the OCI Console Dashboard, or via the CLI:
Next, create a function:
We're not completely dependency-free here, but we're close. Add two small packages:
Here's where the good stuff happens. Our scaffolded function is a basic "hello world", so let's modify it to make a request to the OCI REST API for Object Storage to return a list of buckets in a compartment. We'll pass a few arguments into the function in a JSON object, so add a single argument to the function to accommodate:
When RP auth is enabled for a function, there will be a series of environment variables available from within the function. We're concerned with two of those variables to help us sign our request, the first of which is
OCI_RESOURCE_PRINCIPAL_RPST which contains the path on the machine to a file containing the Remote Principal Session Token (RPST). This is token is formatted as a JWT and contains claims that identify the tenancy and compartment that the function resides within. We'll ultimately parse the RPST to retrieve those claims and use the RPST to sign the request later on, but for now, just read the token into a variable:
Next, parse the claims from the token and grab and store the tenancy ID:
The other environment variable we are concerned with is called
OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM and it contains the path to a private key that we'll also use to sign our request. Grab the path and read the file:
To sign the request, we need to construct a "
keyId". We need to format it in a certain manner so the OCI endpoint recognizes that we're using a session token containing the RPST.
sign() method that has been modified from the documentation mentioned above. The difference here is that we're using the formatted RPST as our keyId and using the provided private key.
Finally, we can return a Promise that contains our signed HTTPS request to the Object Storage endpoint.
Pretty easy, just do:
We'll use the
fn CLI to invoke it and pass the expected JSON object in:
And we'll get back the array of objects containing our Object Storage buckets!
We kept our function light and called an OCI REST endpoint to get the data we needed in a simple manner.
Need More? The full source for this blog post is available, as always, on GitHub at: https://github.com/recursivecodes/rp-demo-fn