From 511ce35b9a770112329f9b2f22f0b22137ad16f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Sun, 12 Feb 2023 10:00:35 +0100 Subject: [PATCH] Add builtin yq support to lima start command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows modifying the template inplace for a single instance, if you don't want to change the default or override for all instances. There are currently some quirks with the modified yaml syntax, like removing empty lines or adding indentation on sequences... Signed-off-by: Anders F Björklund --- README.md | 5 +++ cmd/limactl/start.go | 29 +++++++++++++++++ docs/experimental.md | 4 +++ go.mod | 9 ++++++ go.sum | 22 +++++++++++++ pkg/yqutil/yqutil.go | 66 +++++++++++++++++++++++++++++++++++++++ pkg/yqutil/yqutil_test.go | 65 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 200 insertions(+) create mode 100644 pkg/yqutil/yqutil.go create mode 100644 pkg/yqutil/yqutil_test.go diff --git a/README.md b/README.md index 916968a3cb3..463e829cc12 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,11 @@ $ limactl start --name=default template://docker > NOTE: `limactl start template://TEMPLATE` requires Lima v0.9.0 or later. > Older releases require `limactl start /usr/local/share/doc/lima/examples/TEMPLATE.yaml` instead. +To create an instance "default" with modified parameters: +```console +$ limactl start --set='.cpus = 2 | .memory = "2GiB"' +``` + To see the template list: ```console $ limactl start --list-templates diff --git a/cmd/limactl/start.go b/cmd/limactl/start.go index 8cd4d8be3fb..307f2a0b1c8 100644 --- a/cmd/limactl/start.go +++ b/cmd/limactl/start.go @@ -21,6 +21,7 @@ import ( "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" "github.com/lima-vm/lima/pkg/templatestore" + "github.com/lima-vm/lima/pkg/yqutil" "github.com/mattn/go-isatty" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -36,6 +37,9 @@ $ limactl start To create an instance "default" from a template "docker": $ limactl start --name=default template://docker +To create an instance "default" with modified parameters: +$ limactl start --set='.cpus = 2 | .memory = "2GiB"' + To see the template list: $ limactl start --list-templates @@ -56,6 +60,7 @@ $ cat template.yaml | limactl start --name=local - // TODO: "survey" does not support using cygwin terminal on windows yet startCommand.Flags().Bool("tty", isatty.IsTerminal(os.Stdout.Fd()), "enable TUI interactions such as opening an editor, defaults to true when stdout is a terminal") startCommand.Flags().String("name", "", "override the instance name") + startCommand.Flags().String("set", "", "modify the template inplace, using yq syntax") startCommand.Flags().Bool("list-templates", false, "list available templates and exit") startCommand.Flags().Duration("timeout", start.DefaultWatchHostAgentEventsTimeout, "duration to wait for the instance to be running before timing out") return startCommand @@ -82,6 +87,10 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string) (*store.Instance, e if err != nil { return nil, err } + st.yq, err = cmd.Flags().GetString("set") + if err != nil { + return nil, err + } const yBytesLimit = 4 * 1024 * 1024 // 4MiB if ok, u := guessarg.SeemsTemplateURL(arg); ok { @@ -206,6 +215,9 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string) (*store.Instance, e } } else { logrus.Info("Terminal is not available, proceeding without opening an editor") + if err := modifyInPlace(st); err != nil { + return nil, err + } } saveBrokenEditorBuffer := tty return createInstance(st, saveBrokenEditorBuffer) @@ -261,10 +273,27 @@ func createInstance(st *creatorState, saveBrokenEditorBuffer bool) (*store.Insta type creatorState struct { instName string // instance name yBytes []byte // yaml bytes + yq string // yq expression +} + +func modifyInPlace(st *creatorState) error { + if st.yq == "" { + return nil + } + out, err := yqutil.EvaluateExpression(st.yq, st.yBytes) + if err != nil { + return err + } + st.yBytes = out + return nil } func chooseNextCreatorState(st *creatorState) (*creatorState, error) { for { + if err := modifyInPlace(st); err != nil { + logrus.WithError(err).Warn("Failed to evaluate yq expression") + return st, err + } var ans string prompt := &survey.Select{ Message: fmt.Sprintf("Creating an instance %q", st.instName), diff --git a/docs/experimental.md b/docs/experimental.md index 76787efb206..ca26a8234dc 100644 --- a/docs/experimental.md +++ b/docs/experimental.md @@ -6,3 +6,7 @@ The following features are experimental and subject to change: - `vmType: vz` and relevant configurations (`mountType: virtiofs`, `rosetta`, `[]networks.vzNAT`) - `arch: riscv64` - `video.display: vnc` and relevant configuration (`video.vnc.display`) + +The following flags are experimental and subject to change: + +- `start --set`, yq expression diff --git a/go.mod b/go.mod index 704c41b3607..5acc7db3d55 100644 --- a/go.mod +++ b/go.mod @@ -26,6 +26,7 @@ require ( github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-shellwords v1.0.12 github.com/miekg/dns v1.1.50 + github.com/mikefarah/yq/v4 v4.30.8 github.com/norouter/norouter v0.6.3 github.com/nxadm/tail v1.4.8 github.com/opencontainers/go-digest v1.0.0 @@ -35,6 +36,7 @@ require ( github.com/xorcare/pointer v1.2.2 golang.org/x/sync v0.1.0 golang.org/x/sys v0.5.0 + gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 gotest.tools/v3 v3.4.0 inet.af/tcpproxy v0.0.0-20220326234310-be3ee21c9fa0 k8s.io/api v0.26.1 @@ -45,9 +47,13 @@ require ( require ( github.com/Microsoft/go-winio v0.5.2 // indirect github.com/VividCortex/ewma v1.1.1 // indirect + github.com/a8m/envsubst v1.3.0 // indirect + github.com/alecthomas/participle/v2 v2.0.0-beta.5 // indirect github.com/apparentlymart/go-cidr v1.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1 // indirect + github.com/dimchansky/utfbom v1.1.1 // indirect + github.com/elliotchance/orderedmap v1.5.0 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/fatih/color v1.13.0 // indirect @@ -56,6 +62,7 @@ require ( github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/swag v0.19.14 // indirect + github.com/goccy/go-json v0.10.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.1 // indirect @@ -66,10 +73,12 @@ require ( github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f // indirect + github.com/jinzhu/copier v0.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kr/fs v0.1.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-runewidth v0.0.12 // indirect diff --git a/go.sum b/go.sum index ea75927a5d6..f3bc1d316b3 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,12 @@ github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= +github.com/a8m/envsubst v1.3.0 h1:GmXKmVssap0YtlU3E230W98RWtWCyIZzjtf1apWWyAg= +github.com/a8m/envsubst v1.3.0/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY= +github.com/alecthomas/assert/v2 v2.0.3 h1:WKqJODfOiQG0nEJKFKzDIG3E29CN2/4zR9XGJzKIkbg= +github.com/alecthomas/participle/v2 v2.0.0-beta.5 h1:y6dsSYVb1G5eK6mgmy+BgI3Mw35a3WghArZ/Hbebrjo= +github.com/alecthomas/participle/v2 v2.0.0-beta.5/go.mod h1:RC764t6n4L8D8ITAJv0qdokritYSNR3wV5cVwmIEaMM= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= @@ -161,6 +167,8 @@ github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1 h1:j6vGfla github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1/go.mod h1:QS1XzqZLcDniNYrN7EZefq3wIyb/M2WmJbql4ZKoc1Q= github.com/digitalocean/go-qemu v0.0.0-20210326154740-ac9e0b687001 h1:WAg57gnaAWWjMAELcwHjc2xy0PoXQ5G+vn3+XS6s1jI= github.com/digitalocean/go-qemu v0.0.0-20210326154740-ac9e0b687001/go.mod h1:IetBE52JfFxK46p2n2Rqm+p5Gx1gpu2hRHsrbnPOWZQ= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/diskfs/go-diskfs v1.3.0 h1:D3IVe1y7ybB5SjCO0pOmkWThL9lZEWeanp8rRa0q0sk= github.com/diskfs/go-diskfs v1.3.0/go.mod h1:3pUpCAz75Q11om5RsGpVKUgXp2Z+ATw1xV500glmCP0= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= @@ -178,6 +186,8 @@ github.com/elastic/go-libaudit/v2 v2.3.2/go.mod h1:+ZE0czqmbqtnRkl0fNgpI+HvVVRo/ github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= +github.com/elliotchance/orderedmap v1.5.0 h1:1IsExUsjv5XNBD3ZdC7jkAAqLWOOKdbPTmkHx63OsBg= +github.com/elliotchance/orderedmap v1.5.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= @@ -241,6 +251,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= +github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-yaml v1.9.8 h1:5gMyLUeU1/6zl+WFfR1hN7D2kf+1/eRGa7DFtToiBvQ= github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -358,6 +370,7 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -372,6 +385,8 @@ github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7P github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo= github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= +github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -414,6 +429,8 @@ github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2/go.mod h1:SWzULI github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -452,6 +469,8 @@ github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/mikefarah/yq/v4 v4.30.8 h1:EHovseqMJs9kvE25/2k6VnDs4CrBZN+DFbybUhpPAGM= +github.com/mikefarah/yq/v4 v4.30.8/go.mod h1:8D30GDxhu3+KXll0aFV5msGcdgYRZSPOPVBTbgUQ7Dc= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -510,6 +529,7 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1067,6 +1087,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE= +gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/pkg/yqutil/yqutil.go b/pkg/yqutil/yqutil.go new file mode 100644 index 00000000000..5184b4c65e2 --- /dev/null +++ b/pkg/yqutil/yqutil.go @@ -0,0 +1,66 @@ +package yqutil + +import ( + "bytes" + "fmt" + "os" + + "github.com/mikefarah/yq/v4/pkg/yqlib" + "github.com/sirupsen/logrus" + logging "gopkg.in/op/go-logging.v1" +) + +// EvaluateExpression evaluates the yq expression, and returns the modified yaml. +func EvaluateExpression(expression string, content []byte) ([]byte, error) { + tmpYAMLFile, err := os.CreateTemp("", "lima-yq-*.yaml") + if err != nil { + return nil, err + } + tmpYAMLPath := tmpYAMLFile.Name() + defer os.RemoveAll(tmpYAMLPath) + err = os.WriteFile(tmpYAMLPath, content, 0o600) + if err != nil { + return nil, err + } + + memory := logging.NewMemoryBackend(0) + backend := logging.AddModuleLevel(memory) + logging.SetBackend(backend) + yqlib.InitExpressionParser() + + indent := 2 + encoder := yqlib.NewYamlEncoder(indent, false, yqlib.ConfiguredYamlPreferences) + out := new(bytes.Buffer) + printer := yqlib.NewPrinter(encoder, yqlib.NewSinglePrinterWriter(out)) + decoder := yqlib.NewYamlDecoder(yqlib.ConfiguredYamlPreferences) + + streamEvaluator := yqlib.NewStreamEvaluator() + files := []string{tmpYAMLPath} + err = streamEvaluator.EvaluateFiles(expression, files, printer, decoder) + if err != nil { + logger := logrus.StandardLogger() + for node := memory.Head(); node != nil; node = node.Next() { + entry := logrus.NewEntry(logger).WithTime(node.Record.Time) + prefix := fmt.Sprintf("[%s] ", node.Record.Module) + message := prefix + node.Record.Message() + switch node.Record.Level { + case logging.CRITICAL: + entry.Fatal(message) + case logging.ERROR: + entry.Error(message) + case logging.WARNING: + entry.Warn(message) + case logging.NOTICE: + entry.Info(message) + case logging.INFO: + entry.Info(message) + case logging.DEBUG: + entry.Debug(message) + } + } + return nil, err + } + + return out.Bytes(), nil + +} diff --git a/pkg/yqutil/yqutil_test.go b/pkg/yqutil/yqutil_test.go new file mode 100644 index 00000000000..297a2e2c6b6 --- /dev/null +++ b/pkg/yqutil/yqutil_test.go @@ -0,0 +1,65 @@ +package yqutil + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestEvaluateExpressionSimple(t *testing.T) { + expression := `.cpus = 2 | .memory = "2GiB"` + content := ` +# CPUs +cpus: null + +# Memory size +memory: null +` + // Note: yq currently removes empty lines, but not comments + expected := ` +# CPUs +cpus: 2 +# Memory size +memory: 2GiB +` + out, err := EvaluateExpression(expression, []byte(content)) + assert.NilError(t, err) + assert.Equal(t, expected, string(out)) +} + +func TestEvaluateExpressionComplex(t *testing.T) { + expression := `.mounts += {"location": "foo", "mountPoint": "bar"}` + content := ` +# Expose host directories to the guest, the mount point might be accessible from all UIDs in the guest +# 🟢 Builtin default: null (Mount nothing) +# 🔵 This file: Mount the home as read-only, /tmp/lima as writable +mounts: +- location: "~" + # Configure the mountPoint inside the guest. + # 🟢 Builtin default: value of location + mountPoint: null +` + // Note: yq will use canonical yaml, with indented sequences + // Note: yq will not explicitly quote strings, when not needed + expected := ` +# Expose host directories to the guest, the mount point might be accessible from all UIDs in the guest +# 🟢 Builtin default: null (Mount nothing) +# 🔵 This file: Mount the home as read-only, /tmp/lima as writable +mounts: + - location: "~" + # Configure the mountPoint inside the guest. + # 🟢 Builtin default: value of location + mountPoint: null + - location: foo + mountPoint: bar +` + out, err := EvaluateExpression(expression, []byte(content)) + assert.NilError(t, err) + assert.Equal(t, expected, string(out)) +} + +func TestEvaluateExpressionError(t *testing.T) { + expression := `arch: aarch64` + _, err := EvaluateExpression(expression, []byte("")) + assert.ErrorContains(t, err, "invalid input text") +}