func (cfg *Config) SetCurrentUpgrade(upgradeName string) error {
	bin := cfg.UpgradeBin(upgradeName)
	if err := EnsureBinary(bin); err != nil {
		return err
	}
	link := filepath.Join(cfg.Root(), currentLink)
	safeName := url.PathEscape(upgradeName)
	upgrade := filepath.Join(cfg.Root(), upgradesDir, safeName)
	if _, err := os.Stat(link); err == nil {
		os.Remove(link)
	}
	if err := os.Symlink(upgrade, link); err != nil {
		return errors.Wrap(err, "creating current symlink")
	}
	return nil
}
SetCurrentUpgrade sets the named upgrade to be the current link, returns error if this binary doesn't exist
package main
import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	getter "github.com/hashicorp/go-getter"
	"github.com/pkg/errors"
)
func DoUpgrade(cfg *Config, info *UpgradeInfo) error {
	err := EnsureBinary(cfg.UpgradeBin(info.Name))
	if err == nil {
		return cfg.SetCurrentUpgrade(info.Name)
	}
	if !cfg.AllowDownloadBinaries {
		return errors.Wrap(err, "binary not present, downloading disabled")
	}
	_, err = os.Stat(cfg.UpgradeDir(info.Name))
	if !os.IsNotExist(err) {
		return errors.Errorf("upgrade dir already exists, won't overwrite")
	}
	if err := DownloadBinary(cfg, info); err != nil {
		return errors.Wrap(err, "cannot download binary")
	}
	err = EnsureBinary(cfg.UpgradeBin(info.Name))
	if err != nil {
		return errors.Wrap(err, "downloaded binary doesn't check out")
	}
	return cfg.SetCurrentUpgrade(info.Name)
}
func DownloadBinary(cfg *Config, info *UpgradeInfo) error {
	url, err := GetDownloadURL(info)
	if err != nil {
		return err
	}
	binPath := cfg.UpgradeBin(info.Name)
	err = getter.GetFile(binPath, url)
	if err != nil {
		dirPath := cfg.UpgradeDir(info.Name)
		err = getter.Get(dirPath, url)
	}
	if err != nil {
		return err
	}
	return MarkExecutable(binPath)
}
func MarkExecutable(path string) error {
	info, err := os.Stat(path)
	if err != nil {
		return errors.Wrap(err, "stating binary")
	}
	if info.Mode()&0001 == 1 {
		return nil
	}
	newMode := info.Mode().Perm() | 0111
	return os.Chmod(path, newMode)
}
type UpgradeConfig struct {
	Binaries map[string]string `json:"binaries"`
}
func GetDownloadURL(info *UpgradeInfo) (string, error) {
	doc := strings.TrimSpace(info.Info)
	if _, err := url.Parse(doc); err == nil {
		tmpDir, err := ioutil.TempDir("", "upgrade-manager-reference")
		if err != nil {
			return "", errors.Wrap(err, "create tempdir for reference file")
		}
		defer os.RemoveAll(tmpDir)
		refPath := filepath.Join(tmpDir, "ref")
		err = getter.GetFile(refPath, doc)
		if err != nil {
			return "", errors.Wrapf(err, "downloading reference link %s", doc)
		}
		refBytes, err := ioutil.ReadFile(refPath)
		if err != nil {
			return "", errors.Wrap(err, "reading downloaded reference")
		}
		doc = string(refBytes)
	}
	var config UpgradeConfig
	err := json.Unmarshal([]byte(doc), &config)
	if err == nil {
		url, ok := config.Binaries[osArch()]
		if !ok {
			return "", errors.Errorf("cannot find binary for os/arch: %s", osArch())
		}
		return url, nil
	}
	return "", errors.New("upgrade info doesn't contain binary map")
}
func osArch() string {
	return fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
}