2017-10-13 3 views
0

J'ai une réponse JSON d'un référentiel GitHub avec une liste de téléchargements possibles pour une version donnée (le tableau assets dans le document).Filtrer un document JSON dans Ansible

Je veux obtenir le navigateur télécharger l'URL lorsque le name d'un actif se termine par x64.AppImage.

En Ansible, les filtres sont construits apon jmespath et en utilisant son outil de terminal, je peux interroger l'URL avec l'expression suivante:

assets[?ends_with(name, 'x64.AppImage')].browser_download_url 

Avec le PlayBook suivant, le document JSON est interrogé et stocké dans le json_reply variable.

--- 
- hosts: local 
    tasks: 
    - name: Get list of Rambox releases 
     uri: 
     url: "https://api.github.com/repos/saenzramiro/rambox/releases/latest" 
     body_format: json 
     register: json_reply 

    - name: Filter reply 
     debug: URL -> "{{ item }}" 
     with_items: 
     - "{{ json_reply.json | json_query(json_filter) }}" 
     vars: 
     - json_filter: assets[?ends_with(name, 'x64.AppImage')].browser_download_url 

Cependant, l'exécution de cette donne l'erreur suivante:

fatal: [localhost]: FAILED! => { 
    "msg": "JMESPathError in json_query filter plugin:\nIn function ends_with(), invalid type for value: latest-mac.json, expected one of: ['string'], received: \"unknown\"" 
} 

latest-mac.json est le premier objet du tableau assets.

Comment puis-je rendre Ansible pour itérer sur tout le tableau assets et appliquer mon filtre?

PS:

Si au lieu d'interroger si le name se termine par un mot que je précise directement, le filtre fonctionne:

assets[?name == 'Rambox-0.5.13-x64.AppImage')].browser_download_url 

JSON exemple:

{ 
    "url": "https://api.github.com/repos/saenzramiro/rambox/releases/8001922", 
    "prerelease": false, 
    "created_at": "2017-10-04T21:14:15Z", 
    "published_at": "2017-10-05T01:10:55Z", 
    "assets": [ 
    { 
     "url": "https://api.github.com/repos/saenzramiro/rambox/releases/assets/4985942", 
     "id": 4985942, 
     "name": "latest-mac.json", 
     "uploader": { 
     "login": "saenzramiro", 
     "id": 2694669 
     }, 
     "browser_download_url": "https://github.com/saenzramiro/rambox/releases/download/0.5.13/latest-mac.json" 
    }, 
    { 
     "url": "https://api.github.com/repos/saenzramiro/rambox/releases/assets/4985640", 
     "id": 4985640, 
     "name": "Rambox-0.5.13-x64.AppImage", 
     "uploader": { 
     "login": "saenzramiro", 
     "id": 2694669 
     }, 
     "browser_download_url": "https://github.com/saenzramiro/rambox/releases/download/0.5.13/Rambox-0.5.13-x64.AppImage" 
    } 
    ], 
    "tarball_url": "https://api.github.com/repos/saenzramiro/rambox/tarball/0.5.13" 
} 

Répondre

2

Le problème des erreurs de type dans les filtres JMESPath est discuté dans issue 27299.

Vous pouvez utiliser ce plug-in de filtre patched json_query.py.

Ou appliquez une double conversion à votre objet en guise de solution de contournement: | to_json | from_json |.
Cela convertira l'objet en JSON (donc les chaînes simples) et en arrière, donc json_query traitera les chaînes comme type supporté.

1
  • boucle à travers chaque actif
  • Imprimer l'URL du navigateur de l'élément si elle se termine par x64.AppImage

Solution de ne pas utiliser JMESPath:

- name: Filter reply 
    debug: var=item.browser_download_url 
    with_items: "{{ json_reply.json.assets }}" 
    when: item.browser_download_url | regex_search('x64.AppImage$') 
1

Comme l'a dit @helloV, vous pouvez y parvenir en utilisant Ansible boucles, bien qu'il n'y ait aucune raison d'impliquer une correspondance d'expression régulière. Vous pouvez utiliser le même test que vous utilisez déjà:

- name: Filter reply 
    debug: 
    var: item.browser_download_url 
    with_items: "{{ json_reply.json.assets }}" 
    when: item.name.endswith('x64.AppImage') 

Le problème racine semble être un bug Ansible. L'erreur provient de the following check dans la bibliothèque jmespath:

 if actual_typename not in allowed_types: 
      raise exceptions.JMESPathTypeError(
       function_name, current, 
       self._convert_to_jmespath_type(actual_typename), types) 

Au point de ce code est appelé, le type de valeurs de données dans votre réponse JSON est AnsibleUnsafeText, alors que allowed_types est [str, unicode]. Je pense que la transformation des valeurs des types natifs vers le type AnsibleUnsafeText est probablement une sorte de comportement de module Ansible standard imposé par le module uri.Nous pouvons contourner le problème en utilisant curl au lieu, comme ceci:

- name: Get list of Rambox releases 
    command: > 
    curl -s "https://api.github.com/repos/saenzramiro/rambox/releases/latest" 
    register: json_reply 

Et puis:

- name: Filter reply 
    debug: 
    var: item.browser_download_url 
    with_items: > 
    {{ json_reply.stdout|from_json|json_query('assets[?ends_with(name, `x64.AppImage`)]') }} 
+1

Le problème est discuté ici: https://github.com/ansible/ansible/issues/27299#issuecomment-331068246 et une solution de contournement consiste à utiliser '| to_json | from_json | ' –

+0

@KonstantinSuvorov La solution de contournement que vous avez proposée est la meilleure solution, car elle évite d'avoir à boucler tout le document. Si vous postez la réponse, je l'accepterai. –