Tuesday, 25 February 2020

Iterating OCI CLI list data in bash

For automating tasks with OCI, you have a few options:

  • OCI CLI (bash)
  • Python SDK
  • Go SDK
  • Java SDK
  • REST API

I'm a bash first kinda guy, so would usually opt for trying with the bash solution if the requirement is simple enough. Anything beyond, it's worth moving over to Python or GO.

When you run a command, you will typically get a JSON payload. But I'm in bash, how to I interact with this data?

That's where this nice tool jq comes in. An interface to JSON data, where you can pass in a json path to get the data you want. When you get started with this you will want to leverage the website jqplay.org. It provides a visual interface to the query paths you build, along with some common examples.

So, now over to iterating the data structure. I'm going to take the compartment list as an example.

When calling the command: oci iam compartment list, the data set in my tenancy looks like this:

{
  "data": [
    {
      "compartment-id": "ocid1.tenancy.oc1..xxx",
      "defined-tags": {},
      "description": "education",
      "freeform-tags": {},
      "id": "ocid1.compartment.oc1..xxx",
      "inactive-status": null,
      "is-accessible": null,
      "lifecycle-state": "ACTIVE",
      "name": "education",
      "time-created": "2019-09-20T01:06:31.731000+00:00"
    },
    {
      "compartment-id": "ocid1.tenancy.oc1..xxx",
      "defined-tags": {},
      "description": "idcs-xxx|22540605|foo@gmail.com-12345",
      "freeform-tags": {},
      "id": "ocid1.compartment.oc1..xxx",
      "inactive-status": null,
      "is-accessible": null,
      "lifecycle-state": "ACTIVE",
      "name": "ManagedCompartmentForPaaS",
      "time-created": "2019-09-17T02:56:55.916000+00:00"
    },
    {
      "compartment-id": "ocid1.tenancy.oc1..xxx",
      "defined-tags": {},
      "description": "Learning to use terraform",
      "freeform-tags": {},
      "id": "ocid1.compartment.oc1..xxx",
      "inactive-status": null,
      "is-accessible": null,
      "lifecycle-state": "DELETED",
      "name": "terraform",
      "time-created": "2019-09-25T12:29:43.421000+00:00"
    }
  ]
}

So, in my bash script, what I will normally do is get a list of indexes so I can look at these data sets one by one. To do these, you want to use the "keys" function which will turn an array of all the indexes. We want to remove the array brackets and just end up with a number on each line representing the index. So we end up with a json path of: .data | keys | .[]


And so when we are looping over our data, we just reference the index, to get individual properties for that element.

So, with all this info, our list script looks like this:

#!/bin/bash
set -e

compartmentList=$(oci iam compartment list)

for i in $(echo "$compartmentList" | jq '.data | keys | .[]')
do
    ID=$(echo $compartmentList | jq -r ".data[$i].\"id\"")
    name=$(echo $compartmentList | jq -r ".data[$i].\"name\"")
    desc=$(echo $compartmentList | jq -r ".data[$i].\"description\"")
    lifecycleState=$(echo $compartmentList | jq -r ".data[$i].\"lifecycle-state\"")

    echo "ID: $ID"
    echo "Name: $name"
    echo "Desc: $desc"
    echo "Desc: $lifecycleState"
    echo "****"
done

(side note: with this simple example, there's probbaly a one liner you could do with jq, but real world example are likely more complex and require some use of one or two properties)

Compartments usually underpin other operations you may be analysing in your tenancy, and one thing I discovered the other day is that if a compartment gets removed, things start going haywire! So, what we'll want to do is restrict our list to only include ones with the lifecycle-state of ACTIVE (depending of course on your business requirements).

So, in our script, we could just add a condition:

if [[ "$lifecycleState" == "ACTIVE" ]]
then
    # TODO
fi

However, just to revisit one of my previous blog posts which discussed the query capabilities of the command line client, we can reduce the code and create a reusable component/query.

Go to your oci_cli_rc file and add a new query called active, that looks like this:

active=data[?"lifecycle-state" == `ACTIVE`]

If we use this query in our command, it's worth noting that the data is transformed slightly. Previously it contains an array with the name "data". That property is removed when using this query and now we end up with a raw array, of objects.

So, if we want to use this query to filter out only active compartments, our script now looks like this:

#!/bin/bash
set -e

compartmentList=$(oci iam compartment list --query query://active)

for i in $(echo "$compartmentList" | jq 'keys | .[]')
do
    ID=$(echo $compartmentList | jq -r ".[$i].\"id\"")
    name=$(echo $compartmentList | jq -r ".[$i].\"name\"")
    desc=$(echo $compartmentList | jq -r ".[$i].\"description\"")
    lifecycleState=$(echo $compartmentList | jq -r ".[$i].\"lifecycle-state\"")

    echo "ID: $ID"
    echo "Name: $name"
    echo "Desc: $desc"
    echo "Desc: $lifecycleState"
    echo "****"
done

I made bold that parts that have been changed. You'll notice we just removed the reference to .data; Otherwise pretty well the same.