Skip to content

Commit 6e8fbc4

Browse files
authored
Merge pull request #52 from rancher-sandbox/alpine
Add support for Alpine guest OS
2 parents 36b2ab6 + 1b23c32 commit 6e8fbc4

File tree

7 files changed

+268
-27
lines changed

7 files changed

+268
-27
lines changed

.github/workflows/test.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
timeout-minutes: 40
5353
strategy:
5454
matrix:
55-
example: [examples/default.yaml, examples/debian.yaml, examples/fedora.yaml]
55+
example: [default.yaml, alpine.yaml, debian.yaml, fedora.yaml]
5656
steps:
5757
- uses: actions/setup-go@v2
5858
with:
@@ -81,11 +81,11 @@ jobs:
8181
uses: actions/cache@v2
8282
with:
8383
path: ~/Library/Caches/lima/download
84-
key: ${{ runner.os }}-${{ matrix.example }}
84+
key: ${{ runner.os }}-examples/${{ matrix.example }}
8585
- name: Test
8686
env:
8787
EXAMPLE: ${{ matrix.example }}
88-
run: ./hack/test-example.sh $EXAMPLE
88+
run: ./hack/test-example.sh examples/$EXAMPLE
8989

9090
artifacts:
9191
name: Artifacts

examples/alpine.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
images:
2+
- location: https://github.com/rancher-sandbox/alpine-lima/releases/download/v0.0.1/alpine-lima-ci-3.13.5-x86_64.iso
3+
arch: "x86_64"
4+
5+
mounts:
6+
- location: "~"
7+
writable: false
8+
- location: "/tmp/lima"
9+
writable: true
10+
11+
ssh:
12+
# localPort is changed from 60022 to avoid conflicting with the default.
13+
# (TODO: assign localPort automatically)
14+
localPort: 60020
15+
16+
firmware:
17+
legacyBIOS: true
18+
19+
containerd:
20+
system: false
21+
user: false

hack/test-example.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ limactl validate "$FILE"
3232
declare -A CHECKS=(["systemd"]="1" ["systemd-strict"]="1" ["mount-home"]="1" ["containerd-user"]="1")
3333

3434
case "$NAME" in
35+
"alpine")
36+
WARNING "Alpine does not support systemd"
37+
CHECKS["systemd"]=
38+
CHECKS["containerd-user"]=
39+
;;
3540
"k3s")
3641
ERROR "File \"$FILE\" is not testable with this script"
3742
exit 1

pkg/cidata/user-data.TEMPLATE

Lines changed: 165 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,142 @@ users:
1818
{{- end}}
1919

