Skip to content

Commit 3db7ced

Browse files
authored
Merge pull request #3892 from norio-nomura/yq-provision-mode
feature: "yq" provision mode
2 parents 6414eca + 2f2f47f commit 3db7ced

File tree

16 files changed

+273
-30
lines changed

16 files changed

+273
-30
lines changed

hack/test-templates.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ declare -A CHECKS=(
5656
["user-v2"]=""
5757
["mount-path-with-spaces"]=""
5858
["provision-data"]=""
59+
["provision-yq"]=""
5960
["param-env-variables"]=""
6061
["set-user"]=""
6162
["preserve-env"]="1"
@@ -90,6 +91,7 @@ case "$NAME" in
9091
CHECKS["clone"]="1"
9192
CHECKS["mount-path-with-spaces"]="1"
9293
CHECKS["provision-data"]="1"
94+
CHECKS["provision-yq"]="1"
9395
CHECKS["param-env-variables"]="1"
9496
CHECKS["set-user"]="1"
9597
;;
@@ -199,6 +201,11 @@ if [[ -n ${CHECKS["provision-data"]} ]]; then
199201
limactl shell "$NAME" grep -q fs.inotify.max_user_watches /etc/sysctl.d/99-inotify.conf
200202
fi
201203

204+
if [[ -n ${CHECKS["provision-yq"]} ]]; then
205+
INFO 'Testing that /tmp/param-yq.json was created successfully on provision'
206+
limactl shell "$NAME" grep -q '"YQ": "yq"' /tmp/param-yq.json
207+
fi
208+
202209
if [[ -n ${CHECKS["param-env-variables"]} ]]; then
203210
INFO 'Testing that PARAM env variables are exported to all types of provisioning scripts and probes'
204211
limactl shell "$NAME" test -e /tmp/param-ansible

hack/test-templates/test-misc.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ param:
2020
PROBE: probe
2121
SYSTEM: system
2222
USER: user
23+
YQ: yq
2324

2425
provision:
2526
- mode: ansible
@@ -37,6 +38,10 @@ provision:
3738
content: |
3839
fs.inotify.max_user_watches = 524288
3940
fs.inotify.max_user_instances = 512
41+
- mode: yq
42+
path: "/tmp/param-{{.Param.YQ}}.json"
43+
expression: .YQ = "{{.Param.YQ}}"
44+
user: "{{.User}}"
4045

4146
probes:
4247
- mode: readiness

pkg/cidata/cidata.TEMPLATE.d/boot.sh

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,93 @@ if [ -d "${LIMA_CIDATA_MNT}"/provision.data ]; then
6464
owner=$(deref "LIMA_CIDATA_DATAFILE_${filename}_OWNER")
6565
path=$(deref "LIMA_CIDATA_DATAFILE_${filename}_PATH")
6666
permissions=$(deref "LIMA_CIDATA_DATAFILE_${filename}_PERMISSIONS")
67+
user="${owner%%:*}"
6768
if [ -e "$path" ] && [ "$overwrite" = "false" ]; then
6869
INFO "Not overwriting $path"
6970
else
7071
INFO "Copying $f to $path"
71-
# intermediate directories will be owned by root, regardless of OWNER setting
72-
mkdir -p "$(dirname "$path")"
72+
if ! sudo -iu "${user}" mkdir -p "$(dirname "$path")"; then
73+
WARNING "Failed to create directory for ${path} (as user ${user})"
74+
WARNING "Falling back to creating directory as root to maintain compatibility"
75+
mkdir -p "$(dirname "$path")"
76+
fi
7377
cp "$f" "$path"
7478
chown "$owner" "$path"
7579
chmod "$permissions" "$path"
7680
fi
7781
done
7882
fi
7983

84+
if [ -d "${LIMA_CIDATA_MNT}"/provision.yq ]; then
85+
yq="${LIMA_CIDATA_MNT}/lima-guestagent yq"
86+
for f in "${LIMA_CIDATA_MNT}"/provision.yq/*; do
87+
filename=$(basename "${f}")
88+
format=$(deref "LIMA_CIDATA_YQ_PROVISION_${filename}_FORMAT")
89+
owner=$(deref "LIMA_CIDATA_YQ_PROVISION_${filename}_OWNER")
90+
path=$(deref "LIMA_CIDATA_YQ_PROVISION_${filename}_PATH")
91+
permissions=$(deref "LIMA_CIDATA_YQ_PROVISION_${filename}_PERMISSIONS")
92+
user="${owner%%:*}"
93+
# Creating intermediate directories may fail if the user does not have permission.
94+
# TODO: Create intermediate directories with the specified group ownership.
95+
if ! sudo -iu "${user}" mkdir -p "$(dirname "${path}")"; then
96+
WARNING "Failed to create directory for ${path} (as user ${user})"
97+
CODE=1
98+
continue
99+
fi
100+
# Since CIDATA is mounted with dmode=700,fmode=700,
101+
# `lima-guestagent yq` cannot be executed by non-root users,
102+
# and provision.yq/* files cannot be read by non-root users.
103+
if [ -f "${path}" ]; then
104+
INFO "Updating ${path}"
105+
# If the user does not have write permission, it should fail.
106+
# This avoids changes being made by the wrong user.
107+
if ! sudo -iu "${user}" test -w "${path}"; then
108+
WARNING "File ${path} is not writable by user ${user}"
109+
CODE=1
110+
continue
111+
fi
112+
# Relies on the fact that yq does not change the owner of the existing file.
113+
if ! ${yq} --inplace --from-file "${f}" --input-format "${format}" --output-format "${format}" "${path}"; then
114+
WARNING "Failed to update ${path} (as user ${user})"
115+
CODE=1
116+
continue
117+
fi
118+
else
119+
if [ "${format}" = "auto" ]; then
120+
# yq can't determine the output format from non-existing files
121+
case "${path}" in
122+
*.csv) format=csv ;;
123+
*.ini) format=ini ;;
124+
*.json) format=json ;;
125+
*.properties) format=properties ;;
126+
*.toml) format=toml ;;
127+
*.tsv) format=tsv ;;
128+
*.xml) format=xml ;;
129+
*.yaml | *.yml) format=yaml ;;
130+
*)
131+
format=yaml
132+
WARNING "Cannot determine file type for ${path}, using yaml format"
133+
;;
134+
esac
135+
fi
136+
INFO "Creating ${path}"
137+
if ! ${yq} --null-input --from-file "${f}" --output-format "${format}" | sudo -iu "${user}" tee "${path}"; then
138+
WARNING "Failed to create ${path} (as user ${user})"
139+
CODE=1
140+
continue
141+
fi
142+
fi
143+
if ! sudo -iu "${user}" chown "${owner}" "${path}"; then
144+
WARNING "Failed to set owner for ${path} (as user ${user})"
145+
CODE=1
146+
fi
147+
if ! sudo -iu "${user}" chmod "${permissions}" "${path}"; then
148+
WARNING "Failed to set permissions for ${path} (as user ${user})"
149+
CODE=1
150+
fi
151+
done
152+
fi
153+
80154
if [ -d "${LIMA_CIDATA_MNT}"/provision.system ]; then
81155
for f in "${LIMA_CIDATA_MNT}"/provision.system/*; do
82156
INFO "Executing $f"

pkg/cidata/cidata.TEMPLATE.d/lima.env

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ LIMA_CIDATA_DATAFILE_{{$dataFile.FileName}}_OWNER={{$dataFile.Owner}}
2525
LIMA_CIDATA_DATAFILE_{{$dataFile.FileName}}_PATH={{$dataFile.Path}}
2626
LIMA_CIDATA_DATAFILE_{{$dataFile.FileName}}_PERMISSIONS={{$dataFile.Permissions}}
2727
{{- end}}
28+
{{- range $yqProvision := .YQProvisions}}
29+
LIMA_CIDATA_YQ_PROVISION_{{$yqProvision.FileName}}_FORMAT={{$yqProvision.Format}}
30+
LIMA_CIDATA_YQ_PROVISION_{{$yqProvision.FileName}}_OWNER={{$yqProvision.Owner}}
31+
LIMA_CIDATA_YQ_PROVISION_{{$yqProvision.FileName}}_PATH={{$yqProvision.Path}}
32+
LIMA_CIDATA_YQ_PROVISION_{{$yqProvision.FileName}}_PERMISSIONS={{$yqProvision.Permissions}}
33+
{{- end}}
2834
LIMA_CIDATA_GUEST_INSTALL_PREFIX={{ .GuestInstallPrefix }}
2935
{{- if .Containerd.User}}
3036
LIMA_CIDATA_CONTAINERD_USER=1

pkg/cidata/cidata.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,15 @@ func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, i
335335
Permissions: *f.Permissions,
336336
})
337337
}
338+
if f.Mode == limatype.ProvisionModeYQ {
339+
args.YQProvisions = append(args.YQProvisions, YQProvision{
340+
FileName: fmt.Sprintf("%08d", i),
341+
Format: *f.Format,
342+
Owner: *f.Owner,
343+
Path: *f.Path,
344+
Permissions: *f.Permissions,
345+
})
346+
}
338347
}
339348

340349
return &args, nil
@@ -402,6 +411,11 @@ func GenerateISO9660(ctx context.Context, drv driver.Driver, instDir, name strin
402411
Path: fmt.Sprintf("provision.%s/%08d", f.Mode, i),
403412
Reader: strings.NewReader(*f.Content),
404413
})
414+
case limatype.ProvisionModeYQ:
415+
layout = append(layout, iso9660util.Entry{
416+
Path: fmt.Sprintf("provision.%s/%08d", f.Mode, i),
417+
Reader: strings.NewReader(*f.Expression),
418+
})
405419
case limatype.ProvisionModeBoot:
406420
continue
407421
case limatype.ProvisionModeAnsible:

pkg/cidata/template.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ type DataFile struct {
5858
Permissions string
5959
}
6060

61+
type YQProvision struct {
62+
FileName string
63+
Format string
64+
Owner string
65+
Path string
66+
Permissions string
67+
}
68+
6169
type Disk struct {
6270
Name string
6371
Device string
@@ -93,6 +101,7 @@ type TemplateArgs struct {
93101
Param map[string]string
94102
BootScripts bool
95103
DataFiles []DataFile
104+
YQProvisions []YQProvision
96105
DNSAddresses []string
97106
CACerts CACerts
98107
HostHomeMountPoint string

pkg/limatmpl/embed.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,11 @@ func (tmpl *Template) embedAllScripts(ctx context.Context, embedAll bool) error
648648
if p.Content != nil {
649649
continue
650650
}
651+
case limatype.ProvisionModeYQ:
652+
newName = "expression"
653+
if p.Expression != nil {
654+
continue
655+
}
651656
default:
652657
if p.Script != "" {
653658
continue

pkg/limatmpl/embed_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,9 @@ provision:
343343
- mode: data
344344
file: base1.sh # This comment will move to the "content" key
345345
path: /tmp/data
346+
- mode: yq
347+
file: base1.sh # This comment will move to the "expression" key
348+
path: /tmp/yq
346349
`,
347350
`
348351
# base0.yaml is ignored
@@ -364,6 +367,11 @@ provision:
364367
#!/usr/bin/env bash
365368
echo "This is base1.sh"
366369
path: /tmp/data
370+
- mode: yq
371+
expression: |- # This comment will move to the "expression" key
372+
#!/usr/bin/env bash
373+
echo "This is base1.sh"
374+
path: /tmp/yq
367375
368376
# base0.yaml is ignored
369377
`,

pkg/limatype/lima_yaml.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ const (
235235
ProvisionModeDependency ProvisionMode = "dependency"
236236
ProvisionModeAnsible ProvisionMode = "ansible" // DEPRECATED
237237
ProvisionModeData ProvisionMode = "data"
238+
ProvisionModeYQ ProvisionMode = "yq"
238239
)
239240

240241
type Provision struct {
@@ -245,6 +246,9 @@ type Provision struct {
245246
Playbook string `yaml:"playbook,omitempty" json:"playbook,omitempty"` // DEPRECATED
246247
// All ProvisionData fields must be nil unless Mode is ProvisionModeData
247248
ProvisionData `yaml:",inline"` // Flatten fields for "strict" YAML mode
249+
// ProvisionModeYQ borrows Owner, Path, and Permissions from ProvisionData
250+
Expression *string `yaml:"expression,omitempty" json:"expression,omitempty" jsonschema:"nullable"`
251+
Format *string `yaml:"format,omitempty" json:"format,omitempty" jsonschema:"nullable"`
248252
}
249253

250254
type ProvisionData struct {

pkg/limayaml/defaults.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,13 +405,27 @@ func FillDefault(ctx context.Context, y, d, o *limatype.LimaYAML, filePath strin
405405
if provision.Overwrite == nil {
406406
provision.Overwrite = ptr.Of(true)
407407
}
408+
}
409+
if provision.Mode == limatype.ProvisionModeYQ {
410+
if provision.Expression != nil {
411+
if out, err := executeGuestTemplate(*provision.Expression, instDir, y.User, y.Param); err == nil {
412+
provision.Expression = ptr.Of(out.String())
413+
} else {
414+
logrus.WithError(err).Warnf("Couldn't process expression %q as a template", *provision.Expression)
415+
}
416+
}
417+
if provision.Format == nil {
418+
provision.Format = ptr.Of("auto")
419+
}
420+
}
421+
if provision.Mode == limatype.ProvisionModeData || provision.Mode == limatype.ProvisionModeYQ {
408422
if provision.Owner == nil {
409423
provision.Owner = ptr.Of("root:root")
410424
} else {
411425
if out, err := executeGuestTemplate(*provision.Owner, instDir, y.User, y.Param); err == nil {
412426
provision.Owner = ptr.Of(out.String())
413427
} else {
414-
logrus.WithError(err).Warnf("Couldn't owner %q as a template", *provision.Owner)
428+
logrus.WithError(err).Warnf("Couldn't process owner %q as a template", *provision.Owner)
415429
}
416430
}
417431
// Path is required; validation will throw an error when it is nil

0 commit comments

Comments
 (0)