Custom Resource Structure

Using Ansible Operator, a change in the state of a Custom Resource (CR) signals to the Operator that certain Playbooks/Roles should be executed. As a creator or user of an Ansible Operator, you'll create CRs to test that your Operator has the expected response to each CR trigger. So what does a Custom Resource look like?

Structure of a Custom Resource

A Custom Resource takes the format of a Kubernetes resource. The object definition has mandatory fields:

Mandatory Fields

  • apiVersion: Version of the Custom Resource that will be created
  • kind: Kind of the Custom Resource that will be created
  • metadata: Kubernetes metadata that will be created
  • spec: Key-value list of variables which are passed to Ansible. *Optional* and will be empty by default.
  • annotations: Kubernetes annotations to be appended to the CR. See the below section for Ansible Operator specific annotations.

Annotations

Custom Resource annotations can be applied to modify Ansible Operator behavior:

  • ansible.operator-sdk/reconcile-period: Used to specify the reconciliation interval for the CR.
  • This value is parsed using the standard Golang Package time. Specifically, ParseDuration is used, which will use the default of an 's' suffix giving the value in seconds (e.g. "30" would become "30s" as shown below).

Custom Resource Example

If an Ansible Operator was watching for events from kind: Foo with `apiVersion: foo.example.com/v1alpha1`, an Ansible Playbook/Role could be triggered in response to creation of the resource shown below:

apiVersion: "foo.example.com/v1alpha1"
kind: "Foo"
metadata:
  name: "example"
annotations:
  ansible.operator-sdk/reconcile-period: "30s"

To pass 'extra vars' to the Playbooks/Roles being run by the Operator, you can embed key-value pairs in the spec section of the CR. This is equivalent to how --extra-vars can be passed into the ansible-playbook command.

Example CR with extra-vars

The CR snippet below shows two 'extra vars' (message and newParamater) being passed in via `spec`. Passing 'extra vars' through the CR allows for customization of Ansible logic based on the contents of each CR instance.

# Sample CR definition where some 
# 'extra vars' are passed via the spec
apiVersion: "app.example.com/v1alpha1"
kind: "Database"
metadata:
  name: "example"
spec:
  message: "Hello world 2"
  newParameter: "newParam"

Accessing CR Fields

Now that you've passed 'extra vars' to your Playbook through the CR spec, we need to read them from the Ansible logic that makes up your Operator.

Variables passed in through the CR spec are made available at the top level to be read from Jinja templates. For the CR example above, we could read the vars message and newParameter from a Playbook like so:

- debug:
    msg: "message value from CR spec: {{ message }}"

- debug:
    msg: "newParameter value from CR spec: {{ new_parameter }}"  

Did you notice anything strange about the snippet above? The newParameter variable that we set on our CR spec was accessed as new_parameter. Keep this automatic conversion from camelCase to snake_case in mind, as it will happen to all 'extra vars' passed into the CR spec.

Refer to the next section for further info on reaching into the JSON structure exposed in the Ansible Operator runtime environment.

JSON Structure

When a reconciliation job runs, the content of the associated CR is made available as a set of variables in the Ansible runtime environment. The JSON below is an example of what gets passed into ansible-runner (the Ansible Operator runtime). Note that vars added to the spec section of the CR (message and new_parameter) are placed at the top level of this structure for easy access.

{ "meta": {
        "name": "<cr-name>",
        "namespace": "<cr-namespace>",
  },
  "message": "Hello world 2",
  "new_parameter": "newParam",
  "_app_example_com_database": {
     <Full CR>
   },
}

### Accessing CR metadata

The meta fields provide the CR name and namespace associated with a reconciliation job. These and other nested fields can be accessed with dot notation in Ansible:

- debug:
    msg: "name: {{ meta.name }}, namespace: {{ meta.namespace }}"

In the next step, we'll use operator-sdk to generate Ansible Operator project scaffolding.