Skip to content

Commit 329665d

Browse files
authored
Merge pull request #38 from rancher-sandbox/provision
Implement system and user provisioning scripts
2 parents aeffb15 + 11484cb commit 329665d

File tree

10 files changed

+276
-59
lines changed

10 files changed

+276
-59
lines changed

examples/k3s.yaml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Deploy kubernetes via k3s (which installs a bundled containerd).
2+
#
3+
# It can be accessed from the host by exporting the kubeconfig file;
4+
# the ports are already forwarded automatically by lima:
5+
#
6+
# $ export KUBECONFIG=$PWD/kubeconfig.yaml
7+
# $ limactl shell k3s sudo cat /etc/rancher/k3s/k3s.yaml >$KUBECONFIG
8+
# $ kubectl get no
9+
# NAME STATUS ROLES AGE VERSION
10+
# lima-k3s Ready control-plane,master 69s v1.21.1+k3s1
11+
12+
images:
13+
- location: "~/Downloads/hirsute-server-cloudimg-amd64.img"
14+
arch: "x86_64"
15+
16+
mounts: []
17+
18+
ssh:
19+
localPort: 60022
20+
21+
containerd:
22+
system: false
23+
user: false
24+
25+
provision:
26+
- mode: system
27+
script: |
28+
#!/bin/sh
29+
curl -sfL https://get.k3s.io | sh -
30+
31+
probes:
32+
- script: |
33+
#!/bin/bash
34+
set -eux -o pipefail
35+
if ! timeout 30s bash -c "until test -f /etc/rancher/k3s/k3s.yaml; do sleep 3; done"; then
36+
echo >&2 "k3s is not running yet"
37+
exit 1
38+
fi
39+
hint: |
40+
The k3s kubeconfig file has not yet been created.
41+
Run "lima bash sudo journalctl -u k3s" to check the log.
42+
If that is still empty, check the bottom of the log at "/var/log/cloud-init-output.log".

