Categories for Uncategorized

Raspberry Pi powered Wifi Pictureframe

March 6, 2019 7:09 am Published by
Some days ago I wrote an post about building a picture frame using a Raspberry Pi. The article has been published on opensource.com. The software to show the slideshow has been written by myself and published on github as I wasn’t happy with all the existing solutions. As they either involve using a GL rendered xscreensaver which was terribly slow on the Raspberry Pi or installing Kodi which I think is kind of overkill, just to get a slide show.
Also I wanted the nice feature of a blurred, screen-filling version of the image displayed in the background. This is not possible using the xscreensaver slide show.
Take a look at it if you are as well looking for a lightweight slide show software and let me know what you think!

    Ansible: Passing arrays to BASH scripts

    January 7, 2019 7:56 am Published by

    When using Ansible, it may become handy sooner or later to invoke a BASH script in one of you playbooks. Invoking a BASH script in Ansible can be done using a simple shell task:

    1
    2
    3
    4
    5
    6
    ---
    - hosts: 127.0.0.1
      connection: local
      tasks:
          - name: ensure stuff is done
            shell: ./do_stuff.sh

    This task will execute the bash script do_stuff.sh. Sometimes, it is also necessary to configure the behaviour of the BASH script you are executing. The simplest way to do so is passing environment variables to the bash script as done in the following example.

    1
    2
    3
    4
    5
    6
    7
    8
    ---
    - hosts: 127.0.0.1
      connection: local
      tasks:
          - name: ensure custom stuff is done
            shell: ./do_stuff.sh
            environment:
                STUFF: some_stuff
    

    In the BASH script we can now work with the environment variable as usual:

    1
    2
    3
    #!/bin/bash
    
    echo $STUFF > /tmp/stuff.txt
    

    If we now want to pass multiple values in environment variable as array to the BASH script, we have two different options. Either we pass the array as string, or we parse the array output of Ansible in the bash script.

    Option 1: Pass array as string

    The first option is to pass the multiple values as string instead of a YAML array. In the following example, we separate them by spaces, which results in a single environment variable ARRAY being set when executing the BASH script.

    1
    2
    3
    4
    5
    6
    7
    8
    ---
    - hosts: 127.0.0.1
      connection: local
      tasks:
          - name: pass array as string
            shell: ./do_stuff_string.sh
            environment:
                ARRAY: one two three
    

    To handle those values as array in our BASH script, we simply surround the value of the environment variable with parentheses. Afterwards the ARRAY variable is a BASH array containing the values onetwo, and three.

    1
    2
    3
    4
    5
    6
    7
    #!/bin/bash
    
    ARRAY=($ARRAY)
    :> /tmp/stuff_string.txt
    for a in "${ARRAY[@]}"; do
        echo $a >> /tmp/stuff_string.txt
    done

    Option 2: Parse array

    The second option is much more readable in the Ansible playbook. We define the values as array in YAML:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    ---
    - hosts: 127.0.0.1
      connection: local
      tasks:
          - name: pass array as array
            shell: ./do_stuff_array.sh
            environment:
                ARRAY:
                - one
                - two
                - three
    

    Unfortunately, this environment variable is still set as string, now containing a python string representation of the array:

    1
    [u'one', u'two', u'three']
    

    To parse the array in the BASH script, the simplest way is to use python again, which is able to handle this value out of the box.

    1
    2
    3
    4
    5
    6
    7
    #!/bin/bash
    
    ARRAY=($(python <<< "print(' '.join($ARRAY))"))
    :> /tmp/stuff_array.txt
    for a in "${ARRAY[@]}"; do
        echo $a >> /tmp/stuff_array.txt
    done
    

    With the python one-liner in line 3, ARRAY again is a BASH array with the values onetwo, and three and can be processed further.

    Please note that, to be able to get array elements with spaces passed by ansible, we need to change the array seperator as the following version of the script shows. Otherwise, bash would recognize every single word as separate element of the array:

    1
    2
    3
    4
    5
    6
    7
    #!/bin/bash
    IFS=
    

    We have seen 2 different options to pass an array from Ansible to a BASH script. Option 1, passing it as a simple string variable has some implications, for example an array containing values with spaces might result in a less readable YAML line. Option 2, defining the array as YAML array and parsing it in your BASH script with python makes the definition better readable and more solid, but adds a dependency to python for your BASH script.

    If simple one-word arguments need to be set for your script, option 1 might still be a good option as it is easier to handle in the BASH script and doesn’t include a dependency to python.
    Nevertheless, for more complex playbooks or values in the array, I recommend option 2 as the cleaner solution.

    n’
    ARRAY=( $(python <<< “print(‘n’.join($ARRAY))”) )
    echo “${ARRAY[@]}”
    for a in “${ARRAY[@]}”; do
    echo “$a”
    done
    We have seen 2 different options to pass an array from Ansible to a BASH script. Option 1, passing it as a simple string variable has some implications, for example an array containing values with spaces might result in a less readable YAML line. Option 2, defining the array as YAML array and parsing it in your BASH script with python makes the definition better readable and more solid, but adds a dependency to python for your BASH script.

    If simple one-word arguments need to be set for your script, option 1 might still be a good option as it is easier to handle in the BASH script and doesn’t include a dependency to python.
    Nevertheless, for more complex playbooks or values in the array, I recommend option 2 as the cleaner solution.

    Use Ansible to clone & update private git repositories via ssh

    July 7, 2018 7:21 am Published by

    One of the first things I wanted to do when I started using Ansible was to clone a git repository on a remote machine as I keep configuration, scripts, and source code in github or gitlab repositories. Things that are not meant for the public, I store in private repositories that I want to clone via ssh. Cloning and updating them I now want to automate with Ansible.

    There are different ways to go for this task:

    • Checkout the repo locally and copy it to the server via a Ansible synchronize task
    • Generate an ssh key on the server and allow cloning the repo with that key manually
    • Copy a local ssh key to the server and allow cloning the repo with that key
    • use ssh-agent to load the local key and forward the agent to the server
    While it might be tempting to just copy an ssh key via Ansible to the remote server, I find this quite risky,  as it means you copy a secret to a persistent storage on a remote server. Also, if you version your Ansible playbooks in a git repository as well to be able to execute the playbook from somewhere else, the private key has to be versioned along with it.

    Using ssh-agent, you can easily load your ssh key prior to provisioning the git repo on the remote server without copying it over, and without allowing access to your repo for a different key than the one you have granted access for development.
    Let’s go through this via a simple example. Let’s say you want to run the following playbook, which includes ensuring the git repository github.com/ntlx/my-private-repo is up-to-date.

    1
    2
    3
    4
    5
    6
    7
    ---
    - hosts: webserver
      tasks:
          - name: Ensure repo is up-to-date
            git:
                repo: git@github.com/ntlx/my-private-repo.git
                dest: repos/my-private-repo
    
    I assume you added your public ssh key to your github.com repository so you are able to clone and work on the repository locally. To clone the repository on the remote machine, you need to load your ssh-key to ssh-agent with the following command.

    ssh-add ~/.ssh/id_rsa
    

    Now we need to enable the forwarding of the ssh agent to the remote machine so we can access the loaded key remotely. There are different ways to do so, but I find it most useful to do it in your ansible.cfg like this:

    1
    2
    [ssh_connection]
    ssh_args=-o ForwardAgent=yes
    

    That way, you allow the forwarding for all your Ansible-managed hosts at once.

    Now you can go on executing your playbook and should be able to clone the repository on the remote host.

    To make it even easier, we can add a task to load the ssh-key before executing the other tasks in the playbook. For this, add the local host to your Ansible inventory:

    1
    2
    [local]
    local_machine ansible_connection=local ansible_host=localhost
    

    Now we can add a small shell task to load the ssh-key:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    ---
    - hosts: local
    - name: load ssh key
      shell: |
          ssh-add ~/.ssh/id_rsa
    
    - hosts: webserver
      tasks:
          - name: Ensure repo is up-to-date
            git:
                repo: git@github.com/ntlx/my-private-repo.git
                dest: repos/my-private-repo
    

    When you now execute the playbook, you shouldn’t need to load the ssh-key before.