2020
write_files:
21+
- content: |
22+
#!/bin/sh
23+
# This script pretends that /bin/ash can be used as /bin/bash, so all following
24+
# cloud-init scripts can use `#!/bin/bash` and `set -o pipefail`.
25+
test -f /etc/alpine-release || exit 0
26+
27+
# Redirect bash to ash (built with CONFIG_ASH_BASH_COMPAT) and hope for the best :)
28+
# It does support `set -o pipefail`, but not `[[`.
29+
# /bin/bash can't be a symlink because /bin/ash is a symlink to /bin/busybox
30+
cat >/bin/bash <<'EOF'
31+
#!/bin/sh
32+
exec /bin/ash "$@"
33+
EOF
34+
chmod +x /bin/bash
35+
owner: root:root
36+
path: /var/lib/cloud/scripts/per-boot/00-alpine-ash-as-bash.boot.sh
37+
permissions: '0755'
2138
- content: |
2239
#!/bin/bash
2340
set -eux -o pipefail
2441
25-
{{- if .Containerd.User}}
42+
# Restrict the rest of this script to Alpine until it has been tested with other distros
43+
test -f /etc/alpine-release || exit 0
44+
45+
# Data directories that should be persisted across reboots
46+
DATADIRS="/home /usr/local /etc/containerd /var/lib/containerd"
47+
48+
# When running from RAM try to move persistent data to data-volume
49+
# FIXME: the test for tmpfs mounts is probably Alpine-specific
50+
if [ "$(awk '$2 == "/" {print $3}' /proc/mounts)" == "tmpfs" ]; then
51+
mkdir -p /mnt/data
52+
if [ -e /dev/disk/by-label/data-volume ]; then
53+
mount -t ext4 /dev/disk/by-label/data-volume /mnt/data
54+
else
55+
# Find an unpartitioned disk and create data-volume
56+
DISKS=$(lsblk --list --noheadings --output name,type | awk '$2 == "disk" {print $1}')
57+
for DISK in ${DISKS}; do
58+
IN_USE=false
59+
# Looking for a disk that is not mounted or partitioned
60+
for PART in $(awk '/^\/dev\// {gsub("/dev/", ""); print $1}' /proc/mounts); do
61+
if [ "${DISK}" == "${PART}" -o -e /sys/block/${DISK}/${PART} ]; then
62+
IN_USE=true
63+
break
64+
fi
65+
done
66+
if [ "${IN_USE}" == "false" ]; then
67+
echo 'type=83' | sfdisk --label dos /dev/${DISK}
68+
PART=$(lsblk --list /dev/${DISK} --noheadings --output name,type | awk '$2 == "part" {print $1}')
69+
mkfs.ext4 -L data-volume /dev/${PART}
70+
mount -t ext4 /dev/disk/by-label/data-volume /mnt/data
71+
for DIR in ${DATADIRS}; do
72+
DEST="/mnt/data$(dirname ${DIR})"
73+
mkdir -p ${DIR} ${DEST}
74+
mv ${DIR} ${DEST}
75+
done
76+
break
77+
fi
78+
done
79+
fi
80+
for DIR in ${DATADIRS}; do
81+
if [ -d /mnt/data${DIR} ]; then
82+
[ -e ${DIR} ] && rm -rf ${DIR}
83+
ln -s /mnt/data${DIR} ${DIR}
84+
fi
85+
done
86+
fi
87+
owner: root:root
88+
path: /var/lib/cloud/scripts/per-boot/05-persistent-data-volume.boot.sh
89+
permissions: '0755'
90+
- content: |
91+
#!/sbin/openrc-run
92+
supervisor=supervise-daemon
93+
94+
name="lima-guestagent"
95+
description="Forward ports to the lima-hostagent"
96+
97+
export XDG_RUNTIME_DIR="/run/user/{{.UID}}"
98+
command=/usr/local/bin/lima-guestagent
99+
command_args="daemon"
100+
command_background=true
101+
command_user="{{.User}}:{{.User}}"
102+
pidfile="${XDG_RUNTIME_DIR}/lima-guestagent.pid"
103+
owner: root:root
104+
path: /var/lib/lima-guestagent/lima-guestagent.openrc
105+
permissions: '0755'
106+
- content: |
107+
#!/bin/bash
108+
set -eux -o pipefail
109+
110+
# This script prepares Alpine for lima; there is nothing in here for other distros
111+
test -f /etc/alpine-release || exit 0
112+
113+
# Configure apk repos
114+
BRANCH=edge
115+
VERSION_ID=$(awk -F= '$1=="VERSION_ID" {print $2}' /etc/os-release)
116+
case ${VERSION_ID} in
117+
*_alpha*|*_beta*) BRANCH=edge;;
118+
*.*.*) BRANCH=v${VERSION_ID%.*};;
119+
esac
120+
121+
for REPO in main community; do
122+
URL="https://dl-cdn.alpinelinux.org/alpine/${BRANCH}/${REPO}"
123+
if ! grep -q "^${URL}$" /etc/apk/repositories; then
124+
echo "${URL}" >> /etc/apk/repositories
125+
fi
126+
done
127+
128+
# Alpine doesn't use PAM so we need to explicitly allow public key auth
129+
usermod -p '*' ""{{.User}}""
130+
131+
# Alpine disables TCP forwarding, which is needed by the lima-guestagent
132+
sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/g' /etc/ssh/sshd_config
133+
rc-service sshd reload
134+
135+
# Create directory for the lima-guestagent socket (normally done by systemd)
136+
mkdir -p /run/user/{{.UID}}
137+
chown "{{.User}}" /run/user/{{.UID}}
138+
chmod 700 /run/user/{{.UID}}
139+
140+
# Install the openrc lima-guestagent service script
141+
mv /var/lib/lima-guestagent/lima-guestagent.openrc /etc/init.d/lima-guestagent
142+
143+
# `limactl stop` tells acpid to powerdown
144+
rc-update add acpid
145+
rc-service acpid start
146+
owner: root:root
147+
path: /var/lib/cloud/scripts/per-boot/10-alpine-prep.boot.sh
148+
permissions: '0755'
149+
{{- if .Containerd.User}}
150+
- content: |
151+
#!/bin/bash
152+
set -eux -o pipefail
153+
154+
# This script does not work unless systemd is available
155+
command -v systemctl 2>&1 >/dev/null || exit 0
156+
26157
# Set up env
27158
for f in .profile .bashrc; do
28159
if ! grep -q "# Lima BEGIN" "/home/{{.User}}.linux/$f"; then
@@ -66,7 +197,14 @@ write_files:
66197
67198
# Start systemd session
68199
loginctl enable-linger "{{.User}}"
69-
{{- end}}
200+
owner: root:root
201+
# We do not use per-once.
202+
path: /var/lib/cloud/scripts/per-boot/20-rootless-base.boot.sh
203+
permissions: '0755'
204+
{{- end}}
205+
- content: |
206+
#!/bin/bash
207+
set -eux -o pipefail
70208
71209
# Create mount points
72210
{{- range $val := .Mounts}}
@@ -81,16 +219,22 @@ write_files:
81219
umount /mnt/lima-cidata
82220
83221
# Launch the guestagent service
84-
until [ -e "/run/user/{{.UID}}/systemd/private" ]; do sleep 3; done
85-
sudo -iu "{{.User}}" "XDG_RUNTIME_DIR=/run/user/{{.UID}}" lima-guestagent install-systemd
222+
if [ -f /etc/alpine-release ]; then
223+
rc-update add lima-guestagent default
224+
rc-service lima-guestagent start
225+
else
226+
until [ -e "/run/user/{{.UID}}/systemd/private" ]; do sleep 3; done
227+
sudo -iu "{{.User}}" "XDG_RUNTIME_DIR=/run/user/{{.UID}}" lima-guestagent install-systemd
228+
fi
86229
owner: root:root
87230
# We do not use per-once.
88-
path: /var/lib/cloud/scripts/per-boot/00-base.boot.sh
231+
path: /var/lib/cloud/scripts/per-boot/25-guestagent-base.boot.sh
89232
permissions: '0755'
90233
{{- if or .Mounts .Containerd.System .Containerd.User }}
91234
- content: |
92235
#!/bin/bash
93236
set -eux -o pipefail
237+
94238
# Install minimum dependencies
95239
if command -v apt-get 2>&1 >/dev/null; then
96240
export DEBIAN_FRONTEND=noninteractive
@@ -119,6 +263,15 @@ write_files:
119263
ln -s fusermount3 /usr/bin/fusermount
120264
fi
121265
{{- end}}
266+
elif command -v apk 2>&1 >/dev/null; then
267+
: {{/* make sure the "elif" block is never empty */}}
268+
{{- if .Mounts}}
269+
if ! command -v sshfs 2>&1 >/dev/null; then
270+
apk update
271+
apk add sshfs
272+
fi
273+
modprobe fuse
274+
{{- end}}
122275
fi
123276
# Modify /etc/fuse.conf to allow "-o allow_root"
124277
{{- if .Mounts }}
@@ -127,13 +280,17 @@ write_files:
127280
fi
128281
{{- end}}
129282
owner: root:root
130-
path: /var/lib/cloud/scripts/per-boot/10-install-packages.boot.sh
283+
path: /var/lib/cloud/scripts/per-boot/30-install-packages.boot.sh
131284
permissions: '0755'
132285
{{- end}}
133286
{{- if or .Containerd.System .Containerd.User}}
134287
- content: |
135288
#!/bin/bash
136289
set -eux -o pipefail
290+
291+
# This script does not work unless systemd is available
292+
command -v systemctl 2>&1 >/dev/null || exit 0
293+
137294
if [ ! -x /usr/local/bin/nerdctl ]; then
138295
mkdir -p -m 600 /mnt/lima-cidata
139296
mount -t iso9660 -o ro /dev/disk/by-label/cidata /mnt/lima-cidata
@@ -191,7 +348,7 @@ write_files:
191348
fi
192349
{{- end}}
193350
owner: root:root
194-
path: /var/lib/cloud/scripts/per-boot/20-install-containerd.boot.sh
351+
path: /var/lib/cloud/scripts/per-boot/40-install-containerd.boot.sh
195352
permissions: '0755'
196353
{{- end}}
197354
{{- if .Provision}}
@@ -208,7 +365,7 @@ write_files:
208365
{{- end}}
209366
{{- end}}
210367
owner: root:root
211-
path: /var/lib/cloud/scripts/per-boot/30-execute-provision-scripts.boot.sh
368+
path: /var/lib/cloud/scripts/per-boot/50-execute-provision-scripts.boot.sh
212369
permissions: '0755'
213370
{{- end}}
214371
{{- range $i, $val := .Provision}}

