mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-06-03 01:44:53 +00:00
Drop collector package and use sdk for it (#59)
This commit is contained in:
parent
d3f9f169da
commit
ed372395d2
6
go.mod
6
go.mod
@ -4,17 +4,15 @@ go 1.20
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
github.com/avast/retry-go v3.0.0+incompatible
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1
|
||||
github.com/distribution/distribution v2.8.2+incompatible
|
||||
github.com/erikgeiser/promptkit v0.8.0
|
||||
github.com/google/go-containerregistry v0.15.2
|
||||
github.com/google/go-github/v40 v40.0.0
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/itchyny/gojq v0.12.13
|
||||
github.com/jaypipes/ghw v0.10.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/kairos-io/kairos-sdk v0.0.7
|
||||
github.com/kairos-io/kairos-sdk v0.0.8
|
||||
github.com/labstack/echo/v4 v4.10.2
|
||||
github.com/mitchellh/mapstructure v1.4.2
|
||||
github.com/mudler/go-nodepair v0.0.0-20221223092639-ba399a66fdfb
|
||||
@ -52,6 +50,7 @@ require (
|
||||
github.com/StackExchange/wmi v1.2.1 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/avast/retry-go v2.7.0+incompatible // indirect
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect
|
||||
github.com/aymanbagabas/go-osc52 v1.2.1 // indirect
|
||||
github.com/cavaliergopher/grab v2.0.0+incompatible // indirect
|
||||
@ -99,6 +98,7 @@ require (
|
||||
github.com/huandu/xstrings v1.3.3 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/itchyny/gojq v0.12.12 // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.5 // indirect
|
||||
github.com/jaypipes/pcidb v1.0.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
|
30
go.sum
30
go.sum
@ -93,6 +93,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY
|
||||
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/avast/retry-go v2.7.0+incompatible h1:XaGnzl7gESAideSjr+I8Hki/JBi+Yb9baHlMRPeSC84=
|
||||
github.com/avast/retry-go v2.7.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
||||
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
|
||||
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo=
|
||||
@ -102,6 +104,7 @@ github.com/aymanbagabas/go-osc52 v1.2.1 h1:q2sWUyDcozPLcLabEMd+a+7Ea2DitxZVN9hTx
|
||||
github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
||||
github.com/bool64/dev v0.2.27 h1:mFT+B74mFVgUeUmm/EbfM6ELPA55lEXBjQ/AOHCwCOc=
|
||||
@ -130,6 +133,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
|
||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
|
||||
@ -138,8 +142,11 @@ github.com/containerd/containerd v1.7.1 h1:k8DbDkSOwt5rgxQ3uCI4WMKIJxIndSCBUaGm5
|
||||
github.com/containerd/containerd v1.7.1/go.mod h1:gA+nJUADRBm98QS5j5RPROnt0POQSMK+r7P7EGMC/Qc=
|
||||
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
|
||||
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
|
||||
github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
|
||||
github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
|
||||
github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
@ -176,6 +183,8 @@ github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryef
|
||||
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/eliukblau/pixterm v1.3.1 h1:XeouQViH+lmzCa7sMUoK2cd7qlgHYGLIjwRKaOdJbKA=
|
||||
@ -253,6 +262,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@ -369,6 +379,8 @@ github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+h
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
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/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU=
|
||||
github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOdnu/Sf4=
|
||||
github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
|
||||
@ -391,8 +403,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kairos-io/kairos-sdk v0.0.7 h1:C1lK5QpmBy+ZZB2QIgtemaJ76aw4l5ZGxzb+mh24+Nc=
|
||||
github.com/kairos-io/kairos-sdk v0.0.7/go.mod h1:kPT8LJVmUjwIslJl4LW471D4l91tBOmbGiGoO+NC+7E=
|
||||
github.com/kairos-io/kairos-sdk v0.0.8 h1:3yfxdmUuJoN7ePg+ogpH1PJvuMsLmLcxEXuWoiGdIrg=
|
||||
github.com/kairos-io/kairos-sdk v0.0.8/go.mod h1:Z+1CLqMZq97bzwX2XSIArr8EoniMth3mMYkOOb8L3QY=
|
||||
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329 h1:qq2nCpSrXrmvDGRxW0ruW9BVEV1CN2a9YDOExdt+U0o=
|
||||
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329/go.mod h1:2VPVQDR4wO7KXHwP+DAypEy67rXf+okUx2zjgpCxZw4=
|
||||
github.com/kendru/darwin/go/depgraph v0.0.0-20221105232959-877d6a81060c h1:eKb4PqwAMhlqwXw0W3atpKaYaPGlXE/Fwh+xpCEYaPk=
|
||||
@ -425,6 +437,7 @@ github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN
|
||||
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
|
||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
|
||||
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
|
||||
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
@ -458,6 +471,7 @@ github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWV
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
@ -476,6 +490,7 @@ github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/moby v23.0.2+incompatible h1:W9l+1HGV+mZ4a6RW9ZAHzSYPpqAZ96/ft5gFJAaeh9c=
|
||||
github.com/moby/moby v23.0.2+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
|
||||
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
|
||||
@ -484,9 +499,11 @@ github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mudler/entities v0.0.0-20220905203055-68348bae0f49 h1:P1QgHLh0hX935j6m9K6rlSxc0mkD1UuIAOQEu+1VCW4=
|
||||
github.com/mudler/entities v0.0.0-20220905203055-68348bae0f49/go.mod h1:qquFT9tYp+/NO7tTotto4BT9zSRYSMDxo2PGZwujpFA=
|
||||
github.com/mudler/go-nodepair v0.0.0-20221223092639-ba399a66fdfb h1:F6TP0DW7C0U9sgm9g4uAs0Vp2JSkhn2umlyrNlxUKXw=
|
||||
@ -545,6 +562,7 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
|
||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs=
|
||||
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY=
|
||||
github.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
@ -649,6 +667,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
|
||||
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
@ -672,6 +691,7 @@ github.com/swaggest/jsonschema-go v0.3.51 h1:Cl0hFQ/jtBIP8NlHNuwW6ka3J7zzW5r2jxb
|
||||
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/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tredoe/osutil/v2 v2.0.0-rc.16 h1:5A2SKvyB2c3lhPYUIHyFtu6jbaXlaA3Hu5gWIam8Pik=
|
||||
github.com/tredoe/osutil/v2 v2.0.0-rc.16/go.mod h1:uLRVx/3pb7Y4RQhG8cQFbPE9ha5r81e6MXpBsxbTAYc=
|
||||
@ -705,6 +725,7 @@ github.com/willdonnelly/passwd v0.0.0-20141013001024-7935dab3074c/go.mod h1:xcvf
|
||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
||||
github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
|
||||
github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
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=
|
||||
@ -728,6 +749,7 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@ -739,6 +761,7 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
@ -1177,11 +1200,13 @@ gopkg.in/djherbis/times.v1 v1.3.0 h1:uxMS4iMtH6Pwsxog094W0FYldiNnfY/xba00vq6C2+o
|
||||
gopkg.in/djherbis/times.v1 v1.3.0/go.mod h1:AQlg6unIsrsCEdQYhTzERy542dz6SFdQFZFv6mUY0P8=
|
||||
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/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
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/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
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=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
@ -1202,6 +1227,7 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
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/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -6,12 +6,12 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos-sdk/collector"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
hook "github.com/kairos-io/kairos/v2/internal/agent/hooks"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
config "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
"github.com/nxadm/tail"
|
||||
)
|
||||
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos-sdk/collector"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
hook "github.com/kairos-io/kairos/v2/internal/agent/hooks"
|
||||
@ -22,7 +23,6 @@ import (
|
||||
"github.com/kairos-io/kairos/v2/internal/cmd"
|
||||
"github.com/kairos-io/kairos/v2/pkg/action"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
"github.com/kairos-io/kairos/v2/pkg/elementalConfig"
|
||||
v1 "github.com/kairos-io/kairos/v2/pkg/types/v1"
|
||||
elementalUtils "github.com/kairos-io/kairos/v2/pkg/utils"
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos/v2/internal/cmd"
|
||||
config "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/elementalConfig"
|
||||
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
|
@ -4,9 +4,9 @@ import (
|
||||
"fmt"
|
||||
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos-sdk/collector"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
"github.com/mudler/go-pluggable"
|
||||
)
|
||||
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
sdk "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos-sdk/collector"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
hook "github.com/kairos-io/kairos/v2/internal/agent/hooks"
|
||||
@ -17,7 +18,6 @@ import (
|
||||
"github.com/kairos-io/kairos/v2/internal/cmd"
|
||||
"github.com/kairos-io/kairos/v2/pkg/action"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
"github.com/kairos-io/kairos/v2/pkg/elementalConfig"
|
||||
|
||||
"github.com/mudler/go-pluggable"
|
||||
|
@ -6,11 +6,11 @@ import (
|
||||
"fmt"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos-sdk/collector"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos/v2/pkg/action"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
"github.com/kairos-io/kairos/v2/pkg/elementalConfig"
|
||||
"github.com/kairos-io/kairos/v2/pkg/github"
|
||||
v1 "github.com/kairos-io/kairos/v2/pkg/types/v1"
|
||||
|
29
main.go
29
main.go
@ -4,29 +4,26 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/bundles"
|
||||
"github.com/kairos-io/kairos-sdk/collector"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/schema"
|
||||
"github.com/kairos-io/kairos-sdk/state"
|
||||
"github.com/kairos-io/kairos/v2/internal/agent"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos/v2/internal/common"
|
||||
"github.com/kairos-io/kairos/v2/internal/webui"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/elementalConfig"
|
||||
v1 "github.com/kairos-io/kairos/v2/pkg/types/v1"
|
||||
"github.com/kairos-io/kairos/v2/pkg/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
"regexp"
|
||||
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/kairos-io/kairos/v2/internal/agent"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos/v2/internal/webui"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/bundles"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/schema"
|
||||
"github.com/kairos-io/kairos-sdk/state"
|
||||
"github.com/kairos-io/kairos/v2/internal/common"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
@ -1,483 +0,0 @@
|
||||
// Package configcollector can be used to merge configuration from different
|
||||
// sources into one YAML.
|
||||
package collector
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
|
||||
"github.com/avast/retry-go"
|
||||
"github.com/itchyny/gojq"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const DefaultHeader = "#cloud-config"
|
||||
|
||||
var ValidFileHeaders = []string{
|
||||
"#cloud-config",
|
||||
"#kairos-config",
|
||||
"#node-config",
|
||||
}
|
||||
|
||||
type Configs []*Config
|
||||
|
||||
// We don't allow yamls that are plain arrays because is has no use in Kairos
|
||||
// and there is no way to merge an array yaml with a "map" yaml.
|
||||
type Config map[string]interface{}
|
||||
|
||||
// MergeConfigURL looks for the "config_url" key and if it's found
|
||||
// it downloads the remote config and merges it with the current one.
|
||||
// If the remote config also has config_url defined, it is also fetched
|
||||
// recursively until a remote config no longer defines a config_url.
|
||||
// NOTE: The "config_url" value of the final result is the value of the last
|
||||
// config file in the chain because we replace values when we merge.
|
||||
func (c *Config) MergeConfigURL() error {
|
||||
// If there is no config_url, just return (do nothing)
|
||||
configURL := c.ConfigURL()
|
||||
if configURL == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// fetch the remote config
|
||||
remoteConfig, err := fetchRemoteConfig(configURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// recursively fetch remote configs
|
||||
if err := remoteConfig.MergeConfigURL(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// merge remoteConfig back to "c"
|
||||
return c.MergeConfig(remoteConfig)
|
||||
}
|
||||
|
||||
func (c *Config) toMap() (map[string]interface{}, error) {
|
||||
var result map[string]interface{}
|
||||
data, err := yaml.Marshal(c)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(data, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *Config) applyMap(i interface{}) error {
|
||||
data, err := yaml.Marshal(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(data, c)
|
||||
return err
|
||||
}
|
||||
|
||||
// MergeConfig merges the config passed as parameter back to the receiver Config.
|
||||
func (c *Config) MergeConfig(newConfig *Config) error {
|
||||
var err error
|
||||
|
||||
// convert the two configs into maps
|
||||
aMap, err := c.toMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bMap, err := newConfig.toMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// deep merge the two maps
|
||||
cMap, err := DeepMerge(aMap, bMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// apply the result of the deepmerge into the base config
|
||||
return c.applyMap(cMap)
|
||||
}
|
||||
|
||||
func deepMergeSlices(sliceA, sliceB []interface{}) ([]interface{}, error) {
|
||||
// We use the first item in the slice to determine if there are maps present.
|
||||
// Do we need to do the same for other types?
|
||||
firstItem := sliceA[0]
|
||||
if reflect.ValueOf(firstItem).Kind() == reflect.Map {
|
||||
temp := make(map[string]interface{})
|
||||
|
||||
// first we put in temp all the keys present in a, and assign them their existing values
|
||||
for _, item := range sliceA {
|
||||
for k, v := range item.(map[string]interface{}) {
|
||||
temp[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// then we go through b to merge each of its keys
|
||||
for _, item := range sliceB {
|
||||
for k, v := range item.(map[string]interface{}) {
|
||||
current, ok := temp[k]
|
||||
if ok {
|
||||
// if the key exists, we deep merge it
|
||||
dm, err := DeepMerge(current, v)
|
||||
if err != nil {
|
||||
return []interface{}{}, fmt.Errorf("cannot merge %s with %s", current, v)
|
||||
}
|
||||
temp[k] = dm
|
||||
} else {
|
||||
// otherwise we just set it
|
||||
temp[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return []interface{}{temp}, nil
|
||||
}
|
||||
|
||||
// This implementation is needed because Go 1.19 does not implement compare for {}interface. Once
|
||||
// FIPS can be upgraded to 1.20, we should be able to use this other code:
|
||||
// // for simple slices
|
||||
// for _, v := range sliceB {
|
||||
// i := slices.Index(sliceA, v)
|
||||
// if i < 0 {
|
||||
// sliceA = append(sliceA, v)
|
||||
// }
|
||||
// }
|
||||
for _, vB := range sliceB {
|
||||
found := false
|
||||
for _, vA := range sliceA {
|
||||
if vA == vB {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
sliceA = append(sliceA, vB)
|
||||
}
|
||||
}
|
||||
|
||||
return sliceA, nil
|
||||
}
|
||||
|
||||
func deepMergeMaps(a, b map[string]interface{}) (map[string]interface{}, error) {
|
||||
// go through all items in b and merge them to a
|
||||
for k, v := range b {
|
||||
current, ok := a[k]
|
||||
if ok {
|
||||
// when the key is already set, we don't know what type it has, so we deep merge them in case they are maps
|
||||
// or slices
|
||||
res, err := DeepMerge(current, v)
|
||||
if err != nil {
|
||||
return a, err
|
||||
}
|
||||
a[k] = res
|
||||
} else {
|
||||
a[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// DeepMerge takes two data structures and merges them together deeply. The results can vary depending on how the
|
||||
// arguments are passed since structure B will always overwrite what's on A.
|
||||
func DeepMerge(a, b interface{}) (interface{}, error) {
|
||||
if a == nil && b != nil {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
typeA := reflect.TypeOf(a)
|
||||
typeB := reflect.TypeOf(b)
|
||||
|
||||
// We don't support merging different data structures
|
||||
if typeA.Kind() != typeB.Kind() {
|
||||
return map[string]interface{}{}, fmt.Errorf("cannot merge %s with %s", typeA.String(), typeB.String())
|
||||
}
|
||||
|
||||
if typeA.Kind() == reflect.Slice {
|
||||
return deepMergeSlices(a.([]interface{}), b.([]interface{}))
|
||||
}
|
||||
|
||||
if typeA.Kind() == reflect.Map {
|
||||
return deepMergeMaps(a.(map[string]interface{}), b.(map[string]interface{}))
|
||||
}
|
||||
|
||||
// for any other type, b should take precedence
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// String returns a string which is a Yaml representation of the Config.
|
||||
func (c *Config) String() (string, error) {
|
||||
data, err := yaml.Marshal(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s\n\n%s", DefaultHeader, string(data)), nil
|
||||
}
|
||||
|
||||
func (cs Configs) Merge() (*Config, error) {
|
||||
result := &Config{}
|
||||
|
||||
for _, c := range cs {
|
||||
if err := c.MergeConfigURL(); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
if err := result.MergeConfig(c); err != nil {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func Scan(o *Options, filter func(d []byte) ([]byte, error)) (*Config, error) {
|
||||
configs := Configs{}
|
||||
|
||||
configs = append(configs, parseFiles(o.ScanDir, o.NoLogs)...)
|
||||
|
||||
if o.MergeBootCMDLine {
|
||||
cConfig, err := ParseCmdLine(o.BootCMDLineFile, filter)
|
||||
o.SoftErr("parsing cmdline", err)
|
||||
if err == nil { // best-effort
|
||||
configs = append(configs, cConfig)
|
||||
}
|
||||
}
|
||||
|
||||
return configs.Merge()
|
||||
}
|
||||
|
||||
func allFiles(dir []string) []string {
|
||||
files := []string{}
|
||||
for _, d := range dir {
|
||||
if f, err := listFiles(d); err == nil {
|
||||
files = append(files, f...)
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
// parseFiles returns a list of Configs parsed from files.
|
||||
func parseFiles(dir []string, nologs bool) Configs {
|
||||
result := Configs{}
|
||||
files := allFiles(dir)
|
||||
for _, f := range files {
|
||||
if fileSize(f) > 1.0 {
|
||||
if !nologs {
|
||||
fmt.Printf("warning: skipping %s. too big (>1MB)\n", f)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if strings.Contains(f, "userdata") || filepath.Ext(f) == ".yml" || filepath.Ext(f) == ".yaml" {
|
||||
b, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
if !nologs {
|
||||
fmt.Printf("warning: skipping %s. %s\n", f, err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !HasValidHeader(string(b)) {
|
||||
if !nologs {
|
||||
fmt.Printf("warning: skipping %s because it has no valid header\n", f)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
var newConfig Config
|
||||
err = yaml.Unmarshal(b, &newConfig)
|
||||
if err != nil && !nologs {
|
||||
fmt.Printf("warning: failed to parse config:\n%s\n", err.Error())
|
||||
}
|
||||
result = append(result, &newConfig)
|
||||
} else {
|
||||
if !nologs {
|
||||
fmt.Printf("warning: skipping %s (extension).\n", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func fileSize(f string) float64 {
|
||||
file, err := os.Open(f)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
bytes := stat.Size()
|
||||
kilobytes := (bytes / 1024)
|
||||
megabytes := (float64)(kilobytes / 1024) // cast to type float64
|
||||
|
||||
return megabytes
|
||||
}
|
||||
|
||||
func listFiles(dir string) ([]string, error) {
|
||||
content := []string{}
|
||||
|
||||
err := filepath.Walk(dir,
|
||||
func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !info.IsDir() {
|
||||
content = append(content, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return content, err
|
||||
}
|
||||
|
||||
// ParseCmdLine reads options from the kernel cmdline and returns the equivalent
|
||||
// Config.
|
||||
func ParseCmdLine(file string, filter func(d []byte) ([]byte, error)) (*Config, error) {
|
||||
result := Config{}
|
||||
dotToYAML, err := machine.DotToYAML(file)
|
||||
if err != nil {
|
||||
return &result, err
|
||||
}
|
||||
|
||||
filteredYAML, err := filter(dotToYAML)
|
||||
if err != nil {
|
||||
return &result, err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(filteredYAML, &result)
|
||||
if err != nil {
|
||||
return &result, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// ConfigURL returns the value of config_url if set or empty string otherwise.
|
||||
func (c Config) ConfigURL() string {
|
||||
if val, hasKey := c["config_url"]; hasKey {
|
||||
if s, isString := val.(string); isString {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func fetchRemoteConfig(url string) (*Config, error) {
|
||||
var body []byte
|
||||
result := &Config{}
|
||||
|
||||
err := retry.Do(
|
||||
func() error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("unexpected status: %d", resp.StatusCode)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}, retry.Delay(time.Second), retry.Attempts(3),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
// TODO: improve logging
|
||||
fmt.Printf("WARNING: Couldn't fetch config_url: %s", err)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if !HasValidHeader(string(body)) {
|
||||
// TODO: Print a warning when we implement proper logging
|
||||
fmt.Println("No valid header in remote config: %w", err)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(body, result); err != nil {
|
||||
return result, fmt.Errorf("could not unmarshal remote config to an object: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func HasValidHeader(data string) bool {
|
||||
header := strings.SplitN(data, "\n", 2)[0]
|
||||
|
||||
// Trim trailing whitespaces
|
||||
header = strings.TrimRightFunc(header, unicode.IsSpace)
|
||||
|
||||
// NOTE: we also allow "legacy" headers. Should only allow #cloud-config at
|
||||
// some point.
|
||||
return (header == DefaultHeader) || (header == "#kairos-config") || (header == "#node-config")
|
||||
}
|
||||
|
||||
func (c Config) Query(s string) (res string, err error) {
|
||||
s = fmt.Sprintf(".%s", s)
|
||||
|
||||
var dat map[string]interface{}
|
||||
var dat1 map[string]interface{}
|
||||
|
||||
yamlStr, err := c.String()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Marshall it so it removes the first line which cannot be parsed
|
||||
err = yaml.Unmarshal([]byte(yamlStr), &dat1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Transform it to json so its parsed correctly by gojq
|
||||
b, err := json.Marshal(dat1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := json.Unmarshal(b, &dat); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
query, err := gojq.Parse(s)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
iter := query.Run(dat) // or query.RunWithContext
|
||||
for {
|
||||
v, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if err, ok := v.(error); ok {
|
||||
return res, fmt.Errorf("failed parsing, error: %w", err)
|
||||
}
|
||||
|
||||
dat, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
res += string(dat)
|
||||
}
|
||||
return
|
||||
}
|
@ -1,754 +0,0 @@
|
||||
package collector_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
. "github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"gopkg.in/yaml.v1"
|
||||
)
|
||||
|
||||
var _ = Describe("Config Collector", func() {
|
||||
Describe("Options", func() {
|
||||
var options *Options
|
||||
|
||||
BeforeEach(func() {
|
||||
options = &Options{
|
||||
NoLogs: false,
|
||||
}
|
||||
})
|
||||
|
||||
It("applies a defined option function", func() {
|
||||
option := func(o *Options) error {
|
||||
o.NoLogs = true
|
||||
return nil
|
||||
}
|
||||
|
||||
Expect(options.NoLogs).To(BeFalse())
|
||||
Expect(options.Apply(option)).NotTo(HaveOccurred())
|
||||
Expect(options.NoLogs).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("MergeConfig", func() {
|
||||
var originalConfig, newConfig *Config
|
||||
BeforeEach(func() {
|
||||
originalConfig = &Config{}
|
||||
newConfig = &Config{}
|
||||
})
|
||||
|
||||
Context("different keys", func() {
|
||||
BeforeEach(func() {
|
||||
err := yaml.Unmarshal([]byte(`#cloud-config
|
||||
name: Mario`), originalConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = yaml.Unmarshal([]byte(`#cloud-config
|
||||
surname: Bros`), newConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("gets merged together", func() {
|
||||
Expect(originalConfig.MergeConfig(newConfig)).ToNot(HaveOccurred())
|
||||
surname, isString := (*originalConfig)["surname"].(string)
|
||||
Expect(isString).To(BeTrue())
|
||||
Expect(surname).To(Equal("Bros"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("same keys", func() {
|
||||
Context("when the key is a map", func() {
|
||||
BeforeEach(func() {
|
||||
err := yaml.Unmarshal([]byte(`#cloud-config
|
||||
info:
|
||||
name: Mario
|
||||
`), originalConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = yaml.Unmarshal([]byte(`#cloud-config
|
||||
info:
|
||||
surname: Bros
|
||||
`), newConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
It("merges the keys", func() {
|
||||
Expect(originalConfig.MergeConfig(newConfig)).ToNot(HaveOccurred())
|
||||
info, isMap := (*originalConfig)["info"].(Config)
|
||||
Expect(isMap).To(BeTrue())
|
||||
Expect(info["name"]).To(Equal("Mario"))
|
||||
Expect(info["surname"]).To(Equal("Bros"))
|
||||
Expect(*originalConfig).To(HaveLen(1))
|
||||
Expect(info).To(HaveLen(2))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the key is a string", func() {
|
||||
BeforeEach(func() {
|
||||
err := yaml.Unmarshal([]byte("#cloud-config\nname: Mario"), originalConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = yaml.Unmarshal([]byte("#cloud-config\nname: Luigi"), newConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("overwrites", func() {
|
||||
Expect(originalConfig.MergeConfig(newConfig)).ToNot(HaveOccurred())
|
||||
name, isString := (*originalConfig)["name"].(string)
|
||||
Expect(isString).To(BeTrue())
|
||||
Expect(name).To(Equal("Luigi"))
|
||||
Expect(*originalConfig).To(HaveLen(1))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("MergeConfigURL", func() {
|
||||
var originalConfig *Config
|
||||
BeforeEach(func() {
|
||||
originalConfig = &Config{}
|
||||
})
|
||||
|
||||
Context("when there is no config_url defined", func() {
|
||||
BeforeEach(func() {
|
||||
err := yaml.Unmarshal([]byte("#cloud-config\nname: Mario"), originalConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("does nothing", func() {
|
||||
Expect(originalConfig.MergeConfigURL()).ToNot(HaveOccurred())
|
||||
Expect(*originalConfig).To(HaveLen(1))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when there is a chain of config_url defined", func() {
|
||||
var closeFunc ServerCloseFunc
|
||||
var port int
|
||||
var err error
|
||||
var tmpDir string
|
||||
var originalConfig *Config
|
||||
|
||||
BeforeEach(func() {
|
||||
tmpDir, err = os.MkdirTemp("", "config_url_chain")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
closeFunc, port, err = startAssetServer(tmpDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
originalConfig = &Config{}
|
||||
err = yaml.Unmarshal([]byte(fmt.Sprintf(`#cloud-config
|
||||
config_url: http://127.0.0.1:%d/config1.yaml
|
||||
name: Mario
|
||||
surname: Bros
|
||||
info:
|
||||
job: plumber
|
||||
`, port)), originalConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err := os.WriteFile(path.Join(tmpDir, "config1.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
|
||||
config_url: http://127.0.0.1:%d/config2.yaml
|
||||
surname: Bras
|
||||
`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(path.Join(tmpDir, "config2.yaml"), []byte(`#cloud-config
|
||||
|
||||
info:
|
||||
girlfriend: princess
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
closeFunc()
|
||||
err := os.RemoveAll(tmpDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("merges them all together", func() {
|
||||
err := originalConfig.MergeConfigURL()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
name, ok := (*originalConfig)["name"].(string)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(name).To(Equal("Mario"))
|
||||
|
||||
surname, ok := (*originalConfig)["surname"].(string)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(surname).To(Equal("Bras"))
|
||||
|
||||
info, ok := (*originalConfig)["info"].(Config)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(info["job"]).To(Equal("plumber"))
|
||||
Expect(info["girlfriend"]).To(Equal("princess"))
|
||||
|
||||
Expect(*originalConfig).To(HaveLen(4))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("deepMerge", func() {
|
||||
Context("different types", func() {
|
||||
a := map[string]interface{}{}
|
||||
b := []string{}
|
||||
|
||||
It("merges", func() {
|
||||
_, err := DeepMerge(a, b)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(Equal("cannot merge map[string]interface {} with []string"))
|
||||
|
||||
_, err = DeepMerge(b, a)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(Equal("cannot merge []string with map[string]interface {}"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("simple slices", func() {
|
||||
a := []interface{}{"one", "three"}
|
||||
b := []interface{}{"two", 4}
|
||||
|
||||
It("merges", func() {
|
||||
c, err := DeepMerge(a, b)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(c).To(Equal([]interface{}{"one", "three", "two", 4}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("slices containing maps", func() {
|
||||
a := []interface{}{
|
||||
map[string]interface{}{
|
||||
"users": []interface{}{
|
||||
map[string]interface{}{
|
||||
"kairos": map[string]interface{}{
|
||||
"passwd": "kairos",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
b := []interface{}{
|
||||
map[string]interface{}{
|
||||
"users": []interface{}{
|
||||
map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"passwd": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
It("merges", func() {
|
||||
c, err := DeepMerge(a, b)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
users := c.([]interface{})[0].(map[string]interface{})["users"]
|
||||
Expect(users).To(HaveLen(1))
|
||||
Expect(users).To(Equal([]interface{}{
|
||||
map[string]interface{}{
|
||||
"kairos": map[string]interface{}{
|
||||
"passwd": "kairos",
|
||||
},
|
||||
"foo": map[string]interface{}{
|
||||
"passwd": "bar",
|
||||
},
|
||||
},
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("empty map", func() {
|
||||
a := map[string]interface{}{}
|
||||
b := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
It("merges", func() {
|
||||
c, err := DeepMerge(a, b)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(c).To(Equal(map[string]interface{}{
|
||||
"foo": "bar",
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("simple map", func() {
|
||||
a := map[string]interface{}{
|
||||
"es": "uno",
|
||||
"nl": "een",
|
||||
"#": 0,
|
||||
}
|
||||
b := map[string]interface{}{
|
||||
"en": "one",
|
||||
"nl": "één",
|
||||
"de": "Eins",
|
||||
"#": 1,
|
||||
}
|
||||
|
||||
It("merges", func() {
|
||||
c, err := DeepMerge(a, b)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(c).To(Equal(map[string]interface{}{
|
||||
"#": 1,
|
||||
"de": "Eins",
|
||||
"en": "one",
|
||||
"es": "uno",
|
||||
"nl": "één",
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Scan", func() {
|
||||
Context("duplicated configs", func() {
|
||||
var cmdLinePath, tmpDir1 string
|
||||
var err error
|
||||
|
||||
BeforeEach(func() {
|
||||
tmpDir1, err = os.MkdirTemp("", "config1")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err := os.WriteFile(path.Join(tmpDir1, "local_config_1.yaml"), []byte(`#cloud-config
|
||||
|
||||
stages:
|
||||
initramfs:
|
||||
- name: "Set user and password"
|
||||
users:
|
||||
kairos:
|
||||
passwd: "kairos"
|
||||
hostname: kairos-{{ trunc 4 .Random }}
|
||||
|
||||
install:
|
||||
auto: true
|
||||
reboot: true
|
||||
device: auto
|
||||
grub_options:
|
||||
extra_cmdline: foobarzz
|
||||
bundles:
|
||||
- rootfs_path: /usr/local/lib/extensions/kubo
|
||||
targets:
|
||||
- container://ttl.sh/97d4530c-df80-4eb4-9ae7-39f8f90c26e5:8h
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.WriteFile(path.Join(tmpDir1, "local_config_2.yaml"), []byte(`#cloud-config
|
||||
|
||||
stages:
|
||||
initramfs:
|
||||
- name: "Set user and password"
|
||||
users:
|
||||
kairos:
|
||||
passwd: "kairos"
|
||||
hostname: kairos-{{ trunc 4 .Random }}
|
||||
|
||||
install:
|
||||
auto: true
|
||||
reboot: true
|
||||
device: auto
|
||||
grub_options:
|
||||
extra_cmdline: foobarzz
|
||||
bundles:
|
||||
- rootfs_path: /usr/local/lib/extensions/kubo
|
||||
targets:
|
||||
- container://ttl.sh/97d4530c-df80-4eb4-9ae7-39f8f90c26e5:8h
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
err = os.RemoveAll(tmpDir1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should be the same as just one of them", func() {
|
||||
o := &Options{}
|
||||
err := o.Apply(
|
||||
MergeBootLine,
|
||||
WithBootCMDLineFile(cmdLinePath),
|
||||
Directories(tmpDir1),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, err := Scan(o, config.FilterKeys)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
fmt.Println(c.String())
|
||||
Expect(c.String()).To(Equal(`#cloud-config
|
||||
|
||||
install:
|
||||
auto: true
|
||||
bundles:
|
||||
- rootfs_path: /usr/local/lib/extensions/kubo
|
||||
targets:
|
||||
- container://ttl.sh/97d4530c-df80-4eb4-9ae7-39f8f90c26e5:8h
|
||||
device: auto
|
||||
grub_options:
|
||||
extra_cmdline: foobarzz
|
||||
reboot: true
|
||||
stages:
|
||||
initramfs:
|
||||
- hostname: kairos-{{ trunc 4 .Random }}
|
||||
name: Set user and password
|
||||
users:
|
||||
kairos:
|
||||
passwd: kairos
|
||||
`))
|
||||
})
|
||||
})
|
||||
Context("Deep merge maps within arrays", func() {
|
||||
var cmdLinePath, tmpDir1 string
|
||||
var err error
|
||||
|
||||
BeforeEach(func() {
|
||||
tmpDir1, err = os.MkdirTemp("", "config1")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err := os.WriteFile(path.Join(tmpDir1, "local_config_1.yaml"), []byte(`#cloud-config
|
||||
install:
|
||||
auto: true
|
||||
reboot: false
|
||||
poweroff: false
|
||||
grub_options:
|
||||
extra_cmdline: "console=tty0"
|
||||
options:
|
||||
device: /dev/sda
|
||||
stages:
|
||||
initramfs:
|
||||
- users:
|
||||
kairos:
|
||||
groups:
|
||||
- sudo
|
||||
passwd: kairos
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.WriteFile(path.Join(tmpDir1, "local_config_2.yaml"), []byte(`#cloud-config
|
||||
stages:
|
||||
initramfs:
|
||||
- users:
|
||||
foo:
|
||||
groups:
|
||||
- sudo
|
||||
passwd: bar
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
err = os.RemoveAll(tmpDir1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("merges all the sources accordingly", func() {
|
||||
o := &Options{}
|
||||
err := o.Apply(
|
||||
MergeBootLine,
|
||||
WithBootCMDLineFile(cmdLinePath),
|
||||
Directories(tmpDir1),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, err := Scan(o, config.FilterKeys)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(c.String()).To(Equal(`#cloud-config
|
||||
|
||||
install:
|
||||
auto: true
|
||||
grub_options:
|
||||
extra_cmdline: console=tty0
|
||||
poweroff: false
|
||||
reboot: false
|
||||
options:
|
||||
device: /dev/sda
|
||||
stages:
|
||||
initramfs:
|
||||
- users:
|
||||
foo:
|
||||
groups:
|
||||
- sudo
|
||||
passwd: bar
|
||||
kairos:
|
||||
groups:
|
||||
- sudo
|
||||
passwd: kairos
|
||||
`))
|
||||
})
|
||||
})
|
||||
|
||||
Context("multiple sources are defined", func() {
|
||||
var cmdLinePath, serverDir, tmpDir, tmpDir1, tmpDir2 string
|
||||
var err error
|
||||
var closeFunc ServerCloseFunc
|
||||
var port int
|
||||
|
||||
BeforeEach(func() {
|
||||
// Prepare the cmdline config_url chain
|
||||
serverDir, err = os.MkdirTemp("", "config_url_chain")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
closeFunc, port, err = startAssetServer(serverDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
cmdLinePath = createRemoteConfigs(serverDir, port)
|
||||
|
||||
tmpDir1, err = os.MkdirTemp("", "config1")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err := os.WriteFile(path.Join(tmpDir1, "local_config_1.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
|
||||
config_url: http://127.0.0.1:%d/remote_config_3.yaml
|
||||
local_key_1: local_value_1
|
||||
`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.WriteFile(path.Join(serverDir, "remote_config_3.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
|
||||
config_url: http://127.0.0.1:%d/remote_config_4.yaml
|
||||
remote_key_3: remote_value_3
|
||||
`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(path.Join(serverDir, "remote_config_4.yaml"), []byte(`#cloud-config
|
||||
|
||||
options:
|
||||
remote_option_1: remote_option_value_1
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
tmpDir2, err = os.MkdirTemp("", "config2")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.WriteFile(path.Join(tmpDir2, "local_config_2.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
|
||||
config_url: http://127.0.0.1:%d/remote_config_5.yaml
|
||||
local_key_2: local_value_2
|
||||
`, port)), os.ModePerm)
|
||||
err = os.WriteFile(path.Join(tmpDir2, "local_config_3.yaml"), []byte(`#cloud-config
|
||||
local_key_3: local_value_3
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.WriteFile(path.Join(serverDir, "remote_config_5.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
|
||||
config_url: http://127.0.0.1:%d/remote_config_6.yaml
|
||||
remote_key_4: remote_value_4
|
||||
`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(path.Join(serverDir, "remote_config_6.yaml"), []byte(`#cloud-config
|
||||
|
||||
options:
|
||||
remote_option_2: remote_option_value_2
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
err = os.RemoveAll(serverDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.RemoveAll(tmpDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.RemoveAll(tmpDir1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.RemoveAll(tmpDir2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
closeFunc()
|
||||
})
|
||||
|
||||
It("merges all the sources accordingly", func() {
|
||||
o := &Options{}
|
||||
err := o.Apply(
|
||||
MergeBootLine,
|
||||
WithBootCMDLineFile(cmdLinePath),
|
||||
Directories(tmpDir1, tmpDir2),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, err := Scan(o, config.FilterKeys)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
configURL, ok := (*c)["config_url"].(string)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(configURL).To(MatchRegexp("remote_config_2.yaml"))
|
||||
|
||||
k := (*c)["local_key_1"].(string)
|
||||
Expect(k).To(Equal("local_value_1"))
|
||||
k = (*c)["local_key_2"].(string)
|
||||
Expect(k).To(Equal("local_value_2"))
|
||||
k = (*c)["local_key_3"].(string)
|
||||
Expect(k).To(Equal("local_value_3"))
|
||||
k = (*c)["remote_key_1"].(string)
|
||||
Expect(k).To(Equal("remote_value_1"))
|
||||
k = (*c)["remote_key_2"].(string)
|
||||
Expect(k).To(Equal("remote_value_2"))
|
||||
k = (*c)["remote_key_3"].(string)
|
||||
Expect(k).To(Equal("remote_value_3"))
|
||||
k = (*c)["remote_key_4"].(string)
|
||||
Expect(k).To(Equal("remote_value_4"))
|
||||
|
||||
options := (*c)["options"].(Config)
|
||||
Expect(options["foo"]).To(Equal("bar"))
|
||||
Expect(options["remote_option_1"]).To(Equal("remote_option_value_1"))
|
||||
Expect(options["remote_option_2"]).To(Equal("remote_option_value_2"))
|
||||
|
||||
player := (*c)["player"].(Config)
|
||||
Expect(player["name"]).NotTo(Equal("Toad"))
|
||||
Expect(player["surname"]).To(Equal("Bros"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when files have invalid or missing headers", func() {
|
||||
var serverDir, tmpDir string
|
||||
var err error
|
||||
var closeFunc ServerCloseFunc
|
||||
var port int
|
||||
|
||||
BeforeEach(func() {
|
||||
// Prepare the cmdline config_url chain
|
||||
serverDir, err = os.MkdirTemp("", "config_url_chain")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
closeFunc, port, err = startAssetServer(serverDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
tmpDir, err = os.MkdirTemp("", "config")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Local configs
|
||||
err = os.WriteFile(path.Join(tmpDir, "local_config.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
config_url: http://127.0.0.1:%d/remote_config_1.yaml
|
||||
local_key_1: local_value_1
|
||||
`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// missing header
|
||||
err = os.WriteFile(path.Join(tmpDir, "local_config_2.yaml"),
|
||||
[]byte("local_key_2: local_value_2"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Remote config with valid header
|
||||
err := os.WriteFile(path.Join(serverDir, "remote_config_1.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
config_url: http://127.0.0.1:%d/remote_config_2.yaml
|
||||
remote_key_1: remote_value_1`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Remote config with invalid header
|
||||
err = os.WriteFile(path.Join(serverDir, "remote_config_2.yaml"), []byte(`#invalid-header
|
||||
remote_key_2: remote_value_2`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
closeFunc()
|
||||
err = os.RemoveAll(serverDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.RemoveAll(tmpDir)
|
||||
})
|
||||
|
||||
It("ignores them", func() {
|
||||
o := &Options{}
|
||||
err := o.Apply(Directories(tmpDir), NoLogs)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, err := Scan(o, config.FilterKeys)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect((*c)["local_key_2"]).To(BeNil())
|
||||
Expect((*c)["remote_key_2"]).To(BeNil())
|
||||
|
||||
// sanity check, the rest should be there
|
||||
v, ok := (*c)["config_url"].(string)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(v).To(MatchRegexp("remote_config_2.yaml"))
|
||||
|
||||
v, ok = (*c)["local_key_1"].(string)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(v).To(Equal("local_value_1"))
|
||||
|
||||
v, ok = (*c)["remote_key_1"].(string)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(v).To(Equal("remote_value_1"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("String", func() {
|
||||
var conf *Config
|
||||
BeforeEach(func() {
|
||||
conf = &Config{}
|
||||
err := yaml.Unmarshal([]byte("name: Mario"), conf)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("returns the YAML string representation of the Config", func() {
|
||||
s, err := conf.String()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(s).To(Equal(`#cloud-config
|
||||
|
||||
name: Mario
|
||||
`), s)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Query", func() {
|
||||
var tmpDir string
|
||||
var err error
|
||||
|
||||
BeforeEach(func() {
|
||||
tmpDir, err = os.MkdirTemp("", "config")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(filepath.Join(tmpDir, "b"), []byte(`zz.foo="baa" options.foo=bar`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(path.Join(tmpDir, "local_config.yaml"), []byte(`#cloud-config
|
||||
local_key_1: local_value_1
|
||||
some:
|
||||
other:
|
||||
key: 3
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("can query for keys", func() {
|
||||
o := &Options{}
|
||||
|
||||
err = o.Apply(MergeBootLine, Directories(tmpDir),
|
||||
WithBootCMDLineFile(filepath.Join(tmpDir, "b")),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, err := Scan(o, config.FilterKeys)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
v, err := c.Query("local_key_1")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(v).To(Equal("local_value_1\n"))
|
||||
v, err = c.Query("some")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(v).To(Equal("other:\n key: 3\n"))
|
||||
v, err = c.Query("some.other")
|
||||
Expect(v).To(Equal("key: 3\n"))
|
||||
v, err = c.Query("some.other.key")
|
||||
Expect(v).To(Equal("3\n"))
|
||||
Expect(c.Query("options")).To(Equal("foo: bar\n"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func createRemoteConfigs(serverDir string, port int) string {
|
||||
err := os.WriteFile(path.Join(serverDir, "remote_config_1.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
|
||||
config_url: http://127.0.0.1:%d/remote_config_2.yaml
|
||||
player:
|
||||
remote_key_1: remote_value_1
|
||||
`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.WriteFile(path.Join(serverDir, "remote_config_2.yaml"), []byte(`#cloud-config
|
||||
|
||||
player:
|
||||
surname: Bros
|
||||
remote_key_2: remote_value_2
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
cmdLinePath := filepath.Join(serverDir, "cmdline")
|
||||
// We put the cmdline in the same dir, it doesn't matter.
|
||||
cmdLine := fmt.Sprintf(`config_url="http://127.0.0.1:%d/remote_config_1.yaml" player.name="Toad" options.foo=bar`, port)
|
||||
err = os.WriteFile(cmdLinePath, []byte(cmdLine), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
return cmdLinePath
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
package collector
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Options struct {
|
||||
ScanDir []string
|
||||
BootCMDLineFile string
|
||||
MergeBootCMDLine bool
|
||||
NoLogs bool
|
||||
StrictValidation bool
|
||||
}
|
||||
|
||||
type Option func(o *Options) error
|
||||
|
||||
var NoLogs Option = func(o *Options) error {
|
||||
o.NoLogs = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// SoftErr prints a warning if err is no nil and NoLogs is not true.
|
||||
// It's use to wrap the same handling happening in multiple places.
|
||||
//
|
||||
// TODO: Switch to a standard logging library (e.g. verbose, silent mode etc).
|
||||
func (o *Options) SoftErr(message string, err error) {
|
||||
if !o.NoLogs && err != nil {
|
||||
fmt.Printf("WARNING: %s, %s\n", message, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Options) Apply(opts ...Option) error {
|
||||
for _, oo := range opts {
|
||||
if err := oo(o); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var MergeBootLine = func(o *Options) error {
|
||||
o.MergeBootCMDLine = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func WithBootCMDLineFile(s string) Option {
|
||||
return func(o *Options) error {
|
||||
o.BootCMDLineFile = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func StrictValidation(v bool) Option {
|
||||
return func(o *Options) error {
|
||||
o.StrictValidation = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func Directories(d ...string) Option {
|
||||
return func(o *Options) error {
|
||||
o.ScanDir = d
|
||||
return nil
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package collector_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Config Collector Suite")
|
||||
}
|
||||
|
||||
type ServerCloseFunc func()
|
||||
|
||||
func startAssetServer(path string) (ServerCloseFunc, int, error) {
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
port := listener.Addr().(*net.TCPAddr).Port
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
err := http.Serve(listener, http.FileServer(http.Dir(path)))
|
||||
select {
|
||||
case <-ctx.Done(): // We closed it with the CancelFunc, ignore the error
|
||||
return
|
||||
default: // We didnt' close it, return the error
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
}()
|
||||
|
||||
stopFunc := func() {
|
||||
cancelFunc()
|
||||
listener.Close()
|
||||
}
|
||||
|
||||
return stopFunc, port, nil
|
||||
}
|
@ -8,8 +8,8 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/bundles"
|
||||
"github.com/kairos-io/kairos-sdk/collector"
|
||||
"github.com/kairos-io/kairos-sdk/schema"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
yip "github.com/mudler/yip/pkg/schema"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
Loading…
Reference in New Issue
Block a user