From 32a3ce3f5a86e3d0b79b7c8b1c0cd7fae4076150 Mon Sep 17 00:00:00 2001
From: vass <albin.vass@gmail.com>
Date: Sat, 22 Feb 2020 15:03:38 +0100
Subject: [PATCH] Adds roles to install and run hashicorp packer

Change-Id: I82274ef59eda00a735579082bbd37b64c0599c7d
---
 doc/source/jobs.rst                           |   1 +
 doc/source/packer-jobs.rst                    |   4 +
 doc/source/packer-roles.rst                   |   5 +
 doc/source/roles.rst                          |   1 +
 playbooks/packer/pre.yaml                     |   9 ++
 playbooks/packer/run.yaml                     |   3 +
 roles/ensure-packer/README.rst                |  24 +++
 roles/ensure-packer/defaults/main.yaml        |   7 +
 roles/ensure-packer/tasks/install-packer.yaml |  59 +++++++
 roles/ensure-packer/tasks/main.yaml           |  11 ++
 roles/ensure-packer/vars/main.yaml            |   7 +
 roles/packer/README.rst                       |  32 ++++
 roles/packer/defaults/main.yaml               |   6 +
 roles/packer/tasks/main.yaml                  |  36 +++++
 test-playbooks/packer/packer.json             |  11 ++
 test-playbooks/packer/post.yaml               |   9 ++
 zuul-tests.d/packer-jobs.yaml                 | 144 ++++++++++++++++++
 zuul.d/packer-jobs.yaml                       |  57 +++++++
 18 files changed, 426 insertions(+)
 create mode 100644 doc/source/packer-jobs.rst
 create mode 100644 doc/source/packer-roles.rst
 create mode 100644 playbooks/packer/pre.yaml
 create mode 100644 playbooks/packer/run.yaml
 create mode 100644 roles/ensure-packer/README.rst
 create mode 100644 roles/ensure-packer/defaults/main.yaml
 create mode 100644 roles/ensure-packer/tasks/install-packer.yaml
 create mode 100644 roles/ensure-packer/tasks/main.yaml
 create mode 100644 roles/ensure-packer/vars/main.yaml
 create mode 100644 roles/packer/README.rst
 create mode 100644 roles/packer/defaults/main.yaml
 create mode 100644 roles/packer/tasks/main.yaml
 create mode 100644 test-playbooks/packer/packer.json
 create mode 100644 test-playbooks/packer/post.yaml
 create mode 100644 zuul-tests.d/packer-jobs.yaml
 create mode 100644 zuul.d/packer-jobs.yaml

diff --git a/doc/source/jobs.rst b/doc/source/jobs.rst
index 2c5c56742..6ac0cfea6 100644
--- a/doc/source/jobs.rst
+++ b/doc/source/jobs.rst
@@ -10,4 +10,5 @@ Jobs
    docker-jobs
    go-jobs
    helm-jobs
+   packer-jobs
    system-jobs
diff --git a/doc/source/packer-jobs.rst b/doc/source/packer-jobs.rst
new file mode 100644
index 000000000..24dfc788a
--- /dev/null
+++ b/doc/source/packer-jobs.rst
@@ -0,0 +1,4 @@
+Packer Jobs
+===========
+
+.. zuul:autojob:: packer
diff --git a/doc/source/packer-roles.rst b/doc/source/packer-roles.rst
new file mode 100644
index 000000000..57068faef
--- /dev/null
+++ b/doc/source/packer-roles.rst
@@ -0,0 +1,5 @@
+Packer Roles
+============
+
+.. zuul:autorole:: ensure-packer
+.. zuul:autorole:: packer
diff --git a/doc/source/roles.rst b/doc/source/roles.rst
index 4139b7749..ee316b9fd 100644
--- a/doc/source/roles.rst
+++ b/doc/source/roles.rst
@@ -20,6 +20,7 @@ Roles
    js-roles
    kubernetes-roles
    launchpad-roles
+   packer-roles
    puppet-roles
    python-roles
    system-roles
