diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 0d1de07b..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,87 +0,0 @@ -# Golang CircleCI 2.0 configuration file -# -# Check https://circleci.com/docs/2.0/language-go/ for more details -version: 2.1 - -orbs: - aws-s3: circleci/aws-s3@1.0.11 - -_defaults: &defaults - docker: - - image: techknowlogick/xgo:latest - -commands: - cross_compile: - description: "Cross compile binaries for different os and archs" - steps: - - run: - name: Build binaries - command: | - make - mv $(make target_name) ./artifacts/darknode_linux_amd64 - env GOOS=linux CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc-6 CXX=aarch64-linux-gnu-g++-6 GOARCH=arm64 make - mv $(make target_name) ./artifacts/darknode_linux_arm - env GOOS=darwin CGO_ENABLED=1 CC=o64-clang CXX=o64-clang++ GOARCH=amd64 make - mv $(make target_name) ./artifacts/darknode_darwin_amd64 - release: - description: "Publish a new release" - steps: - - run: - name: Publish release to github - command: | - wget https://www.github.com/tcnksm/ghr/releases/download/v0.13.0/ghr_v0.13.0_linux_amd64.tar.gz - tar xvzf ghr_v0.13.0_linux_amd64.tar.gz - mv ghr_v0.13.0_linux_amd64/ghr ghr - ./ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -soft $(cat ./VERSION) ./artifacts/ - - test_env: - description: "Push the binaries to aws s3, so that we can test before releasing." - steps: - - run: - name: install AWS CLI - command: | - apt-get -y install python3 - apt -y install python-pip python-dev - pip install awscli - - aws-s3/sync: - from: ./artifacts - to: 's3://releases.republicprotocol.com/darknode-cli/temp/' - arguments: '--acl public-read' - overwrite: true - -jobs: - deployment: - <<: *defaults - steps: - - checkout - - run: - name: install tools - command: | - go vet ./... - - cross_compile - - release - test: - <<: *defaults - steps: - - checkout - - run: - name: install tools - command: | - go vet ./... - - cross_compile - - test_env - -workflows: - version: 2.1 - deployment: - jobs: - - deployment: - filters: - branches: - only: - - master - - test: - filters: - branches: - ignore: - - master diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..e0bd2e4a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,45 @@ +name: release +on: + push: + tags: + - '*' +jobs: + release: + container: techknowlogick/xgo:latest + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + - name: Configure git for private modules + env: + TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + run: git config --global url."https://tok-kkk:${TOKEN}@github.com".insteadOf "https://github.com" + - name: Get the version + uses: olegtarasov/get-tag@v2.1 + id: tagName + - name: Build the binary + run: | + go build -o darknode_linux_amd64 -ldflags "-s -w -X main.BinaryVersion=${GIT_TAG_NAME}" ./cmd/*.go + mv darknode_linux_amd64 ./artifacts/ + env GOOS=linux CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc-6 CXX=aarch64-linux-gnu-g++-6 GOARCH=arm64 go build -o darknode_linux_arm -ldflags "-s -w -X main.BinaryVersion=${GIT_TAG_NAME}" ./cmd/*.go + mv darknode_linux_arm ./artifacts/ + env GOOS=darwin CGO_ENABLED=1 CC=o64-clang CXX=o64-clang++ GOARCH=amd64 go build -o darknode_darwin_amd64 -ldflags "-s -w -X main.BinaryVersion=${GIT_TAG_NAME}" ./cmd/*.go + mv darknode_darwin_amd64 ./artifacts/ + env GOOS=darwin CGO_ENABLED=1 CC=o64-clang CXX=o64-clang++ GOARCH=arm64 go build -o darknode_darwin_arm64 -ldflags "-s -w -X main.BinaryVersion=${GIT_TAG_NAME}" ./cmd/*.go + mv darknode_darwin_arm64 ./artifacts/ + - name: Create Release + id: create_release + uses: softprops/action-gh-release@v1 + with: + files: | + ./artifacts/darknode_darwin_amd64 + ./artifacts/darknode_darwin_arm64 + ./artifacts/darknode_linux_amd64 + ./artifacts/darknode_linux_arm + ./artifacts/install.sh + ./artifacts/update.sh + - name: Verify the installation process + run: | + curl https://www.github.com/renproject/darknode-cli/releases/latest/download/install.sh -sSfL | sh + export PATH=$PATH:$HOME/.darknode/bin + darknode --version \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ac8331cf..79138038 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## 3.1.0 +- Support Apple Silicon chip (M1) +- Improve installation/update scripts +- Bug fixes +- Update package dependency +- Move from CircleCI to github action + +## 3.0.15 +- Support deploying a new darknode using an existing config file. + +## 3.0.14 +- Fix bug when fetching latest darknode-release during darknode installation +- Remove `jq` from darknode installation + ## 3.0.13 - Fix pagination issue when fetching latest darknode releases from github. diff --git a/Makefile b/Makefile deleted file mode 100644 index b0768016..00000000 --- a/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -MAIN_VERSION = $(shell cat ./VERSION | tr -d "[:space:]") -FULL_VERSION = ${MAIN_VERSION} - -TARGET = ./darknode-cli-bin - -# For information on flags: https://golang.org/cmd/link/ -LDFLAGS = -s -w -X main.binaryVersion=${FULL_VERSION} - -all: local - -local: clean - $(call build_local,./cmd) - -version: - @ echo ${FULL_VERSION} - -target_name: - @ echo "${TARGET}" - -clean: - rm -rf "${TARGET}" - -define build_local - go build -o ${TARGET} -ldflags="${LDFLAGS}" $(1) -endef - -.PHONY: all local version clean target_name - diff --git a/README.md b/README.md index 8a6c053a..1879444c 100644 --- a/README.md +++ b/README.md @@ -226,3 +226,7 @@ or a script file ```sh darknode exec YOUR-DARKNODE-NAME --file test.sh ``` + +### Github rate limiting + +You can set the `GITHUB_TOKEN` environment variable to increase your github rate limit. \ No newline at end of file diff --git a/VERSION b/VERSION deleted file mode 100644 index 312883d2..00000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -3.0.15 \ No newline at end of file diff --git a/artifacts/install.sh b/artifacts/install.sh index 5084cd99..f5436278 100755 --- a/artifacts/install.sh +++ b/artifacts/install.sh @@ -1,89 +1,263 @@ #!/bin/sh -# define color escape codes -RED='\033[0;31m' -GREEN='\033[0;32m' -NC='\033[0m' - -echo "Installing Darknode CLI..." - -# Install unzip if command not found -if ! [ -x "$(command -v unzip)" ];then - sudo apt-get install unzip -fi - -# creating working directory -mkdir -p $HOME/.darknode/darknodes -mkdir -p $HOME/.darknode/bin -cd $HOME/.darknode - -# get system information -ostype="$(uname -s)" -cputype="$(uname -m)" - -# download darknode binary depending on the system and architecture -if [ "$ostype" = 'Linux' -a "$cputype" = 'x86_64' ]; then - TERRAFORM_URL='https://releases.hashicorp.com/terraform/0.12.24/terraform_0.12.24_linux_amd64.zip' - curl -sL 'https://www.github.com/renproject/darknode-cli/releases/latest/download/darknode_linux_amd64' > ./bin/darknode -elif [ "$ostype" = 'Linux' -a "$cputype" = 'aarch64' ]; then - TERRAFORM_URL='https://releases.hashicorp.com/terraform/0.12.24/terraform_0.12.24_linux_arm.zip' - curl -sL 'https://www.github.com/renproject/darknode-cli/releases/latest/download/darknode_linux_arm' > ./bin/darknode -elif [ "$ostype" = 'Darwin' -a "$cputype" = 'x86_64' ]; then - TERRAFORM_URL='https://releases.hashicorp.com/terraform/0.12.24/terraform_0.12.24_darwin_amd64.zip' - curl -sL 'https://www.github.com/renproject/darknode-cli/releases/latest/download/darknode_darwin_amd64' > ./bin/darknode -else - echo 'unsupported OS type or architecture' - cd .. - rm -rf .darknode - exit 1 -fi - -chmod +x bin/darknode - -# download terraform -curl -s "$TERRAFORM_URL" > terraform.zip -unzip terraform -chmod +x terraform -mv terraform bin/terraform - -# clean up zip files -rm terraform.zip - -# make sure the binary is installed in the path -if ! [ -x "$(command -v darknode)" ]; then - path=$SHELL - shell=${path##*/} - - if [ "$shell" = 'zsh' ] ; then - if [ -f "$HOME/.zprofile" ] ; then - echo '\nexport PATH=$PATH:$HOME/.darknode/bin' >> $HOME/.zprofile - elif [ -f "$HOME/.zshrc" ] ; then - echo '\nexport PATH=$PATH:$HOME/.darknode/bin' >> $HOME/.zshrc - elif [ -f "$HOME/.profile" ] ; then - echo '\nexport PATH=$PATH:$HOME/.darknode/bin' >> $HOME/.profile +set -u + +main() { + # Update this when minimum terraform version is changed. + min_terraform_ver="0.12.24" + cur_terraform_ver="0.12.24" + + # Exit if `darknode` is already installed + if check_cmd darknode; then + err "darknode-cli already installed on this machine" + fi + + # Start installing + echo "Installing darknode-cli ..." + + # Check prerequisites + prerequisites $min_terraform_ver || return 1 + + # Check system information + ostype="$(uname -s | tr '[:upper:]' '[:lower:]')" + cputype="$(uname -m | tr '[:upper:]' '[:lower:]')" + check_architecture "$ostype" "$cputype" + progressBar 10 100 + + # Initialization + ensure mkdir -p "$HOME/.darknode/darknodes" + ensure mkdir -p "$HOME/.darknode/bin" + ensure mkdir -p "$HOME/.darknode/backup" + + # Install terraform + if [ $cputype = "x86_64" ];then + cputype="amd64" + fi + if ! check_cmd terraform; then + terraform_url="https://releases.hashicorp.com/terraform/${cur_terraform_ver}/terraform_${cur_terraform_ver}_${ostype}_${cputype}.zip" + ensure downloader "$terraform_url" "$HOME/.darknode/bin/terraform.zip" + ensure unzip -qq "$HOME/.darknode/bin/terraform.zip" -d "$HOME/.darknode/bin" + ensure chmod +x "$HOME/.darknode/bin/terraform" + rm "$HOME/.darknode/bin/terraform.zip" + fi + progressBar 50 100 + + # Download darknode-cli binary + darknode_url="https://www.github.com/renproject/darknode-cli/releases/latest/download/darknode_${ostype}_${cputype}" + ensure downloader "$darknode_url" "$HOME/.darknode/bin/darknode" + ensure chmod +x "$HOME/.darknode/bin/darknode" + progressBar 90 100 + + # Try adding the darknode directory to PATH + add_path + progressBar 100 100 + sleep 1 + + # Output success message + printf "\n\n" + printf 'If you are using a custom shell, make sure you update your PATH.\n' + printf " export PATH=\$PATH:\$HOME/.darknode/bin" + printf "\n\n" + printf "Done! Restart terminal and run the command below to begin.\n" + printf "\n" + printf "darknode --help\n" +} + +# Check prerequisites for installing darknode-cli. +prerequisites() { + # Check commands + need_cmd uname + need_cmd chmod + need_cmd mkdir + need_cmd rm + + # Install unzip for user if not installed + if ! check_cmd unzip; then + echo "installing prerequisites: unzip" + if ! sudo apt-get install unzip -qq; then + err "need 'unzip' (command not found)" + fi + fi + + # Check either curl or wget is installed. + if ! check_cmd curl; then + if ! check_cmd wget; then + err "need 'curl' or 'wget' (command not found)" + fi fi - elif [ "$shell" = 'bash' ] ; then - if [ -f "$HOME/.bash_profile" ] ; then - echo '\nexport PATH=$PATH:$HOME/.darknode/bin' >> $HOME/.bash_profile - elif [ -f "$HOME/.bashrc" ] ; then - echo '\nexport PATH=$PATH:$HOME/.darknode/bin' >> $HOME/.bashrc - elif [ -f "$HOME/.profile" ] ; then - echo '\nexport PATH=$PATH:$HOME/.darknode/bin' >> $HOME/.profile + + # Check if terraform has been installed. + # If so, make sure it's newer than required version + if check_cmd terraform; then + version="$(terraform --version | grep 'Terraform v' | cut -d "v" -f2)" + major="$(echo $version | cut -d. -f1)" + minor="$(echo $version | cut -d. -f2)" + patch="$(echo $version | cut -d. -f3)" + requiredMajor="$(echo $1 | cut -d. -f1)" + requiredMinor="$(echo $1 | cut -d. -f2)" + requiredPatch="$(echo $1 | cut -d. -f3)" + + if [ "$major" -lt "$requiredMajor" ]; then + err "Please upgrade your terraform to version above $min_terraform_ver" + fi + if [ "$minor" -lt "$requiredMinor" ]; then + err "Please upgrade your terraform to version above $min_terraform_ver" + fi + if [ "$patch" -lt "$requiredPatch" ]; then + err "Please upgrade your terraform to version above $min_terraform_ver" + fi + fi +} + +# Check if darknode-cli supports given system and architecture. +check_architecture() { + ostype="$1" + cputype="$2" + + if [ "$ostype" = 'linux' -a "$cputype" = 'x86_64' ]; then + : + elif [ "$ostype" = 'linux' -a "$cputype" = 'aarch64' ]; then + : + elif [ "$ostype" = 'darwin' -a "$cputype" = 'x86_64' ]; then + if [ "$cputype" = 'x86_64' ]; then + : + elif [ "$cputype" = 'arm64' ]; then + : + else + echo 'unsupported OS type or architecture' + exit 1 + fi + + case $(sw_vers -productVersion) in + 10.*) + # If we're running on macOS, older than 10.13, then we always + # fail to find these options to force fallback + if [ "$(sw_vers -productVersion | cut -d. -f2)" -lt 13 ]; then + # Older than 10.13 + echo "Warning: Detected macOS platform older than 10.13" + return 1 + fi + ;; + 11.*) + # We assume Big Sur will be OK for now + ;; + *) + # Unknown product version, warn and continue + echo "Warning: Detected unknown macOS major version: $(sw_vers -productVersion)" + echo "Warning TLS capabilities detection may fail" + ;; + esac else - echo '\nexport PATH=$PATH:$HOME/.darknode/bin' >> $HOME/.bash_profile + echo 'unsupported OS type or architecture' + exit 1 + fi +} + +# Add the binary path to $PATH. +add_path(){ + if ! check_cmd darknode; then + if [ -f "$HOME/.zprofile" ] ; then + echo "" >> "$HOME/.zprofile" + echo 'export PATH=$PATH:$HOME/.darknode/bin' >> "$HOME/.zprofile" + else + echo 'export PATH=$PATH:$HOME/.darknode/bin' > "$HOME/.zprofile" + fi + + if [ -f "$HOME/.bash_profile" ] ; then + echo "" >> "$HOME/.bash_profile" + echo 'export PATH=$PATH:$HOME/.darknode/bin' >> "$HOME/.bash_profile" + else + echo 'export PATH=$PATH:$HOME/.darknode/bin' > "$HOME/.bash_profile" + fi + + if [ -f "$HOME/.cshrc" ] ; then + echo "" >> "$HOME/.cshrc" + echo 'setenv PATH $PATH\:$HOME/.darknode/bin' >> "$HOME/.cshrc" + fi + + echo "" >> "$HOME/.profile" + echo 'export PATH=$PATH:$HOME/.darknode/bin' >> "$HOME/.profile" fi - elif [ -f "$HOME/.profile" ] ; then - echo '\nexport PATH=$PATH:$HOME/.darknode/bin' >> $HOME/.profile - else - echo '\nexport PATH=$PATH:$HOME/.darknode/bin' >> $HOME/.profile - fi - - echo '' - echo 'If you are using a custom shell, make sure you update your PATH.' - echo "${GREEN}export PATH=\$PATH:\$HOME/.darknode/bin ${NC}" -fi - -echo '' -echo "${GREEN}Done! Restart terminal and run the command below to begin.${NC}" -echo '' -echo "${GREEN}darknode up --help ${NC}" \ No newline at end of file +} + +# Source: https://sh.rustup.rs +check_cmd() { + command -v "$1" > /dev/null 2>&1 +} + +# Source: https://sh.rustup.rs +need_cmd() { + if ! check_cmd "$1"; then + err "need '$1' (command not found)" + fi +} + +# Source: https://sh.rustup.rs +err() { + echo '' + echo "$1" >&2 + exit 1 +} + +# Source: https://sh.rustup.rs +ensure() { + if ! "$@"; then err "command failed: $*"; fi +} + +# This wraps curl or wget. Try curl first, if not installed, use wget instead. +# Source: https://sh.rustup.rs +downloader() { + if check_cmd curl; then + if ! check_help_for curl --proto --tlsv1.2; then + echo "Warning: Not forcing TLS v1.2, this is potentially less secure" + curl --silent --show-error --fail --location "$1" --output "$2" + else + curl --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2" + fi + elif check_cmd wget; then + if ! check_help_for wget --https-only --secure-protocol; then + echo "Warning: Not forcing TLS v1.2, this is potentially less secure" + wget "$1" -O "$2" + else + wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2" + fi + else + echo "Unknown downloader" # should not reach here + fi +} + +# Source: https://sh.rustup.rs +check_help_for() { + local _cmd + local _arg + local _ok + _cmd="$1" + _ok="y" + shift + + for _arg in "$@"; do + if ! "$_cmd" --help | grep -q -- "$_arg"; then + _ok="n" + fi + done + + test "$_ok" = "y" +} + +# Source: https://github.com/fearside/ProgressBar +progressBar() { + _progress=$1 + _done=$((_progress*5/10)) + _left=$((50-_done)) + done="" + if ! [ $_done = "0" ];then + done=$(printf '#%.0s' $(seq $_done)) + fi + left="" + if ! [ $_left = "0" ];then + left=$(printf '=%.0s' $(seq $_left)) + fi + printf "\rProgress : [$done$left] ${_progress}%%" +} + +main "$@" || exit 1 diff --git a/artifacts/update.sh b/artifacts/update.sh index fe2619a2..cc6b9b8a 100755 --- a/artifacts/update.sh +++ b/artifacts/update.sh @@ -1,32 +1,191 @@ #!/bin/sh -echo "Updating Darknode CLI..." +main(){ + # Update this when minimum terraform version is changed. + min_terraform_ver="0.12.24" + cur_terraform_ver="0.12.24" -# Check the os type and cpu architecture -ostype="$(uname -s)" -cputype="$(uname -m)" + # Check if darknode-cli has been installed + if ! check_cmd darknode; then + echo "cannot find darknode-cli" + err "please install darknode-cli first" + fi -if [ -d "$HOME/.darknode" ] && [ -d "$HOME/.darknode/darknodes" ]; then - cd $HOME/.darknode -else - echo "cannot find the darknode-cli" - echo "please install darknode-cli first" + echo "Updating darknode-cli ..." + + # Check terraform version + if check_cmd terraform; then + version="$(terraform --version | grep 'Terraform v' | cut -d "v" -f2)" + major="$(echo $version | cut -d. -f1)" + minor="$(echo $version | cut -d. -f2)" + patch="$(echo $version | cut -d. -f3)" + requiredMajor="$(echo $min_terraform_ver | cut -d. -f1)" + requiredMinor="$(echo $min_terraform_ver | cut -d. -f2)" + requiredPatch="$(echo $min_terraform_ver | cut -d. -f3)" + if [ "$major" -lt "$requiredMajor" ]; then + err "Please upgrade your terraform to version above $min_terraform_ver" + fi + if [ "$minor" -lt "$requiredMinor" ]; then + err "Please upgrade your terraform to version above $min_terraform_ver" + fi + if [ "$patch" -lt "$requiredPatch" ]; then + err "Please upgrade your terraform to version above $min_terraform_ver" + fi + else + install_terraform $cur_terraform_ver + fi + progressBar 40 100 + + # Update the binary + current=$(darknode --version | grep "darknode-cli version" | cut -d ' ' -f 3) + latest=$(get_latest_release "renproject/darknode-cli") + vercomp $current $latest + if [ "$?" -eq "2" ]; then + ostype="$(uname -s | tr '[:upper:]' '[:lower:]')" + cputype="$(uname -m | tr '[:upper:]' '[:lower:]')" + if [ $cputype = "x86_64" ];then + cputype="amd64" + fi + + darknode_url="https://www.github.com/renproject/darknode-cli/releases/latest/download/darknode_${ostype}_${cputype}" + ensure downloader "$darknode_url" "$HOME/.darknode/bin/darknode" + ensure chmod +x "$HOME/.darknode/bin/darknode" + + progressBar 100 100 + sleep 1 + echo '' + echo "Done! Your 'darknode-cli' has been updated to $latest." + else + progressBar 100 100 + echo '' + echo "You're running the latest version" + fi +} + +install_terraform(){ + terraform_ver="$1" + mkdir -p $HOME/.darknode/bin + ostype="$(uname -s | tr '[:upper:]' '[:lower:]')" + cputype="$(uname -m | tr '[:upper:]' '[:lower:]')" + if [ $cputype = "x86_64" ];then + cputype="amd64" + fi + terraform_url="https://releases.hashicorp.com/terraform/${terraform_ver}/terraform_${terraform_ver}_${ostype}_${cputype}.zip" + ensure downloader "$terraform_url" "$HOME/.darknode/bin/terraform.zip" + ensure unzip -qq "$HOME/.darknode/bin/terraform.zip" -d "$HOME/.darknode/bin" + ensure chmod +x "$HOME/.darknode/bin/terraform" + rm "$HOME/.darknode/bin/terraform.zip" +} + +# Source: https://sh.rustup.rs +check_cmd() { + command -v "$1" > /dev/null 2>&1 +} + +# This wraps curl or wget. Try curl first, if not installed, use wget instead. +# Source: https://sh.rustup.rs +downloader() { + if check_cmd curl; then + if ! check_help_for curl --proto --tlsv1.2; then + echo "Warning: Not forcing TLS v1.2, this is potentially less secure" + curl --silent --show-error --fail --location "$1" --output "$2" + else + curl --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2" + fi + elif check_cmd wget; then + if ! check_help_for wget --https-only --secure-protocol; then + echo "Warning: Not forcing TLS v1.2, this is potentially less secure" + wget "$1" -O "$2" + else + wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2" + fi + else + echo "Unknown downloader" # should not reach here + fi +} + +# Source: https://sh.rustup.rs +check_help_for() { + local _cmd + local _arg + local _ok + _cmd="$1" + _ok="y" + shift + + for _arg in "$@"; do + if ! "$_cmd" --help | grep -q -- "$_arg"; then + _ok="n" + fi + done + + test "$_ok" = "y" +} + +# Source: https://sh.rustup.rs +err() { + echo '' + echo "$1" >&2 exit 1 -fi - -# Download the latest binary darknode -if [ "$ostype" = 'Linux' -a "$cputype" = 'x86_64' ]; then - curl -sL 'https://www.github.com/renproject/darknode-cli/releases/latest/download/darknode_linux_amd64' > ./bin/darknode -elif [ "$ostype" = 'Linux' -a "$cputype" = 'aarch64' ]; then - curl -sL 'https://www.github.com/renproject/darknode-cli/releases/latest/download/darknode_linux_arm' > ./bin/darknode -elif [ "$ostype" = 'Darwin' -a "$cputype" = 'x86_64' ]; then - curl -sL 'https://www.github.com/renproject/darknode-cli/releases/latest/download/darknode_darwin_amd64' > ./bin/darknode -else - echo 'unsupported OS type or architecture' - exit 1 -fi - -chmod +x bin/darknode - -echo '' -echo 'Done! Your darknode-cli has been updated.' \ No newline at end of file +} + +# Source: https://sh.rustup.rs +ensure() { + if ! "$@"; then err "command failed: $*"; fi +} + +get_latest_release() { + curl --silent "https://api.github.com/repos/$1/releases/latest" | # Get latest release from GitHub api + grep '"tag_name":' | # Get tag line + sed -E 's/.*"([^"]+)".*/\1/' # Pluck JSON value +} + +# source : https://stackoverflow.com/questions/4023830/how-to-compare-two-strings-in-dot-separated-version-format-in-bash +vercomp () { + if [[ $1 == $2 ]] + then + return 0 + fi + local IFS=. + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)) + do + ver1[i]=0 + done + for ((i=0; i<${#ver1[@]}; i++)) + do + if [[ -z ${ver2[i]} ]] + then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})) + then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})) + then + return 2 + fi + done + return 0 +} + +# Source: https://github.com/fearside/ProgressBar +progressBar() { + _progress=$1 + _done=$((_progress*5/10)) + _left=$((50-_done)) + done="" + if ! [ $_done = "0" ];then + done=$(printf '#%.0s' $(seq $_done)) + fi + left="" + if ! [ $_left = "0" ];then + left=$(printf '=%.0s' $(seq $_left)) + fi + printf "\rProgress : [$done$left] ${_progress}%%" +} + +main "$@" || exit 1 diff --git a/cmd/down.go b/cmd/down.go index b907a439..06688c44 100644 --- a/cmd/down.go +++ b/cmd/down.go @@ -56,7 +56,7 @@ func (s status) err() string { func destroyNode(ctx *cli.Context) error { force := ctx.Bool("force") name := ctx.Args().First() - if err := util.ValidateNodeName(name); err != nil { + if err := util.ValidateNodeExistence(name); err != nil { return err } path := util.NodePath(name) @@ -103,7 +103,7 @@ func withdraw(ctx *cli.Context) error { // Parse the input parameters name := ctx.Args().First() - if err := util.ValidateNodeName(name); err != nil { + if err := util.ValidateNodeExistence(name); err != nil { return err } withdrawAddress := ctx.String("address") @@ -200,7 +200,7 @@ func transfer(transactor *bind.TransactOpts, receiver common.Address, amount eth // renAddress on different network func renAddress(network darknode.Network) string { switch network { - case darknode.Mainnet, darknode.Chaosnet: + case darknode.Mainnet: return "0x408e41876cCCDC0F92210600ef50372656052a38" case darknode.Testnet, darknode.Devnet: return "0x2CD647668494c1B15743AB283A0f980d90a87394" @@ -213,7 +213,7 @@ func renAddress(network darknode.Network) string { func connect(network darknode.Network) (ethclient.Client, error) { logger := logrus.New() switch network { - case darknode.Mainnet, darknode.Chaosnet: + case darknode.Mainnet: return ethclient.New(logger, ethtypes.Mainnet) case darknode.Testnet, darknode.Devnet: return ethclient.New(logger, ethtypes.Kovan) diff --git a/cmd/exec.go b/cmd/exec.go index ae10b4f2..949dbc88 100644 --- a/cmd/exec.go +++ b/cmd/exec.go @@ -35,11 +35,11 @@ func execSingleNode(name, file, script string) error { if err != nil { return err } - return util.RemoteRun(name, string(script)) + return util.RemoteRun(name, string(script), "darknode") } if script != "" { - return util.RemoteRun(name, script) + return util.RemoteRun(name, script, "darknode") } return errors.New("please provide a script file or scripts to run ") diff --git a/cmd/flag.go b/cmd/flag.go index b4a8579d..3113f7f8 100644 --- a/cmd/flag.go +++ b/cmd/flag.go @@ -25,7 +25,7 @@ var ( Usage: "Network of your Darknode (default: mainnet)", } ConfigFlag = cli.StringFlag{ - Name: "config", + Name: "config", Usage: "Path of the config file", } AddressFlag = cli.StringFlag{ @@ -116,8 +116,8 @@ var ( Value: "n1-standard-1", Usage: "An optional Google Cloud machine type (default: n1-standard-1)", } - GcpZoneFlag = cli.StringFlag{ - Name: "gcp-zone", - Usage: "An optional Google Cloud Zone (default: random)", + GcpRegionFlag = cli.StringFlag{ + Name: "gcp-region", + Usage: "An optional Google Cloud Region (default: random)", } ) diff --git a/cmd/main.go b/cmd/main.go index 20b0c038..914c47b7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -9,15 +9,14 @@ import ( "time" "github.com/fatih/color" - "github.com/google/go-github/github" "github.com/hashicorp/go-version" "github.com/renproject/darknode-cli/cmd/provider" "github.com/renproject/darknode-cli/util" "github.com/urfave/cli" ) -// This will be populated on build -var binaryVersion = "undefined" +// BinaryVersion will be populated when building the binary. +var BinaryVersion = "undefined" func init() { rand.Seed(time.Now().UTC().UnixNano()) @@ -28,7 +27,7 @@ func main() { app := cli.NewApp() app.Name = "Darknode CLI" app.Usage = "A command-line tool for managing Darknodes." - app.Version = binaryVersion + app.Version = BinaryVersion // Fetch latest release and check if our version is behind. checkUpdates(app.Version) @@ -46,7 +45,7 @@ func main() { // Digital Ocean DoFlag, DoRegionFlag, DoSizeFlag, DoTokenFlag, // Google Cloud Platform - GcpFlag, GcpZoneFlag, GcpCredFlag, GcpMachineFlag, + GcpFlag, GcpRegionFlag, GcpCredFlag, GcpMachineFlag, }, Action: func(c *cli.Context) error { p, err := provider.ParseProvider(c) @@ -79,7 +78,7 @@ func main() { Usage: "SSH into one of your Darknode", Action: func(c *cli.Context) error { name := c.Args().First() - if err := util.ValidateNodeName(name); err != nil { + if err := util.ValidateNodeExistence(name); err != nil { return err } ip, err := util.IP(name) @@ -152,7 +151,7 @@ func main() { Flags: []cli.Flag{}, Action: func(c *cli.Context) error { name := c.Args().First() - if err := util.ValidateNodeName(name); err != nil { + if err := util.ValidateNodeExistence(name); err != nil { return err } @@ -167,7 +166,7 @@ func main() { }, } - // Show error message and display the help page for the app + // Show error message and display the help page when command not found. app.CommandNotFound = func(c *cli.Context, command string) { if err := cli.ShowAppHelp(c); err != nil { panic(err) @@ -187,10 +186,11 @@ func main() { // checkUpdates fetches the latest release of `darknode-cli` from github and compare the versions. It warns the user if // current version is older than the latest release. func checkUpdates(curVer string) { - // Get latest release ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - client := github.NewClient(nil) + + // Fetch the latest release + client := util.GithubClient(ctx) release, _, err := client.Repositories.GetLatestRelease(ctx, "renproject", "darknode-cli") if err != nil { return diff --git a/cmd/provider/aws.go b/cmd/provider/aws.go index 22360d3e..b8f395dd 100644 --- a/cmd/provider/aws.go +++ b/cmd/provider/aws.go @@ -5,32 +5,14 @@ import ( "math/rand" "strings" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/renproject/darknode-cli/darknode" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/renproject/darknode-cli/util" "github.com/urfave/cli" ) -// All available regions on AWS. -var AllAwsRegions = []string{ - "ap-northeast-1", - "ap-northeast-2", - "ap-south-1", - "ap-southeast-1", - "ap-southeast-2", - "ca-central-1", - "eu-central-1", - "eu-north-1", - "eu-west-1", - "eu-west-2", - "eu-west-3", - "sa-east-1", - "us-east-1", - "us-east-2", - "us-west-1", - "us-west-2", -} - type providerAws struct { accessKey string secretKey string @@ -64,25 +46,25 @@ func (p providerAws) Name() string { } func (p providerAws) Deploy(ctx *cli.Context) error { - name := ctx.String("name") - tags := ctx.String("tags") - config := ctx.String("config") - - latestVersion, err := util.LatestStableRelease() - if err != nil { + // Validate all input params + if err := validateCommonParams(ctx); err != nil { return err } + + name := ctx.String("name") region, instance, err := p.validateRegionAndInstance(ctx) if err != nil { return err } - // Initialization - network, err := darknode.NewNetwork(ctx.String("network")) + // Get the latest darknode version + latestVersion, err := util.LatestStableRelease() if err != nil { return err } - if err := initNode(name, tags, network, config); err != nil { + + // Initialize folder and files for the node + if err := initNode(ctx); err != nil { return err } @@ -98,20 +80,62 @@ func (p providerAws) Deploy(ctx *cli.Context) error { } func (p providerAws) validateRegionAndInstance(ctx *cli.Context) (string, string, error) { - // TODO : use aws api to validate the region and instance type + cred := credentials.NewStaticCredentials(p.accessKey, p.secretKey, "") region := strings.ToLower(strings.TrimSpace(ctx.String("aws-region"))) instance := strings.ToLower(strings.TrimSpace(ctx.String("aws-instance"))) - if region == "" { - region = AllAwsRegions[rand.Intn(len(AllAwsRegions))] + // Get all available regions + sess, err := session.NewSession(&aws.Config{ + Region: aws.String("us-east-1"), + Credentials: cred, + }) + service := ec2.New(sess) + input := &ec2.DescribeRegionsInput{} + result, err := service.DescribeRegions(input) + if err != nil { + return "", "", err } - if !util.StringInSlice(region, AllAwsRegions) { - return "", "", fmt.Errorf("aws region [%v] is not supported yet", region) + regions := make([]string, len(result.Regions)) + for i := range result.Regions { + regions[i] = *result.Regions[i].RegionName } - if instance == "" { - return "", "", fmt.Errorf("instance type [%v] is not supported yet", instance) + if region == "" { + // Randomly select a region which has the given droplet size. + indexes := rand.Perm(len(result.Regions)) + for _, index := range indexes { + region = *result.Regions[index].RegionName + if err := p.instanceTypesAvailability(cred, region, instance); err == nil { + return region, instance, nil + } + } + return "", "", fmt.Errorf("selected instance type [%v] is not available across all regions", instance) + } else { + err = p.instanceTypesAvailability(cred, region, instance) + return region, instance, err } +} - return region, instance, nil +func (p providerAws) instanceTypesAvailability(cred *credentials.Credentials, region, instance string) error { + instanceSession, err := session.NewSession(&aws.Config{ + Region: aws.String(region), + Credentials: cred, + }) + if err != nil { + return err + } + service := ec2.New(instanceSession) + instanceInput := &ec2.DescribeInstanceTypesInput{ + InstanceTypes: []*string{aws.String(instance)}, + } + instanceResult, err := service.DescribeInstanceTypes(instanceInput) + if err != nil { + return err + } + for _, res := range instanceResult.InstanceTypes { + if *res.InstanceType == instance { + return nil + } + } + return fmt.Errorf("instance not avaliable") } diff --git a/cmd/provider/aws_template.go b/cmd/provider/aws_template.go index d70a29bd..3bc6de8c 100644 --- a/cmd/provider/aws_template.go +++ b/cmd/provider/aws_template.go @@ -115,6 +115,11 @@ resource "aws_instance" "darknode" { Name = "{{.Name}}" } + root_block_device { + volume_type = "gp2" + volume_size = 15 + } + provisioner "remote-exec" { inline = [ @@ -189,9 +194,3 @@ output "provider" { output "ip" { value = aws_instance.darknode.public_ip }` - -// {{if .AllocationID}} -// resource "aws_eip_association" "eip_assoc" { -// instance_id = "${aws_instance.darknode.id}" -// allocation_id = "${var.allocation_id}" -// }{{else}}{{end}} diff --git a/cmd/provider/do.go b/cmd/provider/do.go index 16b5ae19..855b90b2 100644 --- a/cmd/provider/do.go +++ b/cmd/provider/do.go @@ -1,14 +1,14 @@ package provider import ( - "encoding/json" - "errors" + "context" "fmt" - "io/ioutil" "math/rand" "net/http" + "strings" + "time" - "github.com/renproject/darknode-cli/darknode" + "github.com/digitalocean/godo" "github.com/renproject/darknode-cli/util" "github.com/urfave/cli" ) @@ -30,30 +30,30 @@ func (p providerDo) Name() string { } func (p providerDo) Deploy(ctx *cli.Context) error { - name := ctx.String("name") - tags := ctx.String("tags") - config := ctx.String("config") - - latestVersion, err := util.LatestStableRelease() - if err != nil { + // Validate all input params + if err := validateCommonParams(ctx); err != nil { return err } - region, droplet, err := validateRegionAndDroplet(ctx) + + name := ctx.String("name") + region, droplet, err := p.validateRegionAndDroplet(ctx) if err != nil { return err } - // Initialization - network, err := darknode.NewNetwork(ctx.String("network")) + // Get the latest darknode version + latestVersion, err := util.LatestStableRelease() if err != nil { return err } - if err := initNode(name, tags, network, config); err != nil { + + // Initialize folder and files for the node + if err := initNode(ctx); err != nil { return err } // Generate terraform config and start deploying - if err := p.tfConfig(name, region, droplet, latestVersion); err != nil { + if err := p.tfConfig(name, region.Slug, droplet, latestVersion); err != nil { return err } if err := runTerraform(name); err != nil { @@ -62,100 +62,47 @@ func (p providerDo) Deploy(ctx *cli.Context) error { return outputURL(name) } -func validateRegionAndDroplet(ctx *cli.Context) (string, string, error) { - region := ctx.String("do-region") - droplet := ctx.String("do-droplet") +func (p providerDo) validateRegionAndDroplet(ctx *cli.Context) (godo.Region, string, error) { + region := strings.ToLower(strings.TrimSpace(ctx.String("do-region"))) + droplet := strings.ToLower(strings.TrimSpace(ctx.String("do-droplet"))) + c, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() - regions, err := availableRegions(ctx) + // Fetch all available regions + client := godo.NewFromToken(p.token) + regions, response, err := client.Regions.List(c, nil) if err != nil { - return "", "", err + return godo.Region{}, "", err + } + if err := util.VerifyStatusCode(response.Response, http.StatusOK); err != nil { + return godo.Region{}, "", err + } + if len(regions) == 0 { + return godo.Region{}, "", fmt.Errorf("account has no available region") } - // Parse the input region or pick one region randomly + // Validate the given region and droplet type. Will use a random region + // if not specified. if region == "" { - if len(regions) == 0 { - return "", "", ErrRegionNotAvailable + // Randomly select a region which has the given droplet size. + indexes := rand.Perm(len(regions)) + for _, index := range indexes { + if util.StringInSlice(droplet, regions[index].Sizes) { + if regions[index].Available { + return regions[index], droplet, nil + } + } } - randomRegion := regions[rand.Intn(len(regions))] - return randomRegion.Slug, droplet, validateDroplet(droplet, randomRegion.Slug, randomRegion.Sizes) + return godo.Region{}, "", fmt.Errorf("selected droplet [%v] not available across all regions", droplet) } else { - var chosenRegion Region - for i := range regions { - if region == regions[i].Slug { - chosenRegion = regions[i] - break + for _, r := range regions { + if r.Slug == region { + if util.StringInSlice(droplet, r.Sizes) { + return r, droplet, nil + } + return godo.Region{}, "", fmt.Errorf("selected droplet [%v] not available in region %v", droplet, region) } } - if chosenRegion.Name == "" { - return "", "", ErrRegionNotAvailable - } - return chosenRegion.Slug, droplet, validateDroplet(droplet, chosenRegion.Slug, chosenRegion.Sizes) - } -} - -// validateDroplet validates whether the droplet is available in the region. -func validateDroplet(droplet, region string, droplets []string) error { - if !util.StringInSlice(droplet, droplets) { - fmt.Printf("[%v] is the selected droplet region.\n", region) - fmt.Printf("Your account can only create below slugs in [%v]:\n", region) - for i := range droplets { - fmt.Println(droplets[i]) - } - fmt.Println("You can find more details about these slugs from https://www.digitalocean.com/pricing") - return ErrInstanceTypeNotAvailable - } - return nil -} - -// Region is the json object returned by the digital-ocean API -type Region struct { - Name string `json:"name"` - Slug string `json:"slug"` - Sizes []string `json:"sizes"` - Features []string `json:"features"` - Available bool `json:"available"` -} - -// availableRegions sends a GET request to Digital Ocean API to get all available regions and droplet sizes of the given -// Digital Ocean token. -func availableRegions(ctx *cli.Context) ([]Region, error) { - token := ctx.String("do-token") - - url := "https://api.digitalocean.com/v2/regions" - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+token) - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return nil, err - } - - // Check the response status code - data, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - if resp.StatusCode != http.StatusOK { - return nil, errors.New(string(data)) - } - - // Unmarshal the response - regions := struct { - Regions []Region `json:"regions"` - }{} - err = json.Unmarshal(data, ®ions) - if err != nil { - return nil, err - } - availableRegions := make([]Region, 0) - for _, i := range regions.Regions { - if i.Available { - availableRegions = append(availableRegions, i) - } + return godo.Region{}, "", fmt.Errorf("region [%v] is not avaliable", region) } - return availableRegions, nil } diff --git a/cmd/provider/gcp.go b/cmd/provider/gcp.go index 1055ad0f..2b6701f1 100644 --- a/cmd/provider/gcp.go +++ b/cmd/provider/gcp.go @@ -7,14 +7,17 @@ import ( "io/ioutil" "math/rand" "os" + "path" + "path/filepath" + "regexp" "strings" "time" - "github.com/renproject/darknode-cli/darknode" "github.com/renproject/darknode-cli/util" "github.com/urfave/cli" "golang.org/x/oauth2/google" "google.golang.org/api/cloudresourcemanager/v1" + "google.golang.org/api/compute/v1" "google.golang.org/api/option" ) @@ -25,36 +28,15 @@ type GcpRegion struct { zones []string } -// AllGcpRegions -var AllGcpRegions = []GcpRegion{ - {"asia-east1", []string{"a", "b", "c"}}, - {"asia-east2", []string{"a", "b", "c"}}, - {"asia-northeast1", []string{"a", "b", "c"}}, - {"asia-northeast2", []string{"a", "b", "c"}}, - {"asia-south1", []string{"a", "b", "c"}}, - {"asia-southeast1", []string{"a", "b", "c"}}, - {"australia-southeast1", []string{"a", "b", "c"}}, - {"europe-north1", []string{"a", "b", "c"}}, - {"europe-west1", []string{"b", "c", "d"}}, - {"europe-west2", []string{"a", "b", "c"}}, - {"europe-west3", []string{"a", "b", "c"}}, - {"europe-west4", []string{"a", "b", "c"}}, - {"europe-west6", []string{"a", "b", "c"}}, - {"northamerica-northeast1", []string{"a", "b", "c"}}, - {"southamerica-east1", []string{"a", "b", "c"}}, - {"us-central1", []string{"a", "b", "c", "f"}}, - {"us-east1", []string{"b", "c", "d"}}, - {"us-east4", []string{"a", "b", "c"}}, - {"us-west1", []string{"a", "b", "c"}}, - {"us-west2", []string{"a", "b", "c"}}, -} - type providerGcp struct { credFile string } func NewGcp(ctx *cli.Context) (Provider, error) { - credFile := ctx.String("gcp-credentials") + credFile, err := filepath.Abs(ctx.String("gcp-credentials")) + if err != nil { + return nil, err + } if _, err := os.Stat(credFile); err != nil { return nil, err } @@ -68,9 +50,21 @@ func (p providerGcp) Name() string { } func (p providerGcp) Deploy(ctx *cli.Context) error { + // Validate all input params + if err := validateCommonParams(ctx); err != nil { + return err + } + + // Validate name for GCP name := ctx.String("name") - tags := ctx.String("tags") - config := ctx.String("config") + reg := "^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$" + match, err := regexp.MatchString(reg, name) + if err != nil { + return err + } + if !match { + return errors.New("for google cloud, name must start with a lowercase letter followed by up to 62 lowercase letters, numbers, or hyphens, and cannot end with a hyphen") + } latestVersion, err := util.LatestStableRelease() if err != nil { @@ -80,22 +74,17 @@ func (p providerGcp) Deploy(ctx *cli.Context) error { if err != nil { return err } - zone, machine, err := p.validateZoneAndMachine(ctx) + region, machine, err := p.validateRegionAndMachine(ctx) if err != nil { return err } - // Initialization - network, err := darknode.NewNetwork(ctx.String("network")) - if err != nil { + // Initialize folder and files for the node + if err := initNode(ctx); err != nil { return err } - if err := initNode(name, tags, network, config); err != nil { - return err - } - // Generate terraform config and start deploying - if err := p.tfConfig(name, projectID, zone, machine, latestVersion); err != nil { + if err := p.tfConfig(name, projectID, region, machine, latestVersion); err != nil { return err } if err := runTerraform(name); err != nil { @@ -112,8 +101,8 @@ func (p providerGcp) projectID() (string, error) { } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - creds, error := google.CredentialsFromJSON(ctx, data, "https://www.googleapis.com/auth/cloud-platform") - if error != nil { + creds, err := google.CredentialsFromJSON(ctx, data, "https://www.googleapis.com/auth/cloud-platform") + if err != nil { return "", err } service, err := cloudresourcemanager.NewService(ctx, option.WithCredentials(creds)) @@ -134,16 +123,127 @@ func (p providerGcp) projectID() (string, error) { return creds.ProjectID, nil } -func (p providerGcp) validateZoneAndMachine(ctx *cli.Context) (string, string, error) { - zone := strings.ToLower(strings.TrimSpace(ctx.String("gcp-zone"))) +func (p providerGcp) validateRegionAndMachine(ctx *cli.Context) (string, string, error) { + region := strings.ToLower(strings.TrimSpace(ctx.String("gcp-region"))) machine := strings.ToLower(strings.TrimSpace(ctx.String("gcp-machine"))) + c, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Retrieve project ID and initialize the computer service + fileName, err := filepath.Abs(ctx.String("gcp-credentials")) + if err != nil { + return "", "", err + } + projectID, err := getProjectID(c, fileName) + if err != nil { + return "", "", err + } + service, err := compute.NewService(c, option.WithCredentialsFile(fileName)) + if err != nil { + return "", "", err + } + // Select a random zone for user if they don't provide one. - if zone == "" { - region := AllGcpRegions[rand.Intn(len(AllGcpRegions))] - return fmt.Sprintf("%v-%v", region.region, region.zones[rand.Intn(len(region.zones))]), machine, nil + var zone string + if region == "" { + zone, err = randomZone(c, service, projectID) + if err != nil { + return "", "", err + } + } else { + zone, err = validateZone(c, service, projectID, region) + if err != nil { + return "", "", err + } + } + + // Validate if the machine type is available in the zone. + if err := validateMachineType(c, service, projectID, zone, machine); err != nil { + return "", "", err } - // todo : validate the zone and machine type return zone, machine, nil } + +// Randomly pick a available zone +func randomZone(ctx context.Context, service *compute.Service, projectID string) (string, error) { + // List all the available zones + zones := []string{} + req := service.Zones.List(projectID) + if err := req.Pages(ctx, func(page *compute.ZoneList) error { + for _, zone := range page.Items { + zones = append(zones, zone.Name) + } + return nil + }); err != nil { + return "", err + } + if len(zones) == 0 { + return "", errors.New("cannot find any available zone under your project") + } + + return zones[rand.Intn(len(zones))], nil +} + +func validateZone(ctx context.Context, service *compute.Service, projectID, input string) (string, error) { + reg := regexp.MustCompile("^(?P[a-z]+-[a-z]+[1-9])(-(?P[a-g]))?$") + if !reg.MatchString(input) { + return "", errors.New("invalid region name") + } + + values := util.CaptureGroups("^(?P[a-z]+-[a-z]+[1-9])(-(?P[a-g]))?$", input) + zone := values["zone"] + + // If user doesn't provide a zone in the region, we only need to validate the + // region and randomly select a zone in the region. + if zone == "" { + availableRegion, err := service.Regions.Get(projectID, input).Context(ctx).Do() + if err != nil { + return "", err + } + + zone = path.Base(availableRegion.Zones[rand.Intn(len(availableRegion.Zones))]) + return zone, nil + } + + // If user gives both region and zone, we only need to validate the zone. + _, err := service.Zones.Get(projectID, input).Context(ctx).Do() + return input, err +} + +func validateMachineType(ctx context.Context, service *compute.Service, projectID, zone, machine string) error { + // List all the available zones + res, err := service.MachineTypes.Get(projectID, zone, machine).Context(ctx).Do() + if err != nil { + return err + } + if res.Name != machine { + return fmt.Errorf("%v type is not available from your project", machine) + } + return nil +} + +func getProjectID(ctx context.Context, fileName string) (string, error) { + // Parse the credential file + credFile, err := filepath.Abs(fileName) + if err != nil { + return "", err + } + jsonFile, err := os.Open(credFile) + if err != nil { + return "", err + } + defer jsonFile.Close() + data, err := ioutil.ReadAll(jsonFile) + if err != nil { + return "", err + } + + // Get the project ID + credentials, err := google.CredentialsFromJSON(ctx, data, compute.ComputeScope) + if err != nil { + return "", err + } + return credentials.ProjectID, nil +} diff --git a/cmd/provider/gcp_template.go b/cmd/provider/gcp_template.go index 30bd1c28..28f545ba 100644 --- a/cmd/provider/gcp_template.go +++ b/cmd/provider/gcp_template.go @@ -63,7 +63,7 @@ resource "google_compute_network" "darknode_network" { } resource "google_compute_firewall" "darknode_firewall" { - network = "${google_compute_network.darknode_network.name}" + network = google_compute_network.darknode_network.name name = "{{.Name}}" allow { @@ -98,7 +98,7 @@ resource "google_compute_instance" "darknode" { } network_interface { - network = "${google_compute_network.darknode_network.name}" + network = google_compute_network.darknode_network.name access_config {} } diff --git a/cmd/provider/provider.go b/cmd/provider/provider.go index 4a5a68b8..cf78ff48 100644 --- a/cmd/provider/provider.go +++ b/cmd/provider/provider.go @@ -1,7 +1,6 @@ package provider import ( - "encoding/json" "errors" "fmt" "io/ioutil" @@ -71,6 +70,46 @@ func ParseProvider(ctx *cli.Context) (Provider, error) { return nil, ErrUnknownProvider } +// Validate the params which are general to all providers. +func validateCommonParams(ctx *cli.Context) error { + // Check the name valida and not been used + name := ctx.String("name") + if err := util.ValidateName(name); err != nil { + return err + } + if err := util.ValidateNodeExistence(name); err == nil { + return fmt.Errorf("node [%v] already exist", name) + } + + // Parse the network + network := darknode.Network(ctx.String("network")) + switch network { + case darknode.Mainnet: + case darknode.Testnet: + case darknode.Devnet: + default: + return errors.New("unknown RenVM network") + } + + // Verify the config file if user wants to use their own config + configFile := ctx.String("config") + if configFile != "" { + // verify the config exist and of the right format + path, err := filepath.Abs(configFile) + if err != nil { + return err + } + if _, err := os.Stat(path); err != nil { + return errors.New("config file doesn't exist") + } + _, err = darknode.NewConfigFromJSONFile(path) + if err != nil { + return fmt.Errorf("incompatible config, err = %w", err) + } + } + return nil +} + // Provider returns the provider of a darknode instance. func GetProvider(name string) (string, error) { if name == "" { @@ -79,14 +118,32 @@ func GetProvider(name string) (string, error) { cmd := fmt.Sprintf("cd %v && terraform output provider", util.NodePath(name)) provider, err := util.CommandOutput(cmd) + if strings.HasPrefix(provider, "\"") { + provider = strings.Trim(provider, "\"") + } return strings.TrimSpace(provider), err } // initialise all files needed by deploying a new node -func initNode(name, tags string, network darknode.Network, configFile string) error { - if err := initNodeDirectory(name, tags); err != nil { +func initNode(ctx *cli.Context) error { + name := ctx.String("name") + path := util.NodePath(name) + configFile := ctx.String("config") + network := darknode.Network(ctx.String("network")) + + // Create directory for the Darknode + if err := os.MkdirAll(path, 0700); err != nil { + return err + } + + // Create `tags.out` file + tags := []byte(strings.TrimSpace(ctx.String("tags"))) + tagsPath := filepath.Join(path, "tags.out") + if err := ioutil.WriteFile(tagsPath, tags, 0600); err != nil { return err } + + // Create `ssh_keypair` and `ssh_keypair.pub` files for the remote instance if err := util.GenerateSshKeyAndWriteToDir(name); err != nil { return err } @@ -94,19 +151,10 @@ func initNode(name, tags string, network darknode.Network, configFile string) er // Use given config for the new darknode var conf darknode.Config if configFile != "" { - path, err := filepath.Abs(configFile) - if err != nil{ - return errors.New("invalid config path") - } - - file, err := os.Open(path) + var err error + conf, err = darknode.NewConfigFromJSONFile(path) if err != nil { - return fmt.Errorf("cannot open config file, err = %v", err) - } - defer file.Close() - - if err := json.NewDecoder(file).Decode(&conf); err != nil { - return err + return errors.New("invalid config file") } } else { var err error @@ -116,37 +164,7 @@ func initNode(name, tags string, network darknode.Network, configFile string) er } } - configData, err := json.MarshalIndent(conf, "", " ") - if err != nil { - return err - } - configPath := filepath.Join(util.NodePath(name), "config.json") - return ioutil.WriteFile(configPath, configData, 0600) -} - -func initNodeDirectory(name, tags string) error { - if name == "" { - return util.ErrEmptyName - } - path := util.NodePath(name) - - // Ask user to destroy the old node first if there's already a node with the name. - if _, err := os.Stat(path); err == nil { - return fmt.Errorf("Node [%v] already exist. \nIf you want to do a fresh deployment, destroy the old one first.", name) - } - - // Make a directory for the new node - if err := os.Mkdir(path, 0700); err != nil { - return err - } - - // Create the `tags.out` file if not exist. - tagsPath := filepath.Join(path, "tags.out") - if _, err := os.Stat(tagsPath); err != nil { - return ioutil.WriteFile(tagsPath, []byte(strings.TrimSpace(tags)), 0600) - } - - return nil + return darknode.ConfigToFile(conf, filepath.Join(util.NodePath(name), "config.json")) } func runTerraform(name string) error { diff --git a/cmd/resize.go b/cmd/resize.go index 13c86e01..e4be1512 100644 --- a/cmd/resize.go +++ b/cmd/resize.go @@ -29,7 +29,7 @@ var ( func resize(ctx *cli.Context) error { name := ctx.Args().Get(0) - if err := util.ValidateNodeName(name); err != nil { + if err := util.ValidateNodeExistence(name); err != nil { return err } newSize := ctx.Args().Get(1) diff --git a/cmd/switch.go b/cmd/switch.go index 96cba8fa..07a7242c 100644 --- a/cmd/switch.go +++ b/cmd/switch.go @@ -41,7 +41,7 @@ func updateServiceStatus(ctx *cli.Context, cmd string) error { } errs := make([]error, len(nodes)) phi.ParForAll(nodes, func(i int) { - errs[i] = util.RemoteRun(nodes[i], script) + errs[i] = util.RemoteRun(nodes[i], script, "darknode") if errs[i] == nil { color.Green("[%v] has been %v.", nodes[i], message) } else { diff --git a/cmd/update.go b/cmd/update.go index 7f4f6379..4c8be3a6 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -9,7 +9,6 @@ import ( "time" "github.com/fatih/color" - "github.com/google/go-github/v31/github" "github.com/hashicorp/go-version" "github.com/renproject/darknode-cli/util" "github.com/renproject/phi" @@ -96,14 +95,14 @@ mv ~/.darknode/bin/darknode-new ~/.darknode/bin/darknode && chmod +x ~/.darknode/bin/darknode && echo %v > ~/.darknode/version && systemctl --user restart darknode`, url, ver) - return util.RemoteRun(name, script) + return util.RemoteRun(name, script, "darknode") } func validateVersion(version string) error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - client := github.NewClient(nil) + client := util.GithubClient(nil) _, response, err := client.Repositories.GetReleaseByTag(ctx, "renproject", "darknode-release", version) if err != nil { return err diff --git a/darknode/bindings/dnr.json b/darknode/bindings/dnr.json index 9e0a2827..8e8dddef 100644 --- a/darknode/bindings/dnr.json +++ b/darknode/bindings/dnr.json @@ -1,48 +1,11 @@ [ - { - "inputs": [ - { - "internalType": "string", - "name": "_VERSION", - "type": "string" - }, - { - "internalType": "contract RenToken", - "name": "_renAddress", - "type": "address" - }, - { - "internalType": "contract DarknodeRegistryStore", - "name": "_storeAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_minimumBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_minimumPodSize", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_minimumEpochIntervalSeconds", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor" - }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", - "name": "_operator", + "name": "_darknodeOperator", "type": "address" }, { @@ -60,37 +23,43 @@ "inputs": [ { "indexed": true, - "internalType": "address", - "name": "_operator", + "internalType": "contract IDarknodePayment", + "name": "_previousDarknodePayment", "type": "address" }, { - "indexed": false, - "internalType": "uint256", - "name": "_amount", - "type": "uint256" + "indexed": true, + "internalType": "contract IDarknodePayment", + "name": "_nextDarknodePayment", + "type": "address" } ], - "name": "LogDarknodeOwnerRefunded", + "name": "LogDarknodePaymentUpdated", "type": "event" }, { "anonymous": false, "inputs": [ { - "indexed": false, - "internalType": "contract IDarknodePayment", - "name": "_previousDarknodePayment", + "indexed": true, + "internalType": "address", + "name": "_darknodeOperator", "type": "address" }, { - "indexed": false, - "internalType": "contract IDarknodePayment", - "name": "_nextDarknodePayment", + "indexed": true, + "internalType": "address", + "name": "_darknodeID", "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" } ], - "name": "LogDarknodePaymentUpdated", + "name": "LogDarknodeRefunded", "type": "event" }, { @@ -99,7 +68,7 @@ { "indexed": true, "internalType": "address", - "name": "_operator", + "name": "_darknodeOperator", "type": "address" }, { @@ -124,7 +93,7 @@ { "indexed": true, "internalType": "address", - "name": "_operator", + "name": "_darknodeOperator", "type": "address" }, { @@ -223,13 +192,13 @@ "anonymous": false, "inputs": [ { - "indexed": false, + "indexed": true, "internalType": "address", "name": "_previousSlasher", "type": "address" }, { - "indexed": false, + "indexed": true, "internalType": "address", "name": "_nextSlasher", "type": "address" @@ -272,6 +241,21 @@ "stateMutability": "view", "type": "function" }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "blacklistRecoverableToken", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, { "constant": false, "inputs": [], @@ -340,6 +324,21 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "constant": true, + "inputs": [], + "name": "deregistrationInterval", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": false, "inputs": [], @@ -379,7 +378,7 @@ "type": "address" } ], - "name": "getDarknodeOwner", + "name": "getDarknodeOperator", "outputs": [ { "internalType": "address payable", @@ -464,6 +463,66 @@ "stateMutability": "view", "type": "function" }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "_VERSION", + "type": "string" + }, + { + "internalType": "contract RenToken", + "name": "_renAddress", + "type": "address" + }, + { + "internalType": "contract DarknodeRegistryStore", + "name": "_storeAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_minimumBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minimumPodSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minimumEpochIntervalSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_deregistrationIntervalSeconds", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_nextOwner", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, { "constant": true, "inputs": [ @@ -812,6 +871,21 @@ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [], @@ -980,7 +1054,7 @@ "constant": false, "inputs": [ { - "internalType": "contract DarknodeRegistry", + "internalType": "contract DarknodeRegistryLogicV1", "name": "_newOwner", "type": "address" } diff --git a/darknode/config.go b/darknode/config.go index 6c507088..56073546 100644 --- a/darknode/config.go +++ b/darknode/config.go @@ -6,6 +6,7 @@ import ( "encoding/json" "math/big" "os" + "path/filepath" "github.com/btcsuite/btcd/btcec" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -69,6 +70,43 @@ func NewConfig(network Network) (Config, error) { }, nil } +// NewConfigFromJSONFile parses a json file that contains the config +// options specified by Config. +func NewConfigFromJSONFile(filename string) (Config, error) { + path, err := filepath.Abs(filename) + if err != nil { + return Config{}, err + } + + file, err := os.Open(path) + if err != nil { + return Config{}, err + } + defer file.Close() + + var conf Config + err = json.NewDecoder(file).Decode(&conf) + return conf, err +} + +// ConfigToFile writes the Config to the target file in json format. +func ConfigToFile(config Config, path string) error { + path, err := filepath.Abs(path) + if err != nil { + return err + } + + file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) + if err != nil { + return err + } + defer file.Close() + + encoder := json.NewEncoder(file) + encoder.SetIndent("", " ") + return encoder.Encode(config) +} + // GeneralConfig is the config struct which contains the common fields across // all versions of darknode configs. type GeneralConfig struct { @@ -100,9 +138,9 @@ func (config GeneralConfig) DnrAddr(client *ethclient.Client) (common.Address, e return config.DarknodeRegistryAddress, nil } -// NewConfigFromJSONFile parses a json file that contains the config -// options specified by Config. -func NewConfigFromJSONFile(filename string) (GeneralConfig, error) { +// NewGeneralConfigFromJSONFile parses a json file that contains the config +// options specified by GeneralConfig. +func NewGeneralConfigFromJSONFile(filename string) (GeneralConfig, error) { file, err := os.Open(filename) if err != nil { return GeneralConfig{}, err diff --git a/darknode/network.go b/darknode/network.go index 9d0525ae..e1426ba1 100644 --- a/darknode/network.go +++ b/darknode/network.go @@ -19,10 +19,6 @@ const ( // without incurring real-world costs. Testnet = Network("testnet") - // Chaosnet is the pre-production version of mainnet for production testing - // with real-world incentives and punishments. - Chaosnet = Network("chaosnet") - // Mainnet is the production network. Mainnet = Network("mainnet") ) @@ -34,8 +30,6 @@ func NewNetwork(network string) (Network, error) { return Devnet, nil case "testnet": return Testnet, nil - case "chaosnet": - return Chaosnet, nil case "mainnet": return Mainnet, nil default: @@ -49,15 +43,6 @@ func (network Network) BootstrapNodes() []addr.MultiAddress { case Mainnet: b1, _ := addr.NewSignedMultiAddressFromString("/ip4/35.180.200.106/tcp/18514/ren/8MGaGCjCjrJMjp7kMrkKzxtmLpbX8q", "mOuxWXwTBDtFvy88ErPOwkux9rR/HHmCuFReSlvLrTBAqwRXVWDNu8e8FjFTdUYg3H3ctWczGLgBRY3CUgNGYwA=") bootstraps = append(bootstraps, b1) - case Chaosnet: - b1, _ := addr.NewSignedMultiAddressFromString("/ip4/3.115.117.251/tcp/18514/ren/8MGrkr3CCG5gxnipWD5RUc8BMQnU1s", "3w8PoELIeSh0sqcb6qONy1FNgSIgP9hELVh44D/IE0saY518C9vWvBYSQn4xUmYRb7Y+nYNPY54NoH1y0zMnXAE=") - b2, _ := addr.NewSignedMultiAddressFromString("/ip4/18.182.28.215/tcp/18514/ren/8MGjmhtNxsqT4NphYt3usvJBXqVTeS", "YntncxzVBMHA+QNwgkdAQc95gSoWXrdf7r1T38+rtYkuCud5EkV7tWy0GDLeSKCvEuOpVtdPFlaVHXQyYNPwVAA=") - b3, _ := addr.NewSignedMultiAddressFromString("/ip4/35.180.66.220/tcp/18514/ren/8MHEtUrZBQuRRtxAgBTTM6Zov3imfP", "Smw7e2DZ7nyPOr94oVuWmezAIDz1+uAkwkmQnH5nI0d+w6bECUV96wlJgvylILIL0cVITn5M1Mfm3fQeMWwVHQE=") - b4, _ := addr.NewSignedMultiAddressFromString("/ip4/15.188.15.210/tcp/18514/ren/8MGob6LJcneeFSiQStU9FvP83W3xMA", "gcePwG3m0JonXpVZD8xJtWr7RnBsBuNViOkZElNAkaEDgYXmOdTBtXd1HMTjbwAilIG+hVlpIRpyC97n2F2z6gE=") - b5, _ := addr.NewSignedMultiAddressFromString("/ip4/18.138.225.107/tcp/18514/ren/8MKUZzR3oM4ALnQ5vjQti1X41DwkEW", "iiiRnd/bkQlVVxr5XKrKi0uHhk4tAs5ct3rUjrYf8NAzhn49D/CAuVYFlxlmM31mCqOFOa1xO7HHZbV4zQJyggE=") - b6, _ := addr.NewSignedMultiAddressFromString("/ip4/3.9.164.193/tcp/18514/ren/8MJSu4N1FgyT4ZYRH9faB9G6oMUUiF", "mmVxayVx6vyOaGR/r4hWezXhszGf9MC3OQxgBuc7kL8+OyvtUl6TwmzmXIXUoZqPRN07IwmGY11BUH1W43lsAgE=") - b7, _ := addr.NewSignedMultiAddressFromString("/ip4/13.209.5.177/tcp/18514/ren/8MG7JhRuoj6SSQuzCWeWCdXRXd6Mn3", "CeHap47KDuH3oLcSUQOvtF/nK8HhjyejOqF+93FqhR0B2yRdOqHoHVUeY6Zsy2kOplo+2YNTpkzL9tDH2CpI2gE=") - bootstraps = append(bootstraps, b1, b2, b3, b4, b5, b6, b7) case Testnet: b1, _ := addr.NewSignedMultiAddressFromString("/ip4/165.22.58.69/tcp/18514/ren/8MHjCu8ZiFaPShXx7SfJ93hpHRMLwv", "5QQTiQuEIWoVotCLDPd4BdjyJPKr+YDT6WsMYxps/q9a+ZwBZMRQbQscfnBQgUwEC2rPVVbTfZITjPKcbVIRJAA=") b2, _ := addr.NewSignedMultiAddressFromString("/ip4/165.22.193.227/tcp/18514/ren/8MJWSxiNmY3ghCYYo14yB1VPq7Su5h", "DEEByr1HSuV0gwyrZSh20Ym9RAP4J0iC+ErUJSpiojwG5TG1y9CI+umHD/gPXkjGdNfkB1wiplFYM6VSeRFidgE=") @@ -98,8 +83,6 @@ func (network Network) ProtocolAddr() common.Address { switch network { case Mainnet: return common.HexToAddress("0xc25167fFa19B4d9d03c7d5aa4682c7063F345b66") - case Chaosnet: - return common.HexToAddress("0xf61e97c464ec0cf48b33262c3a1ef42114275144") case Testnet: return common.HexToAddress("0x59e23c087cA9bd9ce162875811CD6e99134D6d0F") case Devnet: diff --git a/docs/gcp/getting-started-on-gcp.md b/docs/gcp/getting-started-on-gcp.md index 8b934798..28fb0664 100644 --- a/docs/gcp/getting-started-on-gcp.md +++ b/docs/gcp/getting-started-on-gcp.md @@ -58,12 +58,20 @@ darknode list ### Choosing a compute zone -You can specify in which [Compute Engine Zone](https://cloud.google.com/compute/docs/regions-zones/) you deploy your node with the --gcp-zone flag. If omitted, a random zone is selected. +You can specify in which [Compute Engine Zone](https://cloud.google.com/compute/docs/regions-zones/) you deploy your node with the --gcp-region flag and a random zone in that region will be selected. ```sh -darknode up --name my-first-darknode --gcp --gcp-credentials PATH_TO_YOUR_DOWNLOADED_JSON_FILE --gcp-zone europe-west1-b +darknode up --name my-first-darknode --gcp --gcp-credentials PATH_TO_YOUR_DOWNLOADED_JSON_FILE --gcp-region europe-west1 ``` +You can also specify a zone you want to use. + +```sh +darknode up --name my-first-darknode --gcp --gcp-credentials PATH_TO_YOUR_DOWNLOADED_JSON_FILE --gcp-region europe-west1-b +``` + +A random region and zone will be used if this flag is not provided. + ### Choosing a machine type You can specify with which [Machine Type](https://cloud.google.com/compute/docs/machine-types) you deploy your node with the --gcp-machine-type flag. If omitted, n1-standard-1 is selected. diff --git a/go.mod b/go.mod index 85cbd96e..f171207c 100644 --- a/go.mod +++ b/go.mod @@ -3,23 +3,40 @@ module github.com/renproject/darknode-cli go 1.13 require ( - github.com/aws/aws-sdk-go v1.25.19 + cloud.google.com/go v0.51.0 // indirect + github.com/aws/aws-sdk-go v1.41.8 github.com/btcsuite/btcd v0.20.1-beta + github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect + github.com/digitalocean/godo v1.69.1 github.com/ethereum/go-ethereum v1.9.6 github.com/fatih/color v1.7.0 - github.com/google/go-github v17.0.0+incompatible - github.com/google/go-github/v31 v31.0.0 + github.com/golang/protobuf v1.4.3 // indirect + github.com/google/go-github/v39 v39.2.0 + github.com/google/uuid v1.1.5 // indirect + github.com/gorilla/websocket v1.4.2 // indirect github.com/hashicorp/go-version v1.2.0 + github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect + github.com/huin/goupnp v1.0.2 // indirect github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6 + github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect github.com/multiformats/go-multiaddr v0.1.1 github.com/multiformats/go-multihash v0.0.8 + github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/renproject/aw v0.3.7 github.com/renproject/mercury v0.3.15 github.com/renproject/phi v0.1.0 github.com/sirupsen/logrus v1.4.2 - github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d // indirect - github.com/urfave/cli v1.22.1 - golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 - golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 - google.golang.org/api v0.13.0 + github.com/stretchr/testify v1.7.0 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + github.com/urfave/cli v1.22.5 + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 + golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect + golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 // indirect + google.golang.org/api v0.15.0 + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 3f857cee..dd0f543d 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,26 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.51.0 h1:PvKAVQWCtlGUSlZkGW3QLelKaWq7KYv/MW1EboG8bfM= +cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.26.1/go.mod h1:NbSGBSSndYaIhRcBtY9V0U7AyH+x71bG668AuWys/yU= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= 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= @@ -26,25 +34,20 @@ github.com/aristanetworks/goarista v0.0.0-20200310212843-2da4c1f5881b h1:VBFuX8n github.com/aristanetworks/goarista v0.0.0-20200310212843-2da4c1f5881b/go.mod h1:QZe5Yh80Hp1b6JxQdpfSEEe8X7hTyTEZSosSrFf/oJE= github.com/aristanetworks/splunk-hec-go v0.3.3/go.mod h1:1VHO9r17b0K7WmOlLb9nTk/2YanvOEnLMUgsFrxBROc= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/aws/aws-sdk-go v1.25.19 h1:sp3xP91qIAVhWufyn9qM6Zhhn6kX06WJQcmhRj7QTXc= -github.com/aws/aws-sdk-go v1.25.19/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.41.8 h1:j6imzwVyWQYuQxbkPmg2MdMmLB+Zw+U3Ewi59YF8Rwk= +github.com/aws/aws-sdk-go v1.41.8/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/btcsuite/btcd v0.0.0-20190807005414-4063feeff79a/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd h1:qdGvebPBDuYDPGi1WCPjy1tGyMpmDK8IEapSsszn7HE= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723 h1:ZA/jbKoGcVAnER6pCHPEkGdZOV7U1oLUedErBHCUMs0= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -53,6 +56,9 @@ github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/codahale/blake2 v0.0.0-20150924215134-8d10d0420cbf/go.mod h1:BO2rLUAZMrpgh6GBVKi0Gjdqw2MgCtJrtmUdDeZRKjY= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -60,28 +66,29 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= 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.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= -github.com/dgraph-io/badger v1.6.0 h1:DshxFxZWXUcO0xX476VJC07Xsr6ZCBVRHKZ93Oh7Evo= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.1 h1:w9pSFNSdq/JPM1N12Fz/F/bzo993Is1W+Q7HjPzi7yg= github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI= github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-farm v0.0.0-20191112170834-c2139c5d712b h1:SeiGBzKrEtuDddnBABHkp4kq9sBGE9nuYmk6FPTg0zg= github.com/dgryski/go-farm v0.0.0-20191112170834-c2139c5d712b/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/digitalocean/godo v1.69.1 h1:aCyfwth8R3DeOaWB9J9E8v7cjlDIlF19eXTt8R3XhTE= +github.com/digitalocean/godo v1.69.1/go.mod h1:epPuOzTOOJujNo0nduDj2D5O1zu8cSpp9R+DdN0W9I0= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= @@ -103,16 +110,16 @@ github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlK github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= -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/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/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 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -121,69 +128,78 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-github/v31 v31.0.0 h1:JJUxlP9lFK+ziXKimTCprajMApV1ecWD4NB6CCb0plo= -github.com/google/go-github/v31 v31.0.0/go.mod h1:NQPZol8/1sMoWYGN2yaALIBytu17gAWfhbweiEed3pM= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github/v39 v39.2.0 h1:rNNM311XtPOz5rDdsJXAp2o8F67X9FnROXTvto3aSnQ= +github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I= +github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= +github.com/huin/goupnp v1.0.2 h1:RfGLP+h3mvisuWEyybxNq5Eft3NWhHLPeUN72kpKZoI= +github.com/huin/goupnp v1.0.2/go.mod h1:0dxJBVBHqTMjIUMkESDTNgOOx/Mw5wYIfyFmdzSamkM= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= @@ -191,18 +207,21 @@ github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+ github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6 h1:4zOlv2my+vf98jT1nQt4bT/yKWUImevYPJ2H344CloE= github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6/go.mod h1:r/8JmuR0qjuCiEhAolkfvdZgmPiHTnJaG0UXCSeR1Zo= github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89 h1:12K8AlpT0/6QUXSfV0yi4Q0jkbq8NDtIKFtF61AoqV0= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 h1:ZHuwnjpP8LsVsUYqTqeVAI+GfDfJ6UNPrExZF+vX/DQ= github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= +github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559 h1:0VWDXPNE0brOek1Q8bLfzKkvOzwbQE/snjGojlCr8CY= +github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= @@ -213,19 +232,23 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= github.com/mattn/go-colorable v0.1.7/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/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miguelmota/go-ethereum-hdwallet v0.0.0-20190720004541-5f6b3168e4a0 h1:DmK33IJe81zJOzLmzzBHWf2/fGB0tnvqLnvGu8S5wYY= github.com/miguelmota/go-ethereum-hdwallet v0.0.0-20190720004541-5f6b3168e4a0/go.mod h1:VVt+rn/itmf+9eZq9CAabVlsfsHveUNUQ0bRv3ChqxY= @@ -246,20 +269,26 @@ github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU github.com/multiformats/go-multihash v0.0.8 h1:wrYcW5yxSi3dU07n5jnuS5PrNwyHy0zRHGVoUugWvXg= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.9.0 h1:SZjF721BByVj8QH636/8S2DnX4n0Re3SteMmw3N+tzc= github.com/onsi/ginkgo v1.9.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/openconfig/gnmi v0.0.0-20190823184014-89b2bf29312c/go.mod h1:t+O9It+LKzfOAhKTT5O0ehDix+MTqbtT0T9t+7zzOvc= github.com/openconfig/reference v0.0.0-20190727015836-8dfd928c9696/go.mod h1:ym2A+zigScwkSEb/cVQB0/ZMpU3rqiH6X7WRRsxgOGw= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= @@ -267,7 +296,6 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pierrec/lz4 v2.4.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 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= @@ -293,7 +321,6 @@ github.com/renproject/aw v0.3.7 h1:KRqeAs/ALx2EtdCHH5PqVe+tpSTclOxUDexN7O4NndI= github.com/renproject/aw v0.3.7/go.mod h1:CpgAzPcLoCoOEf9HsMMhVtWXuIGTZ1eqAGSPegmj5YI= github.com/renproject/id v0.1.1 h1:KaV31Xp7SSlyUs5O0vHIw9rhhzrJ0lTOkQXVgbgyPEU= github.com/renproject/id v0.1.1/go.mod h1:i4OJzgjl4XLcU7nfU9UshX7PaBVpnTk3gEVj8dKa6f8= -github.com/renproject/kv v1.1.0 h1:xaEVeieSSuJrTFj/uvPveyB/gLqEqurJhQLoOKJF8/s= github.com/renproject/kv v1.1.0/go.mod h1:B+LKu5xLj9t65LrbsSyzTcgLcPdWCd0qxFEtNB7NRo8= github.com/renproject/kv v1.1.2 h1:P18yHdDVJTEZ9yeyx6o82ICY1m6f+VdtAt/ouZez+AU= github.com/renproject/kv v1.1.2/go.mod h1:78bvdAtYiYxCoT9ihVhl8qdmjl7s9fST/FkRLnZ6rXY= @@ -304,14 +331,15 @@ github.com/renproject/phi v0.1.0 h1:ZOn7QeDribk/uV46OhQWcTLxyuLg7P+xR1Hfl5cOQuI= github.com/renproject/phi v0.1.0/go.mod h1:Hrxx2ONVpfByficRjyRd1trecalYr0lo7Z0akx8UXqg= github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 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/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= @@ -334,23 +362,22 @@ github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUW github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= -github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs= -github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= github.com/tjfoc/gmsm v1.3.0/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tyler-smith/go-bip39 v1.0.2 h1:+t3w+KwLXO6154GNJY+qUtIxLTmFjfUmpguQT1OlOT8= github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= +github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk= github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= @@ -358,30 +385,47 @@ github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= -go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4= golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +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/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -392,26 +436,39 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191112182307-2180aed22343 h1:00ohfJ4K98s3m6BGUoBd8nyfp4Yl0GoIKvw5abItTjI= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -419,60 +476,98 @@ golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 h1:dHtDnRWQtSx0Hjq9kvKFpBh9uPPKfQN70NZZmvssGwk= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6 h1:X9xIZ1YU8bLZA3l6gqDUHSFiD0GFI9S548h6C8nDtOY= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 h1:uCLL3g5wH2xjxVREVuAbP9JM5PPKjRbXKRa6IBjkzmU= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.15.0 h1:yzlyyDW/J0w8yNFJIhiAJy4kq74S+1DOLdawELNxFMA= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200218151345-dad8c97a84f5/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -488,12 +583,12 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPAXdG8Oev9e99iLGnl2uJMjc5B+4y3O7x610= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= @@ -510,13 +605,18 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 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 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/util/github.go b/util/github.go new file mode 100644 index 00000000..29a8e70e --- /dev/null +++ b/util/github.go @@ -0,0 +1,84 @@ +package util + +import ( + "context" + "errors" + "fmt" + "net/http" + "os" + "regexp" + "time" + + "github.com/google/go-github/v39/github" + "github.com/hashicorp/go-version" + "golang.org/x/oauth2" +) + +// GithubClient initialize the github client. If an access token has been set as an environment, +// it will use it for oauth to avoid rate limiting. +func GithubClient(ctx context.Context) *github.Client { + accessToken := os.Getenv("GITHUB_TOKEN") + var client *http.Client + if accessToken != "" { + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: accessToken}, + ) + client = oauth2.NewClient(ctx, ts) + } + + return github.NewClient(client) +} + +// LatestStableRelease checks the darknode release repo and return the version +// of the latest release. +func LatestStableRelease() (string, error) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + client := github.NewClient(nil) + opts := &github.ListOptions{ + PerPage: 50, + } + latest, err := version.NewVersion("0.0.0") + if err != nil { + return "", err + } + + // Fetch all releases and find the latest stable release tag + for { + releases, response, err := client.Repositories.ListReleases(ctx, "renproject", "darknode-release", opts) + if err != nil { + return "", err + } + + if response.StatusCode != http.StatusOK { + return "", fmt.Errorf("cannot get latest darknode release from github, error code = %v", response.StatusCode) + } + + verReg := "^v?[0-9]+\\.[0-9]+\\.[0-9]+$" + for _, release := range releases { + match, err := regexp.MatchString(verReg, *release.TagName) + if err != nil { + return "", err + } + if match { + ver, err := version.NewVersion(*release.TagName) + if err != nil { + return "", err + } + if ver.GreaterThan(latest) { + latest = ver + } + } + } + if response.NextPage == 0 { + break + } + opts.Page = response.NextPage + } + if latest.String() == "0.0.0" { + return "", errors.New("cannot find any stable release") + } + + return latest.String(), nil +} diff --git a/util/key.go b/util/key.go index 309808e0..54e15ad9 100644 --- a/util/key.go +++ b/util/key.go @@ -23,9 +23,6 @@ func GenerateSshKeyAndWriteToDir(name string) error { return err } key.Precompute() - if err != nil { - return err - } // Write the private key to file priKeyBytes := x509.MarshalPKCS1PrivateKey(key) @@ -49,12 +46,6 @@ func GenerateSshKeyAndWriteToDir(name string) error { return ioutil.WriteFile(pubKeyPath, pubKeyBytes, 0600) } -func StringifySshPubKey(key ssh.PublicKey) string { - pubKeyBytes := ssh.MarshalAuthorizedKey(key) - - return string(pubKeyBytes) -} - func ParseSshPrivateKey(name string) (ssh.Signer, error) { path := filepath.Join(NodePath(name), "ssh_keypair") sshKey, err := ioutil.ReadFile(path) diff --git a/util/node.go b/util/node.go index 78437822..ae994cab 100644 --- a/util/node.go +++ b/util/node.go @@ -1,19 +1,15 @@ package util import ( - "context" "encoding/hex" "errors" "fmt" "io/ioutil" - "net/http" + "os" "path/filepath" "regexp" "strings" - "time" - "github.com/google/go-github/v31/github" - "github.com/hashicorp/go-version" "github.com/renproject/darknode-cli/darknode" "github.com/renproject/darknode-cli/darknode/addr" "golang.org/x/crypto/ssh" @@ -38,30 +34,42 @@ func ParseNodesFromNameAndTags(name, tags string) ([]string, error) { } else if name == "" && tags != "" { return GetNodesByTags(tags) } else if name != "" && tags == "" { - return []string{name}, ValidateNodeName(name) + return []string{name}, ValidateNodeExistence(name) } else { return nil, ErrTooManyArguments } } -// ValidateNodeName checks if there exists a node with given name. -func ValidateNodeName(name string) error { - files, err := ioutil.ReadDir(filepath.Join(Directory, "/darknodes")) +// ValidateName validates the given darknode name. It should +// 1) Only contains letter, number, "-" and "_". +// 2) No more than 32 characters +// 3) Do not contain any whitespace +func ValidateName(name string) error { + if strings.TrimSpace(name) != name { + return fmt.Errorf("name cannot have whitespace") + } + + nameRegex, err := regexp.Compile("^[a-zA-Z0-9_-]{1,32}$") if err != nil { return err } - for _, f := range files { - if f.Name() == name { - return nil - } + if !nameRegex.MatchString(name) { + return fmt.Errorf("darknode name should be less than 32 characters and not contain any special character") } - return fmt.Errorf("darknode [%v] not found", name) + return nil +} + +// ValidateNodeExistence checks if there exists a node with given name. +func ValidateNodeExistence(name string) error { + path := filepath.Join(Directory, "darknodes", name) + _, err := os.Stat(path) + return err } // Config returns the config of the node with given name. func Config(name string) (darknode.GeneralConfig, error) { path := filepath.Join(NodePath(name), "config.json") - return darknode.NewConfigFromJSONFile(path) + return darknode.NewGeneralConfigFromJSONFile(path) } // ID gets the ID of the node with given name. @@ -82,6 +90,9 @@ func IP(name string) (string, error) { cmd := fmt.Sprintf("cd %v && terraform output ip", NodePath(name)) ip, err := CommandOutput(cmd) + if strings.HasPrefix(ip, "\"") { + ip = strings.Trim(ip, "\"") + } return strings.TrimSpace(ip), err } @@ -164,60 +175,6 @@ func ValidateTags(have, required string) bool { return true } -// LatestStableRelease checks the darknode release repo and return the version -// of the latest release. -func LatestStableRelease() (string, error) { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - client := github.NewClient(nil) - opts := &github.ListOptions{ - PerPage: 25, - } - latest, err := version.NewVersion("0.0.0") - if err != nil { - return "", err - } - - // Fetch all releases and find the latest stable release tag - for { - releases, response, err := client.Repositories.ListReleases(ctx, "renproject", "darknode-release", opts) - if err != nil { - return "", err - } - - if response.StatusCode != http.StatusOK { - return "", fmt.Errorf("cannot get latest darknode release from github, error code = %v", response.StatusCode) - } - - verReg := "^v?[0-9]+\\.[0-9]+\\.[0-9]+$" - for _, release := range releases { - match, err := regexp.MatchString(verReg, *release.TagName) - if err != nil { - return "", err - } - if match { - ver, err := version.NewVersion(*release.TagName) - if err != nil { - return "", err - } - if ver.GreaterThan(latest) { - latest = ver - } - } - } - if response.NextPage == 0{ - break - } - opts.Page = response.NextPage - } - if latest.String() == "0.0.0" { - return "", errors.New("cannot find any stable release") - } - - return latest.String(), nil -} - func isDeployed(name string) bool { path := NodePath(name) script := fmt.Sprintf("cd %v && terraform output ip", path) diff --git a/util/system.go b/util/system.go index cb78fd30..d47e83c5 100644 --- a/util/system.go +++ b/util/system.go @@ -60,8 +60,8 @@ func CommandOutput(commands string) (string, error) { } // RemoteRun runs the script on the instance which host the darknode of given name. -func RemoteRun(name, script string) error { - session, err := connect(name, "darknode") +func RemoteRun(name, script, username string) error { + session, err := connect(name, username) if err != nil { return err } diff --git a/util/util.go b/util/util.go index 5fa0d032..df3aae41 100644 --- a/util/util.go +++ b/util/util.go @@ -1,5 +1,12 @@ package util +import ( + "fmt" + "io/ioutil" + "net/http" + "regexp" +) + // StringInSlice checks whether the string is in the slice func StringInSlice(a string, list []string) bool { for _, b := range list { @@ -21,3 +28,28 @@ func HandleErrs(errs []error) error { return nil } + +func VerifyStatusCode(response *http.Response, expected int) error { + if response.StatusCode != expected { + message, err := ioutil.ReadAll(response.Body) + if err != nil { + return err + } + return fmt.Errorf("code = %v, err = %s", response.StatusCode, message) + } + return nil +} + +// CaptureGroups returns a map which parses all the capture groups. +func CaptureGroups(regEx, input string) (paramsMap map[string]string) { + var compRegEx = regexp.MustCompile(regEx) + match := compRegEx.FindStringSubmatch(input) + + paramsMap = make(map[string]string) + for i, name := range compRegEx.SubexpNames() { + if i > 0 && i <= len(match) { + paramsMap[name] = match[i] + } + } + return +}