Skip to content

Refactor list outputs

Current situation

When using opentf-ctl get xxxs, we are duplicating behaviors from resources to resources, and the format options are limited. We cannot get YAML or JSON data, which makes using opentf-ctl in scripts harder than it should be (csv is not that expressive).

Desired outcome

We should use a generic mechanism, relying on (a subset of) jsonpath, and we should be able to get yaml or json output.

This applies to the following subcommands:

  • get workflows
  • get channels
  • get agents
  • get qualitygate
  • get subscriptions

It does not apply to get workflow, for which it is not relevant.

The changes are:

Those subcommands allow for -o yaml and -o json in addition to -o wide and -o custom-columns=... (the long form --option= is also allowed).

The yaml and json output give a list of objects (workflows, channels, agents, rules, or subscriptions, respectively).

The console output is no longer in CSV form. It uses fixed-width columns now.

> opentf-ctl get workflows --context allinone -o wide
WORKFLOW_ID                           STATUS  FIRST_SEEN_TIMESTAMP        NAME
caa5b611-cf9c-494f-a1ff-e291d1614bd1  FAILED  2023-07-28T09:27:10.277243  demo expressions
29e457d1-d574-40b1-af69-3a4111b60895  DONE    2023-07-28T09:29:14.789645  demo expressions
a30f6c3d-2da7-4e90-8583-a9bc8cd22de2  DONE    2023-07-28T09:30:18.189441  demo expressions

output-columns now allow for more jsonpath-like values. Any value in the object can be queried.

The column format is as follows:

.{segment}[.{segment}]*, where segment is either a field name or * or *~.

*~ can only be used at the end. An error is raised if it is used as an intermediate segment. It returns the keys of the object.

If a segment is not found in the data, <none> is returned.

Assuming the following object:

{
  "metadata": {
    "name": "my name",
    "labels": {
      "label_1": "value_1",
      "label_2": "value_2"
    }
  },
  "jobs": {
    "job_1": {
      "runs-on": ["windows", "foo"],
      "steps": [
        { "run": "echo hi" },
        { "run": "echo bye" }
      ]
    },
    "job_2": {
      "runs-on": ["windows", "bar"],
      "steps": [
        { "run": "echo ola" },
        { "run": "echo alo" }
      ]
    }
  }
}

The following column descriptions give:

  • .metadata.name -> "my name"
  • .metadata.namespace -> "<none>"
  • .metadata.labels.* -> ["value_1", "value_2"]
  • .metadata.labels.*~ -> ["label_1", "label_2"]
  • .jobs.*~ -> ["job_1", "job_2"]
  • .jobs.*.runs-on -> [["windows", "foo"], ["windows", "bar"]]
  • .jobs.*.runs-on.* -> ["windows", "foo", "windows", "bar"]
Edited by Martin Lafaix