Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add command to generate jsonschema for limayaml #2306

Merged
merged 7 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codespellrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
skip = .git,*.pb.desc,*/node_modules/*,*/public/js/*,*/public/scss/*

# Comma separated list of words to be ignored. Words must be lowercased.
ignore-words-list = ans,distroname,testof,hda,ststr
ignore-words-list = ans,distroname,testof,hda,ststr,archtypes

# Check file names as well.
check-filenames = true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
_output/
_artifacts/
lima.REJECTED.yaml
schema-limayaml.json
.config
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,14 @@ ifeq ($(native_compiling),true)
--output _output --prefix $(PREFIX)
endif

################################################################################
schema-limayaml.json: _output/bin/limactl$(exe)
$< generate-jsonschema >$@

.PHONY: check-jsonschema
check-jsonschema: schema-limayaml.json
check-jsonschema --schemafile $< examples/default.yaml

################################################################################
.PHONY: diagrams
diagrams: docs/lima-sequence-diagram.png
Expand Down
59 changes: 59 additions & 0 deletions cmd/limactl/genschema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package main

import (
"encoding/json"
"fmt"

"github.com/invopop/jsonschema"
"github.com/lima-vm/lima/pkg/limayaml"
"github.com/spf13/cobra"
orderedmap "github.com/wk8/go-ordered-map/v2"
)

func newGenSchemaCommand() *cobra.Command {
genschemaCommand := &cobra.Command{
Use: "generate-jsonschema",
Short: "Generate json-schema document",
Args: WrapArgsError(cobra.NoArgs),
RunE: genschemaAction,
Hidden: true,
}
return genschemaCommand
}

func toAny(args []string) []any {
result := []any{nil}
for _, arg := range args {
result = append(result, arg)
}
return result
}

func getProp(props *orderedmap.OrderedMap[string, *jsonschema.Schema], key string) *jsonschema.Schema {
value, ok := props.Get(key)
if !ok {
return nil
}
return value
}

func genschemaAction(cmd *cobra.Command, _ []string) error {
schema := jsonschema.Reflect(&limayaml.LimaYAML{})
// allow Disk to be either string (name) or object (struct)
schema.Definitions["Disk"].Type = "" // was: "object"
schema.Definitions["Disk"].OneOf = []*jsonschema.Schema{
{Type: "string"},
{Type: "object"},
}
properties := schema.Definitions["LimaYAML"].Properties
getProp(properties, "os").Enum = toAny(limayaml.OSTypes)
getProp(properties, "arch").Enum = toAny(limayaml.ArchTypes)
getProp(properties, "mountType").Enum = toAny(limayaml.MountTypes)
getProp(properties, "vmType").Enum = toAny(limayaml.VMTypes)
j, err := json.MarshalIndent(schema, "", " ")
if err != nil {
return err
}
_, err = fmt.Fprintln(cmd.OutOrStdout(), string(j))
return err
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a test? What happens when this is out of the sync with the YAML struct definition?

1 change: 1 addition & 0 deletions cmd/limactl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ func newApp() *cobra.Command {
newDiskCommand(),
newUsernetCommand(),
newGenDocCommand(),
newGenSchemaCommand(),
newSnapshotCommand(),
newProtectCommand(),
newUnprotectCommand(),
Expand Down
25 changes: 13 additions & 12 deletions examples/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ os: null
arch: null

# OpenStack-compatible disk image.
# 🟢 Builtin default: null (must be specified)
# 🟢 Builtin default: none (must be specified)
# 🔵 This file: Ubuntu images
images:
# Try to use release-yyyyMMdd image if available. Note that release-yyyyMMdd will be removed after several months.
Expand Down Expand Up @@ -74,7 +74,7 @@ disk: null
# Expose host directories to the guest, the mount point might be accessible from all UIDs in the guest
# "location" can use these template variables: {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, {{.User}}, and {{.Param.Key}}.
# "mountPoint" can use these template variables: {{.Home}}, {{.Name}}, {{.UID}}, {{.User}}, and {{.Param.Key}}
# 🟢 Builtin default: null (Mount nothing)
# 🟢 Builtin default: [] (Mount nothing)
# 🔵 This file: Mount the home as read-only, /tmp/lima as writable
mounts:
- location: "~"
Expand Down Expand Up @@ -123,8 +123,9 @@ mounts:

# List of mount types not supported by the kernel of this distro.
# Also used to resolve the default mount type when not explicitly specified.
# 🟢 Builtin default: null
mountTypesUnsupported: null
# 🟢 Builtin default: []
mountTypesUnsupported:
# - "9p"

# Mount type for above mounts, such as "reverse-sshfs" (from sshocker), "9p" (QEMU’s virtio-9p-pci, aka virtfs),
# or "virtiofs" (experimental on Linux; needs `vmType: vz` on macOS).
Expand All @@ -139,7 +140,7 @@ mountInotify: null
# instance, labeled by name. (e.g. if the disk is named "data", it will be labeled
# "lima-data" inside the instance). The disk will be mounted inside the instance at
# `/mnt/lima-${VOLUME}`.
# 🟢 Builtin default: null
# 🟢 Builtin default: []
additionalDisks:
# disks should either be a list of disk name strings, for example:
# - "data"
Expand Down Expand Up @@ -222,7 +223,7 @@ containerd:
# Provisioning scripts need to be idempotent because they might be called
# multiple times, e.g. when the host VM is being restarted.
# The scripts can use the following template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}
# 🟢 Builtin default: null
# 🟢 Builtin default: []
# provision:
# # `system` is executed with root privileges
# - mode: system
Expand Down Expand Up @@ -265,7 +266,7 @@ containerd:
# Probe scripts to check readiness.
# The scripts run in user mode. They must start with a '#!' line.
# The scripts can use the following template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}
# 🟢 Builtin default: null
# 🟢 Builtin default: []
# probes:
# # Only `readiness` probes are supported right now.
# - mode: readiness
Expand Down Expand Up @@ -352,7 +353,7 @@ video:

# The instance can get routable IP addresses from the vmnet framework using
# https://github.com/lima-vm/socket_vmnet.
# 🟢 Builtin default: null
# 🟢 Builtin default: []
networks:
# Lima can manage daemons for networks defined in $LIMA_HOME/_config/networks.yaml
# automatically. The socket_vmnet binary must be installed into
Expand Down Expand Up @@ -435,7 +436,7 @@ networks:
# The same template variables as for listing instances can be used, for example {{.Dir}}.
# You can view the complete list of variables using `limactl list --list-fields` command.
# It also includes {{.HostOS}} and {{.HostArch}} vars, for the runtime GOOS and GOARCH.
# 🟢 Builtin default: null
# 🟢 Builtin default: ""
# message: |
# This will be shown to the user.

Expand All @@ -444,7 +445,7 @@ networks:
# to /etc/environment.
# If you set any of "ftp_proxy", "http_proxy", "https_proxy", or "no_proxy", then
# Lima will automatically set an uppercase variant to the same value as well.
# 🟢 Builtin default: null
# 🟢 Builtin default: {}
# env:
# KEY: value

Expand Down Expand Up @@ -480,7 +481,7 @@ hostResolver:
# Static names can be defined here as an alternative to adding them to the hosts /etc/hosts.
# Values can be either other hostnames, or IP addresses. The host.lima.internal name is
# predefined to specify the gateway address to the host.
# 🟢 Builtin default: null
# 🟢 Builtin default: {}
hosts:
# guest.name: 127.1.1.1
# host.name: host.lima.internal
Expand All @@ -492,7 +493,7 @@ hostResolver:
# that has an IPv4 address, to the list. In case this still doesn't work (e.g. VPN
# setups), the servers can be specified here explicitly. If nameservers are specified
# here, then the configuration from network preferences will be ignored.
# 🟢 Builtin default: null
# 🟢 Builtin default: []
# dns:
# - 1.1.1.1
# - 1.0.0.1
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ require (
github.com/goccy/go-yaml v1.12.0
github.com/google/go-cmp v0.6.0
github.com/google/yamlfmt v0.13.0
github.com/invopop/jsonschema v0.12.0
github.com/lima-vm/go-qcow2reader v0.2.0
github.com/lima-vm/sshocker v0.3.4
github.com/mattn/go-isatty v0.0.20
Expand All @@ -39,6 +40,7 @@ require (
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/wk8/go-ordered-map/v2 v2.1.8
golang.org/x/net v0.30.0
golang.org/x/sync v0.8.0
golang.org/x/sys v0.26.0
Expand All @@ -57,8 +59,10 @@ require (
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/a8m/envsubst v1.4.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect
github.com/braydonk/yaml v0.7.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/containerd/errdefs v0.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/digitalocean/go-libvirt v0.0.0-20220804181439-8648fbde413e // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW5
github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a/go.mod h1:QmP9hvJ91BbJmGVGSbutW19IC0Q9phDCLGaomwTJbgU=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/balajiv113/fd v0.0.0-20230330094840-143eec500f3e h1:IdMhFPEfTZQU971tIHx3UhY4l+yCeynprnINrDTSrOc=
github.com/balajiv113/fd v0.0.0-20230330094840-143eec500f3e/go.mod h1:aXGMJsd3XrnUFTuyf/pTGg5jG6CY8JMZ5juywvShjgQ=
github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc=
github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/braydonk/yaml v0.7.0 h1:ySkqO7r0MGoCNhiRJqE0Xe9yhINMyvOAB3nFjgyJn2k=
github.com/braydonk/yaml v0.7.0/go.mod h1:hcm3h581tudlirk8XEUPDBAimBPbmnL0Y45hCRl47N4=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/cheggaaa/pb/v3 v3.1.5 h1:QuuUzeM2WsAqG2gMqtzaWithDJv0i+i6UlnwSCI4QLk=
github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6jM60XI=
github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ=
Expand Down Expand Up @@ -140,6 +144,8 @@ github.com/inetaf/tcpproxy v0.0.0-20240214030015-3ce58045626c h1:gYfYE403/nlrGNY
github.com/inetaf/tcpproxy v0.0.0-20240214030015-3ce58045626c/go.mod h1:Di7LXRyUcnvAcLicFhtM9/MlZl/TNgRSDHORM2c6CMI=
github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9 h1:LZJWucZz7ztCqY6Jsu7N9g124iJ2kt/O62j3+UchZFg=
github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
Expand Down Expand Up @@ -272,6 +278,8 @@ github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down
2 changes: 1 addition & 1 deletion pkg/cidata/cidata.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func templateArgs(bootScripts bool, instDir, name string, instConfig *limayaml.L
if err != nil {
return nil, err
}
mountPoint, err := localpathutil.Expand(f.MountPoint)
mountPoint, err := localpathutil.Expand(*f.MountPoint)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/hostagent/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (a *HostAgent) setupMount(m limayaml.Mount) (*mount, error) {
return nil, err
}

mountPoint, err := localpathutil.Expand(m.MountPoint)
mountPoint, err := localpathutil.Expand(*m.MountPoint)
if err != nil {
return nil, err
}
Expand Down
16 changes: 9 additions & 7 deletions pkg/limayaml/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -619,10 +619,12 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
} else {
logrus.WithError(err).Warnf("Couldn't process mount location %q as a template", mount.Location)
}
if out, err := executeGuestTemplate(mount.MountPoint, instDir, y.Param); err == nil {
mount.MountPoint = out.String()
} else {
logrus.WithError(err).Warnf("Couldn't process mount point %q as a template", mount.MountPoint)
if mount.MountPoint != nil {
if out, err := executeGuestTemplate(*mount.MountPoint, instDir, y.Param); err == nil {
mount.MountPoint = ptr.Of(out.String())
} else {
logrus.WithError(err).Warnf("Couldn't process mount point %q as a template", *mount.MountPoint)
}
}
if i, ok := location[mount.Location]; ok {
if mount.SSHFS.Cache != nil {
Expand Down Expand Up @@ -652,7 +654,7 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
if mount.Writable != nil {
mounts[i].Writable = mount.Writable
}
if mount.MountPoint != "" {
if mount.MountPoint != nil {
mounts[i].MountPoint = mount.MountPoint
}
} else {
Expand Down Expand Up @@ -695,8 +697,8 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
mounts[i].NineP.Cache = ptr.Of(Default9pCacheForRO)
}
}
if mount.MountPoint == "" {
mounts[i].MountPoint = mount.Location
if mount.MountPoint == nil {
mounts[i].MountPoint = ptr.Of(mount.Location)
}
}

Expand Down
8 changes: 4 additions & 4 deletions pkg/limayaml/defaults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func TestFillDefault(t *testing.T) {
},
Mounts: []Mount{
{Location: "/tmp"},
{Location: "{{.Dir}}/{{.Param.ONE}}", MountPoint: "/mnt/{{.Param.ONE}}"},
{Location: "{{.Dir}}/{{.Param.ONE}}", MountPoint: ptr.Of("/mnt/{{.Param.ONE}}")},
},
MountType: ptr.Of(NINEP),
Provision: []Provision{
Expand Down Expand Up @@ -205,7 +205,7 @@ func TestFillDefault(t *testing.T) {
}

expect.Mounts = slices.Clone(y.Mounts)
expect.Mounts[0].MountPoint = expect.Mounts[0].Location
expect.Mounts[0].MountPoint = ptr.Of(expect.Mounts[0].Location)
expect.Mounts[0].Writable = ptr.Of(false)
expect.Mounts[0].SSHFS.Cache = ptr.Of(true)
expect.Mounts[0].SSHFS.FollowSymlinks = ptr.Of(false)
Expand All @@ -217,7 +217,7 @@ func TestFillDefault(t *testing.T) {
expect.Mounts[0].Virtiofs.QueueSize = nil
// Only missing Mounts field is Writable, and the default value is also the null value: false
expect.Mounts[1].Location = fmt.Sprintf("%s/%s", instDir, y.Param["ONE"])
expect.Mounts[1].MountPoint = fmt.Sprintf("/mnt/%s", y.Param["ONE"])
expect.Mounts[1].MountPoint = ptr.Of(fmt.Sprintf("/mnt/%s", y.Param["ONE"]))
expect.Mounts[1].Writable = ptr.Of(false)
expect.Mounts[1].SSHFS.Cache = ptr.Of(true)
expect.Mounts[1].SSHFS.FollowSymlinks = ptr.Of(false)
Expand Down Expand Up @@ -431,7 +431,7 @@ func TestFillDefault(t *testing.T) {
expect.Containerd.Archives = slices.Clone(d.Containerd.Archives)
expect.Containerd.Archives[0].Arch = *d.Arch
expect.Mounts = slices.Clone(d.Mounts)
expect.Mounts[0].MountPoint = expect.Mounts[0].Location
expect.Mounts[0].MountPoint = ptr.Of(expect.Mounts[0].Location)
expect.Mounts[0].SSHFS.Cache = ptr.Of(true)
expect.Mounts[0].SSHFS.FollowSymlinks = ptr.Of(false)
expect.Mounts[0].SSHFS.SFTPDriver = ptr.Of("")
Expand Down
Loading
Loading