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