pkg/hostagent/requirements.go

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ func (a *HostAgent) waitForRequirements(ctx context.Context, label string, requi
2626
a.l.Infof("The %s requirement %d of %d is satisfied", label, i+1, len(requirements))
2727
break retryLoop
2828
}
29+
if req.fatal {
30+
a.l.Infof("No further %s requirements will be checked", label)
31+
return multierror.Append(mErr,
32+
errors.Wrapf(err, "failed to satisfy the %s requirement %d of %d %q: %s; skipping further checks",
33+
label, i+1, len(requirements), req.description, req.debugHint))
34+
}
2935
if j == retries-1 {
3036
mErr = multierror.Append(mErr,
3137
errors.Wrapf(err, "failed to satisfy the %s requirement %d of %d %q: %s",
@@ -52,6 +58,7 @@ type requirement struct {
5258
description string
5359
script string
5460
debugHint string
61+
fatal bool
5562
}
5663

5764
func (a *HostAgent) essentialRequirements() []requirement {
@@ -100,7 +107,7 @@ fi
100107
script: `#!/bin/bash
101108
set -eux -o pipefail
102109
sock="/run/user/$(id -u)/lima-guestagent.sock"
103-
if ! timeout 30s bash -c "until [[ -S \"${sock}\" ]]; do sleep 3; done"; then
110+
if ! timeout 30s bash -c "until [ -S \"${sock}\" ]; do sleep 3; done"; then
104111
echo >&2 "lima-guestagent is not installed yet"
105112
exit 1
106113
fi
@@ -117,20 +124,37 @@ A possible workaround is to run "lima-guestagent install-systemd" in the guest.
117124
func (a *HostAgent) optionalRequirements() []requirement {
118125
req := make([]requirement, 0)
119126
if *a.y.Containerd.System || *a.y.Containerd.User {
120-
req = append(req, requirement{
121-
description: "containerd binaries to be installed",
122-
script: `#!/bin/bash
127+
req = append(req,
128+
requirement{
129+
description: "systemd must be available",
130+
fatal: true,
131+
script: `#!/bin/bash
132+
set -eux -o pipefail
133+
if ! command -v systemctl 2>&1 >/dev/null; then
134+
echo >&2 "systemd is not available on this OS"
135+
exit 1
136+
fi
137+
`,
138+
debugHint: `systemd is required to run containerd, but does not seem to be available.
139+
Make sure that you use an image that supports systemd. If you do not want to run
140+
containerd, please make sure that both 'container.system' and 'containerd.user'
141+
are set to 'false' in the config file.
142+
`,
143+
},
144+
requirement{
145+
description: "containerd binaries to be installed",
146+
script: `#!/bin/bash
123147
set -eux -o pipefail
124148
if ! timeout 30s bash -c "until command -v nerdctl; do sleep 3; done"; then
125149
echo >&2 "nerdctl is not installed yet"
126150
exit 1
127151
fi
128152
`,
129-
debugHint: `The nerdctl binary was not installed in the guest.
153+
debugHint: `The nerdctl binary was not installed in the guest.
130154
Make sure that you are using an officially supported image.
131155
Also see "/var/log/cloud-init-output.log" in the guest.
132156
`,
133-
})
157+
})
134158
}
135159
for _, probe := range a.y.Probes {
136160
if probe.Mode == limayaml.ProbeModeReadiness {

pkg/iso9660util/iso9660util.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,18 @@ func WriteFile(fs filesystem.FileSystem, path string, r io.Reader) (int64, error
6060
defer f.Close()
6161
return io.Copy(f, r)
6262
}
63+
64+
func IsISO9660(imagePath string) (bool, error) {
65+
imageFile, err := os.Open(imagePath)
66+
if err != nil {
67+
return false, err
68+
}
69+
defer imageFile.Close()
70+
71+
fileInfo, err := imageFile.Stat()
72+
if err != nil {
73+
return false, err
74+
}
75+
_, err = iso9660.Read(imageFile, fileInfo.Size(), 0, 0)
76+
return err == nil, nil
77+
}

0 commit comments

Comments
 (0)