adbdkb
adbdkb

Reputation: 2181

Ansible - Read dict items from file and use with loop

I am trying to take this effort a little further and want to download multiple files from BitBucket having different parent directories. So I am trying to use the below script.

I want to put the mappings in a separate file and read it into the script, so that at subsequent times, I can just update that file, without having to touch the script.

- name: Download connector scripts
  get_url:
    url: "http://bbserver:7990/projects/myproject/repos/myrepo/raw/{{ item.filename }}?at=refs%2Fheads%2Fmaster"
    dest: "{{ item.dest }}"
    url_username: '{{ bb_username }}'
    url_password: '{{ bb_password }}'
    force_basic_auth: yes
  loop: "{{ lookup('file', 'scriptnamesnpaths.txt').splitlines() }}"
  register: showdlstatus
  become: yes
  become_user: '{{ bb_username }}'
- debug: var=showdlstatus

And my scriptnamesnpaths.txt looks like this

{filename: 'scripts/dir1/script1.sh',      dest: "/scripts/dir1/"}
{filename: 'scripts/dir1/script2.sh',      dest: "/scripts/dir1/"}
{filename: 'scripts/dir2/script3.sh',      dest: "/scripts/dir2/"}
{filename: 'releases/file1.json',          dest: "/scripts/releases/"}
{filename: 'releases/file2.json',          dest: "/scripts/releases/"}

I basically want to download different scripts from different paths in bitbucket to different locations on the server.

The error I am getting with this construct is

 {"msg": "The task includes an option with an undefined variable. 
 The error was: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'filename'

Which part am I doing wrong or missing? I also tried with loop: "{{ lookup('dict', 'scriptnamesnpaths.txt').splitlines() }}"

But get error about dict definition.

Any pointers or help is appreciated.

Thank you

Upvotes: 0

Views: 797

Answers (2)

user6169671
user6169671

Reputation:

Is there a particular reason for favoring putting the filenames/destinations in a script rather than the task? This would be a simplified version of the task with vars included in it:

- name: Download connector scripts
  get_url:
    url: "http://example.com/{{ item.key }}"
    dest: "{{ item.value }}"
  loop: "{{ files | dict2items }}"
  vars:
    files:
      filename1: "/path/to/dest/dir/"
      filename2: "/path/to/dest/dir/"

Upvotes: 0

larsks
larsks

Reputation: 311968

Rather than using a file lookup, it might be easiest just to make a group_vars file so that Ansible reads it for you automatically.

That is, if you create a file groups_vars/all.yml with this content:

connector_scripts:
  - {filename: 'scripts/dir1/script1.sh',      dest: "/scripts/dir1/"}
  - {filename: 'scripts/dir1/script2.sh',      dest: "/scripts/dir1/"}
  - {filename: 'scripts/dir2/script3.sh',      dest: "/scripts/dir2/"}
  - {filename: 'releases/file1.json',          dest: "/scripts/releases/"}
  - {filename: 'releases/file2.json',          dest: "/scripts/releases/"}

You could then write your task like this:

- name: Download connector scripts
  get_url:
    url: "http://bbserver:7990/projects/myproject/repos/myrepo/raw/{{ item.filename }}?at=refs%2Fheads%2Fmaster"
    dest: "{{ item.dest }}"
    url_username: '{{ bb_username }}'
    url_password: '{{ bb_password }}'
    force_basic_auth: yes
  loop: "{{ connector_scripts }}"
  register: showdlstatus
  become: yes
  become_user: '{{ bb_username }}'

If you really want to use a file lookup, you'll need to deal with the fact that for each iteration of your loop, the variable item is a string. You can't ask for something like item.filename because a string doesn't have an attribute filename (which is the source of the error you're seeing). You would need to de-serialize each line before using it, for example:

- name: Download connector scripts
  get_url:
    url: "http://bbserver:7990/projects/myproject/repos/myrepo/raw/{{ (item|from_yaml).filename }}?at=refs%2Fheads%2Fmaster"
    dest: "{{ (item|from_yaml).dest }}"
    url_username: '{{ bb_username }}'
    url_password: '{{ bb_password }}'
    force_basic_auth: yes
  loop: "{{ connector_scripts }}"
  register: showdlstatus
  become: yes
  become_user: '{{ bb_username }}'

You could simplify that a bit by moving the from_yaml call to a vars section so you only have to do it once:

- name: Download connector scripts
  get_url:
    url: "http://bbserver:7990/projects/myproject/repos/myrepo/raw/{{ item_converted.filename }}?at=refs%2Fheads%2Fmaster"
    dest: "{{ item_converted.dest }}"
    url_username: '{{ bb_username }}'
    url_password: '{{ bb_password }}'
    force_basic_auth: yes
  vars:
    item_converted: "{{ item|from_yaml }}"
  loop: "{{ connector_scripts }}"
  register: showdlstatus
  become: yes
  become_user: '{{ bb_username }}'

...but I think it's much easier to simply use the first solution and drop a file into group_vars.

Upvotes: 2

Related Questions