diff --git a/playbooks/packer/pre.yaml b/playbooks/packer/pre.yaml
new file mode 100644
index 000000000..6872a4d9b
--- /dev/null
+++ b/playbooks/packer/pre.yaml
@@ -0,0 +1,9 @@
+- hosts: all
+  pre_tasks:
+    - name: Install unzip
+      package:
+        name: unzip
+      become: yes
+  roles:
+    - revoke-sudo
+    - ensure-packer
diff --git a/playbooks/packer/run.yaml b/playbooks/packer/run.yaml
new file mode 100644
index 000000000..35099f199
--- /dev/null
+++ b/playbooks/packer/run.yaml
@@ -0,0 +1,3 @@
+- hosts: all
+  roles:
+    - packer
diff --git a/roles/ensure-packer/README.rst b/roles/ensure-packer/README.rst
new file mode 100644
index 000000000..e65c46b70
--- /dev/null
+++ b/roles/ensure-packer/README.rst
@@ -0,0 +1,24 @@
+Install packer
+
+**Role Variables**
+
+.. zuul:rolevar:: packer_install_dir
+   :default: {{ ansible_user_dir }}/.local/bin/
+
+   Directory to install packer in.
+
+.. zuul:rolevar:: packer_version
+   :default: 1.5.5
+
+   Version of packer to install.
+   Zuul will skip the installation if this matches an already installed version of packer.
+
+.. zuul:rolevar:: packer_os
+   :default: {{ ansible_system | lower }}
+
+   OS target of package to install.
+
+.. zuul:rolevar:: packer_arch
+   :default: amd64 / 386
+
+   Architecture target of package to install.
diff --git a/roles/ensure-packer/defaults/main.yaml b/roles/ensure-packer/defaults/main.yaml
new file mode 100644
index 000000000..4c4787235
--- /dev/null
+++ b/roles/ensure-packer/defaults/main.yaml
@@ -0,0 +1,7 @@
+hashicorp_releases_fqdn: https://releases.hashicorp.com
+packer_version: 1.5.5
+packer_install_dir: "{{ ansible_user_dir  }}/.local/bin/"
+packer_os: "{{ ansible_system | lower }}"
+packer_arch: "{{ packer_arch_translation[ansible_architecture] }}"
+packer_package: "packer_{{ packer_version }}_{{ packer_os }}_{{ packer_arch }}"
+packer_executable: "{{ ansible_user_dir }}/.local/bin/packer"
diff --git a/roles/ensure-packer/tasks/install-packer.yaml b/roles/ensure-packer/tasks/install-packer.yaml
new file mode 100644
index 000000000..1553939a9
--- /dev/null
+++ b/roles/ensure-packer/tasks/install-packer.yaml
@@ -0,0 +1,59 @@
+- name: Get packer checksums
+  uri:
+    url: "{{ hashicorp_releases_fqdn }}/\
+          packer/{{ packer_version }}/packer_{{ packer_version }}_SHA256SUMS"
+    return_content: true
+  register: packer_version_checksums
+
+- name: Set packer checksum
+  set_fact:
+    packer_checksum: "{{\
+        packer_version_checksums.content |\
+        regex_search( '[a-z0-9]+  ' + packer_package) |\
+        regex_replace( '(?P<checksum>[a-z0-9]+)  ' + packer_package, '\\g<checksum>')
+      }}"
+
+- name: Create temp directory
+  tempfile:
+    state: directory
+  register: packer_install_tempdir
+
+- name: Download packer archive
+  get_url:
+    url: "{{ hashicorp_releases_fqdn }}/\
+      packer/{{ packer_version }}/{{ packer_package }}.zip"
+    dest: "{{ packer_install_tempdir.path }}/{{ packer_package }}.zip"
+    checksum: "sha256:{{ packer_checksum }}"
+
+- name: Create packer package directory
+  file:
+    path: "{{ packer_install_tempdir.path }}/{{ packer_package }}"
+    state: directory
+
+- name: Unarchive packer
+  unarchive:
+    src: "{{ packer_install_tempdir.path }}/{{ packer_package }}.zip"
+    dest: "{{ packer_install_tempdir.path }}/{{ packer_package }}"
+    remote_src: yes
+
+- name: Make sure installation directory exists
+  file:
+    path: "{{ packer_install_dir }}"
+    state: directory
+
+- name: Install packer
+  copy:
+    src: "{{ packer_install_tempdir.path }}/{{ packer_package }}/packer"
+    dest: "{{ packer_install_dir }}/packer"
+    mode: '0755'
+    owner: "{{ ansible_user }}"
+    remote_src: yes
+
+- name: Set packer executable fact
+  set_fact:
+    packer_executable: "{{ packer_install_dir }}/packer"
+    cacheable: true
+
+- name: Output packer version
+  command: "{{ packer_executable }} version"
+
diff --git a/roles/ensure-packer/tasks/main.yaml b/roles/ensure-packer/tasks/main.yaml
new file mode 100644
index 000000000..f7945f1c2
--- /dev/null
+++ b/roles/ensure-packer/tasks/main.yaml
@@ -0,0 +1,11 @@
+- name: Check installed packer version
+  command: "{{ packer_executable }} version"
+  register: packer_installed_version
+  failed_when: false
+
+- name: Install packer
+  include_tasks: install-packer.yaml
+  when:
+    - packer_installed_version.rc != 0 or
+      "packer_version != (packer_installed_version.msg | \
+       regex_replace(packer_version_pattern, '\\g<version>'))"
diff --git a/roles/ensure-packer/vars/main.yaml b/roles/ensure-packer/vars/main.yaml
new file mode 100644
index 000000000..225b63183
--- /dev/null
+++ b/roles/ensure-packer/vars/main.yaml
@@ -0,0 +1,7 @@
+packer_arch_translation:
+  amd64: amd64
+  x86_64: amd64
+  i386: 386
+
+packer_version_pattern: ^Packer v(?P<version>\d+\.\d+\.\d+).*$
+
diff --git a/roles/packer/README.rst b/roles/packer/README.rst
new file mode 100644
index 000000000..91159fdbb
--- /dev/null
+++ b/roles/packer/README.rst
@@ -0,0 +1,32 @@
+Run packer command. Assumes the appropriate version of packer has been installed.
+
+**Role Variables**
+
+.. zuul:rolevar:: packer_executable
+   :default: {{ ansible_user_dir }}/.local/bin/packer
+
+   Path to packer executable to use.
+
+.. zuul:rolevar:: packer_command
+   :default: build
+
+   Packer command to run.
+   Examples are "build", "validate"
+
+.. zuul:rolevar:: packer_template
+   :default: packer.json
+
+   Packer template file to use when executing packer.
+
+.. zuul:rolevar:: packer_extra_args
+
+   String of extra command line options to pass to packer.
+
+.. zuul:rolevar:: packer_environemnt
+
+   Environment variables to set in packer command.
+
+.. zuul:rolevar:: zuul_workdir
+   :default: {{ zuul.project.src_dir }}
+
+   Directory to run packer in.
diff --git a/roles/packer/defaults/main.yaml b/roles/packer/defaults/main.yaml
new file mode 100644
index 000000000..2312e7a00
--- /dev/null
+++ b/roles/packer/defaults/main.yaml
@@ -0,0 +1,6 @@
+packer_command: build
+packer_executable: "{{ ansible_user_dir }}/.local/bin/packer"
+packer_workdir: "{{ zuul.project.src_dir }}"
+packer_template: "packer.json"
+packer_extra_args: ""
+packer_environment: {}
diff --git a/roles/packer/tasks/main.yaml b/roles/packer/tasks/main.yaml
new file mode 100644
index 000000000..242369208
--- /dev/null
+++ b/roles/packer/tasks/main.yaml
@@ -0,0 +1,36 @@
+- name: Require packer executable
+  fail:
+    msg: packer_executable not defined
+  when: packer_executable is not defined
+
+- name: Create packer variable tempfile
+  tempfile:
+  register: packer_variable_tempfile
+  when: packer_variables is defined
+
+- name: Create packer variables file
+  copy:
+    content: |
+      {{ packer_variables | to_json }}
+    dest: "{{ packer_variable_tempfile.path }}"
+  when: packer_variables is defined
+  no_log: true # We don't want to log this since credentials could be passed this way
+
+- block:
+    - name: Run packer
+      command: >-
+        {{ packer_executable }}
+        {{ packer_command }}
+        {% if packer_variables is defined %}
+        -var-file={{ packer_variable_tempfile.path }}
+        {% endif %}
+        {{ packer_extra_args }}
+        {{ packer_template }}
+      environment: "{{ packer_environment }}"
+      args:
+        chdir: "{{ packer_workdir }}"
+  always:
+    - name: Delete packer variables file
+      file:
+        state: absent
+        path: "{{ packer_variable_tempfile.path }}"
diff --git a/test-playbooks/packer/packer.json b/test-playbooks/packer/packer.json
new file mode 100644
index 000000000..1b5463678
--- /dev/null
+++ b/test-playbooks/packer/packer.json
@@ -0,0 +1,11 @@
+{
+  "variables": {
+      "packer_test_file": "{{env `PACKER_TEST_FILE`}}"
+  },
+  "builders": [{
+      "type": "file",
+      "content": "{{user `packer_test_content`}}",
+      "target": "{{user `packer_test_file` }}"
+  }]
+}
+
diff --git a/test-playbooks/packer/post.yaml b/test-playbooks/packer/post.yaml
new file mode 100644
index 000000000..8d965f52a
--- /dev/null
+++ b/test-playbooks/packer/post.yaml
@@ -0,0 +1,9 @@
+- hosts: all
+  tasks:
+    - name: Ensure test_variable was written to file
+      lineinfile:
+        line: "{{ packer_variables.packer_test_content | replace('\n', '') }}"
+        dest: "{{ packer_environment.PACKER_TEST_FILE }}"
+      check_mode: yes
+      register: test_variable_presence
+      failed_when: test_variable_presence.changed
diff --git a/zuul-tests.d/packer-jobs.yaml b/zuul-tests.d/packer-jobs.yaml
new file mode 100644
index 000000000..038853680
--- /dev/null
+++ b/zuul-tests.d/packer-jobs.yaml
@@ -0,0 +1,144 @@
+- job:
+    name: test-packer
+    parent: packer
+    description: Test packer job
+    post-run: test-playbooks/packer/post.yaml
+    tags: all-platforms
+    files:
+      - roles/ensure-packer/.*
+      - roles/packer/.*
+      - test-playbooks/packer/.*
+      - zuul.d/packer-jobs.yaml
+    vars:
+      packer_install_dir: '{{ ansible_user_dir }}/packer'
+      packer_template: test-playbooks/packer/packer.json
+      packer_environment:
+        PACKER_TEST_FILE: testfile
+      packer_variables:
+        packer_test_content: "abc\n"
+
+- job:
+    name: test-packer-centos-7
+    description: Test packer job on centos-7
+    parent: test-packer
+    tags: auto-generated
+    nodeset:
+      nodes:
+        - name: centos-7
+          label: centos-7
+
+- job:
+    name: test-packer-centos-8
+    description: Test packer job on centos-8
+    parent: test-packer
+    tags: auto-generated
+    nodeset:
+      nodes:
+        - name: centos-8
+          label: centos-8
+
+- job:
+    name: test-packer-debian-stretch
+    description: Test packer job on debian-stretch
+    parent: test-packer
+    tags: auto-generated
+    nodeset:
+      nodes:
+        - name: debian-stretch
+          label: debian-stretch
+
+- job:
+    name: test-packer-fedora-30
+    description: Test packer job on fedora-30
+    parent: test-packer
+    tags: auto-generated
+    nodeset:
+      nodes:
+        - name: fedora-30
+          label: fedora-30
+
+- job:
+    name: test-packer-gentoo-17-0-systemd
+    description: Test packer job on gentoo-17-0-systemd
+    parent: test-packer
+    tags: auto-generated
+    nodeset:
+      nodes:
+        - name: gentoo-17-0-systemd
+          label: gentoo-17-0-systemd
+
+- job:
+    name: test-packer-opensuse-15
+    description: Test packer job on opensuse-15
+    parent: test-packer
+    tags: auto-generated
+    nodeset:
+      nodes:
+        - name: opensuse-15
+          label: opensuse-15
+
+- job:
+    name: test-packer-opensuse-tumbleweed-nv
+    voting: false
+    description: Test packer job on opensuse-tumbleweed
+    parent: test-packer
+    tags: auto-generated
+    nodeset:
+      nodes:
+        - name: opensuse-tumbleweed
+          label: opensuse-tumbleweed
+
+- job:
+    name: test-packer-ubuntu-bionic
+    description: Test packer job on ubuntu-bionic
+    parent: test-packer
+    tags: auto-generated
+    nodeset:
+      nodes:
+        - name: ubuntu-bionic
+          label: ubuntu-bionic
+
+- job:
+    name: test-packer-ubuntu-bionic-plain-nv
+    voting: false
+    description: Test packer job on ubuntu-bionic-plain
+    parent: test-packer
+    tags: auto-generated
+    nodeset:
+      nodes:
+        - name: ubuntu-bionic-plain
+          label: ubuntu-bionic-plain
+
+- job:
+    name: test-packer-ubuntu-xenial
+    description: Test packer job on ubuntu-xenial
+    parent: test-packer
+    tags: auto-generated
+    nodeset:
+      nodes:
+        - name: ubuntu-xenial
+          label: ubuntu-xenial
+
+- project:
+    check:
+      jobs:
+        - test-packer-centos-7
+        - test-packer-centos-8
+        - test-packer-debian-stretch
+        - test-packer-fedora-30
+        - test-packer-gentoo-17-0-systemd
+        - test-packer-opensuse-15
+        - test-packer-opensuse-tumbleweed-nv
+        - test-packer-ubuntu-bionic
+        - test-packer-ubuntu-bionic-plain-nv
+        - test-packer-ubuntu-xenial
+    gate:
+      jobs:
+        - test-packer-centos-7
+        - test-packer-centos-8
+        - test-packer-debian-stretch
+        - test-packer-fedora-30
+        - test-packer-gentoo-17-0-systemd
+        - test-packer-opensuse-15
+        - test-packer-ubuntu-bionic
+        - test-packer-ubuntu-xenial
diff --git a/zuul.d/packer-jobs.yaml b/zuul.d/packer-jobs.yaml
new file mode 100644
index 000000000..d84c42d3b
--- /dev/null
+++ b/zuul.d/packer-jobs.yaml
@@ -0,0 +1,57 @@
+- job:
+    name: packer
+    description: |
+      Base job for packaer operations
+
+      Responds to these variables:
+
+      .. zuul:jobvar:: packer_command
+         :default: build
+
+         Command to pass to packer.
+
+      .. zuul:jobvar:: packer_variables
+
+         Dictionary with variables to pass to packer.
+
+      .. zuul:jobvar:: packer_extra_args
+         :default: ""
+
+         String containing extra arguments to append to the packer command line.
+
+      .. zuul:jobvar:: packer_environemnt
+
+         Environment variables to set in packer command.
+
+      .. zuul:jobvar:: packer_template
+         :default: packer.json
+
+         Packer template json file to use.
+
+      .. zuul:jobvar:: packer_install_dir
+         :default: {{ ansible_user_dir }}/packer/
+
+         Path to install packer in.
+
+      .. zuul:jobvar:: packer_version
+         :default: 1.5.5
+
+         The version of packer to use.
+
+      .. zuul:jobvar:: packer_os
+         :default: {{ ansible_system | lower }}
+
+          OS to use when choosing packer version.
+
+      .. zuul:jobvar:: packer_arch
+         :default: amd64 / 386
+
+          Architecture to use when choosing packer version
+
+      .. zuul:jobvar:: zuul_work_dir
+         :default: {{ zuul.project.src_dir }}
+
+         Path to operate in.
+
+    pre-run: playbooks/packer/pre.yaml
+    run: playbooks/packer/run.yaml