]> git.arvados.org - arvados.git/blob - tools/ansible/build-debian-nspawn-vm.yml
23044: Add integration test for ContainerWebServices.
[arvados.git] / tools / ansible / build-debian-nspawn-vm.yml
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: Apache-2.0
4 #
5 # build-debian-nspawn-vm.yml - Ansible playbook to build a new Debian/Ubuntu
6 # systemd-nspawn VM from scratch
7 #
8 # Run this playbook on a host with systemd-nspawn installed, and it will create
9 # a minimal Debian/Ubuntu system with networking, SSH, and a user account with
10 # full sudo access. This is enough that you can start the VM and run more
11 # Ansible playbooks on it.
12 #
13 # The VM expects to work with a private network. It expects the host to provide
14 # a DHCP lease (e.g., the host is running systemd-networkd) and forward IP.
15 #
16 # You MUST set the following variables to run this playbook:
17 #
18 # * `image_name`: The name of the image to create. This must be a valid DNS
19 #   component. Note a container by this name will be started while the playbook
20 #   is running.
21 #
22 # * `image_authorized_keys`: SSH public key string or URL.
23 #
24 # Other interesting variables you MAY set include:
25 #
26 # * `debootstrap_suite`: The codename of the Debian/Ubuntu release to install,
27 #   like 'bookworm' or 'noble'. The default is Debian stable.
28 #
29 # * `debootstrap_mirror`: The URL of the Debian/Ubuntu mirror to install from.
30 #   You MUST set this to an Ubuntu mirror if you want to install Ubuntu.
31 #
32 # * `image_username`, `image_passhash`, `image_gecos`, `image_shell`: These all
33 #   define parameters for the user account created inside the VM. For details
34 #   about how to generate `image_passhash`, see
35 # <https://docs.ansible.com/ansible/latest/reference_appendices/faq.html#how-do-i-generate-encrypted-passwords-for-the-user-module>
36
37 - name: Bootstrap image
38   hosts: localhost
39   vars:
40     image_path: "/var/lib/machines/{{ image_name }}"
41     debootstrap_suite: stable
42     debootstrap_mirror: "http://deb.debian.org/debian"
43     debootstrap_script: "{{ 'gutsy' if debootstrap_mirror is search('\\bubuntu\\b') else 'sid' }}"
44   tasks:
45     - name: debootstrap
46       become: yes
47       ansible.builtin.command:
48         argv:
49           - debootstrap
50           - --include=dbus,openssh-server,python3,sudo,systemd
51           - "{{ debootstrap_suite }}"
52           - "{{ image_path }}"
53           - "{{ debootstrap_mirror }}"
54           - "{{ debootstrap_script }}"
55         creates: "{{ (image_path, 'etc/os-release')|path_join }}"
56     - name: Set up authorized SSH keys for root
57       become: yes
58       ansible.posix.authorized_key:
59         user: root
60         path: "{{ (image_path, 'root/.ssh/authorized_keys')|path_join }}"
61         key: "{{ image_authorized_keys }}"
62
63 - name: Start VM and add host
64   hosts: localhost
65   vars:
66     image_interface: host0
67   tasks:
68     # We want to start the VM as early as possible because it's easier to
69     # manage the system state when we know it's running. We restart the VM
70     # to ensure it's running from the image we just built.
71     - name: Start VM
72       become: yes
73       ansible.builtin.systemd_service:
74         name: "systemd-nspawn@{{ image_name }}.service"
75         state: restarted
76
77     - name: Enable networking and sshd
78       become: yes
79       ansible.builtin.command:
80         argv:
81           - systemctl
82           - "--machine={{ image_name }}"
83           - enable
84           - --now
85           - ssh
86           - systemd-networkd
87       register: nspawn_enable
88       # Retry if we tried the command faster than the VM could start dbus.
89       until: "nspawn_enable.stderr is not search('^Failed to connect to bus:', multiline=true)"
90       retries: 15
91       delay: 1
92     - name: Wait for VM network
93       become: yes
94       ansible.builtin.command:
95         argv:
96           - systemd-run
97           - "--machine={{ image_name }}"
98           - --wait
99           - /usr/lib/systemd/systemd-networkd-wait-online
100           - "--interface={{ image_interface }}"
101           - --timeout=60
102     - name: Get VM network address
103       become: yes
104       ansible.builtin.command:
105         argv:
106           - systemd-run
107           - "--machine={{ image_name }}"
108           - --pipe
109           - networkctl
110           - status
111           - --json=short
112           - "{{ image_interface }}"
113       register: nspawn_netctl
114     - name: Add VM Ansible host
115       vars:
116         vm_addr: "{{ (nspawn_netctl.stdout|from_json).Addresses|selectattr('ScopeString', '==', 'global')|first }}"
117       ansible.builtin.add_host:
118         name: nspawn_vm
119         ansible_host: "{{ vm_addr.Address|join('.' if vm_addr.Family == 2 else ':') }}"
120         ansible_user: root
121
122 - name: Set up VM user with sudo
123   hosts: nspawn_vm
124   vars:
125     image_username: admin
126     image_passhash: "!"
127     image_gecos: ""
128     image_shell: /usr/bin/bash
129   tasks:
130     - name: Create user account
131       ansible.builtin.user:
132         name: "{{ image_username }}"
133         password: "{{ image_passhash }}"
134         comment: "{{ image_gecos }}"
135         shell: "{{ image_shell }}"
136         groups:
137           - sudo
138         append: yes
139     - name: Set up authorized SSH keys for user
140       ansible.posix.authorized_key:
141         user: "{{ image_username }}"
142         key: "{{ image_authorized_keys }}"
143     - name: Clean up authorized SSH keys for root
144       ansible.posix.authorized_key:
145         user: root
146         key: "{{ image_authorized_keys }}"
147         state: absent
148
149 - name: Stop VM
150   hosts: localhost
151   tasks:
152     - name: Stop VM
153       become: yes
154       ansible.builtin.systemd_service:
155         name: "systemd-nspawn@{{ image_name }}.service"
156         state: stopped