diff --git a/go.mod b/go.mod index 9b2ee0ad..8edd6e3f 100644 --- a/go.mod +++ b/go.mod @@ -11,12 +11,15 @@ require ( github.com/itchyny/gojq v0.12.12 github.com/jaypipes/ghw v0.10.0 github.com/joho/godotenv v1.5.1 + github.com/kairos-io/kairos/v2 v2.0.3 github.com/mudler/go-pluggable v0.0.0-20230126220627-7710299a0ae5 github.com/mudler/yip v1.1.0 github.com/onsi/ginkgo/v2 v2.9.5 - github.com/onsi/gomega v1.27.6 + github.com/onsi/gomega v1.27.7 github.com/pterm/pterm v0.12.61 github.com/qeesung/image2ascii v1.0.1 + github.com/santhosh-tekuri/jsonschema/v5 v5.3.0 + github.com/swaggest/jsonschema-go v0.3.51 github.com/twpayne/go-vfs/v4 v4.2.0 github.com/zcalusic/sysinfo v0.9.5 gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 @@ -52,7 +55,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 // indirect github.com/gookit/color v1.5.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/itchyny/timefmt-go v0.1.5 // indirect @@ -64,12 +67,12 @@ require ( github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/sirupsen/logrus v1.9.0 // indirect + github.com/swaggest/refl v1.1.0 // indirect github.com/twpayne/go-vfs v1.7.2 // indirect github.com/vbatts/tar-split v0.11.3 // indirect github.com/wayneashleyberry/terminal-dimensions v1.1.0 // indirect diff --git a/go.sum b/go.sum index c3c25fb2..3b4ab35b 100644 --- a/go.sum +++ b/go.sum @@ -25,14 +25,14 @@ github.com/Microsoft/hcsshim v0.10.0-rc.8/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCzt github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= +github.com/bool64/dev v0.2.27 h1:mFT+B74mFVgUeUmm/EbfM6ELPA55lEXBjQ/AOHCwCOc= +github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 h1:xz6Nv3zcwO2Lila35hcb0QloCQsc38Al13RNEzWRpX4= github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9/go.mod h1:2wSM9zJkl1UQEFZgSd68NfCgRz1VL1jzy/RjCg+ULrs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= @@ -72,15 +72,11 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -112,19 +108,15 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.15.1 h1:RsJ9NbfxYWF8Wl4VmvkpN3zYATwuvlPq2j20zmcs63E= -github.com/google/go-containerregistry v0.15.1/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE= github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 h1:CqYfpuYIjnlNxM3msdyPRKabhXZWbKjf3Q8BWROFBso= +github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= -github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= -github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= github.com/gookit/color v1.5.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE= github.com/gookit/color v1.5.3/go.mod h1:NUzwzeehUfl7GIb36pqId+UGmRfQcU/WiiyTTeNjHtE= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -133,7 +125,7 @@ github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA= github.com/itchyny/gojq v0.12.12 h1:x+xGI9BXqKoJQZkr95ibpe3cdrTbY8D9lonrK433rcA= github.com/itchyny/gojq v0.12.12/go.mod h1:j+3sVkjxwd7A7Z5jrbKibgOLn0ZfLWkV+Awxr/pyzJE= github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= @@ -145,6 +137,8 @@ github.com/jaypipes/pcidb v1.0.0/go.mod h1:TnYUvqhPBzCKnH34KrIX22kAeEbDCSRJ9cqLR github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kairos-io/kairos/v2 v2.0.3 h1:IOgK+bKklERQpU+ZQ1j/tX2qmKD4CFTQ0vzlMymQvno= +github.com/kairos-io/kairos/v2 v2.0.3/go.mod h1:dzys8LY3s/J7HutuJ3Qoz2Cj1Pp668dXeTRPe00rpnk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= @@ -156,10 +150,8 @@ github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y7 github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c= -github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/lithammer/fuzzysearch v1.1.7 h1:q8rZNmBIUkqxsxb/IlwsXVbCoPIH/0juxjFHY0UIwhU= github.com/lithammer/fuzzysearch v1.1.7/go.mod h1:ZhIlfRGxnD8qa9car/yplC6GmnM14CS07BYAKJJBK2I= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= @@ -176,33 +168,23 @@ github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/mudler/go-pluggable v0.0.0-20230126220627-7710299a0ae5 h1:FaZD86+A9mVt7lh9glAryzQblMsbJYU2VnrdZ8yHlTs= github.com/mudler/go-pluggable v0.0.0-20230126220627-7710299a0ae5/go.mod h1:WmKcT8ONmhDQIqQ+HxU+tkGWjzBEyY/KFO8LTGCu4AI= -github.com/mudler/yip v0.11.5-0.20230124143654-91e88dfb6648 h1:+UZPjgWOTB1LyWI5qHTReIGXDSlXynGl2kIihi/lU98= -github.com/mudler/yip v0.11.5-0.20230124143654-91e88dfb6648/go.mod h1:7d0bnZ326k/bmeTvLZL5ZQx4QNi0a7mfrnGzb+2ZkrA= github.com/mudler/yip v1.1.0 h1:QQLQhD5FQ7ojaP7s7dIll6pSGnwnIplL1zGMSg5lsHQ= github.com/mudler/yip v1.1.0/go.mod h1:GIzGnY6+tP7kaNBsmtisdyuo4cgn/4y6bEOS3GZNtkY= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= -github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= -github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= -github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= -github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= +github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= @@ -219,8 +201,6 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= -github.com/pterm/pterm v0.12.56 h1:3mrF0ytaltvWc32inyGD1Xw4Bpa/20gjGry2rImVIUc= -github.com/pterm/pterm v0.12.56/go.mod h1:7rswprkyxYOse1IMh79w42jvReNHxro4z9oHfqjIdzM= github.com/pterm/pterm v0.12.61 h1:cZFweZ0C4zbBsusyThfgqg0KU0PTnq5xupnGN3Ytxzc= github.com/pterm/pterm v0.12.61/go.mod h1:07yyGZKQr8BpKKBaOZI1qKzzngqUisHdSYR4fQ9Nb4g= github.com/qeesung/image2ascii v1.0.1 h1:Fe5zTnX/v/qNC3OC4P/cfASOXS501Xyw2UUcgrLgtp4= @@ -229,8 +209,10 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.0 h1:uIkTLo0AGRc8l7h5l9r+GcYi9qfVPt6lD4/bhmzfiKo= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -239,13 +221,17 @@ github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/swaggest/assertjson v1.8.0 h1:XSg4p6iOZMjtpV2tW2SXfD1GsOOTsWcm+sOADODu/DU= +github.com/swaggest/jsonschema-go v0.3.51 h1:Cl0hFQ/jtBIP8NlHNuwW6ka3J7zzW5r2jxbLSCUByGY= +github.com/swaggest/jsonschema-go v0.3.51/go.mod h1:QfUB5HaZ8y5TiFtCPhM7QwvPNKxTsYxDJaLHTLq6jgU= +github.com/swaggest/refl v1.1.0 h1:a+9a75Kv6ciMozPjVbOfcVTEQe81t2R3emvaD9oGQGc= +github.com/swaggest/refl v1.1.0/go.mod h1:g3Qa6ki0A/L2yxiuUpT+cuBURuRaltF5SDQpg1kMZSY= github.com/twpayne/go-vfs v1.7.2 h1:ZNYMAXcu2Av8c109USrSGYm8dIIIV0xPlG19I2088Kw= github.com/twpayne/go-vfs v1.7.2/go.mod h1:1eni2ntkiiAHZG27xfLOO4CYvMR4Kw8V7rYiLeeolsQ= github.com/twpayne/go-vfs/v4 v4.2.0 h1:cIjUwaKSCq0y6dT+ev6uLSmKjGTbHCR4xaocROqHFsE= @@ -258,6 +244,8 @@ github.com/wayneashleyberry/terminal-dimensions v1.1.0/go.mod h1:2lc/0eWCObmhRcz github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -270,7 +258,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= +golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b h1:SCE/18RnFsLrjydh/R/s5EVvHoZprqEQUuoxK8q2Pc4= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -295,8 +283,6 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -306,7 +292,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -318,13 +303,11 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -337,8 +320,6 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -346,8 +327,6 @@ golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -366,12 +345,9 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= -golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -422,7 +398,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/schema/install_schema.go b/schema/install_schema.go new file mode 100644 index 00000000..a2d62c7c --- /dev/null +++ b/schema/install_schema.go @@ -0,0 +1,79 @@ +package schema + +import ( + jsonschemago "github.com/swaggest/jsonschema-go" +) + +// InstallSchema represents the install block in the Kairos configuration. It is used to drive automatic installations without user interaction. +type InstallSchema struct { + _ struct{} `title:"Kairos Schema: Install block" description:"The install block is to drive automatic installations without user interaction."` + Auto bool `json:"auto,omitempty" description:"Set to true when installing without Pairing"` + BindMounts []string `json:"bind_mounts,omitempty"` + Bundles []BundleSchema `json:"bundles,omitempty" description:"Add bundles in runtime"` + Device string `json:"device,omitempty" pattern:"^(auto|/|(/[a-zA-Z0-9_-]+)+)$" description:"Device for automated installs" examples:"[\"auto\",\"/dev/sda\"]"` + EphemeralMounts []string `json:"ephemeral_mounts,omitempty"` + EncryptedPartitions []string `json:"encrypted_partitions,omitempty"` + Env []interface{} `json:"env,omitempty"` + GrubOptionsSchema `json:"grub_options,omitempty"` + Image string `json:"image,omitempty" description:"Use a different container image for the installation"` + PowerManagement + SkipEncryptCopyPlugins bool `json:"skip_copy_kcrypt_plugin,omitempty"` +} + +// BundleSchema represents the bundle block which can be used in different places of the Kairos configuration. It is used to reference a bundle and its confguration. +type BundleSchema struct { + DB string `json:"db_path,omitempty"` + LocalFile bool `json:"local_file,omitempty"` + Repository string `json:"repository,omitempty"` + Rootfs string `json:"rootfs_path,omitempty"` + Targets []string `json:"targets,omitempty"` +} + +// GrubOptionsSchema represents the grub options block which can be used in different places of the Kairos configuration. It is used to configure grub. +type GrubOptionsSchema struct { + DefaultFallback string `json:"default_fallback,omitempty" description:"Sets default fallback logic"` + DefaultMenuEntry string `json:"default_menu_entry,omitempty" description:"Change GRUB menu entry"` + ExtraActiveCmdline string `json:"extra_active_cmdline,omitempty" description:"Additional Kernel option cmdline to apply just for active"` + ExtraCmdline string `json:"extra_cmdline,omitempty" description:"Additional Kernel option cmdline to apply"` + ExtraPassiveCmdline string `json:"extra_passive_cmdline,omitempty" description:"Additional Kernel option cmdline to apply just for passive"` + ExtraRecoveryCmdline string `json:"extra_recovery_cmdline,omitempty" description:"Set additional boot commands when booting into recovery"` + NextEntry string `json:"next_entry,omitempty" description:"Set the next reboot entry."` + SavedEntry string `json:"saved_entry,omitempty" description:"Set the default boot entry."` +} + +// PowerManagement is a meta structure to hold the different rules for managing power, which are not compatible between each other. +type PowerManagement struct { +} + +// NoPowerManagement is a meta structure used when the user does not define any power management options or when the user does not want to reboot or poweroff the machine. +type NoPowerManagement struct { + Reboot bool `json:"reboot,omitempty" const:"false" default:"false" description:"Reboot after installation"` + Poweroff bool `json:"poweroff,omitempty" const:"false" default:"false" description:"Power off after installation"` +} + +// RebootOnly is a meta structure used to enforce that when the reboot option is set, the poweroff option is not set. +type RebootOnly struct { + Reboot bool `json:"reboot,omitempty" const:"true" default:"false" required:"true" description:"Reboot after installation"` + Poweroff bool `json:"poweroff,omitempty" const:"false" default:"false" description:"Power off after installation"` +} + +// PowerOffOnly is a meta structure used to enforce that when the poweroff option is set, the reboot option is not set. +type PowerOffOnly struct { + Reboot bool `json:"reboot,omitempty" const:"false" default:"false" description:"Reboot after installation"` + Poweroff bool `json:"poweroff,omitempty" const:"true" default:"false" required:"true" description:"Power off after installation"` +} + +var _ jsonschemago.OneOfExposer = PowerManagement{} + +// The OneOfModel interface is only needed for the tests that check the new schema contain all needed fields +// it can be removed once the new schema is the single source of truth. +type OneOfModel interface { + JSONSchemaOneOf() []interface{} +} + +// JSONSchemaOneOf defines that different which are the different valid power management rules and states that one and only one of them needs to be validated for the entire schema to be valid. +func (PowerManagement) JSONSchemaOneOf() []interface{} { + return []interface{}{ + NoPowerManagement{}, RebootOnly{}, PowerOffOnly{}, + } +} diff --git a/schema/install_schema_test.go b/schema/install_schema_test.go new file mode 100644 index 00000000..50536383 --- /dev/null +++ b/schema/install_schema_test.go @@ -0,0 +1,134 @@ +package schema_test + +import ( + "strings" + + . "github.com/kairos-io/kairos-sdk/schema" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Install Schema", func() { + var config *KConfig + var err error + var yaml string + + JustBeforeEach(func() { + config, err = NewConfigFromYAML(yaml, InstallSchema{}) + Expect(err).ToNot(HaveOccurred()) + }) + + Context("when device is auto", func() { + BeforeEach(func() { + yaml = `#cloud-config +device: auto` + }) + + It("succeedes", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) + + Context("when device is a path", func() { + BeforeEach(func() { + yaml = `#cloud-config +device: /dev/sda` + }) + + It("succeedes", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) + + Context("when device is other than a path or auto", func() { + BeforeEach(func() { + yaml = `#cloud-config +device: foobar` + }) + + It("errors", func() { + Expect(config.IsValid()).NotTo(BeTrue()) + Expect( + strings.Contains(config.ValidationError.Error(), + "does not match pattern '^(auto|/|(/[a-zA-Z0-9_-]+)+)$'", + ), + ).To(BeTrue()) + }) + }) + + Context("when reboot and poweroff are true", func() { + BeforeEach(func() { + yaml = `#cloud-config +device: /dev/sda +reboot: true +poweroff: true` + }) + + It("errors", func() { + Expect(config.IsValid()).NotTo(BeTrue()) + Expect(config.ValidationError.Error()).To(MatchRegexp("value must be false")) + }) + }) + + Context("when reboot is true and poweroff is false", func() { + BeforeEach(func() { + yaml = `#cloud-config +device: /dev/sda +reboot: true +poweroff: false` + }) + + It("succeedes", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) + + Context("when reboot is false and poweroff is true", func() { + BeforeEach(func() { + yaml = `#cloud-config +device: /dev/sda +reboot: false +poweroff: true` + }) + + It("succeedes", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) + + Context("with no power management set", func() { + BeforeEach(func() { + yaml = `#cloud-config +device: /dev/sda` + }) + + It("succeedes", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) + + Context("with all possible options", func() { + BeforeEach(func() { + yaml = `#cloud-config +device: "/dev/sda" +reboot: true +auto: true +image: "docker:.." +bundles: + - rootfs_path: /usr/local/lib/extensions/ + targets: + - container:// +grub_options: + extra_cmdline: "config_url=http://" + extra_active_cmdline: "config_url=http://" + extra_passive_cmdline: "config_url=http://" + default_menu_entry: "foobar" +env: + - foo=barevice: /dev/sda` + }) + + It("succeedes", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) +}) diff --git a/schema/p2p_schema.go b/schema/p2p_schema.go new file mode 100644 index 00000000..e3147cd8 --- /dev/null +++ b/schema/p2p_schema.go @@ -0,0 +1,68 @@ +package schema + +import ( + jsonschemago "github.com/swaggest/jsonschema-go" +) + +// P2PSchema represents the P2P block in the Kairos configuration. It is used to enables and configure the p2p full-mesh functionalities. +type P2PSchema struct { + _ struct{} `title:"Kairos Schema: P2P block" description:"The p2p block enables the p2p full-mesh functionalities."` + Role string `json:"role,omitempty" default:"none" enum:"[\"master\",\"worker\",\"none\"]"` + NetworkID string `json:"network_id,omitempty" description:"User defined network-id. Can be used to have multiple clusters in the same network"` + DNS bool `json:"dns,omitempty" description:"Enable embedded DNS See also: https://mudler.github.io/edgevpn/docs/concepts/overview/dns/"` + DisableDHT bool `json:"disable_dht,omitempty" default:"true" description:"Disabling DHT makes co-ordination to discover nodes only in the local network"` + P2PNetworkExtended + VPN `json:"vpn,omitempty"` +} + +// KubeVIPSchema represents the kubevip block in the Kairos configuration. It sets the Elastic IP used in KubeVIP. Only valid with p2p. +type KubeVIPSchema struct { + _ struct{} `title:"Kairos Schema: KubeVIP block" description:"Sets the Elastic IP used in KubeVIP. Only valid with p2p"` + EIP string `json:"eip,omitempty" example:"192.168.1.110"` + ManifestURL string `json:"manifest_url,omitempty" description:"Specify a manifest URL for KubeVIP." default:""` + Enable bool `json:"enable,omitempty" description:"Enables KubeVIP"` + Interface bool `json:"interface,omitempty" description:"Specifies a KubeVIP Interface" example:"ens18"` +} + +// P2PNetworkExtended is a meta structure to hold the different rules for managing the P2P network, which are not compatible between each other. +type P2PNetworkExtended struct { +} + +// P2PAutoDisabled is used to validate that when p2p.auto is disabled, then neither p2p.auto.ha not p2p.network_token can be set. +type P2PAutoDisabled struct { + NetworkToken string `json:"network_token,omitempty" const:"" required:"true"` + Auto struct { + Enable bool `json:"enable" const:"false" required:"true"` + Ha struct { + Enable bool `json:"enable" const:"false"` + } `json:"ha"` + } `json:"auto"` +} + +// P2PAutoEnabled is used to validate that when p2p.auto is set, p2p.network_token has to be set. +type P2PAutoEnabled struct { + NetworkToken string `json:"network_token" required:"true" minLength:"1" description:"network_token is the shared secret used by the nodes to co-ordinate with p2p"` + Auto struct { + Enable bool `json:"enable,omitempty" const:"true"` + Ha struct { + Enable bool `json:"enable" const:"true"` + MasterNodes int `json:"master_nodes,omitempty" minimum:"1" description:"Number of HA additional master nodes. A master node is always required for creating the cluster and is implied."` + } `json:"ha"` + } `json:"auto,omitempty"` +} + +var _ jsonschemago.OneOfExposer = P2PNetworkExtended{} + +// JSONSchemaOneOf defines that different which are the different valid p2p network rules and states that one and only one of them needs to be validated for the entire schema to be valid. +func (P2PNetworkExtended) JSONSchemaOneOf() []interface{} { + return []interface{}{ + P2PAutoEnabled{}, P2PAutoDisabled{}, + } +} + +// VPN represents the vpn block in the Kairos configuration. +type VPN struct { + Create bool `json:"vpn,omitempty" default:"true"` + Use bool `json:"use,omitempty" default:"true"` + Envs []interface{} `json:"env,omitempty"` +} diff --git a/schema/p2p_schema_test.go b/schema/p2p_schema_test.go new file mode 100644 index 00000000..96bc9cf2 --- /dev/null +++ b/schema/p2p_schema_test.go @@ -0,0 +1,181 @@ +package schema_test + +import ( + "strings" + + . "github.com/kairos-io/kairos-sdk/schema" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("P2P Schema", func() { + var config *KConfig + var err error + var yaml string + + JustBeforeEach(func() { + config, err = NewConfigFromYAML(yaml, P2PSchema{}) + Expect(err).ToNot(HaveOccurred()) + }) + + Context("with role master", func() { + BeforeEach(func() { + yaml = `#cloud-config +role: master +network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="` + }) + + It("succeeds", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) + + Context("with role worker", func() { + BeforeEach(func() { + yaml = `#cloud-config +role: worker +network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="` + }) + + It("succeeds", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) + + Context("with role none", func() { + BeforeEach(func() { + yaml = `#cloud-config +role: none +network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="` + }) + + It("succeeds", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) + + Context("with other role", func() { + BeforeEach(func() { + yaml = `#cloud-config +role: foobar +network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="` + }) + + It("errors", func() { + Expect(config.IsValid()).NotTo(BeTrue()) + Expect(config.ValidationError.Error()).To(MatchRegexp(`value must be one of "master", "worker", "none"`)) + }) + }) + + Context("With a network_token and p2p.auto.enable = false", func() { + BeforeEach(func() { + yaml = `#cloud-config +network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg==" +auto: + enable: false` + }) + + It("errors", func() { + Expect(config.IsValid()).NotTo(BeTrue()) + Expect( + strings.Contains(config.ValidationError.Error(), `value must be true`), + ).To(BeTrue()) + }) + }) + + Context("With an empty network_token and p2p.auto.enable = true", func() { + BeforeEach(func() { + yaml = `#cloud-config +network_token: "" +auto: + enable: true` + }) + + It("Fails", func() { + Expect(config.IsValid()).NotTo(BeTrue()) + Expect( + strings.Contains(config.ValidationError.Error(), + "length must be >= 1, but got 0", + ), + ).To(BeTrue()) + }) + }) + + Context("With a network_token and p2p.auto.enable = true", func() { + BeforeEach(func() { + yaml = `#cloud-config +network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg==" +auto: + enable: true` + }) + + It("succeeds", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) + + Context("With a p2p.auto.enable = false and ha.enable = true", func() { + BeforeEach(func() { + yaml = `#cloud-config +network_token: "" +auto: + enable: false + ha: + enable: true` + }) + + It("errors", func() { + Expect(config.IsValid()).NotTo(BeTrue()) + Expect(config.ValidationError.Error()).To(MatchRegexp("(length must be >= 1, but got 0|value must be true)")) + }) + }) + + Context("HA with 0 master nodes", func() { + BeforeEach(func() { + yaml = `#cloud-config +network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg==" +auto: + enable: true + ha: + enable: true + master_nodes: 0` + }) + + It("fails", func() { + Expect(config.IsValid()).NotTo(BeTrue()) + Expect(config.ValidationError.Error()).To(MatchRegexp("must be >= 1 but found 0")) + }) + }) + + Context("HA", func() { + BeforeEach(func() { + yaml = `#cloud-config +network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg==" +auto: + enable: true + ha: + enable: true + master_nodes: 2` + }) + + It("succeedes", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) + + Context("kubevip", func() { + BeforeEach(func() { + yaml = `#cloud-config +network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg==" +auto: + enable: true + ha: + enable: true + master_nodes: 2` + }) + + It("succeedes", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) +}) diff --git a/schema/root_schema.go b/schema/root_schema.go new file mode 100644 index 00000000..5c7d29d6 --- /dev/null +++ b/schema/root_schema.go @@ -0,0 +1,106 @@ +package schema + +import ( + "encoding/json" + "strings" + + "github.com/santhosh-tekuri/jsonschema/v5" + jsonschemago "github.com/swaggest/jsonschema-go" + "gopkg.in/yaml.v3" +) + +// RootSchema groups all the different schema of the Kairos configuration together. +type RootSchema struct { + _ struct{} `title:"Kairos Schema" description:"Defines all valid Kairos configuration attributes."` + Bundles []BundleSchema `json:"bundles,omitempty" description:"Add bundles in runtime"` + ConfigURL string `json:"config_url,omitempty" description:"URL download configuration from."` + Env []string `json:"env,omitempty"` + FailOnBundleErrors bool `json:"fail_on_bundles_errors,omitempty"` + GrubOptionsSchema `json:"grub_options,omitempty"` + Install InstallSchema `json:"install,omitempty"` + Options []interface{} `json:"options,omitempty" description:"Various options."` + Users []UserSchema `json:"users,omitempty" minItems:"1" required:"true"` + P2P P2PSchema `json:"p2p,omitempty"` +} + +// KConfig is used to parse and validate Kairos configuration files. +type KConfig struct { + Source string + parsed interface{} + ValidationError error + schemaType interface{} +} + +// GenerateSchema takes the given schema type and builds a JSON Schema out of it +// if a URL is passed it will also add it as the $schema key, which is useful when +// defining a version of a Root Schema which will be available online. +func GenerateSchema(schemaType interface{}, url string) (string, error) { + reflector := jsonschemago.Reflector{} + + generatedSchema, err := reflector.Reflect(schemaType) + if err != nil { + return "", err + } + if url != "" { + generatedSchema.WithSchema(url) + } + + generatedSchemaJSON, err := json.MarshalIndent(generatedSchema, "", " ") + if err != nil { + return "", err + } + + return string(generatedSchemaJSON), nil +} + +func (kc *KConfig) validate() { + generatedSchemaJSON, err := GenerateSchema(kc.schemaType, "") + if err != nil { + kc.ValidationError = err + return + } + + sch, err := jsonschema.CompileString("schema.json", string(generatedSchemaJSON)) + if err != nil { + kc.ValidationError = err + return + } + + if err = sch.Validate(kc.parsed); err != nil { + kc.ValidationError = err + } +} + +// IsValid returns true if the schema rules of the configuration are valid. +func (kc *KConfig) IsValid() bool { + kc.validate() + + return kc.ValidationError == nil +} + +// HasHeader returns true if the config has one of the valid headers. +func (kc *KConfig) HasHeader() bool { + var found bool + + availableHeaders := []string{"#cloud-config", "#kairos-config", "#node-config"} + for _, header := range availableHeaders { + if strings.HasPrefix(kc.Source, header) { + found = true + } + } + return found +} + +// NewConfigFromYAML is a constructor for KConfig instances. The source of the configuration is passed in YAML and if there are any issues unmarshaling it will return an error. +func NewConfigFromYAML(s string, st interface{}) (*KConfig, error) { + kc := &KConfig{ + Source: s, + schemaType: st, + } + + err := yaml.Unmarshal([]byte(s), &kc.parsed) + if err != nil { + return kc, err + } + return kc, nil +} diff --git a/schema/root_schema_test.go b/schema/root_schema_test.go new file mode 100644 index 00000000..b398175e --- /dev/null +++ b/schema/root_schema_test.go @@ -0,0 +1,118 @@ +package schema_test + +import ( + "strings" + + . "github.com/kairos-io/kairos-sdk/schema" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Schema", func() { + Context("NewConfigFromYAML", func() { + var config *KConfig + var err error + var yaml string + + JustBeforeEach(func() { + config, err = NewConfigFromYAML(yaml, RootSchema{}) + }) + + Context("With invalid YAML syntax", func() { + BeforeEach(func() { + yaml = `#cloud-config +this is: +- invalid +yaml` + }) + + It("errors", func() { + Expect(err.Error()).To(MatchRegexp("yaml: line 4: could not find expected ':'")) + }) + }) + + Context("When `users` is empty", func() { + BeforeEach(func() { + yaml = `#cloud-config +users: []` + }) + + It("errors", func() { + Expect(err).ToNot(HaveOccurred()) + Expect(config.IsValid()).NotTo(BeTrue()) + Expect(config.ValidationError.Error()).To(MatchRegexp("minimum 1 items required, but found 0 items")) + }) + }) + + Context("without a valid header", func() { + BeforeEach(func() { + yaml = `--- +users: + - name: kairos + passwd: kairos` + }) + + It("is successful but HasHeader returns false", func() { + Expect(err).ToNot(HaveOccurred()) + Expect(config.HasHeader()).To(BeFalse()) + }) + }) + + Context("With a valid config", func() { + BeforeEach(func() { + yaml = `#cloud-config +users: + - name: kairos + passwd: kairos` + }) + + It("is successful", func() { + Expect(err).ToNot(HaveOccurred()) + Expect(config.HasHeader()).To(BeTrue()) + }) + }) + }) + + Context("GenerateSchema", func() { + var url string + var schema string + var err error + + type TestSchema struct { + Key interface{} `json:"key,omitempty" required:"true"` + } + + JustBeforeEach(func() { + schema, err = GenerateSchema(TestSchema{}, url) + Expect(err).ToNot(HaveOccurred()) + }) + + It("does not include the $schema key by default", func() { + Expect(strings.Contains(schema, `$schema`)).To(BeFalse()) + }) + + It("can use any type of schma", func() { + wants := `{ + "required": [ + "key" + ], + "properties": { + "key": {} + }, + "type": "object" +}` + Expect(schema).To(Equal(wants)) + }) + + Context("with a URL", func() { + BeforeEach(func() { + url = "http://foobar" + }) + + It("appends the $schema key", func() { + Expect(strings.Contains(schema, `$schema": "http://foobar"`)).To(BeTrue()) + }) + }) + + }) +}) diff --git a/schema/suite_test.go b/schema/suite_test.go new file mode 100644 index 00000000..97947faa --- /dev/null +++ b/schema/suite_test.go @@ -0,0 +1,13 @@ +package schema_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestConfig(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Config Schemas Suite") +} diff --git a/schema/users_schema.go b/schema/users_schema.go new file mode 100644 index 00000000..2a52c2ea --- /dev/null +++ b/schema/users_schema.go @@ -0,0 +1,11 @@ +package schema + +// UserSchema represents the users block in the Kairos configuration. It allows the creation of users in the system. +type UserSchema struct { + _ struct{} `title:"Kairos Schema: Users block" description:"The users block allows you to create users in the system."` + Name string `json:"name,omitempty" pattern:"([a-z_][a-z0-9_]{0,30})" required:"true" example:"kairos"` + Passwd string `json:"passwd,omitempty" example:"kairos"` + LockPasswd bool `json:"lockPasswd,omitempty" example:"true"` + Groups []string `json:"groups,omitempty" example:"admin"` + SSHAuthorizedKeys []string `json:"ssh_authorized_keys,omitempty" examples:"[\"github:USERNAME\",\"ssh-ed25519 AAAF00BA5\"]"` +} diff --git a/schema/users_schema_test.go b/schema/users_schema_test.go new file mode 100644 index 00000000..e908de50 --- /dev/null +++ b/schema/users_schema_test.go @@ -0,0 +1,77 @@ +package schema_test + +import ( + "strings" + + . "github.com/kairos-io/kairos-sdk/schema" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Users Schema", func() { + var config *KConfig + var err error + var yaml string + + JustBeforeEach(func() { + config, err = NewConfigFromYAML(yaml, UserSchema{}) + Expect(err).ToNot(HaveOccurred()) + }) + + Context("When a user has no name", func() { + BeforeEach(func() { + yaml = `#cloud-config +passwd: foobar` + }) + + It("errors", func() { + Expect(config.IsValid()).NotTo(BeTrue()) + Expect(config.ValidationError.Error()).To(MatchRegexp("missing properties: 'name'")) + }) + }) + + Context("When a user name doesn't fit the pattern", func() { + BeforeEach(func() { + yaml = `#cloud-config +name: "007" +passwd: "bond"` + }) + + It("errors", func() { + Expect(config.IsValid()).NotTo(BeTrue()) + Expect( + strings.Contains(config.ValidationError.Error(), + "does not match pattern '([a-z_][a-z0-9_]{0,30})'", + ), + ).To(BeTrue()) + }) + }) + + Context("With only the required attributes", func() { + BeforeEach(func() { + yaml = `#cloud-config +name: "kairos"` + }) + + It("succeeds", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) + + Context("With all possible attributes", func() { + BeforeEach(func() { + yaml = `#cloud-config +name: "kairos" +passwd: "kairos" +lock_passwd: true +groups: + - "admin" +ssh_authorized_keys: + - github:mudler` + }) + + It("succeeds", func() { + Expect(config.IsValid()).To(BeTrue()) + }) + }) +}) diff --git a/schema/validate.go b/schema/validate.go new file mode 100644 index 00000000..ba25fadb --- /dev/null +++ b/schema/validate.go @@ -0,0 +1,71 @@ +package schema + +import ( + "fmt" + "io" + "net/http" + "os" + "strings" +) + +// JSONSchema builds a JSON Schema based on the Root Schema and the given version +// this is helpful when mapping a validation error. +func JSONSchema(version string) (string, error) { + url := fmt.Sprintf("https://kairos.io/%s/cloud-config.json", version) + schema, err := GenerateSchema(RootSchema{}, url) + if err != nil { + return "", err + } + + return schema, nil +} + +// Validate ensures that a given schema is Valid according to the Root Schema from the agent. +func Validate(source string) error { + var yaml string + + if strings.HasPrefix(source, "http") { + resp, err := http.Get(source) + if err != nil { + return err + } + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + //Convert the body to type string + yaml = string(body) + } else { + // Maybe we should just try to read the string for the normal headers? That would identify a full yaml vs a file + dat, err := os.ReadFile(source) + if err != nil { + if strings.Contains(err.Error(), "no such file or directory") || strings.Contains(err.Error(), "file name too long") { + yaml = source + } else { + return err + } + } else { + yaml = string(dat) + } + } + + config, err := NewConfigFromYAML(yaml, RootSchema{}) + if err != nil { + return err + } + + if !config.HasHeader() { + return fmt.Errorf("missing #cloud-config header") + } + + if config.IsValid() { + return nil + } + + err = config.ValidationError + if err != nil { + return err + } + + return nil +} diff --git a/schema/validate_test.go b/schema/validate_test.go new file mode 100644 index 00000000..aa3acf9c --- /dev/null +++ b/schema/validate_test.go @@ -0,0 +1,106 @@ +package schema_test + +import ( + "os" + "path/filepath" + "strings" + + . "github.com/kairos-io/kairos-sdk/schema" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Validate", func() { + Context("JSONSchema", func() { + It("returns a schema with a url to the given version", func() { + out, err := JSONSchema("0.0.0") + Expect(err).ToNot(HaveOccurred()) + Expect(strings.Contains(out, `$schema": "https://kairos.io/0.0.0/cloud-config.json"`)).To(BeTrue()) + }) + }) + + Context("Validate", func() { + var yaml string + + Context("With a really long config string", func() { + BeforeEach(func() { + yaml = `#cloud-config +users: + - name: kairos + passwd: kairos +vpn: + network_token: "dssdnfjkldashfkjhasdkhfkasjdhfkjhasdjkfhaksjdhfkjashjdkfhioreqwhfuihqweruifhuewrbfhuewrfuyequfhuiehuifheqrihfuiqrehfuirqheiufhreqiuhfuiqheiufhqeuihfuiqrehfiuhqreuifrhiuqehfiuhqeirhfiuewhrfhqwehfriuewhfuihewiuhfruewhrifhwiuehrfiuhweiurfhwueihrfuiwehufhweuihrfuiwerhfuihewruifhewuihfiouwehrfiouhwei" +` + }) + It("validates", func() { + Expect(Validate(yaml)).ToNot(HaveOccurred()) + }) + }) + + Context("with a valid config", func() { + BeforeEach(func() { + yaml = `#cloud-config +users: + - name: kairos + passwd: kairos` + }) + + It("is successful reading it from file", func() { + f, err := os.MkdirTemp("", "tests") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(f) + + path := filepath.Join(f, "config.yaml") + err = os.WriteFile(path, []byte(yaml), 0655) + Expect(err).ToNot(HaveOccurred()) + err = Validate(path) + Expect(err).ToNot(HaveOccurred()) + }) + It("is successful reading it from a string", func() { + Expect(Validate(yaml)).ToNot(HaveOccurred()) + }) + }) + + Context("without a header", func() { + BeforeEach(func() { + yaml = `users: + - name: kairos + passwd: kairos` + }) + + It("is fails", func() { + f, err := os.MkdirTemp("", "tests") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(f) + + path := filepath.Join(f, "config.yaml") + err = os.WriteFile(path, []byte(yaml), 0655) + Expect(err).ToNot(HaveOccurred()) + err = Validate(path) + Expect(err).To(MatchError("missing #cloud-config header")) + }) + }) + + Context("with an invalid rule", func() { + BeforeEach(func() { + yaml = `#cloud-config +users: + - name: 007 + passwd: kairos` + }) + + It("is fails", func() { + f, err := os.MkdirTemp("", "tests") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(f) + + path := filepath.Join(f, "config.yaml") + err = os.WriteFile(path, []byte(yaml), 0655) + Expect(err).ToNot(HaveOccurred()) + err = Validate(path) + Expect(err.Error()).To(MatchRegexp("expected string, but got number")) + }) + }) + }) +})