使用Distrobuilder构建Incus容器镜像

来一起构建用于Incus的容器镜像,实现容器镜像自由吧!

在拥有了自己的Distrobuilder后(还没有?请参见:这篇博客),我们来一起构建用于Incus的容器镜像,实现容器镜像自由吧!

一定会有人疑惑:既然已经有了LXC镜像站,为什么还需要自己构建镜像呢?

这是因为,本文希望描述一个全程可控的,避免过度抽象细节的,创建Incus容器镜像的方法。

预先准备

需要在系统上安装distrobuilder,可以使用snap下载,也可以自己构建


案例1: Alpine Linux镜像

准备根文件系统(rootfs)

既然我们在讨论可控的构建过程,那么这一定包括一个可控的根文件系统(Root FileSystem, rootfs)。

让我们打开downloads | Alpine Linux,下拉找到MINI ROOT FILESYSTEM项目,下载对应架构的根文件系统。

例如,对于64位系统来说,我们将需要下载alpine-minirootfs-3.20.2-x86_64.tar.gz文件。中间的版本号请不要介意。

能不能用镜像站完成这一下载呢?又或者能不能用自己打包的Alpine Linux根文件系统呢?当然可以!这正是可控过程的魅力所在。

当然,distrobuilder实际上也已将下载rootfs的过程封装在程序中。就Alpine Linux来说,distrobuilder下载的rootfs和我们现在手动下载的是同一个文件。

如果更希望让distrobuilder代劳,请跳过当前这一步骤,后面会再展开介绍。

准备构建配置模板

构建镜像时需要的许多信息都由模板文件提供,如镜像的名称、类型、发行版,还有需要安装的软件包、执行的动作等等。

在distrobuilder中,镜像的模板文件是一个YAML文件,标准格式在官方仓库doc/examples/scheme.yaml中给出,文末也附有该文件复制过来的内容:

只不过,格式文件里,有非常多对键值。看起来一头雾水对吧?没关系,加法做不通我们可以做减法。

转到lxc/lxc-ci/images,这一目录下的镜像配置文件,就是Incus官方镜像服务器上,镜像构建所采用的配置文件。我们目前正在构建Alpine Linux,所以我们直接复制alpine.yaml文件中的内容到本地。由于内容太长,同样地,原始内容放在文末。

这文件也太长了吧?而且仔细一看,还有很多似乎我们不太用得上的配置项,例如适用于虚拟机镜像的命令,在容器中是用不到的。因此,让我们来进一步修改这些配置。

修改构建配置文件

Image: 容器镜像配置

首先,文件的第一节image部分,主要定义了镜像的名称和版本号等信息。

为了避免与官方构建的Alpine Linux镜像名称冲突,我们可以稍微修改一下这部分内容:

1
2
3
4
image:
  distribution: "Custom Alpinelinux"
  name: custom-alpine
  release: 3.20

Source: 镜像rootfs来源配置

配置文件第二节,source部分,主要定义了用于构建容器镜像的rootfs如何获得。由于各Linux发行版的rootfs定义与下载地址都有差异,因此distrobuilder预定义了许多Source,用于解析各种操作系统的rootfs下载规则。

对于Alpine Linux,distrobuilder已经定义好了alpinelinux-http这一Source,因此如果希望distrobuilder帮我们自动下载rootfs,此处可定义如下:

1
2
3
4
source:
  downloader: alpinelinux-http
  same_as: 3.12
  url: https://mirrors.hust.edu.cn/alpine/

其中,URL可以是任意Alpine Linux镜像站,以上例子使用了华中科大镜像站。不过,URL也可以file://开头,指向本地已经下载好的rootfs文件包。

如你所见,示例配置文件包含了一大段GPG公钥,用于验证下载的rootfs。如果信任下载源与下载文件完整性,并希望删掉keys部分以缩简配置文件,请在source节内加上skip_verification: true选项。

