diff --git a/Makefile b/Makefile index 14735ba7..f4c8228c 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,11 @@ TEST_OPTS:=$(TEST_OPTS_BASE) -tags no_ci IMG_BOOT_PARAMS:='[{"local_ip":"192.168.0.2","gateway_ip":"192.168.0.1","local_sk":"34992ada3a6daa4fbb5ad8b5b958d993ad4e5ed0f51b5ba822c8370212030826","hypervisor_pks":["027c823e9e183f3a89c5c200705f2017c0df253a66bdfae5aa0755d191713b7520"]}]' +.PHONY: dep + +dep: + GO111MODULE=on go mod vendor -v + check: lint test ## Run linters and tests install-linters: ## Install linters diff --git a/build.conf b/build.conf index b7434ceb..3bd920ba 100644 --- a/build.conf +++ b/build.conf @@ -7,7 +7,8 @@ # PATCH version when you make backwards-compatible bug fixes. # # version must always start with "v" like: v0.1.45-rc -VERSION=v0.1.3 + +VERSION=v0.2.0 # This must match the tags in the github repository # loading the actual path @@ -16,10 +17,12 @@ ROOT=$(pwd) # Internet resources # Armbian latest image dynamic link is https://dl.armbian.com/orangepiprime/Debian_stretch_next.7z # URL for the most recent version of this: https://dl.armbian.com/orangepiprime/archive/ -ARMBIAN_DOWNLOAD_URL="https://archive.armbian.com/orangepiprime/archive/Armbian_20.02.1_Orangepiprime_stretch_current_5.4.20.7z" -# Skywire release download for OS running on destination skyminer -SKYWIRE_DOWNLOAD_URL="https://github.com/skycoin/skywire/releases/download/v0.3.0/skywire-v0.3.0-linux-arm64.tar.gz" +ARMBIAN_DOWNLOAD_URL="https://archive.armbian.com/orangepiprime/archive/Armbian_20.05.2_Orangepiprime_stretch_current_5.4.43.img.xz" + +# Skywire release download for OS running on destination skyminer +SKYWIRE_VERSION=v0.4.0 +SKYWIRE_DOWNLOAD_URL="https://github.com/skycoin/skywire/releases/download/$SKYWIRE_VERSION/skywire-$SKYWIRE_VERSION-linux-arm64.tar.gz" # Offset and sector size of the Armbian image for rootfs (found automatically if not set) IMG_OFFSET="" # 8192 IMG_SECTOR="" # 512 diff --git a/build.sh b/build.sh index 45faa775..5c7c04d5 100755 --- a/build.sh +++ b/build.sh @@ -31,7 +31,7 @@ PARTS_SKYWIRE_DIR=${PARTS_DIR}/skywire PARTS_TOOLS_DIR=${PARTS_DIR}/tools # Image related variables. -ARMBIAN_IMG_7z="" +ARMBIAN_IMG_XZ="" ARMBIAN_IMG="" ARMBIAN_VERSION="" ARMBIAN_KERNEL_VERSION="" @@ -163,9 +163,6 @@ get_skywire() mkdir "${PARTS_SKYWIRE_DIR}/bin" tar xvzf "${_DST}" -C "${PARTS_SKYWIRE_DIR}/bin" || return 1 - info "Renaming 'hypervisor' to 'skywire-hypervisor'..." - mv "${PARTS_SKYWIRE_DIR}/bin/hypervisor" "${PARTS_SKYWIRE_DIR}/bin/skywire-hypervisor" || 0 - info "Cleaning..." rm -rf "${PARTS_SKYWIRE_DIR}/bin/README.md" "${PARTS_SKYWIRE_DIR}/bin/CHANGELOG.md" || return 1 @@ -174,83 +171,91 @@ get_skywire() download_armbian() { - local _DST=${PARTS_ARMBIAN_DIR}/armbian.7z # Download destination file name. + info "Downloading image from ${ARMBIAN_DOWNLOAD_URL}..." + wget -c "${ARMBIAN_DOWNLOAD_URL}" || + (error "Image download failed." && return 1) - info "Downloading image from ${ARMBIAN_DOWNLOAD_URL} to ${_DST} ..." - wget -c "${ARMBIAN_DOWNLOAD_URL}" -O "${_DST}" || - (error "Download failed." && return 1) + info "Downloading checksum from ${ARMBIAN_DOWNLOAD_URL}.sha..." + wget -c "${ARMBIAN_DOWNLOAD_URL}.sha" || + (error "Checksum download failed." && return 1) } # Get the latest ARMBIAN image for Orange Pi Prime get_armbian() { - local ARMBIAN_IMG_7z="armbian.7z" - # change to dest dir - cd "${PARTS_ARMBIAN_DIR}" || - (error "Failed to cd." && return 1) + # change to dest dir + cd "${PARTS_ARMBIAN_DIR}" || + (error "Failed to cd." && return 1) - # user info - info "Getting Armbian image, clearing dest dir first." + local ARMBIAN_IMG_XZ="$(ls Armbian*img.xz || true)" - # test if we have a file in there - if [ -r armbian.7z ] ; then + # user info + info "Getting Armbian image, clearing dest dir first." - # use already downloaded image file - notice "Reusing already downloaded file" - else - # no image in there, must download - info "No cached image, downloading.." + # test if we have a file in there + if [ -r "${ARMBIAN_IMG_XZ}" ] ; then - # download it - download_armbian - fi + # todo: doesn't seem to work, always downloads the image + # todo: download checksum separately, and use it to validate local copy - # extract and check it's integrity - info "Armbian file to process is '${ARMBIAN_IMG_7z}'." - - # check if extracted image is in there to save time - if [ -n "$(ls Armbian*.img || true)" ] ; then - # image already extracted nothing to do - notice "Armbian image already extracted" - else - # extract armbian - info "Extracting image..." - if ! 7z e "${ARMBIAN_IMG_7z}" ; then - error "Extracting failed, file is corrupt? Re-run the script to get it right." - rm "${ARMBIAN_IMG_7z}" &> /dev/null || true - exit 1 - fi - fi + # use already downloaded image file + notice "Reusing already downloaded file" + else + # no image in there, must download + info "No cached image, downloading.." - # check integrity - info "Testing image integrity..." - if ! $(command -v sha256sum) -c --status -- *.sha ; then - error "Integrity of the image is compromised, re-run the script to get it right." - rm -- *img *txt *sha *7z &> /dev/null || true - exit 1 - fi + # download it + download_armbian - # get image filename - ARMBIAN_IMG=$(ls Armbian*.img || true) + local ARMBIAN_IMG_XZ="$(ls Armbian*img.xz || true)" + fi - # imge integrity - info "Image integrity assured via sha256sum." - notice "Final image file is ${ARMBIAN_IMG}" + # extract and check it's integrity + info "Armbian file to process is '${ARMBIAN_IMG_XZ}'." - # get version & kernel version info - ARMBIAN_VERSION=$(echo "${ARMBIAN_IMG}" | awk -F '_' '{ print $2 }') - ARMBIAN_KERNEL_VERSION=$(echo "${ARMBIAN_IMG}" | awk -F '_' '{ print $6 }' | rev | cut -d '.' -f2- | rev) + # check integrity + info "Testing image integrity..." + if ! $(command -v sha256sum) -c --status -- *.sha ; then + error "Integrity of the image is compromised, re-run the script to get it right." + rm -- armbian *txt *sha *xz &> /dev/null || true + exit 1 + fi - # info to the user - notice "Armbian version: ${ARMBIAN_VERSION}" - notice "Armbian kernel version: ${ARMBIAN_KERNEL_VERSION}" + # check if extracted image is in there to save time + if [ -n "$(ls Armbian*.img || true)" ] ; then + # image already extracted nothing to do + notice "Armbian image already extracted" + else + # extract armbian + info "Extracting image..." + if ! 7z e "${ARMBIAN_IMG_XZ}" ; then + error "Extracting failed, file is corrupt? Re-run the script to get it right." + rm "${ARMBIAN_IMG_XZ}" &> /dev/null || true + exit 1 + fi + fi + + # get image filename + ARMBIAN_IMG=$(ls Armbian*.img || true) + + # imge integrity + info "Image integrity assured via sha256sum." + notice "Final image file is ${ARMBIAN_IMG}" + + # get version & kernel version info + ARMBIAN_VERSION=$(echo "${ARMBIAN_IMG}" | awk -F '_' '{ print $2 }') + ARMBIAN_KERNEL_VERSION=$(echo "${ARMBIAN_IMG}" | awk -F '_' '{ print $6 }' | rev | cut -d '.' -f2- | rev) + + # info to the user + notice "Armbian version: ${ARMBIAN_VERSION}" + notice "Armbian kernel version: ${ARMBIAN_KERNEL_VERSION}" } get_all() { - get_armbian || return 1 get_skywire || return 1 + get_armbian || return 1 get_tools || return 1 } @@ -259,7 +264,7 @@ get_all() setup_loop() { # find free loop device - IMG_LOOP=$(losetup -f) + IMG_LOOP=$(sudo losetup -f) # find image sector size (if not user-defined) [[ -z $IMG_SECTOR ]] && @@ -439,7 +444,7 @@ clean_image() clean_output_dir() { # Clean parts. - cd "${PARTS_ARMBIAN_DIR}" && find . -type f ! -name '*.7z' -delete + cd "${PARTS_ARMBIAN_DIR}" && find . -type f ! -name '*.xz' -delete cd "${PARTS_SKYWIRE_DIR}" && find . -type f ! -name '*.tar.gz' -delete && rm -rf bin cd "${FINAL_IMG_DIR}" && find . -type f ! -name '*.tar.gz' -delete diff --git a/cmd/skyconf/skyconf.go b/cmd/skyconf/skyconf.go index 3a132518..6f2c536e 100644 --- a/cmd/skyconf/skyconf.go +++ b/cmd/skyconf/skyconf.go @@ -21,18 +21,11 @@ func init() { flag.StringVar(&filename, "if", filenameDefault, "input file to read from") } -var hvName string +var configFile string func init() { - const hvNameDefault = "/etc/skywire-hypervisor.json" - flag.StringVar(&hvName, "hvf", hvNameDefault, "hypervisor config output file") -} - -var vName string - -func init() { - const vNameDefault = "/etc/skywire-visor.json" - flag.StringVar(&vName, "vf", vNameDefault, "visor config output file") + const configFileDefault = "/etc/skywire-config.json" + flag.StringVar(&configFile, "c", configFileDefault, "skywire config output file") } var keyFile string @@ -76,12 +69,12 @@ func main() { logger.Fatalf("failed to read params: %v", err) } conf := prepconf.Config{ - VisorConf: vName, - HypervisorConf: hvName, - TLSKey: keyFile, - TLSCert: certFile, + BootParams: params, + Filename: configFile, + TLSKey: keyFile, + TLSCert: certFile, } - if err := prepconf.Prepare(logger, conf, params); err != nil { + if err := prepconf.GenerateConfigFile(conf, logger); err != nil { logger.Fatalf("failed to ensure config file: %v", err) } if err := params.PrintEnvs(os.Stdout); err != nil { diff --git a/go.mod b/go.mod index 822dfe3b..bdf736a9 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,22 @@ module github.com/skycoin/skybian go 1.13 require ( - fyne.io/fyne v1.2.3 + fyne.io/fyne v1.4.0 + github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f // indirect github.com/google/go-github v17.0.0+incompatible github.com/mholt/archiver v3.1.1+incompatible github.com/rakyll/statik v0.1.7 - github.com/sirupsen/logrus v1.6.0 + github.com/sirupsen/logrus v1.7.0 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 - github.com/skycoin/dmsg v0.0.0-20201018132543-a08c07be4640 - github.com/skycoin/skycoin v0.26.0 - github.com/skycoin/skywire v0.3.0 + github.com/skycoin/dmsg v0.0.0-20201216183836-dae8a7acfc14 + github.com/skycoin/skycoin v0.27.1 + github.com/skycoin/skywire v0.2.4-0.20201222094854-2e3d9f8fb380 github.com/stretchr/testify v1.6.1 + golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 // indirect + golang.org/x/net v0.0.0-20200625001655-4c5254603344 nhooyr.io/websocket v1.8.4 // indirect ) + +// Uncomment it for tests with alternative branches and run `make dep` + +// replace github.com/skycoin/skywire => ../skywire diff --git a/go.sum b/go.sum index d00a0031..3024132b 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -fyne.io/fyne v1.2.3 h1:5xwtSBNjxxmg+GF/lYvvf4xPzyjgWQoJVrzb+bt5gaA= -fyne.io/fyne v1.2.3/go.mod h1:JhDdBrPP/Kdr1H5ZT3HW8E/6zlz+GkOldWqSirGBDnY= +fyne.io/fyne v1.4.0 h1:4fdy+SIVen+iKtBvpIegi4ox1f5IygtXhNJFS2aEucQ= +fyne.io/fyne v1.4.0/go.mod h1:j6pz0cVZSgVo3VnVS4MSTOW7yjK1KW8erwBnUoWKgqI= github.com/AudriusButkevicius/pfilter v0.0.0-20190627213056-c55ef6137fc6 h1:Apvc4kyfdrOxG+F5dn8osz+45kwGJa6CySQn0tB38SU= github.com/AudriusButkevicius/pfilter v0.0.0-20190627213056-c55ef6137fc6/go.mod h1:1N0EEx/irz4B1qV17wW82TFbjQrE7oX316Cki6eDY0Q= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -8,6 +8,7 @@ github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VictoriaMetrics/metrics v1.12.3/go.mod h1:Z1tSfPfngDn12bTfZSCqArT3OPY3u88J12hSoOhuiRE= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -36,6 +37,8 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -46,6 +49,10 @@ github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5Jflh github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fyne-io/mobile v0.1.1 h1:Snu9tKaVgu81314egPeqMC09z/k4D/bts0n1O2MfPbk= +github.com/fyne-io/mobile v0.1.1/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= @@ -53,6 +60,8 @@ github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluN github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f h1:7MsFMbSn8Lcw0blK4+NEOf8DuHoOBDhJsHz04yh13pM= github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3 h1:q521PfSp5/z6/sD9FZZOWj4d1MLmfQW8PkRnI9M6PCE= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -67,6 +76,8 @@ github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8= @@ -119,7 +130,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/josephspurrier/goversioninfo v0.0.0-20190124120936-8611f5a5ff3f/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE= +github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -150,16 +161,21 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kz/discordrus v1.2.0/go.mod h1:cJ3TiJUUuY5Gm3DNYHnnaUa3iol8VBRPzztAeZm7exc= +github.com/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU= github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU= github.com/mholt/archiver/v3 v3.3.0 h1:vWjhY8SQp5yzM9P6OJ/eZEkmi3UAbRrxCq48MxjAzig= @@ -177,6 +193,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs= github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -190,6 +208,8 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -231,14 +251,22 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/skycoin/dmsg v0.0.0-20201018132543-a08c07be4640 h1:ZE/Q5yP4IKKfrhmBXDaamo0qzkURcQsgOoWr/rsrTmE= github.com/skycoin/dmsg v0.0.0-20201018132543-a08c07be4640/go.mod h1:6PC4Y9Z0b/zWQKt4IjilsXeVVbbHIVbJKL2/W0YUrkU= +github.com/skycoin/dmsg v0.0.0-20201216183836-dae8a7acfc14 h1:ettvdRq5O1+bx1wIZPS9w+BnjzTxhZeNItDgwYI8KHs= +github.com/skycoin/dmsg v0.0.0-20201216183836-dae8a7acfc14/go.mod h1:aP/e1DEQQ16i5AzHy2e+2EFYT3ZN/sIyWRY0js56yY8= github.com/skycoin/noise v0.0.0-20180327030543-2492fe189ae6 h1:1Nc5EBY6pjfw1kwW0duwyG+7WliWz5u9kgk1h5MnLuA= github.com/skycoin/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:UXghlricA7J3aRD/k7p/zBObQfmBawwCxIVPVjz2Q3o= github.com/skycoin/skycoin v0.26.0 h1:xDxe2r8AclMntZ550Y/vUQgwgLtwrf9Wu5UYiYcN5/o= github.com/skycoin/skycoin v0.26.0/go.mod h1:78nHjQzd8KG0jJJVL/j0xMmrihXi70ti63fh8vXScJw= +github.com/skycoin/skycoin v0.27.1 h1:HatxsRwVSPaV4qxH6290xPBmkH/HgiuAoY2qC+e8C9I= +github.com/skycoin/skycoin v0.27.1/go.mod h1:78nHjQzd8KG0jJJVL/j0xMmrihXi70ti63fh8vXScJw= +github.com/skycoin/skywire v0.2.4-0.20201222094854-2e3d9f8fb380 h1:+1N2NqmRfwInq9ZgpW5lVbJL2NDviTv0RpK07OzsJ2I= +github.com/skycoin/skywire v0.2.4-0.20201222094854-2e3d9f8fb380/go.mod h1:WpriDtEIUMF5o3bhAeUpBRzP3Lis0EPPpcLKk4NW43M= github.com/skycoin/skywire v0.3.0 h1:lw5xFQmIFZT03bTz+RR5BPzsL6s4CL29AU2ycDgg6xM= github.com/skycoin/skywire v0.3.0/go.mod h1:+s3WKZOcJtpEPaS3aKAt0L0jP8X7Pb4jDqlATV2E8r4= github.com/skycoin/yamux v0.0.0-20200803175205-571ceb89da9f h1:A5dEM1OE9YhN3LciZU9qPjo7fJ46JeHNi3JCroDkK0Y= @@ -258,20 +286,21 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= -github.com/srwiley/oksvg v0.0.0-20190829233741-58e08c8fe40e h1:LJUrNHytcMXWKxnULIHPe5SCb1jDpO9o672VB1x2EuQ= -github.com/srwiley/oksvg v0.0.0-20190829233741-58e08c8fe40e/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4= -github.com/srwiley/rasterx v0.0.0-20181219215540-696f7edb7a7e h1:FFotfUvew9Eg02LYRl8YybAnm0HCwjjfY5JlOI1oB00= -github.com/srwiley/rasterx v0.0.0-20181219215540-696f7edb7a7e/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU= +github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQOVpMmj5MQnGnv+e8uZNu3xFLgyM= +github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4= +github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 h1:m59mIOBO4kfcNCEzJNy71UkeF4XIx2EVmL9KLwDQdmM= +github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI= @@ -283,6 +312,8 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/valyala/fastrand v1.0.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= +github.com/valyala/histogram v1.1.2/go.mod h1:CZAr6gK9dbD7hYx2s8WSPh0p5x5wETjC+2b3PJVtEdg= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= @@ -291,6 +322,7 @@ github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZ github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E= github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= @@ -309,14 +341,16 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqp golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg= +golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604= +golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -333,6 +367,7 @@ golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191204025024-5ee1b9f4859a h1:+HHJiFUXVOIS9mr1ThqkQD1N8vpFCfCShqADBM12KTc= golang.org/x/net v0.0.0-20191204025024-5ee1b9f4859a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -351,7 +386,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -363,6 +399,16 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 h1:gVCS+QOncANNPlmlO1AhlU3oxs4V9z+gTtPwIk3p2N8= +golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200929083018-4d22bbb62b3c h1:/h0vtH0PyU0xAoZJVcRw1k0Ng+U0JAy3QDiFmppIlIE= +golang.org/x/sys v0.0.0-20200929083018-4d22bbb62b3c/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 h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -375,7 +421,9 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200328031815-3db5fc6bac03/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU= golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -400,6 +448,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -409,6 +459,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/integration/cmd/mock_mbr.go b/integration/cmd/mock_mbr.go index 4abea681..3decbd05 100644 --- a/integration/cmd/mock_mbr.go +++ b/integration/cmd/mock_mbr.go @@ -34,10 +34,10 @@ func main() { switch mode { case 0: // Hypervisor - bp, err = boot.MakeHypervisorParams(gwIP, sk) + bp, err = boot.MakeHypervisorParams(gwIP, sk, "", "") case 1: // Visor. - bp, err = boot.MakeVisorParams(gwIP, gwIP, sk, makeHvPKs(), "123456") + bp, err = boot.MakeVisorParams(gwIP, gwIP, sk, makeHvPKs(), "123456", "", "") default: err = errors.New("invalid mode") } diff --git a/pkg/boot/params.go b/pkg/boot/params.go index dc511637..b77fbd46 100644 --- a/pkg/boot/params.go +++ b/pkg/boot/params.go @@ -35,6 +35,8 @@ const ( LocalSKENV = "SK" HypervisorPKsENV = "HVS" SocksPassEnv = "SS" + WifiNameEnv = "WFN" + WifiPassEnv = "WFP" ) // Modes. @@ -83,11 +85,13 @@ func (m *Mode) UnmarshalText(text []byte) (err error) { // Params are the boot parameters for a given node. type Params struct { - Mode Mode `json:"mode"` - LocalIP net.IP `json:"local_ip"` - GatewayIP net.IP `json:"gateway_ip"` - LocalPK cipher.PubKey `json:"local_pk"` // Not actually encoded to bps. - LocalSK cipher.SecKey `json:"local_sk"` + Mode Mode `json:"mode"` + LocalIP net.IP `json:"local_ip"` + GatewayIP net.IP `json:"gateway_ip"` + WifiEndpointName string `json:"wifi_name,omitempty"` + WifiEndpointPass string `json:"wifi_pass,omitempty"` + LocalPK cipher.PubKey `json:"local_pk"` // Not actually encoded to bps. + LocalSK cipher.SecKey `json:"local_sk"` // only valid if mode == "0x00" (hypervisor) HypervisorPKs cipher.PubKeys `json:"hypervisor_pks,omitempty"` @@ -95,7 +99,7 @@ type Params struct { } // MakeHypervisorParams is a convenience function for creating boot parameters for a hypervisor. -func MakeHypervisorParams(gwIP net.IP, sk cipher.SecKey) (Params, error) { +func MakeHypervisorParams(gwIP net.IP, sk cipher.SecKey, wifiName, wifiPass string) (Params, error) { pk, err := sk.PubKey() if err != nil { return Params{}, err @@ -111,12 +115,17 @@ func MakeHypervisorParams(gwIP net.IP, sk cipher.SecKey) (Params, error) { LocalPK: pk, LocalSK: sk, } + if wifiName != "" || wifiPass != "" { + params.WifiEndpointName = wifiName + params.WifiEndpointPass = wifiPass + } _, err = params.Encode() return params, err } // MakeVisorParams is a convenience function for creating boot parameters for a visor. -func MakeVisorParams(prevIP net.IP, gwIP net.IP, sk cipher.SecKey, hvPKs []cipher.PubKey, socksPC string) (Params, error) { +func MakeVisorParams(prevIP, gwIP net.IP, sk cipher.SecKey, hvPKs []cipher.PubKey, + socksPC string, wifiName, wifiPass string) (Params, error) { pk, err := sk.PubKey() if err != nil { return Params{}, err @@ -134,6 +143,10 @@ func MakeVisorParams(prevIP net.IP, gwIP net.IP, sk cipher.SecKey, hvPKs []ciphe HypervisorPKs: hvPKs, SkysocksPasscode: socksPC, } + if wifiName != "" || wifiPass != "" { + params.WifiEndpointName = wifiName + params.WifiEndpointPass = wifiPass + } _, err = params.Encode() return params, err } @@ -219,16 +232,32 @@ func (bp Params) PrintEnvs(w io.Writer) error { return err } } + if len(bp.WifiEndpointName) > 0 && len(bp.WifiEndpointPass) > 0 { + if err := PrintEnv(w, WifiNameEnv, bp.WifiEndpointName); err != nil { + return err + } + if err := PrintEnv(w, WifiPassEnv, bp.WifiEndpointPass); err != nil { + return err + } + } return nil } +// todo: encode/decode as it is now is infested with magical numbers and manually assembling data +// if boot parameters get more complexe, consider using something +// like encoding/gob or protobuf for better readability/maintainability +// + // Encode encodes the boot parameters in a concise format to be wrote to the MBR. func (bp Params) Encode() ([]byte, error) { keys := bp.LocalSK[:] + toEncode := [][]byte{{byte(bp.Mode)}, bp.LocalIP, bp.GatewayIP, + []byte(bp.SkysocksPasscode), []byte(bp.WifiEndpointName), []byte(bp.WifiEndpointPass)} for _, hvPK := range bp.HypervisorPKs { keys = append(keys, hvPK[:]...) } - raw := bytes.Join([][]byte{{byte(bp.Mode)}, bp.LocalIP, bp.GatewayIP, []byte(bp.SkysocksPasscode), keys}, []byte{sep}) + toEncode = append(toEncode, keys) + raw := bytes.Join(toEncode, []byte{sep}) if len(raw) > size { return nil, ErrParamsTooLarge } @@ -240,15 +269,21 @@ func (bp Params) Encode() ([]byte, error) { // Decode decodes the boot parameters from the given raw bytes. func (bp *Params) Decode(raw []byte) error { - split := bytes.SplitN(raw, []byte{sep}, 5) - if len(split) != 5 { + split := bytes.SplitN(raw, []byte{sep}, 7) + // 5 for a regular config, 7 for wifi-enabled config + if len(split) != 7 { return ErrCannotReadParams } bp.Mode, bp.LocalIP, bp.GatewayIP, bp.SkysocksPasscode = Mode(split[0][0]), split[1], split[2], string(split[3]) - keys := split[4] + if len(split) == 7 { + bp.WifiEndpointName = string(split[4]) + bp.WifiEndpointPass = string(split[5]) + } + + keys := split[6] keys = keys[copy(bp.LocalSK[:], keys):] for { var pk cipher.PubKey @@ -257,6 +292,7 @@ func (bp *Params) Decode(raw []byte) error { } bp.HypervisorPKs = append(bp.HypervisorPKs, pk) } + return nil } diff --git a/pkg/boot/params_test.go b/pkg/boot/params_test.go index cb0a48bd..011bfd40 100644 --- a/pkg/boot/params_test.go +++ b/pkg/boot/params_test.go @@ -93,7 +93,40 @@ func generatePKs(n int) cipher.PubKeys { return out } -func TestBootParams(t *testing.T) { +func TestBootParamsWifi(t *testing.T) { + imgName := prepareMockImg(t) + defer func() { require.NoError(t, os.Remove(imgName)) }() + + _, err := ReadParams(imgName) + require.EqualError(t, err, ErrCannotReadParams.Error()) + + pk, sk := cipher.GenerateKeyPair() + fmt.Println("pk =", pk) + fmt.Println("sk =", sk) + + params := Params{ + Mode: VisorMode, + LocalIP: net.ParseIP("192.168.0.2"), + GatewayIP: net.ParseIP("192.168.0.1"), + LocalSK: sk, + HypervisorPKs: generatePKs(4), + SkysocksPasscode: "", + WifiEndpointName: "testName", + WifiEndpointPass: "pass", + } + + raw, err := params.Encode() + require.NoError(t, err) + require.Len(t, raw, size) + + require.NoError(t, WriteParams(imgName, params)) + + readParams, err := ReadParams(imgName) + require.NoError(t, err) + require.Equal(t, params, readParams) +} + +func TestBootParamsNoWifi(t *testing.T) { imgName := prepareMockImg(t) defer func() { require.NoError(t, os.Remove(imgName)) }() diff --git a/pkg/imager/builder.go b/pkg/imager/builder.go index 86060257..a121d023 100644 --- a/pkg/imager/builder.go +++ b/pkg/imager/builder.go @@ -13,6 +13,7 @@ import ( "github.com/mholt/archiver" "github.com/sirupsen/logrus" + "golang.org/x/net/context" "github.com/skycoin/skybian/pkg/boot" ) @@ -69,6 +70,8 @@ func NewBuilder(log logrus.FieldLogger, root string) (*Builder, error) { }, nil } +var errDownloadCanceled = errors.New("download canceled") + // DownloadPath returns the path to the download file. func (b *Builder) DownloadPath() string { return filepath.Join(b.baseDir, "download"+ExtTarGz) @@ -86,11 +89,15 @@ func (b *Builder) DownloadCurrent() int64 { } // Download starts downloading from the given URL. -func (b *Builder) Download(url string) error { +func (b *Builder) Download(ctx context.Context, url string) error { b.mx.Lock() defer b.mx.Unlock() - return Download(b.log, url, b.DownloadPath(), &b.dlTotal, &b.dlCurrent) + err := Download(ctx, b.log, url, b.DownloadPath(), &b.dlTotal, &b.dlCurrent) + if errors.Is(err, context.Canceled) { + return errDownloadCanceled + } + return err } // ExtractArchive extracts the downloaded archive. diff --git a/pkg/imager/cli_build.go b/pkg/imager/cli_build.go index 00e27069..3edfcc8d 100644 --- a/pkg/imager/cli_build.go +++ b/pkg/imager/cli_build.go @@ -1,6 +1,7 @@ package imager import ( + "context" "errors" "fmt" "os" @@ -33,7 +34,7 @@ func CLIBuild(log logrus.FieldLogger, root, dlURL string, bpsSlice []boot.Params dlErr := make(chan error, 1) dlT := time.NewTicker(time.Second * 1) go func() { - dlErr <- builder.Download(dlURL) + dlErr <- builder.Download(context.Background(), dlURL) close(dlErr) }() diff --git a/pkg/imager/download.go b/pkg/imager/download.go index 1604220b..2af1243b 100644 --- a/pkg/imager/download.go +++ b/pkg/imager/download.go @@ -8,9 +8,10 @@ import ( "sync/atomic" "github.com/sirupsen/logrus" + "golang.org/x/net/context" ) -func Download(log logrus.FieldLogger, url, dst string, total, current *int64) error { +func Download(ctx context.Context, log logrus.FieldLogger, url, dst string, total, current *int64) error { log = log.WithField("func", "Download") // Prepare temp destination file. @@ -22,7 +23,12 @@ func Download(log logrus.FieldLogger, url, dst string, total, current *int64) er defer closeF() // Prepare download response. - resp, err := http.Get(url) + request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return err + } + + resp, err := http.DefaultClient.Do(request) if err != nil { return err } diff --git a/pkg/imager/fyne.go b/pkg/imager/fyne.go index ca2f672c..23b811cb 100644 --- a/pkg/imager/fyne.go +++ b/pkg/imager/fyne.go @@ -21,10 +21,11 @@ import ( "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skybian/pkg/boot" + "github.com/skycoin/skybian/pkg/imager/widgets" ) -// DefaultVisors is the default number of visor boot parameters to generate. -const DefaultVisors = 7 +// DefaultImgNumber is the default number of visor boot parameters to generate. +const DefaultImgNumber = 1 // FyneUI is a UI to handle the image creation process (using Fyne). type FyneUI struct { @@ -38,16 +39,18 @@ type FyneUI struct { releases []Release locations []string - wkDir string - imgLoc string - remImg string - fsImg string - gwIP net.IP - socksPC string - visors int - hvImg bool - hvPKs []cipher.PubKey - bps []boot.Params + wkDir string + imgLoc string + remImg string + fsImg string + gwIP net.IP + wifiName string + wifiPass string + socksPC string + imgNumber int + hvImg bool + hvPKs []cipher.PubKey + bps []boot.Params } // NewFyneUI creates a new Fyne UI. @@ -93,7 +96,13 @@ func (fg *FyneUI) listBaseImgs() ([]string, string) { d.Hide() if err != nil { - dialog.ShowError(err, fg.w) + var message string + if errors.Is(err, ErrNetworkConn) { + message = "Network connection error. Please ensure that you are connected to the internet correctly." + } else { + message = err.Error() + } + widgets.ShowError(message, fg.w) return nil, "" } @@ -103,11 +112,13 @@ func (fg *FyneUI) listBaseImgs() ([]string, string) { func (fg *FyneUI) generateBPS() (string, error) { prevIP := fg.gwIP - bpsSlice := make([]boot.Params, 0, fg.visors+1) + bpsSlice := make([]boot.Params, 0, fg.imgNumber) hvPKs := fg.hvPKs + visorsNumber := fg.imgNumber if fg.hvImg { + visorsNumber-- hvPK, hvSK := cipher.GenerateKeyPair() - hvBps, err := boot.MakeHypervisorParams(fg.gwIP, hvSK) + hvBps, err := boot.MakeHypervisorParams(fg.gwIP, hvSK, fg.wifiName, fg.wifiPass) if err != nil { return "", fmt.Errorf("boot_params[%d]: failed to generate for hypervisor: %v", len(bpsSlice), err) } @@ -115,9 +126,9 @@ func (fg *FyneUI) generateBPS() (string, error) { bpsSlice = append(bpsSlice, hvBps) hvPKs = append(hvPKs, hvPK) } - for i := 0; i < fg.visors; i++ { + for i := 0; i < visorsNumber; i++ { _, vSK := cipher.GenerateKeyPair() - vBps, err := boot.MakeVisorParams(prevIP, fg.gwIP, vSK, hvPKs, fg.socksPC) + vBps, err := boot.MakeVisorParams(prevIP, fg.gwIP, vSK, hvPKs, fg.socksPC, fg.wifiName, fg.wifiPass) if err != nil { return "", fmt.Errorf("boot_params[%d]: failed to generate for visor: %v", len(bpsSlice), err) } @@ -151,12 +162,14 @@ func (fg *FyneUI) build() { switch fg.imgLoc { case fg.locations[0]: - - // Download section. + ctx, cancel := context.WithCancel(context.Background()) dlTitle := "Downloading Base Image" dlMsg := fg.remImg + "\n" + baseURL - dlDialog := dialog.NewProgress(dlTitle, dlMsg, fg.w) + dlDialog := widgets.NewProgress(dlTitle, dlMsg, fg.w, cancel, "Cancel") + dlDialog.Show() + + // Download section. dlDone := make(chan struct{}) go func() { t := time.NewTicker(time.Second) @@ -173,11 +186,16 @@ func (fg *FyneUI) build() { } } }() - err = builder.Download(baseURL) + err = builder.Download(ctx, baseURL) close(dlDone) dlDialog.Hide() if err != nil { - dialog.ShowError(err, fg.w) + if !errors.Is(err, errDownloadCanceled) { + fg.log.Errorf("Error when downloading image %v", err) + dialog.ShowError(err, fg.w) + } else { + fg.log.Info("Download canceled by user") + } return } diff --git a/pkg/imager/fyne_pages.go b/pkg/imager/fyne_pages.go index 5125ef69..58127318 100644 --- a/pkg/imager/fyne_pages.go +++ b/pkg/imager/fyne_pages.go @@ -11,6 +11,7 @@ import ( "strings" "fyne.io/fyne" + "fyne.io/fyne/container" "fyne.io/fyne/dialog" "fyne.io/fyne/layout" "fyne.io/fyne/theme" @@ -39,6 +40,33 @@ func (fg *FyneUI) Page1() fyne.CanvasObject { widget.NewLabelWithStyle(body, fyne.TextAlignLeading, fyne.TextStyle{Monospace: true}))) } +func (fg *FyneUI) makeFilePicker() fyne.CanvasObject { + fsImg := widget.NewEntry() + fsImg.SetPlaceHolder("path to .img file") + fsImg.OnChanged = func(s string) { + fg.fsImg = s + fg.log.Debugf("Set: fg.fsImg = %v", s) + } + fsImg.SetText(fg.fsImg) + d := dialog.NewFileOpen(func(f fyne.URIReadCloser, err error) { + if err != nil { + fg.log.Error(err) + return + } + if f == nil { + return + } + uri := f.URI().String() + // URI includes file:// scheme, and there is no other way to retrieve full file path + filePath := strings.TrimPrefix(uri, "file://") + fg.fsImg = filePath + fsImg.SetText(filePath) + }, fg.w) + btn := widget.NewButton("Open", d.Show) + box := container.NewHBox(btn, fsImg) + return box +} + // Page2 returns the canvas that draws page 2 of the Fyne interface. func (fg *FyneUI) Page2() fyne.CanvasObject { wkDir := newLinkedEntry(&fg.wkDir) @@ -53,26 +81,20 @@ func (fg *FyneUI) Page2() fyne.CanvasObject { } remImg.Hide() - fsImg := widget.NewEntry() - fsImg.SetPlaceHolder("path to .img file") - fsImg.OnChanged = func(s string) { - fg.fsImg = s - fg.log.Debugf("Set: fg.fsImg = %v", s) - } - fsImg.SetText(fg.fsImg) - fsImg.Hide() + fsImgPicker := fg.makeFilePicker() + fsImgPicker.Hide() imgLoc := widget.NewRadio(fg.locations, func(s string) { switch fg.imgLoc = s; s { case fg.locations[0]: remImg.Show() - fsImg.Hide() + fsImgPicker.Hide() case fg.locations[1]: remImg.Hide() - fsImg.Show() + fsImgPicker.Show() default: remImg.Hide() - fsImg.Hide() + fsImgPicker.Hide() } }) imgLoc.SetSelected(fg.imgLoc) @@ -84,11 +106,37 @@ func (fg *FyneUI) Page2() fyne.CanvasObject { fg.log.Debugf("Set: fg.gwIP = %v", s) }) + wifiName := newEntry(fg.wifiName, func(s string) { + fg.wifiName = s + fg.log.Debugf("Set: fg.gwIP = %v", s) + }) + wifiPass := newEntry(fg.wifiPass, func(s string) { + fg.wifiPass = s + fg.log.Debugf("Set: fg.wifiPass = %v", s) + }) + + wifiWidgets := fyne.NewContainerWithLayout(layout.NewVBoxLayout(), widget.NewLabel("Wifi access point name:"), + wifiName, widget.NewLabel("Wifi passcode:"), wifiPass) + wifiWidgets.Hide() + + enableWifi := widget.NewCheck("Generate wi-fi connection", func(b bool) { + if b { + fg.wifiName = wifiName.Text + fg.wifiPass = wifiPass.Text + wifiWidgets.Show() + } else { + fg.wifiName = "" + fg.wifiPass = "" + wifiWidgets.Hide() + } + }) + enableWifi.SetChecked(false) + socksPC := newLinkedEntry(&fg.socksPC) socksPC.SetPlaceHolder("passcode") - visors := newEntry(strconv.Itoa(fg.visors), func(s string) { - fg.visors, _ = strconv.Atoi(s) + imgNumber := newEntry(strconv.Itoa(fg.imgNumber), func(s string) { + fg.imgNumber, _ = strconv.Atoi(s) fg.log.Debugf("Set: fg.visors = %v", s) }) @@ -159,25 +207,33 @@ func (fg *FyneUI) Page2() fyne.CanvasObject { }, Prev: func() { fg.w.SetContent(fg.Page1()) }, Next: func() { - if !checkPage2Inputs(fg, visors.Text) { + if !checkPage2Inputs(fg, imgNumber.Text) { return } - confirmPage2Continue(fg, fg.wkDir, func() { + proceed := func() { + os.Mkdir(fg.wkDir, os.FileMode(0755)) bpsStr, err := fg.generateBPS() if err != nil { dialog.ShowError(err, fg.w) return } fg.w.SetContent(fg.Page3(bpsStr)) - }) + } + if _, err := os.Stat(fg.wkDir); err == nil { + clearWorkDirDialog(fg, fg.wkDir, proceed) + } else { + proceed() + } }, } return makePage(conf, widget.NewLabel("Work Directory:"), wkDir, - widget.NewLabel("Base Image:"), imgLoc, remImg, fsImg, + widget.NewLabel("Base Image:"), imgLoc, remImg, fsImgPicker, widget.NewLabel("Gateway IP:"), gwIP, + enableWifi, + wifiWidgets, widget.NewLabel("Skysocks Passcode:"), socksPC, - widget.NewLabel("Number of Visor Images:"), visors, + widget.NewLabel("Number of images:"), imgNumber, genHvImg, enableHvPKs, hvPKs, hvPKsAdd) } @@ -186,12 +242,12 @@ func (fg *FyneUI) resetPage2Values() { fg.remImg = "" fg.gwIP = net.ParseIP(boot.DefaultGatewayIP) fg.socksPC = "" - fg.visors = DefaultVisors + fg.imgNumber = DefaultImgNumber fg.hvImg = true fg.hvPKs = nil } -func checkPage2Inputs(fg *FyneUI, visorsText string) bool { +func checkPage2Inputs(fg *FyneUI, imgNumText string) bool { if _, err := filepath.Abs(fg.wkDir); err != nil { return showErr(fg, fmt.Errorf("invalid Work Directory: %v", err)) } @@ -214,16 +270,14 @@ func checkPage2Inputs(fg *FyneUI, visorsText string) bool { if fg.gwIP == nil { return showErr(fg, fmt.Errorf("invalid Gateway IP")) } - if _, err := strconv.Atoi(visorsText); err != nil { - return showErr(fg, fmt.Errorf("invalid Number of Visor Images: %v", err)) - } - if fg.visors < 0 { - return showErr(fg, fmt.Errorf("cannot create %d Visor Images", fg.visors)) + if n, err := strconv.Atoi(imgNumText); err != nil || n <= 0 { + return showErr(fg, fmt.Errorf("Number of images should be a positive integer, got: %s", + imgNumText)) } return true } -func confirmPage2Continue(fg *FyneUI, wkDir string, next func()) { +func clearWorkDirDialog(fg *FyneUI, wkDir string, next func()) { cTitle := "Work Directory Already Exists" cMsg := fmt.Sprintf("Directory %s already exists.\nDelete everything and continue?", wkDir) dialog.ShowConfirm(cTitle, cMsg, func(b bool) { diff --git a/pkg/imager/latest_release.go b/pkg/imager/latest_release.go index 48bbeb97..e0fce049 100644 --- a/pkg/imager/latest_release.go +++ b/pkg/imager/latest_release.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "net" "strings" "time" @@ -13,8 +14,9 @@ import ( ) const ( - ghOwner = "skycoin" - ghRepo = "skybian" + ghOwner = "skycoin" + ghRepo = "skybian" + repoRequestTimeout = 5 * time.Second ) func expectedBaseImgAssetName(tag string) string { @@ -95,11 +97,25 @@ func releaseStrings(releases []Release) (rs []string) { return rs } +// ErrNetworkConn is returned when it's impossible to make a request due to the network failure +var ErrNetworkConn = errors.New("Network connection error") + // ListReleases obtains a list of base image releases. // The output 'latest' is non-nil when a latest release is found. func ListReleases(ctx context.Context, log logrus.FieldLogger) (rs []Release, latest *Release, err error) { gh := github.NewClient(nil) + ctx, cancel := context.WithTimeout(ctx, repoRequestTimeout) + defer cancel() ghRs, _, err := gh.Repositories.ListReleases(ctx, ghOwner, ghRepo, nil) + var dnsErr *net.DNSError + if ok := errors.As(err, &dnsErr); ok { + log.Error("Error fetching latest releases: can't resolve address") + return nil, nil, ErrNetworkConn + } + if ctx.Err() == context.DeadlineExceeded { + log.Error("Error fetching latest releases: network timeout") + return nil, nil, ErrNetworkConn + } if err != nil { return nil, nil, err } diff --git a/pkg/imager/widgets/dialogs.go b/pkg/imager/widgets/dialogs.go new file mode 100644 index 00000000..95975452 --- /dev/null +++ b/pkg/imager/widgets/dialogs.go @@ -0,0 +1,233 @@ +package widgets + +import ( + "image/color" + + "fyne.io/fyne" + "fyne.io/fyne/canvas" + "fyne.io/fyne/layout" + "fyne.io/fyne/theme" + "fyne.io/fyne/widget" +) + +// This package has been mostly copied from fyne/dialog +// The reason of this was that fyne/dialog doesn't provide any easy +// way to extend its widgets and to add custom content + +// Another solution would be to write our own widget from scratch, +// but there isn't much point in it since fyne/dialog implementation +// covers the basic functionality of showing/hiding a window already + +const ( + padWidth = 32 + padHeight = 16 + dialogWidth = 400 +) + +type skydialog struct { + callback func(bool) + title string + icon fyne.Resource + + win *widget.PopUp + bg *canvas.Rectangle + content, label fyne.CanvasObject + dismiss *widget.Button + + response chan bool + responded bool + parent fyne.Window +} + +func (d *skydialog) wait() { + select { + case response := <-d.response: + d.responded = true + d.win.Hide() + if d.callback != nil { + d.callback(response) + } + } +} + +func (d *skydialog) setButtons(buttons fyne.CanvasObject) { + d.bg = canvas.NewRectangle(theme.BackgroundColor()) + d.label = widget.NewLabelWithStyle(d.title, fyne.TextAlignLeading, fyne.TextStyle{Bold: true}) + + var content fyne.CanvasObject + if d.icon == nil { + content = fyne.NewContainerWithLayout(d, + &canvas.Image{}, + d.bg, + d.content, + buttons, + d.label, + ) + } else { + bgIcon := canvas.NewImageFromResource(d.icon) + content = fyne.NewContainerWithLayout(d, + bgIcon, + d.bg, + d.content, + buttons, + d.label, + ) + } + + d.win = widget.NewModalPopUp(content, d.parent.Canvas()) + // fixed width is required to make word-wrapping work correctly + // if needed, add new method to set dialog width programmatically, for now it's a constant + d.win.Resize(fyne.NewSize(dialogWidth, d.win.MinSize().Height)) + d.applyTheme() +} + +func (d *skydialog) Layout(obj []fyne.CanvasObject, size fyne.Size) { + d.bg.Move(fyne.NewPos(-theme.Padding(), -theme.Padding())) + d.bg.Resize(size.Add(fyne.NewSize(theme.Padding()*2, theme.Padding()*2))) + + textMin := obj[2].MinSize() + btnMin := obj[3].MinSize().Union(obj[3].Size()) + + // icon + iconHeight := padHeight*2 + textMin.Height + d.label.MinSize().Height - theme.Padding() + obj[0].Resize(fyne.NewSize(iconHeight, iconHeight)) + obj[0].Move(fyne.NewPos(size.Width-iconHeight+theme.Padding(), -theme.Padding())) + + // content (text) + obj[2].Move(fyne.NewPos(size.Width/2-(textMin.Width/2), size.Height-padHeight-btnMin.Height-textMin.Height-theme.Padding())) + obj[2].Resize(fyne.NewSize(textMin.Width, textMin.Height)) + if d.win != nil { + obj[2].Move(fyne.NewPos(theme.Padding(), size.Height-padHeight-btnMin.Height-textMin.Height-theme.Padding())) + obj[2].Resize(fyne.NewSize(size.Width, size.Height+theme.Padding())) + } + + // buttons + obj[3].Resize(btnMin) + obj[3].Move(fyne.NewPos(size.Width/2-(btnMin.Width/2), size.Height-padHeight-btnMin.Height)) +} + +func (d *skydialog) MinSize(obj []fyne.CanvasObject) fyne.Size { + textMin := obj[2].MinSize() + btnMin := obj[3].MinSize().Union(obj[3].Size()) + + width := fyne.Max(fyne.Max(textMin.Width, btnMin.Width), obj[4].MinSize().Width) + padWidth*2 + height := textMin.Height + btnMin.Height + d.label.MinSize().Height + theme.Padding() + padHeight*2 + + return fyne.NewSize(width, height) +} + +func (d *skydialog) applyTheme() { + r, g, b, _ := theme.BackgroundColor().RGBA() + bg := &color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 230} + d.bg.FillColor = bg +} + +func newDialog(title, message string, icon fyne.Resource, callback func(bool), parent fyne.Window) *skydialog { + d := &skydialog{content: newLabel(message), title: title, icon: icon, parent: parent} + + d.response = make(chan bool, 1) + d.callback = callback + + return d +} + +func newLabel(message string) fyne.CanvasObject { + return widget.NewLabelWithStyle(message, fyne.TextAlignCenter, fyne.TextStyle{}) +} + +func (d *skydialog) Show() { + go d.wait() + d.win.Show() +} + +func (d *skydialog) Hide() { + d.win.Hide() + + if !d.responded && d.callback != nil { + d.callback(false) + } +} + +// SetDismissText allows custom text to be set in the confirmation button +func (d *skydialog) SetDismissText(label string) { + d.dismiss.SetText(label) + widget.Refresh(d.win) +} + +// ShowCustom shows a dialog over the specified application using custom +// content. The button will have the dismiss text set. +// The MinSize() of the CanvasObject passed will be used to set the size of the window. +func ShowCustom(title, dismiss string, content fyne.CanvasObject, parent fyne.Window) { + d := &skydialog{content: content, title: title, icon: nil, parent: parent} + d.response = make(chan bool, 1) + + d.dismiss = &widget.Button{Text: dismiss, + OnTapped: func() { + d.response <- false + }, + } + d.setButtons(widget.NewHBox(layout.NewSpacer(), d.dismiss, layout.NewSpacer())) + d.Show() +} + +// ShowError displays an error dialog with a single OK button, that displays given text +// The text is word wrapped +func ShowError(text string, parent fyne.Window) { + label := widget.NewLabelWithStyle(text, fyne.TextAlignLeading, fyne.TextStyle{}) + label.Wrapping = fyne.TextWrapWord + ShowCustom("Error", "Ok", label, parent) +} + +// ShowCustomConfirm shows a dialog over the specified application using custom +// content. The cancel button will have the dismiss text set and the "OK" will use +// the confirm text. The response callback is called on user action. +// The MinSize() of the CanvasObject passed will be used to set the size of the window. +func ShowCustomConfirm(title, confirm, dismiss string, content fyne.CanvasObject, + callback func(bool), parent fyne.Window) { + d := &skydialog{content: content, title: title, icon: nil, parent: parent} + d.response = make(chan bool, 1) + d.callback = callback + + d.dismiss = &widget.Button{Text: dismiss, Icon: theme.CancelIcon(), + OnTapped: func() { + d.response <- false + }, + } + ok := &widget.Button{Text: confirm, Icon: theme.ConfirmIcon(), Style: widget.PrimaryButton, + OnTapped: func() { + d.response <- true + }, + } + d.setButtons(widget.NewHBox(layout.NewSpacer(), d.dismiss, ok, layout.NewSpacer())) + + d.Show() +} + +// ProgressDialog is a simple dialog window that displays text and a progress bar. +type ProgressDialog struct { + *skydialog + + bar *widget.ProgressBar +} + +// SetValue updates the value of the progress bar - this should be between 0.0 and 1.0. +func (p *ProgressDialog) SetValue(v float64) { + p.bar.SetValue(v) +} + +// NewProgress creates a progress dialog and returns the handle. +// Using the returned type you should call Show() and then set its value through SetValue() +// cancelF will be called upon pressing the cancel button +// cancelText will be shown on the cancel button +func NewProgress(title, message string, parent fyne.Window, cancelF func(), cancelText string) *ProgressDialog { + d := newDialog(title, message, theme.InfoIcon(), nil /*cancel?*/, parent) + bar := widget.NewProgressBar() + cancelBtn := &widget.Button{Text: cancelText, Icon: theme.CancelIcon(), + OnTapped: func() { + cancelF() + d.response <- false + }} + content := widget.NewVBox(bar, cancelBtn) + d.setButtons(content) + return &ProgressDialog{d, bar} +} diff --git a/pkg/prepconf/prepare.go b/pkg/prepconf/prepare.go index 0404cc99..79b5a044 100644 --- a/pkg/prepconf/prepare.go +++ b/pkg/prepconf/prepare.go @@ -8,7 +8,6 @@ import ( "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skywire/pkg/app/launcher" - "github.com/skycoin/skywire/pkg/hypervisor" "github.com/skycoin/skywire/pkg/restart" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" @@ -19,64 +18,44 @@ import ( // Config configures how hypervisor and visor images are to be generated. type Config struct { - VisorConf string - HypervisorConf string - TLSCert string - TLSKey string + BootParams boot.Params + Filename string + TLSCert string + TLSKey string } -// Prepare prepares either a hypervisor and visor config file (based on provided -// conf and boot parameters). -func Prepare(logger *log.Logger, conf Config, bp boot.Params) error { - - // generate config struct - type genFn func(conf Config, bp boot.Params) (out interface{}, err error) - - // ensure config file of 'name' exists - // if not, write config generated by 'genConfig' - ensureExists := func(name string, genConfig genFn) error { - //// Do nothing if file exists. - if _, err := os.Stat(name); err == nil { - conf, err := ioutil.ReadFile(name) //nolint:gosec - if err == nil { - logger.Printf("Contents of %q: %q", name, string(conf)) - } - - if len(conf) != 0 { - logger.Printf("Config file %q already exists and is not empty", name) - return nil - } - } - // Create file. - f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0644) //nolint:gosec - if err != nil { - return err - } - // Generate and write config to file. - conf, err := genConfig(conf, bp) - if err != nil { - return err - } - raw, err := json.MarshalIndent(conf, "", "\t") - if err != nil { - return err +func GenerateConfigFile(conf Config, logger *log.Logger) error { + name := conf.Filename + if _, err := os.Stat(name); err == nil { + conf, err := ioutil.ReadFile(name) //nolint:gosec + if err == nil { + logger.Printf("Contents of %q: %q", name, string(conf)) } - _, err = f.Write(raw) - if err1 := f.Close(); err == nil { - err = err1 + + if len(conf) != 0 { + logger.Printf("Config file %q already exists and is not empty", name) + return nil } + } + // Create file. + f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0644) //nolint:gosec + if err != nil { return err } - - // config location and contents depend on mode - switch bp.Mode { - case boot.HypervisorMode: - return ensureExists(conf.HypervisorConf, generateHypervisorConfig) - case boot.VisorMode: - return ensureExists(conf.VisorConf, generateVisorConfig) - default: - return boot.ErrInvalidMode + // Generate and write config to file. + out, err := generateConfig(conf) + if err != nil { + return err } + raw, err := json.MarshalIndent(out, "", "\t") + if err != nil { + return err + } + _, err = f.Write(raw) + if err1 := f.Close(); err == nil { + err = err1 + } + return err } func genKeyPair(bp boot.Params) (pk cipher.PubKey, sk cipher.SecKey, err error) { @@ -88,7 +67,8 @@ func genKeyPair(bp boot.Params) (pk cipher.PubKey, sk cipher.SecKey, err error) return } -func generateVisorConfig(_ Config, bp boot.Params) (interface{}, error) { +func generateConfig(conf Config) (*visorconfig.V1, error) { + bp := conf.BootParams skysocksArgs := func() (args []string) { if bp.SkysocksPasscode != "" { args = []string{"-passcode", bp.SkysocksPasscode} @@ -100,17 +80,30 @@ func generateVisorConfig(_ Config, bp boot.Params) (interface{}, error) { if err != nil { return nil, err } - - out, err := visorconfig.MakeDefaultConfig(nil, "", &sk) + isHypervisor := bp.Mode == boot.HypervisorMode + out, err := visorconfig.MakeDefaultConfig(nil, "", &sk, isHypervisor) if err != nil { return nil, err } + if isHypervisor { + out.Hypervisor.DBPath = "/var/skywire-hypervisor/users.db" + out.Hypervisor.EnableAuth = true + out.Hypervisor.Cookies.BlockKey = cipher.RandByte(32) + out.Hypervisor.Cookies.HashKey = cipher.RandByte(64) + out.Hypervisor.Cookies.FillDefaults() + out.Hypervisor.DmsgDiscovery = skyenv.DefaultDmsgDiscAddr + out.Hypervisor.DmsgPort = skyenv.DmsgHypervisorPort + out.Hypervisor.HTTPAddr = ":8000" + out.Hypervisor.EnableTLS = false // TODO(evanlinjin): TLS is disabled due to a bug in the skyminer Router. + out.Hypervisor.TLSCertFile = conf.TLSCert + out.Hypervisor.TLSKeyFile = conf.TLSKey + err = GenCert(out.Hypervisor.TLSCertFile, out.Hypervisor.TLSKeyFile) + } // TODO(evanlinjin): We need to handle STCP properly. //if out.STCP, err = visor.DefaultSTCPConfig(); err != nil { // return nil, err //} - out.Dmsgpty.AuthFile = "/var/skywire-visor/dsmgpty/whitelist.json" out.Dmsgpty.CLIAddr = "/run/skywire-visor/dmsgpty/cli.sock" out.Transport.LogStore.Type = "file" @@ -118,7 +111,7 @@ func generateVisorConfig(_ Config, bp boot.Params) (interface{}, error) { out.Hypervisors = bp.HypervisorPKs out.LogLevel = skyenv.DefaultLogLevel out.ShutdownTimeout = visorconfig.DefaultTimeout - out.RestartCheckDelay = restart.DefaultCheckDelay.String() + out.RestartCheckDelay = visorconfig.Duration(restart.DefaultCheckDelay) out.Launcher = &visorconfig.V1Launcher{ Discovery: &visorconfig.V1AppDisc{ ServiceDisc: skyenv.DefaultServiceDiscAddr, @@ -160,26 +153,3 @@ func generateVisorConfig(_ Config, bp boot.Params) (interface{}, error) { } return out, nil } - -func generateHypervisorConfig(conf Config, bp boot.Params) (interface{}, error) { - pk, sk, err := genKeyPair(bp) - if err != nil { - return nil, err - } - out := new(hypervisor.Config) - out.PK = pk - out.SK = sk - out.DBPath = "/var/skywire-hypervisor/users.db" - out.EnableAuth = true - out.Cookies.BlockKey = cipher.RandByte(32) - out.Cookies.HashKey = cipher.RandByte(64) - out.Cookies.FillDefaults() - out.DmsgDiscovery = skyenv.DefaultDmsgDiscAddr - out.DmsgPort = skyenv.DmsgHypervisorPort - out.HTTPAddr = ":8000" - out.EnableTLS = false // TODO(evanlinjin): TLS is disabled due to a bug in the skyminer Router. - out.TLSCertFile = conf.TLSCert - out.TLSKeyFile = conf.TLSKey - err = GenCert(out.TLSCertFile, out.TLSKeyFile) - return out, err -} diff --git a/pkg/prepconf/prepare_test.go b/pkg/prepconf/prepare_test.go index 1130175a..7d5a229a 100644 --- a/pkg/prepconf/prepare_test.go +++ b/pkg/prepconf/prepare_test.go @@ -20,48 +20,55 @@ func TestPrepare(t *testing.T) { dir, err := ioutil.TempDir(os.TempDir(), "TestPrepare") require.NoError(t, err) defer func() { require.NoError(t, os.RemoveAll(dir)) }() - - conf := Config{ - VisorConf: filepath.Join(dir, "visor.json"), - HypervisorConf: filepath.Join(dir, "hypervisor.json"), - TLSKey: filepath.Join(dir, "key.pem"), - TLSCert: filepath.Join(dir, "cert.pem"), - } pk, sk := cipher.GenerateKeyPair() - vParams := boot.Params{ - Mode: boot.VisorMode, - LocalIP: net.ParseIP(boot.DefaultGatewayIP), - GatewayIP: net.ParseIP(boot.DefaultGatewayIP), - LocalPK: pk, - LocalSK: sk, - HypervisorPKs: []cipher.PubKey{pk}, - SkysocksPasscode: "test", + vConf := Config{ + Filename: "vskyconf.json", + TLSKey: filepath.Join(dir, "key.pem"), + TLSCert: filepath.Join(dir, "cert.pem"), + BootParams: boot.Params{ + Mode: boot.VisorMode, + LocalIP: net.ParseIP(boot.DefaultGatewayIP), + GatewayIP: net.ParseIP(boot.DefaultGatewayIP), + LocalPK: pk, + LocalSK: sk, + HypervisorPKs: []cipher.PubKey{pk}, + SkysocksPasscode: "test", + }, } - hvParams := boot.Params{ - Mode: boot.HypervisorMode, - LocalIP: net.ParseIP(boot.DefaultGatewayIP), - GatewayIP: net.ParseIP(boot.DefaultGatewayIP), - LocalPK: pk, - LocalSK: sk, - HypervisorPKs: []cipher.PubKey{pk}, - SkysocksPasscode: "test", + hConf := Config{ + Filename: "hvskyconf.json", + TLSKey: filepath.Join(dir, "key.pem"), + TLSCert: filepath.Join(dir, "cert.pem"), + BootParams: boot.Params{ + Mode: boot.HypervisorMode, + LocalIP: net.ParseIP(boot.DefaultGatewayIP), + GatewayIP: net.ParseIP(boot.DefaultGatewayIP), + LocalPK: pk, + LocalSK: sk, + HypervisorPKs: []cipher.PubKey{pk}, + SkysocksPasscode: "test", + }, } - require.NoError(t, Prepare(logger, conf, vParams)) - v1, err := ioutil.ReadFile(conf.VisorConf) + defer func() { + os.Remove(vConf.Filename) + os.Remove(hConf.Filename) + }() + require.NoError(t, GenerateConfigFile(vConf, logger)) + v1, err := ioutil.ReadFile(vConf.Filename) require.NoError(t, err) - vParams.LocalPK, vParams.LocalSK = cipher.GenerateKeyPair() - require.NoError(t, Prepare(logger, conf, vParams)) - v2, err := ioutil.ReadFile(conf.VisorConf) + vConf.BootParams.LocalPK, vConf.BootParams.LocalSK = cipher.GenerateKeyPair() + require.NoError(t, GenerateConfigFile(vConf, logger)) + v2, err := ioutil.ReadFile(vConf.Filename) require.NoError(t, err) - require.NoError(t, Prepare(logger, conf, hvParams)) - v3, err := ioutil.ReadFile(conf.HypervisorConf) + require.NoError(t, GenerateConfigFile(hConf, logger)) + v3, err := ioutil.ReadFile(hConf.Filename) require.NoError(t, err) - hvParams.LocalPK, hvParams.LocalSK = cipher.GenerateKeyPair() - require.NoError(t, Prepare(logger, conf, hvParams)) - v4, err := ioutil.ReadFile(conf.HypervisorConf) + vConf.BootParams.LocalPK, vConf.BootParams.LocalSK = cipher.GenerateKeyPair() + require.NoError(t, GenerateConfigFile(hConf, logger)) + v4, err := ioutil.ReadFile(hConf.Filename) require.NoError(t, err) require.Equal(t, v1, v2) diff --git a/static/migrate-single-binary.sh b/static/migrate-single-binary.sh new file mode 100644 index 00000000..35702797 --- /dev/null +++ b/static/migrate-single-binary.sh @@ -0,0 +1,230 @@ +#!/usr/bin/env bash + +ARCHIVE_NAME="skywire-v0.4.0-linux-arm64.tar.gz" +RELEASE_URL="https://github.com/skycoin/skywire/releases/download/v0.4.0/$ARCHIVE_NAME" + +MIGRATION_DIR="/var/skywire/migration" +MIGRATION_BIN="${MIGRATION_DIR}/bin" +MIGRATION_BACKUP="/var/skywire/backup/migration" +BACKUP_BIN=$MIGRATION_BACKUP/bin +BACKUP_CONF=$MIGRATION_BACKUP/conf +SYSTEMD_DIR="/etc/systemd/system" + +main() { + prepare + update_binaries + update_configs + finalize +} + +prepare() { + echo "Preparing..." + # install jq to merge json configurations + + # update doesn't seem to work for now + #apt update + apt install -y jq + + mkdir -p $BACKUP_CONF $BACKUP_BIN $MIGRATION_DIR $MIGRATION_BIN + + echo "Downloading release..." + + wget -c $RELEASE_URL -O "${MIGRATION_BIN}/${ARCHIVE_NAME}" + tar xfzv "${MIGRATION_BIN}/${ARCHIVE_NAME}" -C $MIGRATION_BIN + + # stop service + echo "stopping and disabling services..." + systemctl stop skywire-visor.service + sleep 2 + systemctl disable skywire-visor.service + systemctl stop skywire-hypervisor.service + sleep 2 + systemctl disable skywire-hypervisor.service +} + +update_binaries() { + echo "Removing old binaries..." + mv $SYSTEMD_DIR/skybian-firstrun.service $MIGRATION_BACKUP + cp $SYSTEMD_DIR/skywire-visor.service $MIGRATION_BACKUP 2> /dev/null + mv $SYSTEMD_DIR/skywire-hypervisor.service $MIGRATION_BACKUP 2> /dev/null + mv /usr/bin/skybian-firstrun $MIGRATION_BACKUP + mv /usr/bin/skywire-hypervisor $MIGRATION_BACKUP 2> /dev/null + mv /usr/bin/skywire-visor $MIGRATION_BACKUP 2> /dev/null + mv /usr/bin/apps/skychat $BACKUP_BIN + mv /usr/bin/apps/skysocks $BACKUP_BIN + mv /usr/bin/apps/skysocks-client $BACKUP_BIN + mv /usr/bin/apps/vpn-client $BACKUP_BIN + mv /usr/bin/apps/vpn-server $BACKUP_BIN + + echo "Setting up new binaries..." + mv "${MIGRATION_BIN}/skywire-visor" /usr/bin/ + mv "${MIGRATION_BIN}/apps/skychat" /usr/bin/apps/ + mv "${MIGRATION_BIN}/apps/skysocks" /usr/bin/apps/ + mv "${MIGRATION_BIN}/apps/skysocks-client" /usr/bin/apps/ + mv "${MIGRATION_BIN}/apps/vpn-client" /usr/bin/apps/ + mv "${MIGRATION_BIN}/apps/vpn-server" /usr/bin/apps/ +} + +update_configs() { + echo "Removing old configs..." + # move existing configs + mv /etc/skywire-visor.json $BACKUP_CONF 2> /dev/null + mv /etc/skywire-hypervisor.json $BACKUP_CONF 2> /dev/null + + # change skywire-visor service to support new binary + sed -i 's#ExecStart.*#ExecStart=/usr/bin/skywire-visor -c /etc/skywire-config.json#' $SYSTEMD_DIR/skywire-visor.service + if [ -f "${BACKUP_CONF}/skywire-visor.json" ] ; then + gen_visor_config + fi + + if [ -f "${BACKUP_CONF}/skywire-hypervisor.json" ] ; then + gen_hypervisor_config + fi +} + +finalize() { + # reload systemd service definitions + systemctl daemon-reload + systemctl enable skywire-visor.service + rm -rf $MIGRATION_BIN/* + reboot +} + +# looks like merged visor/hypervisor config format is compatible +# with old visor, so no chages required +gen_visor_config() { + echo "Generating visor config..." + # todo: update transport log location? + cp "${BACKUP_CONF}/skywire-visor.json" /etc/skywire-config.json +} + +gen_hypervisor_config() { + echo "Generating hypervisor config..." + + local SRC="${BACKUP_CONF}/skywire-hypervisor.json" + local RESULT="${BACKUP_CONF}/skywire-config.json" + local PK=$(jq '.public_key' $SRC) + local SK=$(jq '.secret_key' $SRC) + + # if someone is running both visor and hypervisor, use existing visor config + # as a template + if [ -f "${BACKUP_CONF}/skywire-visor.json" ] ; then + HV_CONF_TPL=$(cat "${BACKUP_CONF}/skywire-visor.json") + fi + + # add hypervisor key + echo "$HV_CONF_TPL" | jq '.hypervisor={}' > $RESULT + + update_key $SRC ".public_key" ".pk" $RESULT + update_key $SRC ".secret_key" ".sk" $RESULT + update_key $SRC ".db_path" ".hypervisor.db_path" $RESULT + update_key $SRC ".enable_auth" ".hypervisor.enable_auth" $RESULT + update_key $SRC ".cookies" ".hypervisor.cookies" $RESULT + update_key $SRC ".dmsg_port" ".hypervisor.dmsg_port" $RESULT + update_key $SRC ".http_addr" ".hypervisor.http_addr" $RESULT + update_key $SRC ".enable_tls" ".hypervisor.enable_tls" $RESULT + update_key $SRC ".tls_cert_file" ".hypervisor.tls_cert_file" $RESULT + update_key $SRC ".tls_key_file" ".hypervisor.tls_key_file" $RESULT + mv $RESULT /etc/skywire-config.json +} + +# accept 4 arguments: source, key, target key and target +# look for the key under source, and put it into the target +# under target key +update_key() { + local SRC=${1:?Need source from which to update} + local KEY=${2:?Need key to update} + local TARGET_KEY=${3:?Need target key} + local TARGET=${4:?Need json file as a target} + local VAL=$(cat $SRC | jq "$KEY") + local RES=$(jq "$TARGET_KEY=$VAL" "$TARGET") + echo "$RES" > $TARGET +} + +HV_CONF_TPL=' +{ + "version": "v1.0.0", + "sk": "3291b0af73b2ac7287188ddb5e03fb49c8ac445e2efa2d4aa4dbb0e5162ab9e2", + "pk": "034904885ee8905abcc3fd005dd15c5dab656afa400521725e7627a059dd994a31", + "dmsg": { + "discovery": "http://dmsg.discovery.skywire.skycoin.com", + "sessions_count": 1 + }, + "dmsgpty": { + "port": 22, + "authorization_file": "./dmsgpty/whitelist.json", + "cli_network": "unix", + "cli_address": "/tmp/dmsgpty.sock" + }, + "stcp": { + "pk_table": null, + "local_address": ":7777" + }, + "transport": { + "discovery": "http://transport.discovery.skywire.skycoin.com", + "address_resolver": "http://address.resolver.skywire.skycoin.com", + "log_store": { + "type": "file", + "location": "./transport_logs" + }, + "trusted_visors": null + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "http://routefinder.skywire.skycoin.com", + "route_finder_timeout": "10s" + }, + "uptime_tracker": { + "addr": "http://uptime-tracker.skywire.skycoin.com" + }, + "launcher": { + "discovery": { + "update_interval": "30s", + "proxy_discovery_addr": "http://service.discovery.skycoin.com" + }, + "apps": [ + { + "name": "skychat", + "args": [ + "-addr", + ":8001" + ], + "auto_start": true, + "port": 1 + }, + { + "name": "skysocks", + "auto_start": true, + "port": 3 + }, + { + "name": "skysocks-client", + "auto_start": false, + "port": 13 + }, + { + "name": "vpn-server", + "auto_start": false, + "port": 44 + }, + { + "name": "vpn-client", + "auto_start": false, + "port": 43 + } + ], + "server_addr": "localhost:5505", + "bin_path": "/usr/bin/apps", + "local_path": "/var/skywire-visor/apps" + }, + "hypervisors": [], + "cli_addr": "localhost:3435", + "log_level": "info", + "shutdown_timeout": "10s", + "restart_check_delay": "1s" +} +' + +main "$@" diff --git a/static/skybian-firstrun b/static/skybian-firstrun index 0f61b709..45e5fb3f 100644 --- a/static/skybian-firstrun +++ b/static/skybian-firstrun @@ -6,16 +6,16 @@ # - Values of the boot params. DEV_FILE=/dev/mmcblk0 -VISOR_CONF=/etc/skywire-visor.json -HYPERVISOR_CONF=/etc/skywire-hypervisor.json +CONFIG_FILE=/etc/skywire-config.json TLS_KEY=/etc/skywire-hypervisor/key.pem TLS_CERT=/etc/skywire-hypervisor/cert.pem NET_NAME="Wired connection 1" +WIFI_NAME="Wireless connection 1" # Stop here if config files are already generated. -if [[ -f "$VISOR_CONF" || -f "$HYPERVISOR_CONF" ]]; then +if [[ -f "$CONFIG_FILE" ]]; then echo "Nothing to be done here." systemctl disable skybian-firstrun.service exit 0 @@ -26,7 +26,7 @@ fi # area of the boot device. This starts at position +0E0(hex) and has 216 bytes. setup_skywire() { - if ! readonly BOOT_PARAMS=$(/usr/bin/skyconf -if=$DEV_FILE -vf=$VISOR_CONF -hvf=$HYPERVISOR_CONF -keyf=$TLS_KEY -certf=$TLS_CERT); then + if ! readonly BOOT_PARAMS=$(/usr/bin/skyconf -if=$DEV_FILE -c=$CONFIG_FILE -keyf=$TLS_KEY -certf=$TLS_CERT); then echo "Failed to setup skywire environment." return 1 fi @@ -63,7 +63,7 @@ setup_network() if [[ -n "$IP" ]]; then echo "Setting manual IP to $IP for $NET_NAME." - nmcli con mod "$NET_NAME" ipv4.addresses "$IP" ipv4.method "manual" + nmcli con mod "$NET_NAME" ipv4.addresses "$IP/24" ipv4.method "manual" fi if [[ -n "$GW" ]]; then @@ -73,31 +73,73 @@ setup_network() nmcli con mod "$NET_NAME" ipv4.dns "1.0.0.1, 1.1.1.1" } -setup_network || exit 1 + +setup_wifi() +{ + echo "Setting up wifi connection $WIFI_NAME..." + nmcli c add type wifi con-name "$WIFI_NAME" ifname wlan0 ssid $WFN + if [[ -n "$WFP" ]]; then + nmcli c modify "$WIFI_NAME" wifi-sec.key-mgmt wpa-psk wifi-sec.psk $WFP + fi + + if [[ -n "$IP" && -n "$GW" ]]; then + echo "Setting manual IP to $IP for $WIFI_NAME." + nmcli con mod "$WIFI_NAME" ipv4.addresses "$IP/24" ipv4.method "manual" + fi + + if [[ -n "$GW" ]]; then + echo "Setting manual Gateway IP to $GW for $WIFI_NAME." + nmcli con mod "$WIFI_NAME" ipv4.gateway "$GW" + fi + nmcli con mod "$WIFI_NAME" ipv4.dns "1.0.0.1, 1.1.1.1" + nmcli con down "$WIFI_NAME" + sleep 3 + nmcli con up "$WIFI_NAME" +} + +# assume wifi should be configured instead of ethernet when wifi name env var is set +if [[ -n "$WFN" ]]; then + setup_wifi || exit 1 +else + setup_network || exit 1 +fi for file in /etc/ssh/ssh_host* ; do - echo "[skybian-firstrun] Checking $file:" - cat "$file" - done - -# Enable associated service. -case $MD in - "VISOR") - echo "Enabling 'skywire-visor.service'." - systemctl enable skywire-visor.service - sleep 2 - systemctl start skywire-visor.service - ;; - "HYPERVISOR") - echo "Enabling 'skywire-hypervisor.service'." - systemctl enable skywire-hypervisor.service - sleep 2 - systemctl start skywire-hypervisor.service - ;; - *) - exit 1 - ;; -esac + echo "[skybian-firstrun] Checking $file:" + cat "$file" +done + +echo "Enabling 'skywire-visor.service'." +systemctl enable skywire-visor.service +sleep 2 +systemctl start skywire-visor.service + +install_ntp() +{ + # (courtesy of https://github.com/some4/skywire-install-bash/blob/master/install.sh) + # Stop timesyncd: + systemctl stop systemd-timesyncd.service + + # Backup (but don't overwrite an existing) config. If not, sed will keep + # appending file: + cp -n /etc/systemd/timesyncd.conf /etc/systemd/timesyncd.orig + # Use fresh copy in case installer used on existing system: + cp /etc/systemd/timesyncd.orig /etc/systemd/timesyncd.conf + + # When system is set to sync with RTC the time can't be updated and NTP + # is crippled. Switch off that setting with: + timedatectl set-local-rtc 0 + timedatectl set-ntp on + apt update && apt install -y ntp + + systemctl disable systemd-timesyncd.service + + info "Restarting NTP..." + systemctl restart ntp.service + # Set hardware clock to UTC (which doesn't have daylight savings): + hwclock -w +} +install_ntp || logger "Failed to setup ntp service" systemctl disable skybian-firstrun.service exit 0 diff --git a/static/skywire-hypervisor.service b/static/skywire-hypervisor.service deleted file mode 100644 index 910b8a91..00000000 --- a/static/skywire-hypervisor.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Skywire Hypervisor -After=network.target - -[Service] -Type=simple -User=root -Group=root -ExecStart=/usr/bin/skywire-hypervisor -c /etc/skywire-hypervisor.json -Restart=on-failure -RestartSec=20 -TimeoutSec=30 - -[Install] -WantedBy=multi-user.target diff --git a/static/skywire-visor.service b/static/skywire-visor.service index 9963e907..0f82fd0f 100644 --- a/static/skywire-visor.service +++ b/static/skywire-visor.service @@ -6,7 +6,7 @@ After=network.target Type=simple User=root Group=root -ExecStart=/usr/bin/skywire-visor /etc/skywire-visor.json +ExecStart=/usr/bin/skywire-visor -c /etc/skywire-config.json Restart=on-failure RestartSec=20 TimeoutSec=30