Initialize config repository
diff --git a/nodepool/virt_images/README.md b/nodepool/virt_images/README.md
new file mode 100644
index 0000000..3fed60e
--- /dev/null
+++ b/nodepool/virt_images/README.md
@@ -0,0 +1,12 @@
+# Virt-customize based nodepool image
+
+This directory contains nodepool image built using virt-customize-dib elements.
+
+To use a playbook, add this to a nodepool yaml file:
+
+```yaml
+diskimages:
+  - name: cloud-fedora-rawhide
+    python-path: /usr/bin/python3
+    dib-cmd: /usr/bin/dib-virt-customize /etc/nodepool/virt_images/cloud-fedora-rawhide.yaml
+```
diff --git a/nodepool/virt_images/cloud-fedora-rawhide.yaml b/nodepool/virt_images/cloud-fedora-rawhide.yaml
new file mode 100644
index 0000000..07d2ecc
--- /dev/null
+++ b/nodepool/virt_images/cloud-fedora-rawhide.yaml
@@ -0,0 +1,38 @@
+---
+- name: Build a fedora cloud image suitable for Zuul
+  hosts: localhost
+  vars:
+    image: Fedora-Cloud-Base-Rawhide.x86_64.qcow2
+    extra_packages:
+      # Extra system tools
+      - pigz
+      - bridge-utils
+      - wget
+      - unzip
+      # Basic CI tools
+      - make
+      - gcc
+      - patch
+  tasks:
+    - block:
+      - import_role:
+          name: discover-rawhide
+      - import_role:
+          name: base-appliance
+      - import_role:
+          name: base
+      - import_role:
+          name: sshd-config
+      - import_role:
+          name: network-config
+      - import_role:
+          name: zuul-user
+      - import_role:
+          name: base-install-packages
+      - import_role:
+          name: base-customize
+      - import_role:
+          name: base-finalize
+      always:
+      - import_role:
+          name: base-cleanup
diff --git a/nodepool/virt_images/roles/base-appliance/tasks/main.yaml b/nodepool/virt_images/roles/base-appliance/tasks/main.yaml
new file mode 100644
index 0000000..e5e83fd
--- /dev/null
+++ b/nodepool/virt_images/roles/base-appliance/tasks/main.yaml
@@ -0,0 +1,12 @@
+- name: Download appliance
+  unarchive:
+    src: http://download.libguestfs.org/binaries/appliance/appliance-1.46.0.tar.xz
+    remote_src: yes
+    dest: /tmp
+  args:
+    creates: /tmp/appliance
+
+- set_fact:
+    virt_customize_env:
+      LIBGUESTFS_PATH: '/tmp/appliance'
+      LIBGUESTFS_BACKEND: 'direct'
diff --git a/nodepool/virt_images/roles/base-cleanup/defaults/main.yaml b/nodepool/virt_images/roles/base-cleanup/defaults/main.yaml
new file mode 100644
index 0000000..dd9240b
--- /dev/null
+++ b/nodepool/virt_images/roles/base-cleanup/defaults/main.yaml
@@ -0,0 +1,2 @@
+---
+image_tmp_dir: "/var/tmp/{{ image_output | basename }}"
diff --git a/nodepool/virt_images/roles/base-cleanup/tasks/main.yaml b/nodepool/virt_images/roles/base-cleanup/tasks/main.yaml
new file mode 100644
index 0000000..29c01ea
--- /dev/null
+++ b/nodepool/virt_images/roles/base-cleanup/tasks/main.yaml
@@ -0,0 +1,5 @@
+---
+- name: Remove tmp directory
+  file:
+    path: "{{ image_tmp_dir }}"
+    state: absent
diff --git a/nodepool/virt_images/roles/base-customize/defaults/main.yaml b/nodepool/virt_images/roles/base-customize/defaults/main.yaml
new file mode 100644
index 0000000..ed97d53
--- /dev/null
+++ b/nodepool/virt_images/roles/base-customize/defaults/main.yaml
@@ -0,0 +1 @@
+---
diff --git a/nodepool/virt_images/roles/base-customize/tasks/main.yaml b/nodepool/virt_images/roles/base-customize/tasks/main.yaml
new file mode 100644
index 0000000..c1d397d
--- /dev/null
+++ b/nodepool/virt_images/roles/base-customize/tasks/main.yaml
@@ -0,0 +1,7 @@
+---
+- debug:
+    msg: "Running: {{ ' '.join(virt_customize_cmd) }}"
+
+- name: Run virt-customize
+  command: "{{ ' '.join(virt_customize_cmd) }}"
+  environment: "{{ virt_customize_env|default({}) }}"
diff --git a/nodepool/virt_images/roles/base-finalize/defaults/main.yaml b/nodepool/virt_images/roles/base-finalize/defaults/main.yaml
new file mode 100644
index 0000000..ed97d53
--- /dev/null
+++ b/nodepool/virt_images/roles/base-finalize/defaults/main.yaml
@@ -0,0 +1 @@
+---
diff --git a/nodepool/virt_images/roles/base-finalize/tasks/main.yaml b/nodepool/virt_images/roles/base-finalize/tasks/main.yaml
new file mode 100644
index 0000000..9fb8968
--- /dev/null
+++ b/nodepool/virt_images/roles/base-finalize/tasks/main.yaml
@@ -0,0 +1,8 @@
+---
+- name: Create raw file
+  command: "qemu-img convert -O raw {{ image_file }} {{ image_output }}.raw"
+  when: raw_type | default(False) | bool
+
+- name: Create qcow file
+  command: "mv {{ image_file }} {{ image_output }}.qcow2"
+  when: qcow2_type | default(False) | bool
diff --git a/nodepool/virt_images/roles/base-install-packages/defaults/main.yaml b/nodepool/virt_images/roles/base-install-packages/defaults/main.yaml
new file mode 100644
index 0000000..ed97d53
--- /dev/null
+++ b/nodepool/virt_images/roles/base-install-packages/defaults/main.yaml
@@ -0,0 +1 @@
+---
diff --git a/nodepool/virt_images/roles/base-install-packages/tasks/main.yaml b/nodepool/virt_images/roles/base-install-packages/tasks/main.yaml
new file mode 100644
index 0000000..588de54
--- /dev/null
+++ b/nodepool/virt_images/roles/base-install-packages/tasks/main.yaml
@@ -0,0 +1,7 @@
+---
+- set_fact:
+    cmd:
+      - "--install '{{ extra_packages | join(',') }}'"
+
+- set_fact:
+    virt_customize_cmd: "{{ virt_customize_cmd + cmd }}"
diff --git a/nodepool/virt_images/roles/base/defaults/main.yaml b/nodepool/virt_images/roles/base/defaults/main.yaml
new file mode 100644
index 0000000..a1cdfac
--- /dev/null
+++ b/nodepool/virt_images/roles/base/defaults/main.yaml
@@ -0,0 +1,10 @@
+---
+image_cache_dir: "/var/cache/nodepool"
+image_wipe_cache: False
+memsize: 1024
+base_packages:
+  - traceroute
+  - iproute
+  - git
+  - rsync
+extra_packages: []
diff --git a/nodepool/virt_images/roles/base/tasks/main.yaml b/nodepool/virt_images/roles/base/tasks/main.yaml
new file mode 100644
index 0000000..88f8799
--- /dev/null
+++ b/nodepool/virt_images/roles/base/tasks/main.yaml
@@ -0,0 +1,70 @@
+---
+- assert:
+    that:
+      - image_url is defined
+      - image_checksum is defined
+      - image is defined
+      - image_url != ''
+      - image_checksum != ''
+      - image != ''
+
+- name: Set some runtime facts
+  set_fact:
+    image_cache_file: "{{ image_cache_dir }}/{{ image }}"
+    image_tmp_dir: "/var/tmp/{{ image_output | basename }}"
+
+- name: Make sure cache directory exist
+  file:
+    path: "{{ image_cache_dir }}"
+    state: directory
+
+- name: Delete previous image cache
+  file:
+    path: "{{ image_cache_file }}"
+    state: absent
+  when: image_wipe_cache
+
+- name: Check if image is already downloaded
+  stat:
+    path: "{{ image_cache_file }}"
+  register: _image_cache_file_stat
+
+- name: Download if checksum doesn't match
+  get_url:
+    url: "{{ image_url }}"
+    dest: "{{ image_cache_file }}"
+    checksum: "{{ image_checksum }}"
+  when: not _image_cache_file_stat.stat.exists
+
+- name: Extract the image if necessary
+  command: "xz -k -d {{ image_cache_file }}.xz"
+  args:
+    chdir: "{{ image_cache_dir }}"
+    creates: "{{ image_cache_file }}"
+
+- name: Update the cache
+  command: "virt-customize -m {{ memsize }} -a {{ image_cache_file }} --update"
+  environment: "{{ virt_customize_env|default({}) }}"
+
+- name: Create tmp directory
+  file:
+    path: "{{ image_tmp_dir }}"
+    state: directory
+    mode: '0755'
+
+- name: Set filename copy fact
+  set_fact:
+    image_file: "{{ image_tmp_dir }}/{{ image_cache_file | basename }}"
+
+- name: Copy the image
+  copy:
+    src: "{{ image_cache_file }}"
+    dest: "{{ image_file }}"
+    remote_src: true
+    mode: '0644'
+
+- set_fact:
+    virt_customize_cmd:
+      - "virt-customize -m {{ memsize }} -a {{ image_file }}"
+      - "--selinux-relabel"
+      - "--install '{{ base_packages | join(',') }}'"
diff --git a/nodepool/virt_images/roles/discover-rawhide/defaults/main.yaml b/nodepool/virt_images/roles/discover-rawhide/defaults/main.yaml
new file mode 100644
index 0000000..e87cb9c
--- /dev/null
+++ b/nodepool/virt_images/roles/discover-rawhide/defaults/main.yaml
@@ -0,0 +1 @@
+base_url: https://dl.fedoraproject.org/pub/fedora/linux/development/rawhide/Cloud/x86_64/images/
diff --git a/nodepool/virt_images/roles/discover-rawhide/tasks/main.yaml b/nodepool/virt_images/roles/discover-rawhide/tasks/main.yaml
new file mode 100644
index 0000000..51e47c8
--- /dev/null
+++ b/nodepool/virt_images/roles/discover-rawhide/tasks/main.yaml
@@ -0,0 +1,45 @@
+- tempfile:
+    state: file
+  register: tempfile
+
+- file:
+    path: "{{ tempfile.path }}"
+    state: absent
+
+- name: Fetch publication page
+  get_url:
+    url: "{{ base_url }}"
+    dest: "{{ tempfile.path }}"
+
+- name: Find rawhide qcow2 url
+  command: sed -n "/qcow2/ s/.*\(Fedora-Cloud-Base-Rawhide-.*\)<\/a>.*/\1/p" {{ tempfile.path }}
+  register: get_qcow_image_name
+
+- name: Find checksum file url
+  command: sed -n "/CHECKSUM/ s/.*\(Fedora-Cloud-Rawhide-.*\)<\/a>.*/\1/p" {{ tempfile.path }}
+  register: get_checksum_name
+
+- set_fact:
+    checksums_url: "{{ base_url }}{{ get_checksum_name.stdout }}"
+
+- file:
+    path: "{{ tempfile.path }}"
+    state: absent
+
+- name: Fetch checksum file
+  get_url:
+    url: "{{ checksums_url }}"
+    dest: "{{ tempfile.path }}"
+
+- name: Find checksum
+  command: sed -n "/SHA256 ({{ get_qcow_image_name.stdout }}) = / s/.* = \(.*\)/\1/p" {{ tempfile.path }}
+  register: get_checksum
+
+- set_fact:
+    image_url: "{{ base_url }}{{ get_qcow_image_name.stdout }}"
+    image_checksum: "sha256:{{ get_checksum.stdout }}"
+
+- debug:
+    msg: |
+      Discovered image_url: {{ image_url }}
+      Discovered image_checksum: {{ image_checksum }}
diff --git a/nodepool/virt_images/roles/network-config/defaults/main.yaml b/nodepool/virt_images/roles/network-config/defaults/main.yaml
new file mode 100644
index 0000000..dd9240b
--- /dev/null
+++ b/nodepool/virt_images/roles/network-config/defaults/main.yaml
@@ -0,0 +1,2 @@
+---
+image_tmp_dir: "/var/tmp/{{ image_output | basename }}"
diff --git a/nodepool/virt_images/roles/network-config/tasks/main.yaml b/nodepool/virt_images/roles/network-config/tasks/main.yaml
new file mode 100644
index 0000000..5d49314
--- /dev/null
+++ b/nodepool/virt_images/roles/network-config/tasks/main.yaml
@@ -0,0 +1,12 @@
+---
+- set_fact:
+    cmd:
+      - "--append-line '/etc/sysctl.conf:net.ipv6.conf.all.disable_ipv6 = 1'"
+      - "--append-line '/etc/sysctl.conf:net.ipv6.conf.default.disable_ipv6 = 1'"
+      - "--append-line '/etc/sysconfig/network:IPV6INIT=no'"
+      - "--append-line '/etc/sysconfig/network:IPV6_AUTOCONF=no'"
+      - "--append-line '/etc/sysconfig/network:IPV6_DEFROUTE=no'"
+      - "--append-line '/etc/yum.conf:ip_resolve=4'"
+
+- set_fact:
+    virt_customize_cmd: "{{ virt_customize_cmd + cmd }}"
diff --git a/nodepool/virt_images/roles/sshd-config/defaults/main.yaml b/nodepool/virt_images/roles/sshd-config/defaults/main.yaml
new file mode 100644
index 0000000..dd9240b
--- /dev/null
+++ b/nodepool/virt_images/roles/sshd-config/defaults/main.yaml
@@ -0,0 +1,2 @@
+---
+image_tmp_dir: "/var/tmp/{{ image_output | basename }}"
diff --git a/nodepool/virt_images/roles/sshd-config/files/sshd_config b/nodepool/virt_images/roles/sshd-config/files/sshd_config
new file mode 100644
index 0000000..df17bfa
--- /dev/null
+++ b/nodepool/virt_images/roles/sshd-config/files/sshd_config
@@ -0,0 +1,20 @@
+HostKey /etc/ssh/ssh_host_rsa_key
+HostKey /etc/ssh/ssh_host_ecdsa_key
+HostKey /etc/ssh/ssh_host_ed25519_key
+KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256
+Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
+MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com
+SyslogFacility AUTHPRIV
+AuthorizedKeysFile .ssh/authorized_keys
+PasswordAuthentication no
+ChallengeResponseAuthentication no
+GSSAPIAuthentication no
+GSSAPICleanupCredentials no
+UsePAM yes
+X11Forwarding no
+UseDNS no
+AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
+AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
+AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
+AcceptEnv XMODIFIERS
+Subsystem sftp  /usr/libexec/openssh/sftp-server
diff --git a/nodepool/virt_images/roles/sshd-config/tasks/main.yaml b/nodepool/virt_images/roles/sshd-config/tasks/main.yaml
new file mode 100644
index 0000000..ff62665
--- /dev/null
+++ b/nodepool/virt_images/roles/sshd-config/tasks/main.yaml
@@ -0,0 +1,13 @@
+---
+- name: Prepare sshd_config file
+  copy:
+    src: files/sshd_config
+    dest: "{{ image_tmp_dir }}/sshd_config"
+
+- set_fact:
+    cmd:
+      - "--copy-in '{{ image_tmp_dir }}/sshd_config:/etc/ssh/'"
+      - "--chmod '0600:/etc/ssh/sshd_config'"
+
+- set_fact:
+    virt_customize_cmd: "{{ virt_customize_cmd + cmd }}"
diff --git a/nodepool/virt_images/roles/zuul-user/defaults/main.yaml b/nodepool/virt_images/roles/zuul-user/defaults/main.yaml
new file mode 100644
index 0000000..dd9240b
--- /dev/null
+++ b/nodepool/virt_images/roles/zuul-user/defaults/main.yaml
@@ -0,0 +1,2 @@
+---
+image_tmp_dir: "/var/tmp/{{ image_output | basename }}"
diff --git a/nodepool/virt_images/roles/zuul-user/tasks/main.yaml b/nodepool/virt_images/roles/zuul-user/tasks/main.yaml
new file mode 100644
index 0000000..2382ba4
--- /dev/null
+++ b/nodepool/virt_images/roles/zuul-user/tasks/main.yaml
@@ -0,0 +1,27 @@
+---
+- name: Prepare the sudoers file
+  copy:
+    content: |
+      Defaults    !requiretty
+      zuul-worker ALL=(ALL) NOPASSWD:ALL
+    dest: "{{ image_tmp_dir }}/zuul-worker"
+
+- name: Prepare the authorized_keys file
+  copy:
+    src: /var/lib/nodepool/.ssh/zuul_rsa.pub
+    dest: "{{ image_tmp_dir }}/authorized_keys"
+    remote_src: true
+
+- set_fact:
+    cmd:
+      - "--run-command 'adduser -m zuul-worker'"
+      - "--mkdir '/home/zuul-worker/.ssh'"
+      - "--chmod '0700:/home/zuul-worker/.ssh'"
+      - "--copy-in '{{ image_tmp_dir }}/authorized_keys:/home/zuul-worker/.ssh/'"
+      - "--chmod '0600:/home/zuul-worker/.ssh/authorized_keys'"
+      - "--run-command 'chown -R zuul-worker:zuul-worker /home/zuul-worker/.ssh/'"
+      - "--copy-in '{{ image_tmp_dir }}/zuul-worker:/etc/sudoers.d/'"
+      - "--chmod '0440:/etc/sudoers.d/zuul-worker'"
+
+- set_fact:
+    virt_customize_cmd: "{{ virt_customize_cmd + cmd }}"