Best Practices from Oracle Development's A‑Team

Creating a web site using OCI services Part 3 - Hard Coding Configuration is bad, mkay

This is part 3 of my "n" part series on misusing OCI services to host a website. If you haven't read parts 1 and 2 it's probably best to jump back to the first one and start there.

In part 2 I mentioned that I made two big mistakes - naming the bucket "demo" instead of "demosite" and then hard coding the name of the bucket in my code.

So how do we fix this?

I mean we could just edit the code and rename the bucket. But that doesn't make it easy to change the bucket name again in the future, and it doesn't make the code re-usable. So if we're going to fix this we may as well do it the right way.

Step 1: Copy existing content to the new Bucket

Our demo site is just a few files so we could just copy them over by hand. But where's the fun in that?

Instead, let's use the OCI CLI and string together a couple of other Unix commands...

If you want to copy/paste:

oci os object list --bucket-name demo --all | jq '.data[].name' | awk '{print "oci os object copy --bucket-name demo --destination-bucket demosite --source-object-name",$1}' | sh

Obviously you should update the Bucket names as appropriate for your environment!

That command line:

  1. lists all of the objects in the bucket
  2. uses jq to get just the names
  3. builds a command line for each that will send a copy command to OCI
  4. and finally pumps them all into sh to be executed.

There are other ways to do this including the object_storage_bulk_copy.py script in the OCI Python SDK examples. But if you have OCI CLI, and JQ installed this is just as easy.

Technically the copy is asynchronous so I'm supposed to say that it might take a moment to occur. But by the time you go back into the console and look at the destination bucket they will probably already have been copied.

Step 2: Move configuration settings to the Application

Applications in Fn and individual Functions can have Configuration settings associated with them. These settings are key-value pairs and in the former case are sent to every Function in the application. In the latter case, the settings apply only to the one Function.

You can see and update configuration settings for either from the OCI console OR from either the OCI or Fn CLI.

In this case, let's associate the setting with the Application (for reasons that I'll get into later). Go ahead and navigate to your Application in the console and add a new setting with the Key "bucketname" and the value set to the name of your bucket - in my case "demo" to use the old bucket or "demosite" to use the new one.

Step 3: Update the code to read the setting

In a Python Function, the Configuration keys are sent to the Function in the os environment. Even so, Fn encourages you to use the Config field in the InvokeContext to access those settings - just in case things change in the future.

My code currently has this:

bucket_name = "demo"

We just need to change that out to this:

bucket_name = ctx.Config().get("bucketname")

And while we're at it there's this other chunk of code that's staring at us:

config = {
    # replace with the region you are using
    "region": "ca-toronto-1"

If we're going to move the bucket name out of this code and into the configuration we may as well do that one too!

Up at the beginning of the function let's declare a variable to hold it:

# we are going to use the Resource Principal signer to sign out requests to Object Store:
signer = oci.auth.signers.get_resource_principals_signer()
# by default use the same region as I'm running in
region = region = signer.region
if "region" in ctx.Config():
    # but allow a config setting to override that
    region = ctx.Config().get("region")

And then later:

config = {
    "region": region

If this is confusing I'll link you to a Project in my personal git repo in a later post.

You'll notice a couple of interesting bits here. The first is that I retrieve the region from the Signer. And only then do I look for a setting with the name "region". This way the default behavior is the most sensible - use the same region the code is running in, but allow someone to override it if they want via the config.

Step 4: Redeploy your code & Test

Re-run your fn deploy to push your new code up to OCI.

Then load the website.

If you did everything right nothing will have changed.

What's Next?

If you got this far thanks!

Back in the first post in the series I said this:

But you can also do something actually clever: use Functions to add business logic. And since you're using fully managed services you don't have to worry about the complexity of running an app server, load balancers, compute instances, or all the rest.

And that is what's next. We're going to integrate some business logic. Specifically I'm going to protect part of the static site and make users sign in via OpenID Connect.

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.Captcha