Conditionals based on empty JSON dict

12 views
Skip to first unread message

Panagiotis Atmatzidis

unread,
Jun 19, 2017, 11:35:57 AM6/19/17
to Ansible Project
Hello list,

I'm trying to automate an API call that adds an 'A' record to the linode DNS service. The relevant playbook snippet is:

----
   - name: create linode 'A' record
      uri:
        url: "https://api.linode.com/?api_key={{ linode_api_key }}&api_action={{ linode_api_action }}&domainid={{ linode_domain_id }}&type={{ linode_record_type }}&target={{ public_ip }}&name={{ instance_name }}&TTL_sec={{ linode_record_ttl }}"
        return_content: yes
      register: json_resp
    - name: set JSON response
      set_fact:
        linode_api_error:   "{{ (json_resp.content|from_json).ERRORARRAY.0 }}"
    - fail: msg="Setting DNS entry failed with error '{{ linode_api_error.ERRORARRAY.0.ERRORMESSAGE }}'"
      when: (linode_api_error is defined)       # <=== The problem is here
    - debug: msg="DNS A record set for '{{ instance_name  }}.host.com' targets to '{{ public_ip }}'"
      when: (linode_api_error not defined)     # <=== The problem is here
----

A successful entry looks returns a JSON object that looks like this (the ERRORARRAY is empty):

{
  "ACTION": "domain.resource.create",
  "DATA": {
    "ResourceID": 8016441
  },
  "ERRORARRAY": []
}

An unsuccessful entry looks like this:

{
  "ACTION": "domain.resource.create",
  "DATA": {},
  "ERRORARRAY": [
    {
      "ERRORMESSAGE": "Authentication failed",
      "ERRORCODE": 4
    }
  ]
}

After I figured how the API works[1] I opted for a check on the JSON "ERRORARRAY" dictionary. But every approach I took failed. What I'd like to achieve is the following:

1) If the "linode_api_key" is defined make the URI call (this is not implemented here in case you're wondering). The variable is set by linode_api_key: "{{ lookup('env','LINODE_API_KEY') }}" - not sure what happens when the env variable is not set.
2) If the "uri" call returns error (this can be checked only by the ERRORARRAY), fail.

Any suggestions on how to fix the code or handle this in a more optimised way are more than welcome!


[1]: My first thought was to test against status code (401 for failure and 20x for success). But a failed request will still return a "200" status code. I believe that is a peculiar API design choice. I think that Facebook returns "200" for failed calls too.

J Hawkesworth

unread,
Jun 20, 2017, 4:54:59 AM6/20/17
to Ansible Project
Not tried, but wondering if you could use

- name: work out if create failed
   set_fact:
     create_a_record_failed: true
   when: linode_api_error.ERRORARRAY|length > 0

to detect if you need to fail, then you can retrieve the error message in a separate step.

    - fail: msg="Setting DNS entry failed with error '{{ linode_api_error.ERRORARRAY.0.ERRORMESSAGE }}'"
      when:create_a_record_failed|default(false)

(I think default(false) would get around the problem that you haven't necessarily defined create_a_record_failed if there is no error.
Reply all
Reply to author
Forward
0 new messages