不过,既然我们正在尽可能追求可控,我们一定会想:

  • alpinelinux-http源在下载rootfs文件之外,是否还有额外的操作/Tricks?
  • 不考虑后文安装软件包等定制操作,在当前这一步是否有通用于所有Linux发行版rootfs的方法?
  • 是否可以利用已预先准备好的rootfs?

尽管根据sources/alpine-http.go源代码,就目前来说,并没有什么下载之外的额外小Tricks。但到目前为止,distrobuilder最新版3.0并没有直接提供使用现有rootfs目录直接构建容器系统的选项。如果直接在配置文件中删除source一节,进行构建时也会报错。

但是,distrobuilder的确提供了一种使用任意rootfs文件包的Source,即rootfs-http,相关源代码可见sources/rootfs-http.go。简单来说只需要提供一个指向rootfs包的URL,本地网络皆可。

因此,上述source节也可以改为(使用file://指向本地文件时需要使用绝对路径):

1
2
3
source:
  downloader: rootfs-http
  url: file:///absolute/path/to/alpine-minirootfs-3.20.2-x86_64.tar.gz

Target: 目标特定配置

如果仅希望构建Incus容器镜像,不需要LXC镜像和虚拟机镜像,可以直接把这一节大幅精简:

1
2
targets:
  incus:

如有需要,请直接参阅Targets - distrobuilder documentation官方文档。

Files: 文件生成规则配置

和上面类似地,如果不需要Incus虚拟机镜像,仅仅需要保留用于Incus容器的部分,可以直接删除掉所有包含以下标记的虚拟机专用文件生成规则。

1
2
types:
  - vm

Packages: 软件包安装预定义配置

这部分将定义Incus在构建镜像时,需要使用何种包管理器,安装哪些软件包,大部分内容可以直接照抄示例。同上,可以删掉所有虚拟机限定的配置。

重点讨论一下软件源相关配置。官方手册中,与定制软件源相关的选项位于packages节下,可以通过添加repositories节设置额外的软件源。例如:

1
2
3
4
5
6
7
8
packages:
  # Other parameters
  # ...
  repositories:
    - name: hust
      url: |-
        https://mirrors.hust.edu.cn/alpine/v3.20/main
        https://mirrors.hust.edu.cn/alpine/v3.20/community

但是,这种方法会将新软件源附加到原本的软件源后,并不会替换掉速度可能非常缓慢的官方软件源。

如果希望替换掉rootfs中的官方软件源,我们可以不采用这种方案,请往下看。

Actions: 自定义操作

在这一部分,我们可以定义在容器构建的不同的时机要执行的操作(Action)

目前,distrobuilder提供的执行时机(触发器/Trigger)包括:

  • post-unpack: 在rootfs被解压释放后执行。
  • post-update: 如果packages.updatetrue,在包管理器完成更新后执行。
  • post-packages: 在包管理器安装完所有指定的包后执行。
  • post-files: 在files配置块执行完成后执行。这一部分默认仅在build-lxc, build-incuspack-lxcpack-incus操作中执行。如果希望在build-dir操作中也执行这一块,需要指定--with-post-files选项。

例如,如果我们希望把rootfs中官方的软件源配置为镜像站,则可以添加一个post-unpack操作,内容如下:

1
2
3
4
5
6
actions:
- trigger: post-unpack
  action: |-
    #!/bin/sh
    set -eux
    sed -i 's/dl-cdn.alpinelinux.org/mirrors.hust.edu.cn/g' /etc/apk/repositories

该命令会在rootfs解压挂载后,容器执行apk update更新软件源之前执行。

Mappings: 架构名称映射配置

这部分主要是告诉Distrobuilder如何处理不同架构名称之间的映射关系,如amd64x86_64aarch64arm64等。

这部分按照模板直接保留就行,详情见Mappings - distrobuilder documentation官方文档。

不得不感慨一下,Linux由于各种历史包袱和不一致的规范,有时会略显混乱。

最终配置文件

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
image:
  name: custom-alpine
  distribution: "Custom Alpinelinux"
  release: 3.20

source:
  downloader: rootfs-http
  url: file:///absolute/path/to/alpine-minirootfs-3.20.2-x86_64.tar.gz

targets:
  incus:

files:
- path: /etc/hostname
  generator: hostname

- path: /etc/hosts
  generator: hosts

- path: /etc/network/interfaces
  generator: dump
  content: |-
    auto eth0
    iface eth0 inet dhcp
    hostname $(hostname)

- path: /etc/inittab
  generator: dump
  content: |-
    # /etc/inittab
    ::sysinit:/sbin/openrc sysinit
    ::sysinit:/sbin/openrc boot
    ::wait:/sbin/openrc default

    # Set up a couple of getty's
    ::respawn:/sbin/getty 38400 console
    tty1::respawn:/sbin/getty 38400 tty1
    tty2::respawn:/sbin/getty 38400 tty2
    tty3::respawn:/sbin/getty 38400 tty3
    tty4::respawn:/sbin/getty 38400 tty4

    # Stuff to do for the 3-finger salute
    ::ctrlaltdel:/sbin/reboot

    # Stuff to do before rebooting
    ::shutdown:/sbin/openrc shutdown

- path: /etc/inittab
  generator: template
  name: inittab
  content: |-
    # /etc/inittab
    ::sysinit:/sbin/openrc sysinit
    ::sysinit:/sbin/openrc boot
    ::wait:/sbin/openrc default

    # Set up a couple of getty's
    ::respawn:/sbin/getty 38400 console

    # Stuff to do for the 3-finger salute
    ::ctrlaltdel:/sbin/reboot

    # Stuff to do before rebooting
    ::shutdown:/sbin/openrc shutdown

- name: meta-data
  generator: cloud-init
  variants:
  - cloud

- name: network-config
  generator: cloud-init
  content: |-
    version: 1
    config:
    - type: physical
      name: eth0
      subnets:
      - type: dhcp
        control: auto
  variants:
  - cloud

- name: user-data
  generator: cloud-init
  variants:
  - cloud

- name: vendor-data
  generator: cloud-init
  variants:
  - cloud

packages:
  manager: apk
  update: true
  cleanup: true
  sets:
  - packages:
    - alpine-base
    - logrotate
    - doas
    action: install

  - packages:
    - cloud-init
    - openssh
    - e2fsprogs-extra
    action: install
    variants:
    - cloud

  - packages:
    - py3-pyserial
    - py3-netifaces
    action: install
    variants:
    - cloud
    releases:
    - 3.17
    - 3.18
    - 3.19
    - 3.20
    - edge

actions:
- trigger: post-unpack
  action: |-
    #!/bin/sh
    set -eux
    sed -i 's/dl-cdn.alpinelinux.org/mirrors.hust.edu.cn/g' /etc/apk/repositories

- trigger: post-packages
  action: |-
    #!/bin/sh
    set -eux

    rm -f /var/cache/apk/*

- trigger: post-packages
  action: |-
    #!/bin/sh
    set -eux

    # Rewrite configuration for LXC
    sed -i 's/#rc_sys=""/rc_sys="lxc"/' /etc/rc.conf

    # Honor fstab by not making the localmount script a noop
    sed -i 's/-lxc//' /etc/init.d/localmount

    # Enable services
    for svc_name in bootmisc syslog devfs; do
        ln -s /etc/init.d/${svc_name} /etc/runlevels/boot/${svc_name}
    done

    for svc_name in networking crond; do
        ln -s /etc/init.d/${svc_name} /etc/runlevels/default/${svc_name}
    done
  types:
  - container

- trigger: post-files
  action: |-
    #!/bin/sh
    set -eux

    setup-cloud-init
  variants:
  - cloud

mappings:
  architecture_map: alpinelinux

我们将最终的配置文件保存,随便起个名字,比方说alpine.yaml即可。

开始构建

新建rootfsimages两个文件夹,分别用于暂存解压的rootfs,以及构建生成的镜像文件。

1
2
mkdir rootfs
mkdir images

首先需要执行的是build-dir指令。

1
sudo distrobuilder build-dir alpine.yaml rootfs

再接着执行pack-incus指令。

1
sudo distrobuilder pack-incus alpine.yaml rootfs images

等待一切完成,images文件夹下就存放着构建好的容器镜像了。

此时一定有人会问:为什么要分两步?为什么不直接执行build-incus指令?

答案当然是可以的。

1
sudo distrobuilder build-incus alpine.yaml images

有什么区别呢?我们来看看Distrobuilder开发者在论坛中的讨论

Oops, I said build instead of pack here as that’s likely what you actually want to use.build-incus is equivalent of build-dir + pack-incus.

The reason why we do it in two steps in our CI is because we produce both LXC and Incus images so that lets us use the same rootfs for both. But if all you care about is the LXD/Incus image, just use build-incus directly.

导入Incus并开始使用

拥有了镜像,只需要导入Incus就可以开始使用了。

1
incus image import images/incus.tar.xz images/rootfs.squashfs --alias custom-alpine/3.20

命令末尾的--alias选项用于给镜像定义一个别名。如果没有别名,我们就只能用镜像的哈希指代镜像了。

接着创建实例,启动镜像。

1
2
incus launch local:custom-alpine/3.20 test-alp
incus exec test-alp -- /bin/ash

附录

配置文件

doc/examples/scheme.yaml

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# This example contains every possible key

image:
  description: |-
    here goes the image description
  distribution: distro
  release: release
  architecture: x86_64
  expiry: 30d
  variant: default
  name: distro-release-x86_64
  serial: some-random-string

source:
  downloader: ubuntu-http
  url: http://archive.ubuntu.com
  keys:
    - 0xdeadbeaf
  keyserver: http://keyserver.ubuntu.com
  variant: default
  suite: suite
  same_as: bionic
  skip_verification: false
  components:
    - main

targets:
  lxc:
    create_message: |-
      You just created an {{ image.description }} container.

      To enable SSH, run: apt install openssh-server
      No default root or user password are set by LXC.

    config:
      - type: all
        before: 5
        content: |-
          lxc.include = LXC_TEMPLATE_CONFIG/ubuntu.common.conf

      - type: user
        before: 5
        content: |-
          lxc.include = LXC_TEMPLATE_CONFIG/ubuntu.userns.conf

      - type: all
        after: 4
        content: |-
          lxc.include = LXC_TEMPLATE_CONFIG/common.conf

      - type: user
        after: 4
        content: |-
          lxc.include = LXC_TEMPLATE_CONFIG/userns.conf

      - type: all
        content: |-
          lxc.arch = {{ image.architecture_personality }}
  incus:
    vm:
      size: 2147483648
      filesystem: ext4

files:
  - generator: dump
    path: /some/path
    content: |-
      here goes the content
    name: name
    mode: 0644
    gid: 1000
    uid: 1000
    template:
      properties:
        key: value
      when:
        - always
    templated: true
    releases:
      - a
      - b
    architectures:
      - x86_64
    variants:
      - default

  - generator: hostname
    path: /etc/hostname

  - generator: hosts
    path: /etc/hosts

  - generator: remove
    path: /root/file

  - generator: template
    name: foo
    content: |-
      Here goes the content
    template:
      properties:
        key: value
      when:
        - create
        - copy

packages:
  manager: apt
  custom_manager:
    clean:
      cmd: mgr
      flags:
        - clean
    install:
      cmd: mgr
      flags:
        - install
    remove:
      cmd: mgr
      flags:
        - remove
    refresh:
      cmd: mgr
      flags:
        - refresh
    update:
      cmd: mgr
      flags:
        - update
    flags:
      - --yes
  update: true
  cleanup: false
  sets:
    - packages:
        - gnupg
      action: install
      early: true

    - packages:
        - vim
      action: install
      releases:
        - a
        - b
      architectures:
        - x86_64
      variants:
        - default

    - packages:
        - lightdm
      action: install
      flags:
        - --no-install-recommends

    - packages:
        - grub
      action: remove

  repositories:
    - name: reponame
      url: |-
        deb http://archive.ubuntu.com/ubuntu {{ image.release }}-updates main restricted universe multiverse
      type: type
      key: 0xdeadbeaf
      releases:
        - a
        - b
      architectures:
        - x86_64
      variants:
        - default

actions:
  - trigger: post-unpack
    action: |-
      #!/bin/sh

      do something after the rootfs has been unpacked

  - trigger: post-files
    action: |-
      #!/bin/sh

      do something after the files section has been processed

  - trigger: post-update
    action: |-
      #!/bin/sh

      do something after packages have been processed

  - trigger: post-packages
    action: |-
      #!/bin/sh

      do something after the packages section has been processed

    releases:
      - a
      - b
    architectures:
      - x86_64
    variants:
      - default

mappings:
  architectures:
    a: b
    c: d
  architecture_map: debian

environment:
  clear_defaults: true
  variables:
    - key: FOO
      value: bar

lxc/lxc-ci/images/alpine.yaml

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
image:
  distribution: "alpinelinux"

source:
  downloader: alpinelinux-http
  same_as: 3.12
  url: https://mirror.csclub.uwaterloo.ca/alpine/
  keys:
  # 0482D84022F52DF1C4E7CD43293ACD0907D9495A
  - |-
    -----BEGIN PGP PUBLIC KEY BLOCK-----

    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
    bcgRRcklNr8mTzJH
    =ckew
    -----END PGP PUBLIC KEY BLOCK-----

targets:
  lxc:
    create_message: |
      You just created an {{ image.description }} container.

    config:
    - type: all
      before: 5
      content: |-
        lxc.include = LXC_TEMPLATE_CONFIG/alpine.common.conf
    - type: user
      before: 5
      content: |-
        lxc.include = LXC_TEMPLATE_CONFIG/alpine.userns.conf
    - type: all
      after: 4
      content: |-
        lxc.include = LXC_TEMPLATE_CONFIG/common.conf
    - type: user
      after: 4
      content: |-
        lxc.include = LXC_TEMPLATE_CONFIG/userns.conf
    - type: all
      content: |-
        lxc.arch = {{ image.architecture_personality }}
  incus:
    vm:
      filesystem: ext4

files:
- path: /etc/hostname
  generator: hostname

- path: /etc/hosts
  generator: hosts

- generator: fstab
  types:
  - vm

- generator: incus-agent
  types:
  - vm

- path: /etc/default/grub
  generator: dump
  pongo: true
  content: |-
    # Set the recordfail timeout
    GRUB_RECORDFAIL_TIMEOUT=0

    # Do not wait on grub prompt
    GRUB_TIMEOUT=0

    # Set the default commandline
    GRUB_CMDLINE_LINUX_DEFAULT="console=tty1 console=ttyS0 modules=sd-mod,usb-storage,{{ targets.incus.vm.filesystem }} rootfstype={{ targets.incus.vm.filesystem }}"

    # Set the grub console type
    GRUB_TERMINAL=console

    # Disable os-prober
    GRUB_DISABLE_OS_PROBER=true
  types:
  - vm

- path: /etc/network/interfaces
  generator: dump
  content: |-
    auto eth0
    iface eth0 inet dhcp
    hostname $(hostname)

- path: /etc/inittab
  generator: dump
  content: |-
    # /etc/inittab
    ::sysinit:/sbin/openrc sysinit
    ::sysinit:/sbin/openrc boot
    ::wait:/sbin/openrc default

    # Set up a couple of getty's
    ::respawn:/sbin/getty 38400 console
    tty1::respawn:/sbin/getty 38400 tty1
    tty2::respawn:/sbin/getty 38400 tty2
    tty3::respawn:/sbin/getty 38400 tty3
    tty4::respawn:/sbin/getty 38400 tty4

    # Stuff to do for the 3-finger salute
    ::ctrlaltdel:/sbin/reboot

    # Stuff to do before rebooting
    ::shutdown:/sbin/openrc shutdown

- path: /etc/inittab
  generator: template
  name: inittab
  content: |-
    # /etc/inittab
    ::sysinit:/sbin/openrc sysinit
    ::sysinit:/sbin/openrc boot
    ::wait:/sbin/openrc default

    # Set up a couple of getty's
    ::respawn:/sbin/getty 38400 console

    # Stuff to do for the 3-finger salute
    ::ctrlaltdel:/sbin/reboot

    # Stuff to do before rebooting
    ::shutdown:/sbin/openrc shutdown

- name: meta-data
  generator: cloud-init
  variants:
  - cloud

- name: network-config
  generator: cloud-init
  content: |-
    version: 1
    config:
    - type: physical
      name: eth0
      subnets:
      - type: dhcp
        control: auto
  variants:
  - cloud

- name: user-data
  generator: cloud-init
  variants:
  - cloud

- name: vendor-data
  generator: cloud-init
  variants:
  - cloud

packages:
  manager: apk
  update: true
  cleanup: true
  sets:
  - packages:
    - alpine-base
    - logrotate
    - doas
    action: install

  - packages:
    - cloud-init
    - openssh
    - e2fsprogs-extra
    action: install
    variants:
    - cloud

  - packages:
    - py3-pyserial
    - py3-netifaces
    action: install
    variants:
    - cloud
    releases:
    - 3.17
    - 3.18
    - 3.19
    - 3.20
    - edge

  - packages:
    - acpi
    - gcc
    - grub-efi
    - linux-virt
    - udev
    action: install
    types:
    - vm

actions:
- trigger: post-packages
  action: |-
    #!/bin/sh
    set -eux

    rm -f /var/cache/apk/*

- trigger: post-packages
  action: |-
    #!/bin/sh
    set -eux

    # Rewrite configuration for LXC
    sed -i 's/#rc_sys=""/rc_sys="lxc"/' /etc/rc.conf

    # Honor fstab by not making the localmount script a noop
    sed -i 's/-lxc//' /etc/init.d/localmount

    # Enable services
    for svc_name in bootmisc syslog devfs; do
        ln -s /etc/init.d/${svc_name} /etc/runlevels/boot/${svc_name}
    done

    for svc_name in networking crond; do
        ln -s /etc/init.d/${svc_name} /etc/runlevels/default/${svc_name}
    done
  types:
  - container

- trigger: post-files
  action: |-
    #!/bin/sh
    set -eux

    for svc_name in acpid bootmisc hostname hwclock loadkmap modules networking swap sysctl syslog urandom; do
        ln -fs /etc/init.d/${svc_name} /etc/runlevels/boot/${svc_name}
    done

    for svc_name in devfs dmesg hwdrivers mdev udev udev-settle udev-trigger; do
        ln -fs /etc/init.d/${svc_name} /etc/runlevels/sysinit/${svc_name}
    done

    target=/boot/grub/grub.cfg
    grub-mkconfig -o "${target}"
    sed -i "s#root=[^ ]*#root=${DISTROBUILDER_ROOT_UUID}#g" "${target}"

    TARGET="x86_64"
    [ "$(uname -m)" = "aarch64" ] && TARGET="arm64"
    grub-install --target=${TARGET}-efi --no-nvram --removable
    grub-install --target=${TARGET}-efi --no-nvram

    # Attempt to correct errors in the installation of all packages.
    apk fix
  types:
  - vm

- trigger: post-files
  action: |-
    #!/bin/sh
    set -eux

    for svc_name in sshd; do
        ln -fs /etc/init.d/${svc_name} /etc/runlevels/default/${svc_name}
    done

    # Tweak to prevent cloud-init from getting stuck.
    echo "datasource_list: ['NoCloud', 'ConfigDrive', 'LXD', 'None']" > /etc/cloud/cloud.cfg.d/99_lxc.cfg
  types:
  - vm
  variants:
  - cloud

- trigger: post-files
  action: |-
    #!/bin/sh
    set -eux

    setup-cloud-init
  variants:
  - cloud

mappings:
  architecture_map: alpinelinux