pkg/cidata/cidata.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ func GenerateISO9660(isoPath, name string, y *limayaml.LimaYAML) error {
2929
return err
3030
}
3131
args := TemplateArgs{
32-
Name: name,
33-
User: u.Username,
34-
UID: uid,
32+
Name: name,
33+
User: u.Username,
34+
UID: uid,
35+
Provision: y.Provision,
36+
Containerd: Containerd{System: *y.Containerd.System, User: *y.Containerd.User},
3537
}
3638
for _, f := range sshutil.DefaultPubKeys() {
3739
args.SSHPubKeys = append(args.SSHPubKeys, f.Content)

pkg/cidata/template.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
_ "embed"
55
"path/filepath"
66

7+
"github.com/AkihiroSuda/lima/pkg/limayaml"
8+
79
"github.com/AkihiroSuda/lima/pkg/templateutil"
810
"github.com/containerd/containerd/identifiers"
911
"github.com/pkg/errors"
@@ -16,12 +18,18 @@ var (
1618
metaDataTemplate string
1719
)
1820

21+
type Containerd struct {
22+
System bool
23+
User bool
24+
}
1925
type TemplateArgs struct {
2026
Name string // instance name
2127
User string // user name
2228
UID int
2329
SSHPubKeys []string
2430
Mounts []string // abs path, accessible by the User
31+
Provision []limayaml.Provision
32+
Containerd Containerd
2533
}
2634

2735
func ValidateTemplateArgs(args TemplateArgs) error {

pkg/cidata/user-data.TEMPLATE

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ users:
1313
sudo: ALL=(ALL) NOPASSWD:ALL
1414
lock_passwd: true
1515
ssh-authorized-keys:
16-
{{range $val := .SSHPubKeys}}
16+
{{- range $val := .SSHPubKeys}}
1717
- {{$val}}
18-
{{end}}
18+
{{- end}}
1919

2020
write_files:
2121
- content: |
2222
#!/bin/bash
2323
set -eux -o pipefail
2424
25+
{{- if .Containerd.User}}
2526
# Enable rootless containers
2627
if [ ! -e "/etc/systemd/system/[email protected]/lima.conf" ]; then
2728
mkdir -p "/etc/systemd/system/[email protected]"
@@ -47,12 +48,13 @@ write_files:
4748
grep -qw "{{.User}}" $f || echo "{{.User}}:100000:65536" >> $f
4849
done
4950
loginctl enable-linger "{{.User}}"
51+
{{- end}}
5052
5153
# Create mount points
52-
{{range $val := .Mounts}}
54+
{{- range $val := .Mounts}}
5355
mkdir -p "{{$val}}"
5456
chown "{{$.User}}" "{{$val}}" || true
55-
{{end}}
57+
{{- end}}
5658
5759
# Install or update the guestagent binary
5860
mkdir -p -m 600 /mnt/lima-cidata
@@ -67,23 +69,50 @@ write_files:
6769
# We do not use per-once.
6870
path: /var/lib/cloud/scripts/per-boot/00-base.boot.sh
6971
permissions: '0755'
72+
{{- if or .Mounts .Containerd.User}}
7073
- content: |
7174
#!/bin/bash
7275
set -eux -o pipefail
7376
# Install minimum dependencies
7477
if command -v apt-get 2>&1 >/dev/null; then
7578
export DEBIAN_FRONTEND=noninteractive
7679
apt-get update
77-
apt-get install -y sshfs uidmap
80+
{{- if .Mounts}}
81+
apt-get install -y sshfs
82+
{{- end }}
83+
{{- if .Containerd.User}}
84+
apt-get install -y uidmap
85+
{{- end }}
7886
elif command -v dnf 2>&1 >/dev/null; then
79-
dnf install -y fuse-sshfs shadow-utils
87+
: {{/* make sure the "elif" block is never empty */}}
88+
{{- if .Mounts}}
89+
dnf install -y fuse-sshfs
90+
{{- end}}
91+
{{- if .Containerd.User}}
92+
dnf install -y shadow-utils
93+
{{- end}}
8094
fi
8195
owner: root:root
8296
path: /var/lib/cloud/scripts/per-boot/10-install-packages.boot.sh
8397
permissions: '0755'
98+
{{- end}}
99+
{{- if or .Containerd.System .Containerd.User}}
84100
- content: |
85101
#!/bin/bash
86102
set -eux -o pipefail
103+
if [ ! -x /usr/local/bin/nerdctl ]; then
104+
version="0.8.3"
105+
goarch="amd64"
106+
if [ "$(uname -m )" = "aarch64 "]; then
107+
goarch="arm64"
108+
fi
109+
curl -fsSL https://github.com/containerd/nerdctl/releases/download/v${version}/nerdctl-full-${version}-linux-${goarch}.tar.gz | tar Cxz /usr/local
110+
fi
111+
{{- if .Containerd.System}}
112+
systemctl enable containerd
113+
systemctl start containerd
114+
{{- end}}
115+
{{- if .Containerd.User}}
87116
modprobe tap || true
88117
if [ ! -e "/home/{{.User}}.linux/.config/containerd/config.toml" ]; then
89118
mkdir -p "/home/{{.User}}.linux/.config/containerd"
@@ -95,18 +124,37 @@ write_files:
95124
EOF
96125
chown -R "{{.User}}" "/home/{{.User}}.linux/.config"
97126
fi
98-
if [ ! -x /usr/local/bin/nerdctl ]; then
99-
version="0.8.3"
100-
goarch="amd64"
101-
if [ "$(uname -m )" = "aarch64 "]; then
102-
goarch="arm64"
103-
fi
104-
curl -fsSL https://github.com/containerd/nerdctl/releases/download/v${version}/nerdctl-full-${version}-linux-${goarch}.tar.gz | tar Cxz /usr/local
127+
if [ ! -e "/home/{{.User}}}}.linux/.config/systemd/user/containerd.service" ]; then
105128
until [ -e "/run/user/{{.UID}}/systemd/private" ]; do sleep 3; done
106129
sudo -iu "{{.User}}" "XDG_RUNTIME_DIR=/run/user/{{.UID}}" containerd-rootless-setuptool.sh install
107130
sudo -iu "{{.User}}" "XDG_RUNTIME_DIR=/run/user/{{.UID}}" containerd-rootless-setuptool.sh install-buildkit
108131
sudo -iu "{{.User}}" "XDG_RUNTIME_DIR=/run/user/{{.UID}}" containerd-rootless-setuptool.sh install-stargz
109132
fi
133+
{{- end}}
110134
owner: root:root
111135
path: /var/lib/cloud/scripts/per-boot/20-install-containerd.boot.sh
112136
permissions: '0755'
137+
{{- end}}
138+
{{- if .Provision}}
139+
- content: |
140+
#!/bin/bash
141+
set -eu -o pipefail
142+
{{- range $i, $val := .Provision}}
143+
{{- $script := printf "/var/lib/lima-guestagent/provision-%02d-%s" $i $val.Mode}}
144+
{{- if eq $val.Mode "system"}}
145+
{{$script}}
146+
{{- else}}
147+
until [ -e "/run/user/{{.UID}}/systemd/private" ]; do sleep 3; done
148+
sudo -iu "{{.User}}" "XDG_RUNTIME_DIR=/run/user/{{.UID}}" {{$script}}
149+
{{- end}}
150+
{{- end}}
151+
owner: root:root
152+
path: /var/lib/cloud/scripts/per-boot/30-execute-provision-scripts.boot.sh
153+
permissions: '0755'
154+
{{- end}}
155+
{{- range $i, $val := .Provision}}
156+
- content: {{printf "%q" $val.Script}}
157+
owner: root:root
158+
path: {{printf "/var/lib/lima-guestagent/provision-%02d-%s" $i $val.Mode}}
159+
permissions: '0755'
160+
{{- end}}

pkg/hostagent/hostagent.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func (a *HostAgent) Run(ctx context.Context) error {
5454
return nil
5555
})
5656
var mErr error
57-
if err := a.waitForRequirements(ctx, "essential", essentialRequirements); err != nil {
57+
if err := a.waitForRequirements(ctx, "essential", a.essentialRequirements()); err != nil {
5858
mErr = multierror.Append(mErr, err)
5959
}
6060
mounts, err := a.setupMounts(ctx)
@@ -71,7 +71,7 @@ func (a *HostAgent) Run(ctx context.Context) error {
7171
return unmountMErr
7272
})
7373
go a.watchGuestAgentEvents(ctx)
74-
if err := a.waitForRequirements(ctx, "optional", optionalRequirements); err != nil {
74+
if err := a.waitForRequirements(ctx, "optional", a.optionalRequirements()); err != nil {
7575
mErr = multierror.Append(mErr, err)
7676
}
7777
return mErr

pkg/hostagent/requirements.go

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package hostagent
22

33
import (
44
"context"
5+
"github.com/AkihiroSuda/lima/pkg/limayaml"
56
"time"
67

78
"github.com/AkihiroSuda/sshocker/pkg/ssh"
@@ -54,8 +55,9 @@ type requirement struct {
5455
debugHint string
5556
}
5657

57-
var essentialRequirements = []requirement{
58-
{
58+
func (a *HostAgent) essentialRequirements() []requirement {
59+
req := make([]requirement, 0)
60+
req = append(req, requirement{
5961
description: "ssh",
6062
script: `#!/bin/bash
6163
true
@@ -64,23 +66,25 @@ true
6466
Make sure that the YAML field "ssh.localPort" is not used by other processes on the host.
6567
If any private key under ~/.ssh is protected with a passphrase, you need to have ssh-agent to be running.
6668
`,
67-
},
68-
{
69-
description: "sshfs binary to be installed",
70-
script: `#!/bin/bash
69+
})
70+
if len(a.y.Mounts) > 0 {
71+
req = append(req, requirement{
72+
description: "sshfs binary to be installed",
73+
script: `#!/bin/bash
7174
set -eux -o pipefail
7275
if ! timeout 30s bash -c "until command -v sshfs; do sleep 3; done"; then
7376
echo >&2 "sshfs is not installed yet"
7477
exit 1
7578
fi
7679
`,
77-
debugHint: `The sshfs binary was not installed in the guest.
80+
debugHint: `The sshfs binary was not installed in the guest.
7881
Make sure that you are using an officially supported image.
7982
Also see "/var/log/cloud-init-output.log" in the guest.
8083
A possible workaround is to run "apt-get install sshfs" in the guest.
8184
`,
82-
},
83-
{
85+
})
86+
}
87+
req = append(req, requirement{
8488
description: "the guest agent to be running",
8589
script: `#!/bin/bash
8690
set -eux -o pipefail
@@ -95,22 +99,36 @@ Make sure that you are using an officially supported image.
9599
Also see "/var/log/cloud-init-output.log" in the guest.
96100
A possible workaround is to run "lima-guestagent install-systemd" in the guest.
97101
`,
98-
},
102+
})
103+
return req
99104
}
100105

101-
var optionalRequirements = []requirement{
102-
{
103-
description: "containerd binaries to be installed",
104-
script: `#!/bin/bash
106+
func (a *HostAgent) optionalRequirements() []requirement {
107+
req := make([]requirement, 0)
108+
if *a.y.Containerd.System || *a.y.Containerd.User {
109+
req = append(req, requirement{
110+
description: "containerd binaries to be installed",
111+
script: `#!/bin/bash
105112
set -eux -o pipefail
106113
if ! timeout 30s bash -c "until command -v nerdctl; do sleep 3; done"; then
107114
echo >&2 "nerdctl is not installed yet"
108115
exit 1
109116
fi
110117
`,
111-
debugHint: `The nerdctl binary was not installed in the guest.
118+
debugHint: `The nerdctl binary was not installed in the guest.
112119
Make sure that you are using an officially supported image.
113120
Also see "/var/log/cloud-init-output.log" in the guest.
114121
`,
115-
},
122+
})
123+
}
124+
for _, probe := range a.y.Probes {
125+
if probe.Mode == limayaml.ProbeModeReadiness {
126+
req = append(req, requirement{
127+
description: probe.Description,
128+
script: probe.Script,
129+
debugHint: probe.Hint,
130+
})
131+
}
132+
}
133+
return req
116134
}

