From 33eb2d9a84e7b8408354df6645ad9056b6c7352a Mon Sep 17 00:00:00 2001 From: mudler Date: Thu, 11 Aug 2022 13:30:51 +0200 Subject: [PATCH] :art: Blend CLI into provider --- go.mod | 16 +- go.sum | 17 ++ internal/cli/bridge.go | 118 ++++++++++ internal/cli/register.go | 46 ++++ internal/cli/start.go | 310 ++++++++++++++++++++++++++ internal/provider/recovery.go | 136 +++++++++++ internal/provider/recovery_linux.go | 12 + internal/provider/recovery_windows.go | 8 + internal/provider/start.go | 26 +++ main.go | 38 +--- 10 files changed, 699 insertions(+), 28 deletions(-) create mode 100644 internal/cli/bridge.go create mode 100644 internal/cli/register.go create mode 100644 internal/cli/start.go create mode 100644 internal/provider/recovery.go create mode 100644 internal/provider/recovery_linux.go create mode 100644 internal/provider/recovery_windows.go create mode 100644 internal/provider/start.go diff --git a/go.mod b/go.mod index cb0fe8b..44e87d6 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/c3os-io/provider-c3os go 1.18 replace github.com/c3os-io/c3os => /home/mudler/_git/c3os +replace github.com/c3os-io/c3os/sdk => /home/mudler/_git/c3os/sdk require ( github.com/c3os-io/c3os v0.0.0-00010101000000-000000000000 @@ -14,7 +15,9 @@ require ( github.com/mudler/yip v0.0.0-20220725150231-976737b2353c github.com/onsi/ginkgo/v2 v2.1.3 github.com/onsi/gomega v1.19.0 + github.com/urfave/cli v1.22.9 go.uber.org/zap v1.21.0 + gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -35,14 +38,18 @@ require ( github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 // indirect github.com/containerd/cgroups v1.0.4 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/denisbrodbeck/machineid v1.0.1 // indirect + github.com/disintegration/imaging v1.6.2 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect + github.com/eliukblau/pixterm v1.3.1 // indirect github.com/flynn/noise v1.0.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -68,7 +75,9 @@ require ( github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jbenet/goprocess v0.1.4 // indirect + github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 // indirect github.com/joho/godotenv v1.4.0 // indirect + github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329 // indirect github.com/klauspost/compress v1.15.5 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/koron/go-ssdp v0.0.3 // indirect @@ -97,6 +106,9 @@ require ( github.com/libp2p/go-yamux/v3 v3.1.2 // indirect github.com/libp2p/zeroconf/v2 v2.1.1 // indirect github.com/lucas-clemente/quic-go v0.27.1 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect + github.com/makiuchi-d/gozxing v0.1.1 // indirect github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect @@ -141,6 +153,8 @@ require ( github.com/raulk/clock v1.1.0 // indirect github.com/raulk/go-watchdog v1.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 // indirect github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect @@ -161,6 +175,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.8.0 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect + golang.org/x/image v0.0.0-20191206065243-da761ea9ff43 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/net v0.0.0-20220630215102-69896b714898 // indirect golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect @@ -171,6 +186,5 @@ require ( golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 // indirect lukechampine.com/blake3 v1.1.7 // indirect ) diff --git a/go.sum b/go.sum index 5ff1940..9406feb 100644 --- a/go.sum +++ b/go.sum @@ -190,6 +190,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -219,6 +220,7 @@ github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUn github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= +github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/diskfs/go-diskfs v1.1.1/go.mod h1:afUPxxu+x1snp4aCY2bKR0CoZ/YFJewV3X2UEr2nPZE= github.com/diskfs/go-diskfs v1.2.1-0.20211109185859-9509735ba7a4/go.mod h1:IoDpuEbpS+D+yCGdoOm6GNfyTeEws77ALvcMQFxmenw= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= @@ -236,6 +238,7 @@ github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0 github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/eliukblau/pixterm v1.3.1 h1:XeouQViH+lmzCa7sMUoK2cd7qlgHYGLIjwRKaOdJbKA= +github.com/eliukblau/pixterm v1.3.1/go.mod h1:on5ueknFt+ZFVvIVVzQ7/JXwPjv5fJd8Q1Ybh7XixfU= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= @@ -264,6 +267,7 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 h1:Y5Q2mEwfzjMt5+3u70Gtw93ZOu2UuPeeeTBDntF7FoY= +github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= @@ -536,6 +540,7 @@ github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 h1:dy+DS31tGEGCsZzB45HmJJNHjur8GDgtRNX9U7HnSX4= +github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240/go.mod h1:3P4UH/k22rXyHIJD2w4h2XMqPX4Of/eySEZq9L6wqc4= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= @@ -557,6 +562,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= 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/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -871,14 +877,18 @@ github.com/lucas-clemente/quic-go v0.23.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2 github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg= github.com/lucas-clemente/quic-go v0.27.1 h1:sOw+4kFSVrdWOYmUjufQ9GBVPqZ+tu+jMtXxXNmRJyk= github.com/lucas-clemente/quic-go v0.27.1/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc= +github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9S1I= +github.com/makiuchi-d/gozxing v0.1.1/go.mod h1:eRIHbOjX7QWxLIDJoQuMLhuXg9LAuw6znsUtRkNw9DU= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= @@ -1210,6 +1220,7 @@ github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBO github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= @@ -1252,6 +1263,7 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= @@ -1321,6 +1333,7 @@ github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw= github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= @@ -1438,6 +1451,7 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1471,7 +1485,9 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191206065243-da761ea9ff43 h1:gQ6GUSD102fPgli+Yb4cR/cGaHF7tNBt+GYoRCpGC7s= +golang.org/x/image v0.0.0-20191206065243-da761ea9ff43/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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= @@ -1650,6 +1666,7 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/cli/bridge.go b/internal/cli/bridge.go new file mode 100644 index 0000000..71dbba8 --- /dev/null +++ b/internal/cli/bridge.go @@ -0,0 +1,118 @@ +package cli + +import ( + "context" + "fmt" + "net" + "time" + + "github.com/c3os-io/c3os/pkg/config" + "github.com/c3os-io/c3os/pkg/utils" + "github.com/ipfs/go-log" + "github.com/mudler/edgevpn/api" + "github.com/mudler/edgevpn/pkg/logger" + "github.com/mudler/edgevpn/pkg/node" + "github.com/mudler/edgevpn/pkg/services" + "github.com/mudler/edgevpn/pkg/vpn" + qr "github.com/mudler/go-nodepair/qrcode" + "github.com/urfave/cli" +) + +// bridge is just starting a VPN with edgevpn to the given network token. +func bridge(c *cli.Context) error { + qrCodePath := "" + fromQRCode := false + var serviceUUID, sshPassword string + + if c.String("qr-code-image") != "" { + qrCodePath = c.String("qr-code-image") + fromQRCode = true + } + if c.Bool("qr-code-snapshot") { + qrCodePath = "" + fromQRCode = true + } + + token := c.String("network-token") + + if fromQRCode { + recoveryToken := qr.Reader(qrCodePath) + data := utils.DecodeRecoveryToken(recoveryToken) + if len(data) != 3 { + fmt.Println("Token not decoded correctly") + return fmt.Errorf("invalid token") + } + token = data[0] + serviceUUID = data[1] + sshPassword = data[2] + if serviceUUID == "" || sshPassword == "" || token == "" { + return fmt.Errorf("decoded invalid values") + } + } + + ctx := context.Background() + + nc := config.Network(token, c.String("address"), c.String("log-level"), "c3os0") + + lvl, err := log.LevelFromString(nc.LogLevel) + if err != nil { + lvl = log.LevelError + } + llger := logger.New(lvl) + + o, vpnOpts, err := nc.ToOpts(llger) + if err != nil { + llger.Fatal(err.Error()) + } + + opts := []node.Option{} + + if !fromQRCode { + // We just connect to a VPN token + o = append(o, + services.Alive( + time.Duration(20)*time.Second, + time.Duration(10)*time.Second, + time.Duration(10)*time.Second)...) + + if c.Bool("dhcp") { + // Adds DHCP server + address, _, err := net.ParseCIDR(c.String("address")) + if err != nil { + return err + } + nodeOpts, vO := vpn.DHCP(llger, 15*time.Minute, c.String("lease-dir"), address.String()) + o = append(o, nodeOpts...) + vpnOpts = append(vpnOpts, vO...) + } + + opts, err = vpn.Register(vpnOpts...) + if err != nil { + return err + } + } else { + // We hook into a service + llger.Info("Connecting to service", serviceUUID) + llger.Info("SSH access password is", sshPassword) + llger.Info("SSH server reachable at 127.0.0.1:2200") + opts = append(opts, node.WithNetworkService( + services.ConnectNetworkService( + 30*time.Second, + serviceUUID, + "127.0.0.1:2200", + ), + )) + llger.Info("To connect, keep this terminal open and run in another terminal 'ssh 127.0.0.1 -p 2200' the password is ", sshPassword) + llger.Info("Note: the connection might not be available instantly and first attempts will likely fail.") + llger.Info(" Few attempts might be required before establishing a tunnel to the host.") + } + + e, err := node.New(append(o, opts...)...) + if err != nil { + return err + } + + go api.API(ctx, c.String("api"), 5*time.Second, 20*time.Second, e, nil, false) //nolint:errcheck + + return e.Start(ctx) +} diff --git a/internal/cli/register.go b/internal/cli/register.go new file mode 100644 index 0000000..2774280 --- /dev/null +++ b/internal/cli/register.go @@ -0,0 +1,46 @@ +package cli + +import ( + "context" + "fmt" + "io/ioutil" + + nodepair "github.com/mudler/go-nodepair" + qr "github.com/mudler/go-nodepair/qrcode" +) + +func register(loglevel, arg, configFile, device string, reboot, poweroff bool) error { + b, _ := ioutil.ReadFile(configFile) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // dmesg -D to suppress tty ev + fmt.Println("Sending registration payload, please wait") + + config := map[string]string{ + "device": device, + "cc": string(b), + } + + if reboot { + config["reboot"] = "" + } + + if poweroff { + config["poweroff"] = "" + } + + err := nodepair.Send( + ctx, + config, + nodepair.WithReader(qr.Reader), + nodepair.WithToken(arg), + nodepair.WithLogLevel(loglevel), + ) + if err != nil { + return err + } + + fmt.Println("Payload sent, installation will start on the machine briefly") + return nil +} diff --git a/internal/cli/start.go b/internal/cli/start.go new file mode 100644 index 0000000..abdafc6 --- /dev/null +++ b/internal/cli/start.go @@ -0,0 +1,310 @@ +package cli + +import ( + "encoding/base64" + "fmt" + "os" + "strconv" + "strings" + + edgeVPNClient "github.com/mudler/edgevpn/api/client" + + providerConfig "github.com/c3os-io/provider-c3os/internal/provider/config" + "github.com/urfave/cli" + "gopkg.in/yaml.v1" + + "github.com/mudler/edgevpn/api/client/service" + "github.com/mudler/edgevpn/pkg/node" +) + +var networkAPI = []cli.Flag{ + &cli.StringFlag{ + Name: "api", + Usage: "API Address", + Value: "http://localhost:8080", + }, + &cli.StringFlag{ + Name: "network-id", + Value: "c3os", + Usage: "Kubernetes Network Deployment ID", + }, +} + +func Start() error { + app := &cli.App{ + Name: "c3os", + Version: "0.1", + Author: "Ettore Di Giacinto", + Usage: "c3os CLI to bootstrap, upgrade, connect and manage a c3os network", + Description: ` +The c3os CLI can be used to manage a c3os box and perform all day-two tasks, like: +- register a node +- connect to a node in recovery mode +- to establish a VPN connection +- set, list roles +- interact with the network API + +and much more. + +For all the example cases, see: https://docs.c3os.io . +`, + UsageText: ``, + Copyright: "Ettore Di Giacinto", + + Commands: []cli.Command{ + { + Name: "register", + UsageText: "register --reboot --device /dev/sda /image/snapshot.png", + Usage: "Registers and bootstraps a node", + Description: ` + Bootstraps a node which is started in pairing mode. It can send over a configuration file used to install the c3os node. + + For example: + $ c3os register --config config.yaml --device /dev/sda ~/Downloads/screenshot.png + + will decode the QR code from ~/Downloads/screenshot.png and bootstrap the node remotely. + + If the image is omitted, a screenshot will be taken and used to decode the QR code. + + See also https://docs.c3os.io/installation/device_pairing/ for documentation. + `, + ArgsUsage: "Register optionally accepts an image. If nothing is passed will take a screenshot of the screen and try to decode the QR code", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Usage: "C3OS YAML configuration file", + }, + &cli.StringFlag{ + Name: "device", + Usage: "Device used for the installation target", + }, + &cli.BoolFlag{ + Name: "reboot", + Usage: "Reboot node after installation", + }, + &cli.BoolFlag{ + Name: "poweroff", + Usage: "Shutdown node after installation", + }, + &cli.StringFlag{ + Name: "log-level", + Usage: "Set log level", + }, + }, + + Action: func(c *cli.Context) error { + args := c.Args() + var ref string + if len(args) == 1 { + ref = args[0] + } + + return register(c.String("log-level"), ref, c.String("config"), c.String("device"), c.Bool("reboot"), c.Bool("poweroff")) + }, + }, + { + Name: "bridge", + UsageText: "bridge --network-token XXX", + Usage: "Connect to a c3os VPN network", + Description: ` + Starts a bridge with a c3os network or a node. + + # With a network + + By default, "bridge" will create a VPN network connection to the node with the token supplied, thus it requires elevated permissions in order to work. + + For example: + + $ sudo c3os bridge --network-token + + Will start a VPN, which local ip is fixed to 10.1.0.254 (tweakable with --address). + + The API will also be accessible at http://127.0.0.1:8080 + + # With a node + + "c3os bridge" can be used also to connect over to a node in recovery mode. When operating in this modality c3os bridge requires no specific permissions, indeed a tunnel + will be created locally to connect to the machine remotely. + + For example: + + $ c3os bridge --qr-code-image /path/to/image.png + + Will scan the QR code in the image and connect over. Further instructions on how to connect over will be printed out to the screen. + + See also: https://docs.c3os.io/after_install/troubleshooting/#connect-to-the-cluster-network and https://docs.c3os.io/after_install/recovery_mode/ + + `, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "network-token", + Required: false, + EnvVar: "NETWORK_TOKEN", + Usage: "Network token to connect over", + }, + &cli.StringFlag{ + Name: "log-level", + Required: false, + EnvVar: "LOGLEVEL", + Value: "info", + Usage: "Bridge log level", + }, + &cli.BoolFlag{ + Name: "qr-code-snapshot", + Required: false, + Usage: "Bool to take a local snapshot instead of reading from an image file for recovery", + EnvVar: "QR_CODE_SNAPSHOT", + }, + &cli.StringFlag{ + Name: "qr-code-image", + Usage: "Path to an image containing a valid QR code for recovery mode", + Required: false, + EnvVar: "QR_CODE_IMAGE", + }, + &cli.StringFlag{ + Name: "api", + Value: "127.0.0.1:8080", + Usage: "Listening API url", + }, + &cli.BoolFlag{ + Name: "dhcp", + EnvVar: "DHCP", + Usage: "Enable DHCP", + }, + &cli.StringFlag{ + Value: "10.1.0.254/24", + Name: "address", + EnvVar: "ADDRESS", + Usage: "Specify an address for the bridge", + }, + &cli.StringFlag{ + Value: "/tmp/c3os", + Name: "lease-dir", + EnvVar: "lease-dir", + Usage: "DHCP Lease directory", + }, + }, + Action: bridge, + }, + { + Name: "get-kubeconfig", + Usage: "Return a deployment kubeconfig", + UsageText: "Retrieve a c3os network kubeconfig (only for automated deployments)", + Description: ` + Retrieve a network kubeconfig and prints out to screen. + + If a deployment was bootstrapped with a network token, you can use this command to retrieve the master node kubeconfig of a network id. + + For example: + + $ c3os get-kubeconfig --network-id c3os + `, + Flags: networkAPI, + Action: func(c *cli.Context) error { + cc := service.NewClient( + c.String("network-id"), + edgeVPNClient.NewClient(edgeVPNClient.WithHost(c.String("api")))) + str, _ := cc.Get("kubeconfig", "master") + b, _ := base64.RawURLEncoding.DecodeString(str) + masterIP, _ := cc.Get("master", "ip") + fmt.Println(strings.ReplaceAll(string(b), "127.0.0.1", masterIP)) + return nil + }, + }, + { + Name: "role", + Usage: "Set or list node roles", + Subcommands: []cli.Command{ + { + Flags: networkAPI, + Name: "set", + Usage: "Set a node role", + UsageText: "c3os role set master", + Description: ` + Sets a node role propagating the setting to the network. + + A role must be set prior to the node joining a network. You can retrieve a node UUID by running "c3os uuid". + + Example: + + $ (node A) c3os uuid + $ (node B) c3os role set master + `, + Action: func(c *cli.Context) error { + cc := service.NewClient( + c.String("network-id"), + edgeVPNClient.NewClient(edgeVPNClient.WithHost(c.String("api")))) + return cc.Set("role", c.Args()[0], c.Args()[1]) + }, + }, + { + Flags: networkAPI, + Name: "list", + Description: "List node roles", + Action: func(c *cli.Context) error { + cc := service.NewClient( + c.String("network-id"), + edgeVPNClient.NewClient(edgeVPNClient.WithHost(c.String("api")))) + advertizing, _ := cc.AdvertizingNodes() + fmt.Println("Node\tRole") + for _, a := range advertizing { + role, _ := cc.Get("role", a) + fmt.Printf("%s\t%s\n", a, role) + } + return nil + }, + }, + }, + }, + { + Name: "create-config", + Aliases: []string{"c"}, + UsageText: "Create a config with a generated network token", + + Usage: "Creates a pristine config file", + Description: ` + Prints a vanilla YAML configuration on screen which can be used to bootstrap a c3os network. + `, + ArgsUsage: "Optionally takes a token rotation interval (seconds)", + + Action: func(c *cli.Context) error { + l := int(^uint(0) >> 1) + args := c.Args() + if len(args) > 0 { + if i, err := strconv.Atoi(args[0]); err == nil { + l = i + } + } + cc := &providerConfig.Config{C3OS: &providerConfig.C3OS{NetworkToken: node.GenerateNewConnectionData(l).Base64()}} + y, _ := yaml.Marshal(cc) + fmt.Println(string(y)) + return nil + }, + }, + { + Name: "generate-token", + Aliases: []string{"g"}, + UsageText: "Generate a network token", + Usage: "Creates a new token", + Description: ` + Generates a new token which can be used to bootstrap a c3os network. + `, + ArgsUsage: "Optionally takes a token rotation interval (seconds)", + + Action: func(c *cli.Context) error { + l := int(^uint(0) >> 1) + args := c.Args() + if len(args) > 0 { + if i, err := strconv.Atoi(args[0]); err == nil { + l = i + } + } + fmt.Println(node.GenerateNewConnectionData(l).Base64()) + return nil + }, + }, + }, + } + + return app.Run(os.Args) +} diff --git a/internal/provider/recovery.go b/internal/provider/recovery.go new file mode 100644 index 0000000..cd20de5 --- /dev/null +++ b/internal/provider/recovery.go @@ -0,0 +1,136 @@ +package agent + +import ( + "context" + "fmt" + "io" + "os/exec" + "time" + + "github.com/c3os-io/c3os/internal/cmd" + config "github.com/c3os-io/c3os/pkg/config" + "github.com/c3os-io/c3os/pkg/utils" + "github.com/ipfs/go-log" + + machine "github.com/c3os-io/c3os/pkg/machine" + "github.com/creack/pty" + "github.com/gliderlabs/ssh" + "github.com/mudler/edgevpn/pkg/logger" + "github.com/mudler/edgevpn/pkg/node" + "github.com/mudler/edgevpn/pkg/services" + nodepair "github.com/mudler/go-nodepair" + qr "github.com/mudler/go-nodepair/qrcode" + "github.com/pterm/pterm" +) + +const recoveryAddr = "127.0.0.1:2222" + +func startRecoveryService(ctx context.Context, token, name, address, loglevel string) error { + + nc := config.Network(token, "", loglevel, "c3osrecovery0") + + lvl, err := log.LevelFromString(loglevel) + if err != nil { + lvl = log.LevelError + } + llger := logger.New(lvl) + + o, _, err := nc.ToOpts(llger) + if err != nil { + llger.Fatal(err.Error()) + } + + o = append(o, + services.Alive( + time.Duration(20)*time.Second, + time.Duration(10)*time.Second, + time.Duration(10)*time.Second)...) + + // opts, err := vpn.Register(vpnOpts...) + // if err != nil { + // return err + // } + o = append(o, services.RegisterService(llger, time.Duration(5*time.Second), name, address)...) + + e, err := node.New(o...) + if err != nil { + return err + } + + return e.Start(ctx) +} + +func Recovery() error { + + cmd.PrintBranding(DefaultBanner) + + agentConfig, err := LoadConfig() + if err != nil { + return err + } + + tk := nodepair.GenerateToken() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + serviceUUID := utils.RandStringRunes(10) + generatedPassword := utils.RandStringRunes(7) + + if err := startRecoveryService(ctx, tk, serviceUUID, recoveryAddr, "fatal"); err != nil { + return err + } + + cmd.PrintText(agentConfig.Branding.Recovery, "Recovery") + + time.Sleep(5 * time.Second) + + pterm.Info.Printfln( + "starting ssh server on '%s', password: '%s' service: '%s' ", recoveryAddr, generatedPassword, serviceUUID) + + qr.Print(utils.EncodeRecoveryToken(tk, serviceUUID, generatedPassword)) + + go sshServer(recoveryAddr, generatedPassword) + + // Wait for user input and go back to shell + utils.Prompt("") //nolint:errcheck + cancel() + // give tty1 back + svc, err := machine.Getty(1) + if err == nil { + svc.Start() //nolint:errcheck + } + + return nil +} + +func sshServer(listenAdddr, password string) { + ssh.Handle(func(s ssh.Session) { + cmd := exec.Command("bash") + ptyReq, winCh, isPty := s.Pty() + if isPty { + cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term)) + f, err := pty.Start(cmd) + if err != nil { + pterm.Warning.Println("Failed reserving tty") + } + go func() { + for win := range winCh { + setWinsize(f, win.Width, win.Height) + } + }() + go func() { + io.Copy(f, s) //nolint:errcheck + }() + io.Copy(s, f) //nolint:errcheck + cmd.Wait() //nolint:errcheck + } else { + io.WriteString(s, "No PTY requested.\n") //nolint:errcheck + s.Exit(1) //nolint:errcheck + } + }) + + pterm.Info.Println(ssh.ListenAndServe(listenAdddr, nil, ssh.PasswordAuth(func(ctx ssh.Context, pass string) bool { + return pass == password + }), + )) +} diff --git a/internal/provider/recovery_linux.go b/internal/provider/recovery_linux.go new file mode 100644 index 0000000..1bc9c8f --- /dev/null +++ b/internal/provider/recovery_linux.go @@ -0,0 +1,12 @@ +package provider + +import ( + "os" + "syscall" + "unsafe" +) + +func setWinsize(f *os.File, w, h int) { + syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), //nolint:errcheck + uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0}))) //nolint:errcheck +} diff --git a/internal/provider/recovery_windows.go b/internal/provider/recovery_windows.go new file mode 100644 index 0000000..93f724b --- /dev/null +++ b/internal/provider/recovery_windows.go @@ -0,0 +1,8 @@ +package provider + +import ( + "os" +) + +func setWinsize(f *os.File, w, h int) { +} diff --git a/internal/provider/start.go b/internal/provider/start.go new file mode 100644 index 0000000..48ff2bf --- /dev/null +++ b/internal/provider/start.go @@ -0,0 +1,26 @@ +package provider + +import ( + "os" + + "github.com/c3os-io/c3os/sdk/bus" + + "github.com/mudler/go-pluggable" +) + +func Start() error { + factory := pluggable.NewPluginFactory() + + // Input: bus.EventInstallPayload + // Expected output: map[string]string{} + factory.Add(bus.EventInstall, Install) + + factory.Add(bus.EventBootstrap, Bootstrap) + + // Input: config + // Expected output: string + factory.Add(bus.EventChallenge, Challenge) + + return factory.Run(pluggable.EventType(os.Args[1]), os.Stdin, os.Stdout) + +} diff --git a/main.go b/main.go index 68ea11b..7a355af 100644 --- a/main.go +++ b/main.go @@ -5,39 +5,23 @@ import ( "os" "github.com/c3os-io/c3os/sdk/bus" + cli "github.com/c3os-io/provider-c3os/internal/cli" "github.com/c3os-io/provider-c3os/internal/provider" - "github.com/mudler/go-pluggable" ) -func main() { - factory := pluggable.NewPluginFactory() - - if len(os.Args) <= 2 { - fmt.Println("Invalid arguments") - os.Exit(1) - } - - // TODO: - // if os.Args[1] not in sdk.Events - // kick the CLI - // otherwise kick the provider plugin - - // then symlink this as the c3os-cli. - - // Input: bus.EventInstallPayload - // Expected output: map[string]string{} - factory.Add(bus.EventInstall, provider.Install) - - factory.Add(bus.EventBootstrap, provider.Bootstrap) - - // Input: config - // Expected output: string - factory.Add(bus.EventChallenge, provider.Challenge) - - err := factory.Run(pluggable.EventType(os.Args[1]), os.Stdin, os.Stdout) +func checkErr(err error) { if err != nil { fmt.Println(err) os.Exit(1) } + os.Exit(0) +} + +func main() { + if len(os.Args) >= 2 && bus.IsEventDefined(os.Args[1]) { + checkErr(provider.Start()) + } + + checkErr(cli.Start()) }