Search code examples
jsonansiblejson-query

Piping to json_query in Ansible reveals no result?


I have an Ansible playbook that amongst other generates a Let's Encrypt certificate by cert-manager on my cluster server.

But I have to wait until the certificate is ready, before I can continue to the next task in my playbook.

That gave me the following Ansible task:

- name: Get TLS certificate secret and wait until ready
  command:
    cmd: 'kubectl get certificate tls-rancher-ingress -n cattle-system -ojson'
  register: certificate_result
  changed_when: (certificate_result.stdout | json_query('status.conditions[0].status') == '"True"')
  retries: 10
  delay: 30

In the code above the certificate is named tls-rancher-ingress and I am getting general information about the certificate by calling the kubectl. The output is presented as JSON.

Output (slightly anonymised) from the kubectl command is on this form:

{
        "apiVersion": "cert-manager.io/v1",
        "kind": "Certificate",
        "metadata": {
            "creationTimestamp": "2024-05-27T23:19:38Z",
            "generation": 2,
            "name": "tls-rancher-ingress",
            "namespace": "cattle-system",
            "resourceVersion": "2331753",
            "uid": "30b816cd-f548-4bf7-90d2-47a0406cdbb1"
        },
        "spec": {
            "commonName": "rancher.example.com",
            "dnsNames": [
                "rancher.example.com"
            ],
            "issuerRef": {
                "kind": "ClusterIssuer",
                "name": "letsencrypt-prod"
            },
            "secretName": "tls-rancher-ingress"
        },
        "status": {
            "conditions": [
                {
                    "lastTransitionTime": "2024-05-28T00:38:38Z",
                    "message": "Certificate is up to date and has not expired",
                    "observedGeneration": 2,
                    "reason": "Ready",
                    "status": "True",
                    "type": "Ready"
                }
            ],
            "notAfter": "2024-08-25T23:38:35Z",
            "notBefore": "2024-05-27T23:38:36Z",
            "renewalTime": "2024-07-26T23:38:35Z",
            "revision": 2
        }
    }

But I am not interested in the whole JSON object, but rather only when the path status.conditions[0].status is equal to "True".

I thought I could just use the pipe filter to send certificate_result.stdout over to json_query("status.conditions[0].status") and then get a response back that is either "True" or "False".

Hover using a task to debug the change_when line reveals json_query returns an empty string?

I used the following task for my debug:

- name: Debug changed_when
  debug:
    msg: "DEBUG: {{ certificate_result.stdout | community.general.json_query('status.conditions[0].status') }}"

It doesn't matter if I encapsulate the string using single quotes or double quotes.

Is there anyone that can explain what is going on?

Is it because stdout is not being treated as an JSON object and if so: What is the right way to send it to json_query?

And for reference:

json_query is part of the community.general collection, that has been installed where I run my Ansible playbook.


Solution

  • After a bit more searching I made it work.

    The filter to use was from_json, which confuses me a bit.

    Because I though output from certificate_result.stdout would be treated as a string in spite the structure of output is a JSON object and therefore I had to to use the to_json to convert it into a queryable JSON object.

    But I apparently got it a bit... backwards?

    Therefore the correct command is:

    certificate_result.stdout | from_json | json_query("status.conditions[0].status") == "True"
    

    And the whole task is as follows:

    - name: Get TLS certificate secret and wait until ready
      command:
        cmd: 'kubectl get certificate tls-rancher-ingress -n cattle-system -ojson'
      register: certificate_result
      until: certificate_result.stdout | from_json | json_query("status.conditions[0].status") == "True"
    
      retries: 10
      delay: 30
      changed_when: false
    

    A quick explanation on what is going on:

    The kubectl command is called every 30 seconds, which output its content as a JSON object.

    And want the task to loop until the result for the query path status.conditions[0].status is true.

    The task will fail, if the query does not return true after the 10th iteration, which is same as task is timing out after 300 seconds due to 30 second break.

    The changed_when is just a flag for ansible to tell if any value was updated by this task, which is not the case, so it should always return the value "false".