Sometimes you need to install software or scripts from a GitHub repository. Of course Ansible is perfectly capable of doing this in several ways. One easy but efficient way is to use a simple get_url task for this purpose.
During a new server-setup (the first time the playbook runs) this works fine. The software is not yet installed (checked upon using stat) and therefore the latest version is automatically retrieved from the remote GitHub repository.
But what about future/newer releases? Should I re-download and overwrite the currently installed software on every playbook run? That's just using resources (compute, bandwidth) for nothing - especially if we're talking of large software or scripts. And the playbook will run longer without any benefit.
My goal in this situation is to compare the currently installed version to the latest available version on the GitHub repo. Only if the versions don't match, the software should be re-downloaded and re-installed. And this should all be handled by Ansible - of course.
GitHub offers a free REST API which allows to list repository tags on a repository (an alternative is to list releases). Let's use the public repository of the monitoring plugin check_rancher2 for this purpose:
ck@mint ~ $ curl https://api.github.com/repos/Napsty/check_rancher2/tags
[
{
"name": "1.12.0",
"zipball_url": "https://api.github.com/repos/Napsty/check_rancher2/zipball/refs/tags/1.12.0",
"tarball_url": "https://api.github.com/repos/Napsty/check_rancher2/tarball/refs/tags/1.12.0",
"commit": {
"sha": "3774696ae67e9160f4f5f5f8b429c26bda79bbec",
"url": "https://api.github.com/repos/Napsty/check_rancher2/commits/3774696ae67e9160f4f5f5f8b429c26bda79bbec"
},
"node_id": "MDM6UmVmMTM5MTU4MjgxOnJlZnMvdGFncy8xLjEyLjA="
},
{
"name": "1.11.0",
"zipball_url": "https://api.github.com/repos/Napsty/check_rancher2/zipball/refs/tags/1.11.0",
"tarball_url": "https://api.github.com/repos/Napsty/check_rancher2/tarball/refs/tags/1.11.0",
"commit": {
"sha": "f36626b1509fd2cd61a6c6395be130d0a9e730cf",
"url": "https://api.github.com/repos/Napsty/check_rancher2/commits/f36626b1509fd2cd61a6c6395be130d0a9e730cf"
},
"node_id": "MDM6UmVmMTM5MTU4MjgxOnJlZnMvdGFncy8xLjExLjA="
},
[...]
The response is a JSON document which contains all the tags of this repository. The relevant key is "name". To only list the tags, we can use the jq command, filtering only for the "name" key:
ck@mint ~ $ curl https://api.github.com/repos/Napsty/check_rancher2/tags | jq -r ".[].name"
1.12.0
1.11.0
1.10.0
1.9.0
1.8.0
1.7.1
1.7.0
1.6.1
1.6.0
1.5.0
1.4.0
1.3.0
1.2.3
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
1.0.0
And if we only want the latest tag, we specifically select the first array index [0] of the output:
ck@mint ~ $ curl https://api.github.com/repos/Napsty/check_rancher2/tags | jq -r ".[0].name"
1.12.0
Nice. This works. But how do we translate this into an Ansible playbook?
So far we know: We need to use a HTTP request to the GitHub API and parse the JSON response for the first "name" key in the array. But which module should we use? This answer on StackOverflow gives a very good hint to use the uri module, although the question has nothing to do with tags.
- name: CHECK_RANCHER2 - Check for newest available check_rancher2 version
uri:
url: https://api.github.com/repos/Napsty/check_rancher2/tags
method: GET
return_content: yes
status_code: 200
body_format: json
register: result
By defining body_format: json will save/convert the response as JSON and is therefore re-usable inside the Ansible run.
The full output of this task is saved in the "result" variable. If you're an old fox like me, you know Ansible doesn't just save the HTTP response in this variable; there's plenty of meta data around. Best thing is to use the debug module to show what exactly Ansible stores in the "result" variable:
- debug:
msg: "{{ result }}"
ignore_errors: True
After running these playbook tasks, the output shows there's quite some data saved into "result":
TASK [debug] ***************************************************************************************
ok: [target] => {
"msg": {
"accept_ranges": "bytes",
"access_control_allow_origin": "*",
"access_control_expose_headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset",
"cache_control": "public, max-age=60, s-maxage=60",
"changed": false,
"connection": "close",
"content": "[{\"name\":\"v2.9.0\",\"zipball_url\":\"https://api.github.com/repos/wp-cli/wp-cli/zipball/refs/tags/v2.9.0\",\"tarball_url\":\"https://api.github.com/repos/wp-cli/wp-cli/tarball/refs/tags/v2.9.0\",\"commit\":{\"sha\":\"8a3befba2d947fbf5cc6d1941edf2dd99da4d4b7\",\"url\":\"https://api.github.com/repos/wp-cli/wp-cli/commits/8a3befba2d947fbf5cc6d1941edf2dd99da4d4b7\"},\"node_id\":\"MDM6UmVmMjM2MDc1NTpyZWZzL3RhZ3MvdjIuOS4w\"},{\"name\":\"v2.8.1\",\"zipball_url\":\"https://api.github.com/repos/wp-cli/wp-cli/zipball/refs/tags/v2.8.1\", [...]
[...]
"failed": false,
"json": [
{
"commit": {
"sha": "3774696ae67e9160f4f5f5f8b429c26bda79bbec",
"url": "https://api.github.com/repos/Napsty/check_rancher2/commits/3774696ae67e9160f4f5f5f8b429c26bda79bbec"
},
"name": "1.12.0",
"node_id": "MDM6UmVmMTM5MTU4MjgxOnJlZnMvdGFncy8xLjEyLjA=",
"tarball_url": "https://api.github.com/repos/Napsty/check_rancher2/tarball/refs/tags/1.12.0",
"zipball_url": "https://api.github.com/repos/Napsty/check_rancher2/zipball/refs/tags/1.12.0"
},
{
"commit": {
"sha": "f36626b1509fd2cd61a6c6395be130d0a9e730cf",
"url": "https://api.github.com/repos/Napsty/check_rancher2/commits/f36626b1509fd2cd61a6c6395be130d0a9e730cf"
},
"name": "1.11.0",
"node_id": "MDM6UmVmMTM5MTU4MjgxOnJlZnMvdGFncy8xLjExLjA=",
"tarball_url": "https://api.github.com/repos/Napsty/check_rancher2/tarball/refs/tags/1.11.0",
"zipball_url": "https://api.github.com/repos/Napsty/check_rancher2/zipball/refs/tags/1.11.0"
},
[...]
The JSON response is first saved into a "content" key and then translated by Ansible into a nested JSON structure (saved into the "json" key). This means we should be able to access the latest "name" key we need using "result.json[0].name", similar to what we used with jq before.
- debug:
msg: "{{ result.json[0].name }}"
ignore_errors: True
And the output of the playbook run shows the newest version/tag from the GitHub repo:
TASK [debug] ***************************************************************************************
ok: [target] => {
"msg": "1.12.0"
}
This variable (result.json[0].name) can now be used inside the playbook to do a local version comparison.
Here you go.
# Check GitHub API for newest tag of check_rancher2
- name: CHECK_RANCHER2 - Check for newest available check_rancher2 version
uri:
url: https://api.github.com/repos/Napsty/check_rancher2/tags
method: GET
return_content: yes
status_code: 200
body_format: json
register: result
# Debug output for the full result variable
- debug:
msg: "{{ result }}"
ignore_errors: True
# Debug output to see the latest tag. This should show an output like: 1.12.0
- debug:
msg: "{{ result.json[0].name }}"
ignore_errors: True
No comments yet.
AWS Android Ansible Apache Apple Atlassian BSD Backup Bash Bluecoat CMS Chef Cloud Coding Consul Containers CouchDB DB DNS Database Databases Docker ELK Elasticsearch Filebeat FreeBSD Galera Git GlusterFS Grafana Graphics HAProxy HTML Hacks Hardware Icinga Influx Internet Java KVM Kibana Kodi Kubernetes LVM LXC Linux Logstash Mac Macintosh Mail MariaDB Minio MongoDB Monitoring Multimedia MySQL NFS Nagios Network Nginx OSSEC OTRS Observability Office OpenSearch PGSQL PHP Perl Personal PostgreSQL Postgres PowerDNS Proxmox Proxy Python Rancher Rant Redis Roundcube SSL Samba Seafile Security Shell SmartOS Solaris Surveillance Systemd TLS Tomcat Ubuntu Unix VMWare VMware Varnish Virtualization Windows Wireless Wordpress Wyse ZFS Zoneminder