pkg/limayaml/default.TEMPLATE.yaml

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,44 @@ video:
5858
# Default: "none"
5959
display: "none"
6060

61-
#UNIMPLEMENTED| provision:
62-
#UNIMPLEMENTED| # `system` is executed with the root privilege
63-
#UNIMPLEMENTED| system: |
64-
#UNIMPLEMENTED| #!/bin/bash
65-
#UNIMPLEMENTED| set -eux -o pipefail
66-
#UNIMPLEMENTED| export DEBIAN_FRONTEND=noninteractive
67-
#UNIMPLEMENTED| apt-get install -y vim
68-
#UNIMPLEMENTED| # `user` is executed without the root privilege
69-
#UNIMPLEMENTED| user: |
70-
#UNIMPLEMENTED| #!/bin/bash
71-
#UNIMPLEMENTED| set -eux -o pipefail
72-
#UNIMPLEMENTED| cat <<EOF > ~/.vimrc
73-
#UNIMPLEMENTED| set number
74-
#UNIMPLEMENTED| EOF
61+
containerd:
62+
# Enable system-wide containerd (systemctl enable containerd)
63+
# Default: false
64+
system: false
65+
# Enable user-scoped containerd (systemctl --user enable containerd)
66+
# Default: true
67+
user: true
68+
69+
# Provisioning scripts need to be idempotent because they might be called
70+
# multiple times, e.g. when the host VM is being restarted.
71+
# provision:
72+
# # `system` is executed with the root privilege
73+
# - mode: system
74+
# script: |
75+
# #!/bin/bash
76+
# set -eux -o pipefail
77+
# export DEBIAN_FRONTEND=noninteractive
78+
# apt-get install -y vim
79+
# # `user` is executed without the root privilege
80+
# - mode: user
81+
# script: |
82+
# #!/bin/bash
83+
# set -eux -o pipefail
84+
# cat <<EOF > ~/.vimrc
85+
# set number
86+
# EOF
87+
88+
# probes:
89+
# # Only `readiness` probes are supported right now.
90+
# - mode: readiness
91+
# description: vim to be installed
92+
# script: |
93+
# #!/bin/bash
94+
# set -eux -o pipefail
95+
# if ! timeout 30s bash -c "until command -v vim; do sleep 3; done"; then
96+
# echo >&2 "vim is not installed yet"
97+
# exit 1
98+
# fi
99+
# hint: |
100+
# vim was not installed in the guest. Make sure the package system is working correctly.
101+
# Also see "/var/log/cloud-init-output.log" in the guest.

0 commit comments

Comments
 (0)