diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f96b9fc..e9cca42 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -53,7 +53,7 @@ jobs: name: Build binary launcher strategy: matrix: - go-version: [ 1.15.x ] + go-version: [ 1.16.x ] os: [ linux, darwin, windows ] arch: [ amd64, arm64 ] exclude: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3b0527b..b3d5f93 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: name: Build artifacts strategy: matrix: - go-version: [ 1.15.x ] + go-version: [ 1.16.x ] os: [ linux, darwin, windows ] arch: [ amd64, arm64 ] exclude: diff --git a/launcher/cmd/console.go b/launcher/cmd/console.go index a58e6f2..b75d4ab 100644 --- a/launcher/cmd/console.go +++ b/launcher/cmd/console.go @@ -1,3 +1,23 @@ package cmd -// TODO console +import ( + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(consoleCmd) +} + +var consoleCmd = &cobra.Command{ + Use: "console", + Short: "Start the console", + PreRunE: func(cmd *cobra.Command, args []string) error { + return launcher.Apply() + }, + RunE: func(cmd *cobra.Command, args []string) error { + ctx, cancel := newContext() + defer cancel() + return launcher.StartConsole(ctx) + }, +} + diff --git a/launcher/cmd/restart.go b/launcher/cmd/restart.go deleted file mode 100644 index 17a3202..0000000 --- a/launcher/cmd/restart.go +++ /dev/null @@ -1,22 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -func init() { - rootCmd.AddCommand(restartCmd) -} - -var restartCmd = &cobra.Command{ - Use: "restart", - Short: "Restart services", - PreRunE: func(cmd *cobra.Command, args []string) error { - return launcher.Apply() - }, - RunE: func(cmd *cobra.Command, args []string) error { - ctx, cancel := newContext() - defer cancel() - return launcher.Restart(ctx) - }, -} diff --git a/launcher/cmd/root.go b/launcher/cmd/root.go index 1f7858c..c121af2 100644 --- a/launcher/cmd/root.go +++ b/launcher/cmd/root.go @@ -14,7 +14,7 @@ import ( var ( rootCmd = &cobra.Command{ Use: "launcher", - Short: fmt.Sprintf("XUD environment launcher"), + Short: fmt.Sprintf("OpenDEX environment launcher"), SilenceUsage: true, SilenceErrors: true, } @@ -22,12 +22,13 @@ var ( ) func init() { + var err error + if err := os.Setenv("DOCKER_API_VERSION", "1.40"); err != nil { panic(err) } - var err error - launcher, err = core.NewLauncher() - if err != nil { + launcher = core.NewLauncher() + if err := launcher.Init(); err != nil { panic(err) } err = launcher.AddServiceFlags(rootCmd) @@ -69,3 +70,7 @@ func newContext() (context.Context, func()) { return ctx, _cancel } + +func CommonPreRunE(cmd *cobra.Command, args []string) error { + return launcher.Apply() +} diff --git a/launcher/cmd/setup.go b/launcher/cmd/setup.go index 1c355c4..6e28d21 100644 --- a/launcher/cmd/setup.go +++ b/launcher/cmd/setup.go @@ -7,6 +7,7 @@ import ( type SetupOptions struct { NoPull bool + Interactive bool } var ( @@ -15,15 +16,14 @@ var ( func init() { setupCmd.PersistentFlags().BoolVar(&setupOpts.NoPull, "nopull", false, "don't pull images") + setupCmd.PersistentFlags().BoolVarP(&setupOpts.Interactive, "interactive", "i", false, "interactive setup") rootCmd.AddCommand(setupCmd) } var setupCmd = &cobra.Command{ Use: "setup", Short: "Set up OpenDEX environment", - PreRunE: func(cmd *cobra.Command, args []string) error { - return launcher.Apply() - }, + PreRunE: CommonPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx, cancel := newContext() defer cancel() diff --git a/launcher/cmd/start.go b/launcher/cmd/start.go index 182f671..c565d53 100644 --- a/launcher/cmd/start.go +++ b/launcher/cmd/start.go @@ -17,6 +17,7 @@ var startCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { ctx, cancel := newContext() defer cancel() - return launcher.Start(ctx) + // FIXME enable start multiple services + return launcher.Start(ctx, args[1]) }, } diff --git a/launcher/cmd/status.go b/launcher/cmd/status.go index c8e1867..81ed1cd 100644 --- a/launcher/cmd/status.go +++ b/launcher/cmd/status.go @@ -1,32 +1,117 @@ package cmd import ( - "errors" "fmt" + "github.com/opendexnetwork/opendex-docker/launcher/utils" "github.com/spf13/cobra" + "os" + "os/exec" + "strings" ) func init() { rootCmd.AddCommand(statusCmd) } +type StatusUpdate struct { + Service string + Status string +} + var statusCmd = &cobra.Command{ Use: "status", Short: "Get service status", - PreRunE: func(cmd *cobra.Command, args []string) error { - return launcher.Apply() - }, + PreRunE: CommonPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx, cancel := newContext() defer cancel() - if len(args) < 1 { - return errors.New("service name required") + + // Checking Docker + err := utils.Run(ctx, exec.Command("docker", "info")) + if err != nil { + return err } - status, err := launcher.Status(ctx, args[0]) + + // Changing working directory + wd, err := os.Getwd() if err != nil { return err } - fmt.Println(status) + defer os.Chdir(wd) + + if err := os.Chdir(launcher.NetworkDir); err != nil { + return err + } + + t := utils.SimpleTable{ + Columns: []utils.TableColumn{ + { + ID: "service", + Display: "SERVICE", + }, + { + ID: "status", + Display: "STATUS", + }, + }, + Records: []utils.TableRecord{}, + } + + var statusMap = make(map[string]string) + + names := args + if len(names) == 0 { + names = launcher.Services.Keys() + } + + for _, name := range names { + if ! launcher.HasService(name) { + return fmt.Errorf("no such service: %s", name) + } + statusMap[name] = "" + t.Records = append(t.Records, utils.TableRecord{ + Fields: map[string]string{ + "service": name, + "status": "", + }, + }) + } + + updates := make(chan StatusUpdate) + + for _, name_ := range names { + name := name_ + go func() { + status, err := launcher.Status(ctx, name) + if err != nil { + if strings.HasPrefix(err.Error(), "Error:") { + status = err.Error() + } else { + status = "Error: " + err.Error() + } + updates <- StatusUpdate{Service: name, Status: status} + return + } + updates <- StatusUpdate{Service: name, Status: status} + }() + } + + t.Print() + + i := 0 + + for i < len(names) { + update := <-updates + //fmt.Println(i, update) + t.PrintUpdate(utils.TableRecord{ + Fields: map[string]string{ + "service": update.Service, + "status": update.Status, + }, + }) + i++ + } + return nil }, } diff --git a/launcher/core/backupto.go b/launcher/core/backupto.go index e672f61..64fb5c3 100644 --- a/launcher/core/backupto.go +++ b/launcher/core/backupto.go @@ -17,7 +17,7 @@ func (t *Launcher) BackupTo(ctx context.Context, location string) error { if err := t.Gen(ctx); err != nil { return err } - if err := t.upOpendexd(ctx); err != nil { + if err := t.Opendexd.Start(ctx); err != nil { return err } return nil diff --git a/launcher/core/cleanup.go b/launcher/core/cleanup.go index 223a50a..e65658c 100644 --- a/launcher/core/cleanup.go +++ b/launcher/core/cleanup.go @@ -18,10 +18,7 @@ import ( func (t *Launcher) stopService(ctx context.Context, name string) error { t.Logger.Debugf("Stopping %s", name) - s, err := t.GetService(name) - if err != nil { - return err - } + s := t.GetService(name) if err := s.Stop(ctx); err != nil { if strings.Contains(err.Error(), "No such container") { t.Logger.Debugf("Service %s stopped already", name) @@ -50,19 +47,7 @@ func (t *Launcher) stopService(ctx context.Context, name string) error { return nil } -func (t *Launcher) Create(ctx context.Context) error { - return nil -} - -func (t *Launcher) Start(ctx context.Context) error { - return nil -} - -func (t *Launcher) Restart(ctx context.Context) error { - return nil -} - -func (t *Launcher) Stop(ctx context.Context) error { +func (t *Launcher) Stop(ctx context.Context, services ...string) error { if t.Network != types.Simnet { if err := t.stopService(ctx, "boltz"); err != nil { return err @@ -145,7 +130,8 @@ func (t *Launcher) finalDown(ctx context.Context) error { return fmt.Errorf("create docker client: %w", err) } - for _, service := range t.ServicesOrder { + for _, name := range t.Services.Keys() { + service := t.Services.Get(name) containerName := fmt.Sprintf("%s_%s_1", t.Network, service) _, err := client.ContainerInspect(ctx, containerName) if err != nil { @@ -183,6 +169,17 @@ func (t *Launcher) finalDown(ctx context.Context) error { } func (t *Launcher) Cleanup(ctx context.Context) error { + // Changing working directory + wd, err := os.Getwd() + if err != nil { + return err + } + defer os.Chdir(wd) + + if err := os.Chdir(t.NetworkDir); err != nil { + return err + } + // stop all running services if err := t.Stop(ctx); err != nil { return fmt.Errorf("stop: %w", err) diff --git a/launcher/core/collections/ordered_map.go b/launcher/core/collections/ordered_map.go new file mode 100644 index 0000000..e39b26b --- /dev/null +++ b/launcher/core/collections/ordered_map.go @@ -0,0 +1,28 @@ +package collections + +type OrderedMap struct { + m map[string]interface{} + keys []string +} + +func NewOrderedMap() *OrderedMap { + return &OrderedMap{ + m: make(map[string]interface{}), + keys: []string{}, + } +} + +func (t *OrderedMap) Get(key string) interface{} { + return t.m[key] +} + +func (t *OrderedMap) Put(key string, value interface{}) { + if _, existed := t.m[key]; !existed { + t.keys = append(t.keys, key) + } + t.m[key] = value +} + +func (t *OrderedMap) Keys() []string { + return t.keys +} diff --git a/launcher/core/console/bash.go b/launcher/core/console/bash.go new file mode 100644 index 0000000..1f0ce6d --- /dev/null +++ b/launcher/core/console/bash.go @@ -0,0 +1,200 @@ +package console + +import ( + "github.com/sirupsen/logrus" + "os" + "os/exec" +) + +var logger = logrus.NewEntry(logrus.StandardLogger()) + +var help = `\ +Opendex-cli shortcut commands + addcurrency add a currency + [decimal_places] + [token_address] + addpair add a trading pair + [quote_currency] + ban ban a remote node + buy place a buy order + [order_id] + closechannel close any payment channels with a + [node_identifier ] [--force] peer + connect connect to a remote node + create create a new opendexd instance and set a + password + discovernodes discover nodes from a specific peer + getbalance [currency] get total balance for a given + currency + getinfo get general info from the local opendexd + node + getnodeinfo get general information about a + known node + listcurrencies list available currencies + listorders [pair_id] [owner] list orders from the order book + [limit] + listpairs get order book's available pairs + listpeers list connected peers + openchannel open a payment channel with a peer + [node_identifier] [push_amount] + orderbook [pair_id] [precision] display the order book, with orders + aggregated per price point + removecurrency remove a currency + removeorder [quantity] remove an order + removepair remove a trading pair + restore [backup_directory] restore an opendexd instance from seed + sell place a sell order + [order_id] + shutdown gracefully shutdown local opendexd node + streamorders [existing] stream order added, removed, and + swapped events (DEMO) + tradehistory [limit] list completed trades + tradinglimits [currency] trading limits for a given currency + unban unban a previously banned remote + [--reconnect] node + unlock unlock local opendexd node + walletdeposit gets an address to deposit funds to + opendexd + walletwithdraw [amount] [currency] withdraws on-chain funds from opendexd + [destination] [fee] + +General commands + report report issue + logs show service log + start start service + stop stop service + restart restart service + up bring up the environment + help show this help + exit exit opendexd-ctl shell + +CLI commands + bitcoin-cli bitcoind cli + litecoin-cli litecoind cli + lndbtc-lncli lnd cli + lndltc-lncli lnd cli + geth geth cli + opendex-cli opendexd cli + boltzcli boltz cli + +Boltzcli shortcut commands + boltzcli deposit + --inbound [inbound_balance] deposit from boltz (btc/ltc) + boltzcli withdraw +
withdraw from boltz channel +` + +func writeInitScript(network string, launcherExecutable string, f *os.File) { + f.WriteString(`\ +export NETWORK='` + network + `' +export OPENDEX_LAUNCHER='` + launcherExecutable + `' +export PS1="$NETWORK > " +function help() { + echo "` + help + `" +} +function status() { + "$OPENDEX_LAUNCHER" status +} +function start() { + docker start ${NETWORK}_${1}_1 +} +function stop() { + docker stop ${NETWORK}_${1}_1 +} +function restart() { + docker restart ${NETWORK}_${1}_1 +} +function down() { + "$OPENDEX_LAUNCHER" down +} +function logs() { + docker logs --tail=100 ${NETWORK}_${1}_1 +} +function report() { + cat < < + \___/| .__/\___|_||_|___/|___/_/\_\ + |_| +-------------------------------------------------------------- +`) + +type Console struct { + Banner string + ShowBanner bool +} + +var DefaultConsole *Console + +func init() { + DefaultConsole = &Console{ + Banner: DefaultBanner, + ShowBanner: true, + } +} + +func (t *Console) Start(launcherExecutable string) error { + if t.ShowBanner { + fmt.Println(t.Banner) + } + if runtime.GOOS == "windows" { + return startPowershell() + } else { + return startBash(launcherExecutable) + } +} diff --git a/launcher/core/console/powershell.go b/launcher/core/console/powershell.go new file mode 100644 index 0000000..2d72932 --- /dev/null +++ b/launcher/core/console/powershell.go @@ -0,0 +1,5 @@ +package console + +func startPowershell() error { + return nil +} diff --git a/launcher/core/gen.go b/launcher/core/gen.go index c8bc619..59b0824 100644 --- a/launcher/core/gen.go +++ b/launcher/core/gen.go @@ -12,8 +12,8 @@ func (t *Launcher) exportDockerComposeYaml() (string, error) { var b strings.Builder b.WriteString("version: \"2.4\"\n") b.WriteString("services:\n") - for _, name := range t.ServicesOrder { - s := t.Services[name] + for _, name := range t.Services.Keys() { + s := t.Services.Get(name) if s.IsDisabled() { continue } @@ -108,11 +108,11 @@ func (t *Launcher) exportConfigJson() (string, error) { Services: []ServiceConfig{}, } - for _, name := range t.ServicesOrder { + for _, name := range t.Services.Keys() { if name == "proxy" { continue } - s := t.Services[name] + s := t.Services.Get(name) rpc, err := s.GetRpcParams() if err != nil { return "", err diff --git a/launcher/core/launcher.go b/launcher/core/launcher.go index 710b9be..2afe111 100644 --- a/launcher/core/launcher.go +++ b/launcher/core/launcher.go @@ -1,25 +1,12 @@ package core import ( - "bufio" - "crypto/tls" + "context" "errors" "fmt" "github.com/iancoleman/strcase" - "github.com/mitchellh/go-homedir" - "github.com/opendexnetwork/opendex-docker/launcher/log" - "github.com/opendexnetwork/opendex-docker/launcher/service/arby" - "github.com/opendexnetwork/opendex-docker/launcher/service/bitcoind" - "github.com/opendexnetwork/opendex-docker/launcher/service/boltz" - "github.com/opendexnetwork/opendex-docker/launcher/service/connext" - "github.com/opendexnetwork/opendex-docker/launcher/service/geth" - "github.com/opendexnetwork/opendex-docker/launcher/service/litecoind" - "github.com/opendexnetwork/opendex-docker/launcher/service/lnd" - "github.com/opendexnetwork/opendex-docker/launcher/service/opendexd" - "github.com/opendexnetwork/opendex-docker/launcher/service/proxy" - "github.com/opendexnetwork/opendex-docker/launcher/service/webui" + "github.com/opendexnetwork/opendex-docker/launcher/core/console" "github.com/opendexnetwork/opendex-docker/launcher/types" - "github.com/opendexnetwork/opendex-docker/launcher/utils" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -27,14 +14,12 @@ import ( "os" "path/filepath" "reflect" - "runtime" - "strings" ) + type Launcher struct { Logger *logrus.Entry - Services map[string]types.Service - ServicesOrder []string + Services *ServiceMap ServicesConfig map[string]interface{} HomeDir string @@ -51,6 +36,7 @@ type Launcher struct { ConfigFile string PasswordUnsetMarker string + BackupUnsetMarker string ExternalIp string rootCmd *cobra.Command @@ -59,88 +45,22 @@ type Launcher struct { rootLogger *logrus.Logger LogFile *os.File -} -func defaultHomeDir() (string, error) { - homeDir, err := homedir.Dir() - if err != nil { - return "", fmt.Errorf("get home directory: %s", err) - } - switch runtime.GOOS { - case "linux": - return filepath.Join(homeDir, ".opendex-docker"), nil - case "darwin": - return filepath.Join(homeDir, "Library", "Application Support", "OpendexDocker"), nil - case "windows": - return filepath.Join(homeDir, "AppData", "Local", "OpendexDocker"), nil - default: - return "", fmt.Errorf("unsupported operating system: %s", runtime.GOOS) - } -} + Console *console.Console -func getNetwork() types.Network { - if value, ok := os.LookupEnv("NETWORK"); ok { - return types.Network(value) - } - return "mainnet" -} + Executable string -func getNetworkDir(homeDir string, network types.Network) string { - if value, ok := os.LookupEnv("NETWORK_DIR"); ok { - return value - } - return filepath.Join(homeDir, string(network)) -} - -func getBackupDir(networkDir string, dockerComposeFile string) string { - dir := getDefaultBackupDir(networkDir) - - // TODO parse backup location from 1) opendexd container 2) docker-compose file 3) config.json - f, err := os.Open(dockerComposeFile) - if err != nil { - return dir - } - defer f.Close() - scanner := bufio.NewScanner(f) - for scanner.Scan() { - line := scanner.Text() - if strings.Contains(line, "/root/backup") { - line = strings.TrimSpace(line) - // fix broken colon (before) in backup location ( - line = strings.ReplaceAll(line, "::", ":") - line = strings.TrimPrefix(line, "- ") - line = strings.TrimSuffix(line, ":/root/backup") - return line - } - } - return dir -} - -func getDefaultBackupDir(networkDir string) string { - return filepath.Join(networkDir, "backup") -} - -func getExternalIp(networkDir string) string { - // Backward compatible with lnd.env - lndEnv := filepath.Join(networkDir, "lnd.env") - f, err := os.Open(lndEnv) - if err != nil { - return "" - } - defer f.Close() - scanner := bufio.NewScanner(f) - for scanner.Scan() { - line := scanner.Text() - parts := strings.Split(line, "=") - if len(parts) == 2 { - key := strings.TrimSpace(parts[0]) - value := strings.TrimSpace(parts[1]) - if key == "EXTERNAL_IP" { - return value - } - } - } - return "" + // services + Proxy *Proxy + Bitcoind *Bitcoind + Litecoind *Litecoind + Geth *Geth + Lndbtc *Lnd + Lndltc *Lnd + Connext *Connext + Opendexd *Opendexd + Arby *Arby + Boltz *Boltz } func checkFolderPermission(networkDir string) error { @@ -148,110 +68,20 @@ func checkFolderPermission(networkDir string) error { return nil } -func NewLauncher() (*Launcher, error) { - homeDir, err := defaultHomeDir() - if err != nil { - return nil, err - } - - if _, err := os.Stat(homeDir); os.IsNotExist(err) { - if err := os.Mkdir(homeDir, 0755); err != nil { - return nil, err - } - } - - network := getNetwork() - networkDir := getNetworkDir(homeDir, network) - - if _, err := os.Stat(networkDir); os.IsNotExist(err) { - if err := os.Mkdir(networkDir, 0755); err != nil { - return nil, err - } - } - - //if runtime.GOOS == "linux" { - // user := os.Getenv("USER") - // c := exec.Command("sudo", "chmod", "-R", fmt.Sprintf("%s:%s", user, user), networkDir) - // _ = c.Run() - //} else if runtime.GOOS == "darwin" { - // user := os.Getenv("USER") - // c := exec.Command("sudo", "chmod", "-R", fmt.Sprintf("%s:staff", user), networkDir) - // _ = c.Run() - //} - if err := checkFolderPermission(networkDir); err != nil { - return nil, err - } - - dataDir := filepath.Join(networkDir, "data") - - if _, err := os.Stat(dataDir); os.IsNotExist(err) { - if err := os.Mkdir(dataDir, 0755); err != nil { - return nil, err - } - } - - logsDir := filepath.Join(networkDir, "logs") - - if _, err := os.Stat(logsDir); os.IsNotExist(err) { - if err := os.Mkdir(logsDir, 0755); err != nil { - return nil, err - } - } - - dockerComposeFile := filepath.Join(networkDir, "docker-compose.yml") - configFile := filepath.Join(networkDir, "config.json") - passwordUnsetMarker := filepath.Join(networkDir, ".password-unset") - // migration for legacy .default-password file - legacyPasswordUnsetMarker := filepath.Join(networkDir, ".default-password") - if utils.FileExists(legacyPasswordUnsetMarker) { - if err := os.Rename(legacyPasswordUnsetMarker, passwordUnsetMarker); err != nil { - return nil, err - } - } - - backupDir := getBackupDir(networkDir, dockerComposeFile) - defaultBackupDir := getDefaultBackupDir(networkDir) - - externalIp := getExternalIp(networkDir) - - config := tls.Config{RootCAs: nil, InsecureSkipVerify: true} - - logfile := filepath.Join(logsDir, "launcher.log") - f, err := os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666) - if err != nil { - return nil, err - } - log.SetOutput(f) - - l := Launcher{ - Logger: log.NewLogger("launcher"), - Services: make(map[string]types.Service), - - HomeDir: homeDir, - Network: network, - NetworkDir: networkDir, - DataDir: dataDir, - LogsDir: logsDir, - BackupDir: backupDir, - DefaultBackupDir: defaultBackupDir, - DockerComposeFile: dockerComposeFile, - ConfigFile: configFile, - PasswordUnsetMarker: passwordUnsetMarker, - ExternalIp: externalIp, - client: &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &config, - }, - }, - LogFile: f, +func NewLauncher() *Launcher { + return &Launcher{ + Services: NewServiceMap(), + Console: console.DefaultConsole, + Executable: getExecutable(), } +} - l.Services, l.ServicesOrder, err = initServices(&l, network) +func getExecutable() string { + exe, err := filepath.Abs(os.Args[0]) if err != nil { - return nil, err + panic(err) } - - return &l, nil + return exe } func getDefaultValue(dv reflect.Value, fieldName string) interface{} { @@ -307,8 +137,8 @@ func (t *Launcher) AddServiceFlags(cmd *cobra.Command) error { t.ServicesConfig = make(map[string]interface{}) - for _, name := range t.ServicesOrder { - s := t.Services[name] + for _, name := range t.Services.Keys() { + s := t.Services.Get(name) defaultConfig := s.GetDefaultConfig() @@ -332,169 +162,6 @@ func (t *Launcher) AddServiceFlags(cmd *cobra.Command) error { return nil } -func initServices(ctx types.Context, network types.Network) (map[string]types.Service, []string, error) { - var services []types.Service - var order []string - - proxy_, err := proxy.New(ctx, "proxy") - if err != nil { - return nil, nil, err - } - - lndbtc, err := lnd.New(ctx, "lndbtc", lnd.Bitcoin) - if err != nil { - return nil, nil, err - } - - lndltc, err := lnd.New(ctx, "lndltc", lnd.Litecoin) - if err != nil { - return nil, nil, err - } - - connext_, err := connext.New(ctx, "connext") - if err != nil { - return nil, nil, err - } - - opendexd_, err := opendexd.New(ctx, "opendexd") - if err != nil { - return nil, nil, err - } - - arby_, err := arby.New(ctx, "arby") - if err != nil { - return nil, nil, err - } - - webui_, err := webui.New(ctx, "webui") - if err != nil { - return nil, nil, err - } - - switch network { - case "simnet": - services = []types.Service{ - proxy_, - lndbtc, - lndltc, - connext_, - opendexd_, - arby_, - webui_, - } - order = []string{ - "proxy", - "lndbtc", - "lndltc", - "connext", - "opendexd", - "arby", - "webui", - } - case "testnet": - bitcoind_, err := bitcoind.New(ctx, "bitcoind") - if err != nil { - return nil, nil, err - } - - litecoind_, err := litecoind.New(ctx, "litecoind") - if err != nil { - return nil, nil, err - } - - geth_, err := geth.New(ctx, "geth") - if err != nil { - return nil, nil, err - } - - boltz_, err := boltz.New(ctx, "boltz") - if err != nil { - return nil, nil, err - } - - services = []types.Service{ - proxy_, - bitcoind_, - litecoind_, - geth_, - lndbtc, - lndltc, - connext_, - opendexd_, - arby_, - boltz_, - webui_, - } - order = []string{ - "proxy", - "bitcoind", - "litecoind", - "geth", - "lndbtc", - "lndltc", - "connext", - "opendexd", - "arby", - "boltz", - "webui", - } - case "mainnet": - bitcoind_, err := bitcoind.New(ctx, "bitcoind") - if err != nil { - return nil, nil, err - } - - litecoind_, err := litecoind.New(ctx, "litecoind") - if err != nil { - return nil, nil, err - } - - geth_, err := geth.New(ctx, "geth") - if err != nil { - return nil, nil, err - } - - boltz_, err := boltz.New(ctx, "boltz") - if err != nil { - return nil, nil, err - } - - services = []types.Service{ - proxy_, - bitcoind_, - litecoind_, - geth_, - lndbtc, - lndltc, - connext_, - opendexd_, - arby_, - boltz_, - webui_, - } - order = []string{ - "proxy", - "bitcoind", - "litecoind", - "geth", - "lndbtc", - "lndltc", - "connext", - "opendexd", - "arby", - "boltz", - "webui", - } - } - - result := make(map[string]types.Service) - for _, s := range services { - result[s.GetName()] = s - } - - return result, order, nil -} - func (t *Launcher) Run() error { err := t.rootCmd.Execute() if err != nil { @@ -503,18 +170,10 @@ func (t *Launcher) Run() error { return nil } -func (t *Launcher) GetService(name string) (types.Service, error) { - if s, ok := t.Services[name]; ok { - return s, nil - } - return nil, fmt.Errorf("service not found: %s", name) -} - // apply configurations into services func (t *Launcher) Apply() error { - for _, name := range t.ServicesOrder { - s := t.Services[name] - //t.Logger.Debugf("Apply %s", s.GetName()) + for _, name := range t.Services.Keys() { + s := t.Services.Get(name) if err := s.Apply(t.ServicesConfig[name]); err != nil { return err } @@ -522,12 +181,16 @@ func (t *Launcher) Apply() error { return nil } +func (t *Launcher) GetService(name string) types.Service { + return t.Services.Get(name) +} + func (t *Launcher) GetNetwork() types.Network { return t.Network } func (t *Launcher) GetExternalIp() string { - return "" + return t.ExternalIp } func (t *Launcher) GetNetworkDir() string { @@ -545,3 +208,11 @@ func (t *Launcher) GetDataDir() string { func (t *Launcher) Close() { _ = t.LogFile.Close() } + +func (t *Launcher) StartConsole(ctx context.Context) error { + return t.Console.Start(t.Executable) +} + +func (t *Launcher) HasService(name string) bool { + return t.GetService(name) != nil +} diff --git a/launcher/core/launcher_init.go b/launcher/core/launcher_init.go new file mode 100644 index 0000000..a3c6816 --- /dev/null +++ b/launcher/core/launcher_init.go @@ -0,0 +1,219 @@ +package core + +import ( + "bufio" + "fmt" + "github.com/mitchellh/go-homedir" + "github.com/opendexnetwork/opendex-docker/launcher/log" + "github.com/opendexnetwork/opendex-docker/launcher/service/lnd" + "github.com/opendexnetwork/opendex-docker/launcher/types" + "github.com/opendexnetwork/opendex-docker/launcher/utils" + "os" + "path/filepath" + "runtime" + "strings" +) + +func (t *Launcher) Init() error { + var err error + + if t.HomeDir, err = ensureHomeDir(); err != nil { + return err + } + + t.Network = getNetwork() + + if t.NetworkDir, err = ensureNetworkDir(t.HomeDir, t.Network); err != nil { + return err + } + + // fix folder permission + //if runtime.GOOS == "linux" { + // user := os.Getenv("USER") + // c := exec.Command("sudo", "chmod", "-R", fmt.Sprintf("%s:%s", user, user), networkDir) + // _ = c.Run() + //} else if runtime.GOOS == "darwin" { + // user := os.Getenv("USER") + // c := exec.Command("sudo", "chmod", "-R", fmt.Sprintf("%s:staff", user), networkDir) + // _ = c.Run() + //} + if err := checkFolderPermission(t.NetworkDir); err != nil { + return err + } + + t.DataDir = filepath.Join(t.NetworkDir, "data") + + if _, err := os.Stat(t.DataDir); os.IsNotExist(err) { + if err := os.Mkdir(t.DataDir, 0755); err != nil { + return err + } + } + + logsDir := filepath.Join(t.NetworkDir, "logs") + + if _, err := os.Stat(logsDir); os.IsNotExist(err) { + if err := os.Mkdir(logsDir, 0755); err != nil { + return err + } + } + + dockerComposeFile := filepath.Join(t.NetworkDir, "docker-compose.yml") + t.ConfigFile = filepath.Join(t.NetworkDir, "config.json") + passwordUnsetMarker := filepath.Join(t.NetworkDir, ".password-unset") + // migration for legacy .default-password file + legacyPasswordUnsetMarker := filepath.Join(t.NetworkDir, ".default-password") + if utils.FileExists(legacyPasswordUnsetMarker) { + if err := os.Rename(legacyPasswordUnsetMarker, passwordUnsetMarker); err != nil { + return err + } + } + t.BackupUnsetMarker = filepath.Join(t.NetworkDir, ".backup-unset") + + t.BackupDir = getBackupDir(t.NetworkDir, dockerComposeFile) + t.DefaultBackupDir = getDefaultBackupDir(t.NetworkDir) + + t.ExternalIp = getExternalIp(t.NetworkDir) + + //config := tls.Config{RootCAs: nil, InsecureSkipVerify: true} + + t.Logger = log.NewLogger("launcher") + logfile := filepath.Join(logsDir, "launcher.log") + f, err := os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666) + if err != nil { + return err + } + log.SetOutput(f) + + t.Proxy = NewProxy(t, "proxy") + t.Bitcoind = NewBitcoind(t, "bitcoind") + t.Litecoind = NewLitecoind(t, "litecoind") + t.Geth = NewGeth(t, "geth") + t.Lndbtc = NewLnd(t, "lndbtc", lnd.Bitcoin) + t.Lndltc = NewLnd(t, "lndltc", lnd.Litecoin) + t.Connext = NewConnext(t, "connext") + t.Opendexd = NewOpendexd(t, "opendexd") + t.Arby = NewArby(t, "arby") + t.Boltz = NewBoltz(t, "boltz") + + t.Services.Put("proxy", t.Proxy) + t.Services.Put("bitcoind", t.Bitcoind) + t.Services.Put("litecoind", t.Litecoind) + t.Services.Put("geth", t.Geth) + t.Services.Put("lndbtc", t.Lndbtc) + t.Services.Put("lndltc", t.Lndltc) + t.Services.Put("connext", t.Connext) + t.Services.Put("opendexd", t.Opendexd) + t.Services.Put("arby", t.Arby) + t.Services.Put("boltz", t.Boltz) + + return nil +} + +func getHomeDir() (string, error) { + homeDir, err := homedir.Dir() + if err != nil { + return "", fmt.Errorf("get home directory: %s", err) + } + switch runtime.GOOS { + case "linux": + return filepath.Join(homeDir, ".opendex-docker"), nil + case "darwin": + return filepath.Join(homeDir, "Library", "Application Support", "OpendexDocker"), nil + case "windows": + return filepath.Join(homeDir, "AppData", "Local", "OpendexDocker"), nil + default: + return "", fmt.Errorf("unsupported operating system: %s", runtime.GOOS) + } +} + +func ensureHomeDir() (string, error) { + dir, err := getHomeDir() + if err != nil { + return "", err + } + + if ! utils.FileExists(dir) { + if err := os.Mkdir(dir, 0755); err != nil { + return "", err + } + } + + return dir, nil +} + +func getNetwork() types.Network { + if value, ok := os.LookupEnv("NETWORK"); ok { + return types.Network(value) + } + return "mainnet" +} + +func getNetworkDir(homeDir string, network types.Network) string { + if value, ok := os.LookupEnv("NETWORK_DIR"); ok { + return value + } + return filepath.Join(homeDir, string(network)) +} + +func ensureNetworkDir(homeDir string, network types.Network) (string, error) { + dir := getNetworkDir(homeDir, network) + + if ! utils.FileExists(dir) { + if err := os.Mkdir(dir, 0755); err != nil { + return "", err + } + } + + return dir, nil +} + +func getBackupDir(networkDir string, dockerComposeFile string) string { + dir := getDefaultBackupDir(networkDir) + + // TODO parse backup location from 1) opendexd container 2) docker-compose file 3) config.json + f, err := os.Open(dockerComposeFile) + if err != nil { + return dir + } + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + if strings.Contains(line, "/root/backup") { + line = strings.TrimSpace(line) + // fix broken colon (before) in backup location ( + line = strings.ReplaceAll(line, "::", ":") + line = strings.TrimPrefix(line, "- ") + line = strings.TrimSuffix(line, ":/root/backup") + return line + } + } + return dir +} + +func getDefaultBackupDir(networkDir string) string { + return filepath.Join(networkDir, "backup") +} + +func getExternalIp(networkDir string) string { + // Backward compatible with lnd.env + lndEnv := filepath.Join(networkDir, "lnd.env") + f, err := os.Open(lndEnv) + if err != nil { + return "" + } + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + parts := strings.Split(line, "=") + if len(parts) == 2 { + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + if key == "EXTERNAL_IP" { + return value + } + } + } + return "" +} diff --git a/launcher/core/launcher_setup.go b/launcher/core/launcher_setup.go new file mode 100644 index 0000000..918d471 --- /dev/null +++ b/launcher/core/launcher_setup.go @@ -0,0 +1,294 @@ +package core + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "github.com/gorilla/websocket" + "github.com/opendexnetwork/opendex-docker/launcher/service/proxy" + "github.com/opendexnetwork/opendex-docker/launcher/utils" + "net/url" + "os" + "os/exec" + "os/signal" + "path/filepath" + "sync" + "time" +) + +const ( + DefaultWalletPassword = "OpenDEX!Rocks" + ServiceStuckThreshold = 100 + StatusQueryInterval = 10 * time.Second +) + +var ( + errInterrupted = errors.New("interrupted") +) + +func (t *Launcher) Setup(ctx context.Context, pull bool) error { + tty := utils.Isatty(os.Stdin) + + if tty { + fmt.Printf("🚀 Launching %s environment\n", t.Network) + ctx = context.WithValue(ctx, "interactive", true) + } else { + ctx = context.WithValue(ctx, "interactive", false) + } + t.Logger.Debugf("Setup %s (%s)", t.Network, t.NetworkDir) + + // Checking Docker + err := utils.Run(ctx, exec.Command("docker", "info")) + if err != nil { + return fmt.Errorf("docker is not ready: %w", err) + } + + // Changing working directory + wd, err := os.Getwd() + if err != nil { + return fmt.Errorf("get working directory: %w", err) + } + defer os.Chdir(wd) + + if err := os.Chdir(t.NetworkDir); err != nil { + return fmt.Errorf("change directory: %w", err) + } + + logfile := filepath.Join(t.NetworkDir, "logs", fmt.Sprintf("%s.log", t.Network)) + f, err := os.Create(logfile) + if err != nil { + return fmt.Errorf("create %s: %w", logfile, err) + } + defer f.Close() + + _, err = f.WriteString("Waiting for XUD dependencies to be ready\n") + if err != nil { + return fmt.Errorf("write log: %w", err) + } + + if err := t.Gen(ctx); err != nil { + return fmt.Errorf("generate files: %w", err) + } + + if tty { + fmt.Printf("🌍 Checking for updates ...\n") + } + + if pull { + if err := t.Pull(ctx); err != nil { + return fmt.Errorf("pull: %w", err) + } + } + + if tty { + fmt.Printf("🏃 Warming up ...\n") + } + + if err := t.Start(ctx, "proxy"); err != nil { + return err + } + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() + t.Logger.Debugf("Attaching to proxy") + if err := t.attachToProxy(ctx); err != nil { + t.Logger.Errorf("Attach to proxy: %s", err) + } + }() + + //if t.Opendexd.HasWallet() + + if err := t.Start(ctx, "opendexd"); err != nil { + return err + } + + //if err := t.Start(ctx, "arby"); err != nil { + // return err + //} + + if err := t.Start(ctx, "boltz"); err != nil { + return err + } + + _, err = f.WriteString("Start shell\n") + if err != nil { + return fmt.Errorf("write log: %w", err) + } + _ = f.Close() + + if tty { + // enter into console + return t.StartConsole(ctx) + } else { + fmt.Println("Attached to proxy. Press Ctrl-C to detach from it.") + wg.Wait() + } + + return nil +} + +//func (t *Launcher) upProxy(ctx context.Context) error { +// return t.upService(ctx, "proxy", func(status string) bool { +// if status == "Ready" { +// return true +// } +// return false +// }) +//} + + + +//func (t *Launcher) getProxyApiUrl() (string, error) { +// s, err := t.GetService("proxy") +// if err != nil { +// return "", fmt.Errorf("get service: %w", err) +// } +// rpc, err := s.GetRpcParams() +// if err != nil { +// return "", fmt.Errorf("get rpc params: %w", err) +// } +// return rpc.(proxy.RpcParams).ToUri(), nil +//} + +//type ApiError struct { +// Message string `json:"message"` +//} + +//func (t *Launcher) createWallets(ctx context.Context) error { +// //interactive := ctx.Value("interactive").(bool) +// s, err := t.GetService("opendexd") +// if err != nil { +// return err +// } +// return s.(*service.Opendexd).CreateWallet(ctx, DefaultWalletPassword) +//} + +//func (t *Launcher) unlockWallets(ctx context.Context, password string) error { +// //interactive := ctx.Value("interactive").(bool) +// s, err := t.GetService("opendexd") +// if err != nil { +// return err +// } +// return s.(*service.Opendexd).UnlockWallet(ctx, DefaultWalletPassword) +//} + + + +//func (t *Launcher) upOpendexd(ctx context.Context) error { +// // Do you want to create a new opendexd environment or restore an existing one? +// // 1) Create New +// // 2) Restore Existing +// // Please choose: 1 +// +// // Please enter a path to a destination where to store a backup of your environment. It includes everything, but NOT your on-chain wallet balance which is secured by your opendexd SEED. The path should be an external drive, like a USB or network drive, which is permanently available on your device since backups are written constantly. +// // +// // Enter path to backup location: /media/USB/ +// // Checking... OK. +// return t.upService(ctx, "opendexd", func(status string) bool { +// if status == "Ready" { +// return true +// } +// if status == "Waiting for channels" { +// return true +// } +// if strings.HasPrefix(status, "Wallet missing") { +// if err := t.createWallets(ctx); err != nil { +// t.Logger.Errorf("Failed to create: %s", err) +// return false +// } +// _, err := os.Create(t.PasswordUnsetMarker) +// if err != nil { +// t.Logger.Errorf("Failed to create .default-password: %s", err) +// return false +// } +// return false +// } else if strings.HasPrefix(status, "Wallet locked") { +// if t.UsingDefaultPassword() { +// if err := t.unlockWallets(ctx, DefaultWalletPassword); err != nil { +// t.Logger.Errorf("Failed to unlock: %s", err) +// if strings.Contains(err.Error(), "password is incorrect") { +// _ = os.Remove(t.PasswordUnsetMarker) +// return true // don't try to unlock with wrong password infinitely +// } +// return false +// } +// return false +// } +// return true +// } +// return false +// }) +//} +// +//func (t *Launcher) upArby(ctx context.Context) error { +// return t.upService(ctx, "arby", func(status string) bool { +// return true +// }) +//} +// +//func (t *Launcher) upBoltz(ctx context.Context) error { +// return t.upService(ctx, "boltz", func(status string) bool { +// return true +// }) +//} + +func (t *Launcher) attachToProxy(ctx context.Context) error { + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, os.Interrupt) + + s := t.GetService("proxy") + + params, err := s.GetRpcParams() + if err != nil { + return err + } + + port := params.(proxy.RpcParams).Port + + u := url.URL{Scheme: "wss", Host: fmt.Sprintf("127.0.0.1:%d", port), Path: "/launcher"} + t.Logger.Debugf("Connecting to %s", u.String()) + + config := tls.Config{RootCAs: nil, InsecureSkipVerify: true} + + dialer := websocket.DefaultDialer + dialer.TLSClientConfig = &config + c, _, err := dialer.Dial(u.String(), nil) + if err != nil { + return err + } + defer c.Close() + + t.Logger.Debugf("Attached to proxy") + + done := make(chan struct{}) + + go func() { + defer close(done) + t.serve(ctx, c) + }() + + for { + select { + case <-done: + return nil + case <-interrupt: + t.Logger.Debugf("Interrupted") + err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) + if err != nil { + t.Logger.Errorf("write close: %s", err) + return nil + } + select { + case <-done: + case <-time.After(time.Second): + } + return nil + } + } +} + + diff --git a/launcher/core/service_map.go b/launcher/core/service_map.go new file mode 100644 index 0000000..d4ef62e --- /dev/null +++ b/launcher/core/service_map.go @@ -0,0 +1,28 @@ +package core + +import ( + "github.com/opendexnetwork/opendex-docker/launcher/core/collections" + "github.com/opendexnetwork/opendex-docker/launcher/types" +) + +type ServiceMap struct { + m *collections.OrderedMap +} + +func NewServiceMap() *ServiceMap { + return &ServiceMap{ + m: collections.NewOrderedMap(), + } +} + +func (t *ServiceMap) Get(name string) types.Service { + return t.m.Get(name).(types.Service) +} + +func (t *ServiceMap) Put(name string, service types.Service) { + t.m.Put(name, service) +} + +func (t *ServiceMap) Keys() []string { + return t.m.Keys() +} diff --git a/launcher/core/setup.go b/launcher/core/setup.go deleted file mode 100644 index e54db48..0000000 --- a/launcher/core/setup.go +++ /dev/null @@ -1,455 +0,0 @@ -package core - -import ( - "bytes" - "context" - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "github.com/gorilla/websocket" - "github.com/opendexnetwork/opendex-docker/launcher/service/proxy" - "github.com/opendexnetwork/opendex-docker/launcher/utils" - "golang.org/x/sync/errgroup" - "net/http" - "net/url" - "os" - "os/exec" - "os/signal" - "path/filepath" - "strings" - "sync" - "time" -) - -const ( - DefaultWalletPassword = "OpenDEX!Rocks" - ServiceStuckThreshold = 100 - StatusQueryInterval = 10 * time.Second -) - -var ( - errInterrupted = errors.New("interrupted") -) - -func (t *Launcher) Pull(ctx context.Context) error { - t.Logger.Debugf("Pulling images") - cmd := exec.Command("docker-compose", "pull") - return utils.Run(ctx, cmd) -} - -func (t *Launcher) Setup(ctx context.Context, pull bool) error { - t.Logger.Debugf("Setup %s (%s)", t.Network, t.NetworkDir) - - // Checking Docker - err := utils.Run(ctx, exec.Command("docker", "info")) - if err != nil { - return fmt.Errorf("docker is not ready: %w", err) - } - - wd, err := os.Getwd() - if err != nil { - return fmt.Errorf("get working directory: %w", err) - } - defer os.Chdir(wd) - - if err := os.Chdir(t.NetworkDir); err != nil { - return fmt.Errorf("change directory: %w", err) - } - - logfile := filepath.Join(t.NetworkDir, "logs", fmt.Sprintf("%s.log", t.Network)) - f, err := os.Create(logfile) - if err != nil { - return fmt.Errorf("create %s: %w", logfile, err) - } - defer f.Close() - - _, err = f.WriteString("Waiting for XUD dependencies to be ready\n") - if err != nil { - return fmt.Errorf("write log: %w", err) - } - - if err := t.Gen(ctx); err != nil { - return fmt.Errorf("generate files: %w", err) - } - - if pull { - if err := t.Pull(ctx); err != nil { - return fmt.Errorf("pull: %w", err) - } - } - - t.Logger.Debugf("Bring up proxy") - if err := t.upProxy(ctx); err != nil { - return fmt.Errorf("up proxy: %w", err) - } - - var wg sync.WaitGroup - wg.Add(1) - - go func() { - defer wg.Done() - t.Logger.Debugf("Attaching to proxy") - if err := t.attachToProxy(ctx); err != nil { - t.Logger.Errorf("Attach to proxy: %s", err) - } - }() - - t.Logger.Debugf("Bring up layer 2 services") - if err := t.upLayer2(ctx); err != nil { - return fmt.Errorf("up layer2: %w", err) - } - - t.Logger.Debugf("Bring up opendexd") - if err := t.upOpendexd(ctx); err != nil { - return fmt.Errorf("up opendexd: %w", err) - } - - t.Logger.Debugf("Bring up additional services") - if err := t.upArby(ctx); err != nil { - return fmt.Errorf("up arby: %w", err) - } - if err := t.upBoltz(ctx); err != nil { - return fmt.Errorf("up boltz: %w", err) - } - - _, err = f.WriteString("Start shell\n") - if err != nil { - return fmt.Errorf("write log: %w", err) - } - _ = f.Close() - - fmt.Println("Attached to proxy. Press Ctrl-C to detach from it.") - wg.Wait() - - return nil -} - -func (t *Launcher) upProxy(ctx context.Context) error { - return t.upService(ctx, "proxy", func(status string) bool { - if status == "Ready" { - return true - } - return false - }) -} - -func (t *Launcher) upLnd(ctx context.Context, name string) error { - return t.upService(ctx, name, func(status string) bool { - if status == "Ready" { - return true - } - if strings.HasPrefix(status, "Syncing 100.00%") { - return true - } - if strings.HasPrefix(status, "Syncing 99.99%") { - return true - } - if strings.HasPrefix(status, "Wallet locked") { - return true - } - return false - }) -} - -func (t *Launcher) upConnext(ctx context.Context) error { - return t.upService(ctx, "connext", func(status string) bool { - if status == "Ready" { - return true - } - return false - }) -} - -func (t *Launcher) getProxyApiUrl() (string, error) { - s, err := t.GetService("proxy") - if err != nil { - return "", fmt.Errorf("get service: %w", err) - } - rpc, err := s.GetRpcParams() - if err != nil { - return "", fmt.Errorf("get rpc params: %w", err) - } - return rpc.(proxy.RpcParams).ToUri(), nil -} - -type ApiError struct { - Message string `json:"message"` -} - -func (t *Launcher) createWallets(ctx context.Context, password string) error { - apiUrl, err := t.getProxyApiUrl() - if err != nil { - return fmt.Errorf("get proxy api url: %w", err) - } - createUrl := fmt.Sprintf("%s/api/v1/opendexd/create", apiUrl) - payload := map[string]interface{}{ - "password": password, - } - body, err := json.Marshal(payload) - if err != nil { - return fmt.Errorf("encode payload: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "POST", createUrl, bytes.NewBuffer(body)) - if err != nil { - return fmt.Errorf("create request: %w", err) - } - req.Header.Set("Content-Type", "application/json") - resp, err := t.client.Do(req) - if err != nil { - return fmt.Errorf("do reqeust: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var body ApiError - err := json.NewDecoder(resp.Body).Decode(&body) - if err != nil { - return fmt.Errorf("[http %d] decode error: %w", resp.StatusCode, err) - } - return fmt.Errorf("[http %d] %s", resp.StatusCode, body.Message) - } - - return nil -} - -func (t *Launcher) unlockWallets(ctx context.Context, password string) error { - apiUrl, err := t.getProxyApiUrl() - if err != nil { - return fmt.Errorf("get proxy api url: %w", err) - } - createUrl := fmt.Sprintf("%s/api/v1/opendexd/unlock", apiUrl) - payload := map[string]interface{}{ - "password": password, - } - body, err := json.Marshal(payload) - if err != nil { - return fmt.Errorf("encode payload: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "POST", createUrl, bytes.NewBuffer(body)) - if err != nil { - return fmt.Errorf("create request: %w", err) - } - req.Header.Set("Content-Type", "application/json") - resp, err := t.client.Do(req) - if err != nil { - return fmt.Errorf("do reqeust: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var body ApiError - err := json.NewDecoder(resp.Body).Decode(&body) - if err != nil { - return fmt.Errorf("[http %d] decode error: %w", resp.StatusCode, err) - } - return fmt.Errorf("[http %d] %s", resp.StatusCode, body.Message) - } - - return nil -} - -func (t *Launcher) upService(ctx context.Context, name string, checkFunc func(string) bool) error { - s, err := t.GetService(name) - if err != nil { - return fmt.Errorf("get service: %w", err) - } - if s.IsDisabled() { - // if the service is running then we will stop it and remove the container - if s.IsRunning() { - if err := s.Stop(ctx); err != nil { - return err - } - if err := s.Remove(ctx); err != nil { - return err - } - } - return nil - } - if err := s.Up(ctx); err != nil { - return fmt.Errorf("up: %s", err) - } - prevStatus := "" - count := 0 - for { - if count >= ServiceStuckThreshold { - if ctx.Value("rescue").(bool) { - if s.Rescue(ctx) { - count = 0 - } else { - break - } - } else { - break - } - } - - status, err := s.GetStatus(ctx) - if err != nil { - t.Logger.Errorf("Failed to get status: %s", err) - if prevStatus == "" { - count++ - } else { - count = 0 - } - prevStatus = "" - } else { - t.Logger.Debugf("[status] %s: %s", name, status) - if prevStatus == status { - count++ - } else { - count = 0 - } - prevStatus = status - - if status == "Container missing" || status == "Container exited" { - return fmt.Errorf("%s: %s", name, status) - } - - if checkFunc(status) { - break - } - } - select { - case <-ctx.Done(): // context cancelled - return errInterrupted - case <-time.After(StatusQueryInterval): // retry - } - } - - if count >= ServiceStuckThreshold { - return fmt.Errorf("%s stuck", name) - } - - return nil -} - -func (t *Launcher) upOpendexd(ctx context.Context) error { - return t.upService(ctx, "opendexd", func(status string) bool { - if status == "Ready" { - return true - } - if status == "Waiting for channels" { - return true - } - if strings.HasPrefix(status, "Wallet missing") { - if err := t.createWallets(ctx, DefaultWalletPassword); err != nil { - t.Logger.Errorf("Failed to create: %s", err) - return false - } - _, err := os.Create(t.PasswordUnsetMarker) - if err != nil { - t.Logger.Errorf("Failed to create .default-password: %s", err) - return false - } - return false - } - if strings.HasPrefix(status, "Wallet locked") { - if t.UsingDefaultPassword() { - if err := t.unlockWallets(ctx, DefaultWalletPassword); err != nil { - t.Logger.Errorf("Failed to unlock: %s", err) - if strings.Contains(err.Error(), "password is incorrect") { - _ = os.Remove(t.PasswordUnsetMarker) - return true // don't try to unlock with wrong password infinitely - } - return false - } - return false - } - return true - } - return false - }) -} - -func (t *Launcher) upArby(ctx context.Context) error { - return t.upService(ctx, "arby", func(status string) bool { - return true - }) -} - -func (t *Launcher) upBoltz(ctx context.Context) error { - return t.upService(ctx, "boltz", func(status string) bool { - return true - }) -} - -func (t *Launcher) attachToProxy(ctx context.Context) error { - interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, os.Interrupt) - - s, err := t.GetService("proxy") - if err != nil { - return err - } - - params, err := s.GetRpcParams() - if err != nil { - return err - } - - port := params.(proxy.RpcParams).Port - - u := url.URL{Scheme: "wss", Host: fmt.Sprintf("127.0.0.1:%d", port), Path: "/launcher"} - t.Logger.Debugf("Connecting to %s", u.String()) - - config := tls.Config{RootCAs: nil, InsecureSkipVerify: true} - - dialer := websocket.DefaultDialer - dialer.TLSClientConfig = &config - c, _, err := dialer.Dial(u.String(), nil) - if err != nil { - return err - } - defer c.Close() - - t.Logger.Debugf("Attached to proxy") - - done := make(chan struct{}) - - go func() { - defer close(done) - t.serve(ctx, c) - }() - - for { - select { - case <-done: - return nil - case <-interrupt: - t.Logger.Debugf("Interrupted") - err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) - if err != nil { - t.Logger.Errorf("write close: %s", err) - return nil - } - select { - case <-done: - case <-time.After(time.Second): - } - return nil - } - } -} - -func (t *Launcher) upLayer2(ctx context.Context) error { - g, ctx := errgroup.WithContext(ctx) - - g.Go(func() error { - return t.upLnd(ctx, "lndbtc") - }) - - g.Go(func() error { - return t.upLnd(ctx, "lndltc") - }) - - g.Go(func() error { - return t.upConnext(ctx) - }) - - if err := g.Wait(); err != nil { - return err - } - - return nil -} diff --git a/launcher/core/start.go b/launcher/core/start.go new file mode 100644 index 0000000..10ec211 --- /dev/null +++ b/launcher/core/start.go @@ -0,0 +1,8 @@ +package core + +import "context" + +func (t *Launcher) Start(ctx context.Context, service string) error { + s := t.GetService(service) + return s.Start(ctx) +} diff --git a/launcher/core/status.go b/launcher/core/status.go index 00f7ad9..7d676e1 100644 --- a/launcher/core/status.go +++ b/launcher/core/status.go @@ -2,10 +2,7 @@ package core import "context" -func (t *Launcher) Status(ctx context.Context, name string) (string, error) { - s, err := t.GetService(name) - if err != nil { - return "", err - } +func (t *Launcher) Status(ctx context.Context, service string) (string, error) { + s := t.GetService(service) return s.GetStatus(ctx) } diff --git a/launcher/core/stop.go b/launcher/core/stop.go new file mode 100644 index 0000000..9a8bc95 --- /dev/null +++ b/launcher/core/stop.go @@ -0,0 +1 @@ +package core diff --git a/launcher/core/types.go b/launcher/core/types.go new file mode 100644 index 0000000..0272ded --- /dev/null +++ b/launcher/core/types.go @@ -0,0 +1,32 @@ +package core + +import ( + "github.com/opendexnetwork/opendex-docker/launcher/service/arby" + "github.com/opendexnetwork/opendex-docker/launcher/service/bitcoind" + "github.com/opendexnetwork/opendex-docker/launcher/service/boltz" + "github.com/opendexnetwork/opendex-docker/launcher/service/connext" + "github.com/opendexnetwork/opendex-docker/launcher/service/geth" + "github.com/opendexnetwork/opendex-docker/launcher/service/litecoind" + "github.com/opendexnetwork/opendex-docker/launcher/service/lnd" + "github.com/opendexnetwork/opendex-docker/launcher/service/opendexd" + "github.com/opendexnetwork/opendex-docker/launcher/service/proxy" +) + +type Proxy = proxy.Service +var NewProxy = proxy.New +type Opendexd = opendexd.Service +var NewOpendexd = opendexd.New +type Lnd = lnd.Service +var NewLnd = lnd.New +type Connext = connext.Service +var NewConnext = connext.New +type Arby = arby.Service +var NewArby = arby.New +type Boltz = boltz.Service +var NewBoltz = boltz.New +type Bitcoind = bitcoind.Service +var NewBitcoind = bitcoind.New +type Litecoind = litecoind.Service +var NewLitecoind = litecoind.New +type Geth = geth.Service +var NewGeth = geth.New diff --git a/launcher/core/update.go b/launcher/core/update.go index 88a448c..3aa8cea 100644 --- a/launcher/core/update.go +++ b/launcher/core/update.go @@ -3,8 +3,16 @@ package core import ( "context" "fmt" + "github.com/opendexnetwork/opendex-docker/launcher/utils" + "os/exec" ) +func (t *Launcher) Pull(ctx context.Context) error { + t.Logger.Debugf("Pulling images") + cmd := exec.Command("docker-compose", "pull") + return utils.Run(ctx, cmd) +} + func (t *Launcher) Update(ctx context.Context) { // TODO update fmt.Println("to be implemented") diff --git a/launcher/go.mod b/launcher/go.mod index d9222c8..3e518f5 100644 --- a/launcher/go.mod +++ b/launcher/go.mod @@ -17,6 +17,7 @@ require ( github.com/iancoleman/strcase v0.1.3 github.com/magefile/mage v1.11.0 // indirect github.com/magiconair/properties v1.8.4 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect @@ -32,7 +33,7 @@ require ( github.com/spf13/viper v1.7.1 golang.org/x/net v0.0.0-20210222171744-9060382bd457 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43 // indirect + golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 // indirect golang.org/x/text v0.3.5 // indirect google.golang.org/genproto v0.0.0-20210222212404-3e1e516060db // indirect google.golang.org/grpc v1.35.0 // indirect diff --git a/launcher/go.sum b/launcher/go.sum index a7ac213..d9354b7 100644 --- a/launcher/go.sum +++ b/launcher/go.sum @@ -4,48 +4,78 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR 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 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0 h1:9x7Bx0A9R5/M9jibeJeZWqjeVEIxYW9fZYqB9a70/bY= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/containerd/containerd v1.4.3 h1:ijQT13JedHSHrQGWFcGEwzcNKrAGIiZ+jSD5QQG07SY= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= @@ -60,18 +90,26 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 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/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 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= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= @@ -80,10 +118,13 @@ github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +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-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/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 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= 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= @@ -99,6 +140,7 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= 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/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -106,12 +148,17 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw 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/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 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/pprof v0.0.0-20190515194954-54271f7e092f h1:Jnx61latede7zDD3DiiP4gmNz33uK0U5HDUaF0a/HVQ= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -119,29 +166,49 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 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/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U= github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= @@ -149,21 +216,31 @@ github.com/iancoleman/strcase v0.1.3 h1:dJBk1m2/qjL1twPLf68JND55vvivMupZ4wIzE8CT github.com/iancoleman/strcase v0.1.3/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= 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= @@ -175,16 +252,26 @@ github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzR github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= @@ -195,16 +282,21 @@ github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxd github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -215,25 +307,38 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +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.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -245,7 +350,9 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= @@ -270,6 +377,7 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -278,15 +386,23 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -295,13 +411,16 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-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-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/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= @@ -309,12 +428,15 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk 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-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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= @@ -339,6 +461,7 @@ golang.org/x/net v0.0.0-20210222171744-9060382bd457 h1:hMm9lBjyNLe/c9C6bElQxp4ws golang.org/x/net v0.0.0-20210222171744-9060382bd457/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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/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= @@ -367,6 +490,7 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -374,6 +498,9 @@ golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 h1:joucsQqXmyBVxViHCPFjG3hx8 golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43 h1:SgQ6LNaYJU0JIuEHv9+s6EbhSCwYeAf5Yvj6lpYlqAE= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 h1:pDMpM2zh2MT0kHy037cKlSby2nEhD50SYqwQk76Nm40= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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= @@ -409,20 +536,24 @@ golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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.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.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 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= @@ -461,15 +592,18 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -486,5 +620,7 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh 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 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/launcher/service/arby/service.go b/launcher/service/arby/service.go index 5528b55..fbf50ed 100644 --- a/launcher/service/arby/service.go +++ b/launcher/service/arby/service.go @@ -17,22 +17,16 @@ type Service struct { *Base } -func New(ctx types.Context, name string) (*Service, error) { - s, err := base.New(ctx, name) - if err != nil { - return nil, err - } +func New(ctx types.ServiceContext, name string) *Service { + s := base.New(ctx, name) return &Service{ Base: s, - }, nil + } } func (t *Service) getOpendexd() (*Opendexd, error) { - s, err := t.Context.GetService("opendexd") - if err != nil { - return nil, err - } + s := t.Context.GetService("opendexd") sOpendexd, ok := s.(*Opendexd) if !ok { return nil, errors.New("cannot convert to *ond.Service") diff --git a/launcher/service/base/service.go b/launcher/service/base/service.go index 30e887b..e19be68 100644 --- a/launcher/service/base/service.go +++ b/launcher/service/base/service.go @@ -21,7 +21,7 @@ import ( type Service struct { Name string - Context types.Context + Context types.ServiceContext Hostname string Image string @@ -37,10 +37,10 @@ type Service struct { Logger *logrus.Entry } -func New(ctx types.Context, name string) (*Service, error) { +func New(ctx types.ServiceContext, name string) *Service { client, err := docker.NewClientWithOpts(docker.FromEnv) if err != nil { - return nil, fmt.Errorf("failed to create Docker client: %s", err) + panic(err) } return &Service{ @@ -57,7 +57,7 @@ func New(ctx types.Context, name string) (*Service, error) { Volumes: []string{}, Disabled: false, DataDir: "", - }, nil + } } func (t *Service) GetName() string { @@ -81,40 +81,68 @@ func (t *Service) GetName() string { // Name Command State Ports // ------------------------------ // +// Example 4. +// Name Command State Ports +// -------------------------------------------------------------------------------- +// testnet_opendexd_1 /entrypoint.sh Up 0.0.0.0:49153->18885/tcp, +// 18887/tcp, 28887/tcp, 8887/tcp func (t *Service) GetContainerName(ctx context.Context) string { - c := exec.Command("docker-compose", "ps", t.Name) + c := exec.Command("docker-compose", "ps", "-a", t.Name) output, _ := utils.Output(ctx, c) output = strings.TrimSpace(output) lines := strings.Split(output, "\n") n := len(lines) + defaultName := fmt.Sprintf("%s_%s_1", t.Context.GetNetwork(), t.Name) - if n == 2 { - // example 3 - return "" - } else if n == 3 { - // example 1 - return strings.Split(lines[2], " ")[0] - } else if n == 1 { - // example 2 - // TODO parse error - return "" + if n == 0 { + return defaultName + } else if strings.HasPrefix("ERROR:", lines[0]) { + return defaultName } else { - return "" + var j int + for i, line := range lines { + if strings.ReplaceAll(line, "-", "") == "" { + j = i + break + } + } + if j >= n { + return defaultName + } else { + if j + 1 >= n { + return defaultName + } + return strings.Split(lines[j+1], " ")[0] + } } } func (t *Service) GetStatus(ctx context.Context) (string, error) { c, err := t.getContainer(ctx) if err != nil { + if strings.Contains(err.Error(), "No such container") { + return "Container missing", nil + } return "", err } return fmt.Sprintf("Container %s", c.State.Status), nil } +func (t *Service) Create(ctx context.Context) error { + c := exec.Command("docker-compose", "up", "-d", "--no-start", t.Name) + return utils.Run(ctx, c) +} + +func (t *Service) Up(ctx context.Context) error { + c := exec.Command("docker-compose", "up", "-d", t.Name) + return utils.Run(ctx, c) +} + func (t *Service) Start(ctx context.Context) error { - c := exec.Command("docker-compose", "start", t.Name) + //c := exec.Command("docker-compose", "start", t.Name) + c := exec.Command("docker-compose", "up", "-d", t.Name) return utils.Run(ctx, c) } @@ -128,15 +156,7 @@ func (t *Service) Restart(ctx context.Context) error { return utils.Run(ctx, c) } -func (t *Service) Create(ctx context.Context) error { - c := exec.Command("docker-compose", "up", "-d", "--no-start", t.Name) - return utils.Run(ctx, c) -} -func (t *Service) Up(ctx context.Context) error { - c := exec.Command("docker-compose", "up", "-d", t.Name) - return utils.Run(ctx, c) -} func (t *Service) demuxLogsReader(reader io.Reader) io.Reader { r, w := io.Pipe() diff --git a/launcher/service/bitcoind/service.go b/launcher/service/bitcoind/service.go index 2788a61..d0a53fd 100644 --- a/launcher/service/bitcoind/service.go +++ b/launcher/service/bitcoind/service.go @@ -2,7 +2,6 @@ package bitcoind import ( "fmt" - "github.com/opendexnetwork/opendex-docker/launcher/service" "github.com/opendexnetwork/opendex-docker/launcher/service/base" "github.com/opendexnetwork/opendex-docker/launcher/types" ) @@ -26,21 +25,14 @@ type RpcParams struct { Zmqpubrawtx string `json:"zmqpubrawtx"` } -func New(ctx types.Context, name string) (*Service, error) { - if ctx.GetNetwork() == types.Simnet { - return nil, service.ErrForbiddenService - } - - s, err := base.New(ctx, name) - if err != nil { - return nil, err - } +func New(ctx types.ServiceContext, name string) *Service { + s := base.New(ctx, name) return &Service{ Base: s, ContainerDataDir: "/root/.bitcoind", RpcParams: RpcParams{}, - }, nil + } } func (t *Service) Apply(cfg interface{}) error { diff --git a/launcher/service/boltz/service.go b/launcher/service/boltz/service.go index b9dedd4..305d520 100644 --- a/launcher/service/boltz/service.go +++ b/launcher/service/boltz/service.go @@ -3,7 +3,6 @@ package boltz import ( "context" "fmt" - "github.com/opendexnetwork/opendex-docker/launcher/service" "github.com/opendexnetwork/opendex-docker/launcher/service/base" "github.com/opendexnetwork/opendex-docker/launcher/types" ) @@ -14,19 +13,12 @@ type Service struct { *Base } -func New(ctx types.Context, name string) (*Service, error) { - if ctx.GetNetwork() == types.Simnet { - return nil, service.ErrForbiddenService - } - - s, err := base.New(ctx, name) - if err != nil { - return nil, err - } +func New(ctx types.ServiceContext, name string) *Service { + s := base.New(ctx, name) return &Service{ Base: s, - }, nil + } } type Info struct { @@ -82,15 +74,8 @@ func (t *Service) Apply(cfg interface{}) error { return err } var lndbtc, lndltc types.Service - var err error - lndbtc, err = t.Context.GetService("lndbtc") - if err != nil { - return err - } - lndltc, err = t.Context.GetService("lndltc") - if err != nil { - return err - } + lndbtc = t.Context.GetService("lndbtc") + lndltc = t.Context.GetService("lndltc") t.Volumes = append(t.Volumes, fmt.Sprintf("%s:/root/.boltz", t.DataDir), diff --git a/launcher/service/connext/service.go b/launcher/service/connext/service.go index 670689c..cf6550d 100644 --- a/launcher/service/connext/service.go +++ b/launcher/service/connext/service.go @@ -23,15 +23,12 @@ type Service struct { *Base } -func New(ctx types.Context, name string) (*Service, error) { - s, err := base.New(ctx, name) - if err != nil { - return nil, err - } +func New(ctx types.ServiceContext, name string) *Service { + s := base.New(ctx, name) return &Service{ Base: s, - }, nil + } } func (t *Service) IsHealthy(ctx context.Context) bool { @@ -128,10 +125,7 @@ func (t *Service) Apply(cfg interface{}) error { } func (t *Service) getEthProvider() (string, error) { - s, err := t.Context.GetService("geth") - if err != nil { - return "", err - } + s := t.Context.GetService("geth") params, err := s.GetRpcParams() if err != nil { diff --git a/launcher/service/geth/service.go b/launcher/service/geth/service.go index bbb20d3..cbae1c9 100644 --- a/launcher/service/geth/service.go +++ b/launcher/service/geth/service.go @@ -39,15 +39,12 @@ type Service struct { RpcParams RpcParams } -func New(ctx types.Context, name string) (*Service, error) { - s, err := base.New(ctx, name) - if err != nil { - return nil, err - } +func New(ctx types.ServiceContext, name string) *Service { + s := base.New(ctx, name) return &Service{ Base: s, - }, nil + } } func (t *Service) Apply(cfg interface{}) error { diff --git a/launcher/service/litecoind/service.go b/launcher/service/litecoind/service.go index 4cf3619..e167688 100644 --- a/launcher/service/litecoind/service.go +++ b/launcher/service/litecoind/service.go @@ -11,16 +11,13 @@ type Service struct { *Base } -func New(ctx types.Context, name string) (*Service, error) { - s, err := bitcoind.New(ctx, name) - if err != nil { - return nil, err - } +func New(ctx types.ServiceContext, name string) *Service { + s := bitcoind.New(ctx, name) s.ContainerDataDir = "/root/.litecoind" return &Service{ Base: s, - }, nil + } } func (t *Service) Apply(cfg interface{}) error { diff --git a/launcher/service/lnd/service.go b/launcher/service/lnd/service.go index 63d3fb4..1e0dcab 100644 --- a/launcher/service/lnd/service.go +++ b/launcher/service/lnd/service.go @@ -35,16 +35,13 @@ type Service struct { Chain Chain } -func New(ctx types.Context, name string, chain Chain) (*Service, error) { - s, err := base.New(ctx, name) - if err != nil { - return nil, err - } +func New(ctx types.ServiceContext, name string, chain Chain) *Service { + s := base.New(ctx, name) return &Service{ Base: s, Chain: chain, - }, nil + } } type Info struct { @@ -179,18 +176,12 @@ func (t *Service) UseNeutrino() bool { } switch t.Chain { case Bitcoin: - s, err := t.Context.GetService("bitcoind") - if err != nil { - panic(err) - } + s := t.Context.GetService("bitcoind") if s.GetMode() == "neutrino" || s.GetMode() == "light" { return true } case Litecoin: - s, err := t.Context.GetService("litecoind") - if err != nil { - panic(err) - } + s := t.Context.GetService("litecoind") if s.GetMode() == "neutrino" || s.GetMode() == "light" { return true } @@ -309,10 +300,7 @@ func (t *Service) Apply(cfg interface{}) error { } else { switch t.Chain { case Bitcoin: - s, err := t.Context.GetService("bitcoind") - if err != nil { - return err - } + s := t.Context.GetService("bitcoind") mode := s.(*bitcoind.Service).Mode if mode == bitcoind.External { params, err := s.GetRpcParams() @@ -330,10 +318,7 @@ func (t *Service) Apply(cfg interface{}) error { t.Environment["NEUTRINO"] = "True" } case Litecoin: - s, err := t.Context.GetService("litecoind") - if err != nil { - return err - } + s := t.Context.GetService("litecoind") mode := s.(*litecoind.Service).Mode if mode == bitcoind.External { params, err := s.GetRpcParams() diff --git a/launcher/service/opendexd/ops.go b/launcher/service/opendexd/ops.go new file mode 100644 index 0000000..d7c1139 --- /dev/null +++ b/launcher/service/opendexd/ops.go @@ -0,0 +1,123 @@ +package opendexd + +import ( + "context" + "fmt" + "github.com/opendexnetwork/opendex-docker/launcher/service/proxy" + "github.com/opendexnetwork/opendex-docker/launcher/utils" + "os" + "os/exec" + "path/filepath" + "strings" +) + +const ( + DefaultWalletPassword = "OpenDEX!Rocks" +) + +func (t *Service) HasWallet() bool { + nodekey := filepath.Join(t.DataDir, "nodekey.dat") + return utils.FileExists(nodekey) +} + +func (t *Service) IsWalletLocked() bool { + status, err := t.GetStatus(context.Background()) + if err != nil { + return false + } + return strings.HasPrefix(status, "Wallet locked") +} + +func (t *Service) getProxy() *proxy.Service { + s := t.Context.GetService("proxy") + return s.(*proxy.Service) +} + +func (t *Service) CreateWallet(ctx context.Context, password string) error { + if password == "" { + c := exec.Command("docker-compose", "exec", "opendexd", "opendex-cli", "create") + c.Stdin = os.Stdin + c.Stdout = os.Stdout + c.Stderr = os.Stderr + return c.Run() + } else { + proxy := t.getProxy() + return proxy.ApiClient.V1OpendexdCreate(ctx, password) + } +} + +func (t *Service) RestoreWallet(ctx context.Context) error { + return nil +} + +func (t *Service) UnlockWallet(ctx context.Context, password string) error { + if password == "" { + c := exec.Command("docker-compose", "exec", "opendexd", "opendex-cli", "unlock") + c.Stdin = os.Stdin + c.Stdout = os.Stdout + c.Stderr = os.Stderr + return c.Run() + } else { + proxy := t.getProxy() + return proxy.ApiClient.V1OpendexdUnlock(ctx, password) + } +} + +func (t *Service) CheckBackup(ctx context.Context) error { + return nil +} + +func (t *Service) ChangeBackupLocation(ctx context.Context) { + +} + +func (t *Service) SetupWallet(ctx context.Context) error { + tty := utils.Isatty(os.Stdin) + + if t.HasWallet() { + if t.IsWalletLocked() { + if tty { + t.UnlockWallet(ctx, "") + } else { + if err := t.UnlockWallet(ctx, DefaultWalletPassword); err != nil { + return err + } + } + } + } else { + if tty { + for { + fmt.Println("Do you want to create a new opendexd environment or restore an existing one?") + fmt.Println("1) Create New") + fmt.Println("2) Restore Existing") + fmt.Print("Please choose: ") + var choice string + if _, err := fmt.Scanf("%s", &choice); err != nil { + return err + } + choice = strings.TrimSpace(choice) + switch choice { + case "1": + if err := t.CreateWallet(ctx, ""); err != nil { + return err + } + break + case "2": + if err := t.RestoreWallet(ctx); err != nil { + return err + } + break + } + } + } else { + if err := t.CreateWallet(ctx, DefaultWalletPassword); err != nil { + return err + } + } + } + return nil +} + +func (t *Service) SetupBackup(ctx context.Context) error { + return nil +} diff --git a/launcher/service/opendexd/service.go b/launcher/service/opendexd/service.go index 0202512..42b85ad 100644 --- a/launcher/service/opendexd/service.go +++ b/launcher/service/opendexd/service.go @@ -8,9 +8,12 @@ import ( "github.com/opendexnetwork/opendex-docker/launcher/service" "github.com/opendexnetwork/opendex-docker/launcher/service/base" "github.com/opendexnetwork/opendex-docker/launcher/types" + "github.com/opendexnetwork/opendex-docker/launcher/utils" + "golang.org/x/sync/errgroup" "os" "path/filepath" "strings" + "time" ) type Base = base.Service @@ -20,16 +23,13 @@ type Service struct { RpcParams RpcParams } -func New(ctx types.Context, name string) (*Service, error) { - s, err := base.New(ctx, name) - if err != nil { - return nil, err - } +func New(ctx types.ServiceContext, name string) *Service { + s := base.New(ctx, name) return &Service{ Base: s, RpcParams: RpcParams{}, - }, nil + } } type LndInfo struct { @@ -105,6 +105,9 @@ func (t *Service) GetStatus(ctx context.Context) (string, error) { } else if strings.Contains(err.Output, "is opendexd running?") { // could not connect to opendexd at localhost:18886, is opendexd running? return "Starting...", nil + } else if strings.Contains(err.Output, "No connection established") { + // Error: 14 UNAVAILABLE: No connection established + return "Starting...", nil } } return "", fmt.Errorf("get info: %w", err) @@ -152,15 +155,9 @@ func (t *Service) Apply(cfg interface{}) error { t.Environment["PRESERVE_CONFIG"] = "false" } - lndbtc, err := t.Context.GetService("lndbtc") - if err != nil { - return err - } + lndbtc := t.Context.GetService("lndbtc") - lndltc, err := t.Context.GetService("lndltc") - if err != nil { - return err - } + lndltc := t.Context.GetService("lndltc") t.Volumes = append(t.Volumes, fmt.Sprintf("%s:/root/.opendex", t.DataDir)) t.Volumes = append(t.Volumes, fmt.Sprintf("%s:/root/.lndbtc", lndbtc.GetDataDir())) @@ -202,3 +199,221 @@ type RpcParams struct { func (t *Service) GetRpcParams() (interface{}, error) { return t.RpcParams, nil } + +type LndSyncing struct { + Name string + Status string +} + +func printSyncing(syncing chan LndSyncing) { + t := utils.SimpleTable{ + Columns: []utils.TableColumn{ + { + ID: "service", + Display: "SERVICE", + }, + { + ID: "status", + Display: "STATUS", + }, + }, + Records: []utils.TableRecord{ + { + Fields: map[string]string{ + "service": "lndbtc", + "status": "Syncing...", + }, + }, + { + Fields: map[string]string{ + "service": "lndltc", + "status": "Syncing...", + }, + }, + }, + } + + fmt.Println() + fmt.Println("Syncing light clients:") + + t.Print() + + for e := range syncing { + t.PrintUpdate(utils.TableRecord{ + Fields: map[string]string{ + "service": e.Name, + "status": e.Status, + }, + }) + } +} + +func (t *Service) upLnd(ctx context.Context, name string, syncing chan LndSyncing) error { + return t.upService(ctx, name, func(status string) bool { + syncing <- LndSyncing{ + Name: name, + Status: status, + } + + if status == "Ready" { + return true + } + if strings.HasPrefix(status, "Syncing 100.00%") { + return true + } + if strings.HasPrefix(status, "Syncing 99.99%") { + return true + } + if strings.HasPrefix(status, "Wallet locked") { + return true + } + + return false + }) +} + +func (t *Service) upConnext(ctx context.Context) error { + return t.upService(ctx, "connext", func(status string) bool { + if status == "Ready" { + return true + } + return false + }) +} + +func (t *Service) upService(ctx context.Context, name string, checkFunc func(string) bool) error { + s := t.Context.GetService(name) + if s.IsDisabled() { + // if the service is running then we will stop it and remove the container + if s.IsRunning() { + if err := s.Stop(ctx); err != nil { + return err + } + if err := s.Remove(ctx); err != nil { + return err + } + } + return nil + } + if err := s.Up(ctx); err != nil { + return fmt.Errorf("up: %s", err) + } + i := 0 + tty := utils.Isatty(os.Stdin) + for { + if i > 0 && i % 10 == 0 { + if tty { + //fmt.Printf("Still waiting for %s to be ready...\n", t.Name) + } + } + + status, err := s.GetStatus(ctx) + if err != nil { + return err + } + + t.Logger.Debugf("[status] %s: %s", name, status) + i++ + + if status == "Container missing" || status == "Container exited" { + return fmt.Errorf("%s: %s", name, status) + } + + if checkFunc(status) { + break + } + + select { + case <-ctx.Done(): // context cancelled + return errors.New("interrupted") + case <-time.After(3 * time.Second): // retry + } + } + + return nil +} + +func (t *Service) upDeps(ctx context.Context) error { + g, ctx := errgroup.WithContext(ctx) + + syncing := make(chan LndSyncing) + + g.Go(func() error { + return t.upLnd(ctx, "lndbtc", syncing) + }) + + g.Go(func() error { + return t.upLnd(ctx, "lndltc", syncing) + }) + + g.Go(func() error { + return t.upConnext(ctx) + }) + + interactive := ctx.Value("interactive").(bool) + if interactive { + go printSyncing(syncing) + } + + defer close(syncing) + + return g.Wait() +} + +func (t *Service) Start(ctx context.Context) error { + // make sure layer 2 services (lndbtc, lndltc, and connext) is ready + + if err := t.upDeps(ctx); err != nil { + return err + } + + tty := utils.Isatty(os.Stdin) + + if tty { + fmt.Println("đŸ•šī¸ Loading OpenDEX console ...") + } + + if err := t.SetupBackup(ctx); err != nil { + return err + } + + if err := t.Base.Start(ctx); err != nil { + return err + } + + i := 0 + for { + if i > 0 && i % 10 == 0 { + if tty { + fmt.Printf("Still waiting for %s to be ready...\n", t.Name) + } + } + status, err := t.GetStatus(ctx) + if err != nil { + return err + } + if status == "Container missing" || status == "Container exited" { + return fmt.Errorf("%s: %s", t.Name, status) + } else if status == "Ready" { + break + } else if status == "Waiting for channels" { + break + } else if strings.HasPrefix(status, "Wallet missing") { + break + } else if strings.HasPrefix(status, "Wallet locked") { + break + } + select { + case <-ctx.Done(): + return errors.New("interrupted") + case <-time.After(3 * time.Second): + } + i++ + } + + if err := t.SetupWallet(ctx); err != nil { + return err + } + + return nil +} diff --git a/launcher/service/proxy/api.go b/launcher/service/proxy/api.go new file mode 100644 index 0000000..6b33238 --- /dev/null +++ b/launcher/service/proxy/api.go @@ -0,0 +1,87 @@ +package proxy + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" +) + +type ApiClient struct { + AssembleUrl func(string) string + RestClient *RestClient +} + +type RestClient struct { + HttpClient *http.Client +} + +type ApiError struct { + Message string `json:"message"` +} + +func (t *RestClient) Post(ctx context.Context, url string, payload interface{}) (*http.Response, error) { + body, err := json.Marshal(payload) + if err != nil { + return nil, err + } + req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(body)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + return t.HttpClient.Do(req) +} + +func decodeError(resp *http.Response) error { + var body ApiError + err := json.NewDecoder(resp.Body).Decode(&body) + if err != nil { + return err + } + return fmt.Errorf("HTTP %d: %s", resp.StatusCode, body.Message) +} + +// V1OpendexdCreate issues a POST request to /api/v1/opendexd/create +func (t *ApiClient) V1OpendexdCreate(ctx context.Context, password string) error { + url := t.AssembleUrl("/v1/opendexd/create") + payload := map[string]interface{}{ + "password": password, + } + resp, err := t.RestClient.Post(ctx, url, payload) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return decodeError(resp) + } + + return nil +} + +// V1OpendexdRestore issues a POST request to /api/v1/opendexd/restore +func (t *ApiClient) V1OpendexdRestore() { + +} + +// V1OpendexdUnlock issues a POST request to /api/v1/opendexd/unlock +func (t *ApiClient) V1OpendexdUnlock(ctx context.Context, password string) error { + url := t.AssembleUrl("/v1/opendexd/unlock") + payload := map[string]interface{}{ + "password": password, + } + resp, err := t.RestClient.Post(ctx, url, payload) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return decodeError(resp) + } + + return nil +} diff --git a/launcher/service/proxy/service.go b/launcher/service/proxy/service.go index 4252c6a..695609c 100644 --- a/launcher/service/proxy/service.go +++ b/launcher/service/proxy/service.go @@ -6,6 +6,7 @@ import ( "github.com/opendexnetwork/opendex-docker/launcher/service/base" "github.com/opendexnetwork/opendex-docker/launcher/types" "net" + "net/http" "runtime" "time" ) @@ -15,13 +16,11 @@ type Base = base.Service type Service struct { *Base RpcParams RpcParams + ApiClient *ApiClient } -func New(ctx types.Context, name string) (*Service, error) { - s, err := base.New(ctx, name) - if err != nil { - return nil, err - } +func New(ctx types.ServiceContext, name string) *Service { + s := base.New(ctx, name) var port uint16 switch ctx.GetNetwork() { @@ -33,13 +32,24 @@ func New(ctx types.Context, name string) (*Service, error) { port = 8889 } - return &Service{ + t := &Service{ Base: s, RpcParams: RpcParams{ Type: "HTTP", Port: port, }, - }, nil + ApiClient: &ApiClient{ + RestClient: &RestClient{ + HttpClient: http.DefaultClient, + }, + }, + } + + t.ApiClient.AssembleUrl = func(endpoint string) string { + return fmt.Sprintf("%s/api%s", t.RpcParams.ToUri(), endpoint) + } + + return t } func (t *Service) checkApiPort() error { diff --git a/launcher/service/webui/service.go b/launcher/service/webui/service.go index 61f1294..ecb6964 100644 --- a/launcher/service/webui/service.go +++ b/launcher/service/webui/service.go @@ -11,15 +11,12 @@ type Service struct { *Base } -func New(ctx types.Context, name string) (*Service, error) { - s, err := base.New(ctx, name) - if err != nil { - return nil, err - } +func New(ctx types.ServiceContext, name string) *Service { + s := base.New(ctx, name) return &Service{ Base: s, - }, nil + } } func (t *Service) Apply(cfg interface{}) error { diff --git a/launcher/types/context.go b/launcher/types/context.go deleted file mode 100644 index c22b0a7..0000000 --- a/launcher/types/context.go +++ /dev/null @@ -1,10 +0,0 @@ -package types - -type Context interface { - GetNetwork() Network - GetNetworkDir() string - GetService(name string) (Service, error) - GetExternalIp() string - GetBackupDir() string - GetDataDir() string -} diff --git a/launcher/types/service.go b/launcher/types/service.go index b6cfb6c..0488b7c 100644 --- a/launcher/types/service.go +++ b/launcher/types/service.go @@ -33,3 +33,12 @@ type Service interface { Rescue(ctx context.Context) bool } + +type ServiceContext interface { + GetService(name string) Service + GetNetwork() Network + GetNetworkDir() string + GetExternalIp() string + GetBackupDir() string + GetDataDir() string +} diff --git a/launcher/utils/cli.go b/launcher/utils/cli.go new file mode 100644 index 0000000..5987197 --- /dev/null +++ b/launcher/utils/cli.go @@ -0,0 +1,142 @@ +package utils + +import ( + "fmt" + "strings" +) + +const ( + DefaultTableWidth = 63 +) + +type SimpleTable struct { + Columns []TableColumn + Records []TableRecord +} + +type TableColumn struct { + ID string + Display string + Width int +} + +type TableRecord struct { + Fields map[string]string +} + +func (t *SimpleTable) Layout() { + for i, c := range t.Columns { + max := 0 + for _, r := range t.Records { + if len(r.Fields[c.ID]) > max { + max = len(r.Fields[c.ID]) + } + } + if len(c.Display) > max { + max = len(c.Display) + } + t.Columns[i].Width = max + } + + n := len(t.Columns) + + // extend last column to fit table width + s := 0 + for i, c := range t.Columns { + if i == n - 1 { + s = s + 4 // left border, padding, right border + if DefaultTableWidth - s > c.Width { + t.Columns[i].Width = DefaultTableWidth - s + } + } + s += c.Width + 3 // 2 padding, 1 left border + } +} + +// Print the table like +// ┌─────────â”Ŧ─────────────────────────────────────────────────────┐ +// │ SERVICE │ STATUS │ +// ├─────────â”ŧ─────────────────────────────────────────────────────┤ +// │ lndbtc │ Syncing 34.24% (610000/1781443) │ +// ├─────────â”ŧ─────────────────────────────────────────────────────┤ +// │ lndltc │ Syncing 12.17% (191000/1568645) │ +// └─────────┴─────────────────────────────────────────────────────┘ +func (t *SimpleTable) Print() { + t.Layout() + + n := len(t.Columns) + + // print tale top border + // ┌─────────â”Ŧ─────────────────────────────────────────────────────┐ + for i, c := range t.Columns { + if i == 0 { + fmt.Print("┌") + } else { + fmt.Print("â”Ŧ") + } + fmt.Print(strings.Repeat("─", c.Width + 2)) + if i == n - 1 { + fmt.Print("┐\n") + } + } + + // print table head + // │ SERVICE │ STATUS │ + for i, c := range t.Columns { + fmt.Print("│ ") + fmt.Print(c.Display) + fmt.Print(strings.Repeat(" ", c.Width - len(c.Display) + 1)) + if i == n - 1 { + fmt.Print("│\n") + } + } + + // print table records + // ├─────────â”ŧ─────────────────────────────────────────────────────┤ + // │ lndbtc │ Syncing 34.24% (610000/1781443) │ + for _, r := range t.Records { + for i, c := range t.Columns { + if i == 0 { + fmt.Print("├") + } else { + fmt.Print("â”ŧ") + } + fmt.Print(strings.Repeat("─", c.Width + 2)) + if i == n - 1 { + fmt.Print("┤\n") + } + } + for i, c := range t.Columns { + fmt.Print("│ ") + fmt.Print(r.Fields[c.ID]) + fmt.Print(strings.Repeat(" ", c.Width - len(r.Fields[c.ID]) + 1)) + if i == n - 1 { + fmt.Print("│\n") + } + } + } + + // print table bottom border + // └─────────┴─────────────────────────────────────────────────────┘ + for i, c := range t.Columns { + if i == 0 { + fmt.Print("└") + } else { + fmt.Print("┴") + } + fmt.Print(strings.Repeat("─", c.Width + 2)) + if i == n - 1 { + fmt.Print("┘\n") + } + } +} + +func (t *SimpleTable) PrintUpdate(x TableRecord) { + for i, r := range t.Records { + if r.Fields["service"] == x.Fields["service"] { + t.Records[i].Fields["status"] = x.Fields["status"] + } + } + fmt.Printf("\033[%dA", 2 * len(t.Records) + 3) + t.Print() +} diff --git a/launcher/utils/file.go b/launcher/utils/file.go index c6dc48c..bffe44e 100644 --- a/launcher/utils/file.go +++ b/launcher/utils/file.go @@ -1,6 +1,9 @@ package utils -import "os" +import ( + "github.com/mattn/go-isatty" + "os" +) func FileExists(path string) bool { if _, err := os.Stat(path); os.IsNotExist(err) { @@ -16,3 +19,12 @@ func IsDir(path string) bool { } return info.IsDir() } + +// Isatty checks if a given file is a terminal or not +func Isatty(f *os.File) bool { + if isatty.IsTerminal(f.Fd()) { + return true + } else { + return false + } +} diff --git a/opendex.sh b/opendex.sh index 5e1e367..9ce78f5 100755 --- a/opendex.sh +++ b/opendex.sh @@ -1,3 +1,110 @@ -#!/bin/bash +#!/bin/sh -echo "to be implemented" +set -e + +LAUNCHER_VERSION="v1.0.0-rc.5" + +assemble_launcher_download_url() { + case $(uname) in + Darwin) os="darwin";; + Linux) os="linux";; + *) + echo "Unsupported kernel: $(uname)" + exit 1 + ;; + esac + + case $(uname -m) in + x86_64) arch="amd64";; + aarch64) + if [ "$os" = "darwin" ]; then + echo "The arm64 macOS has not been supported yet." + exit 1 + fi + arch="arm64" + ;; + *) + echo "Unsupported machine: $(name -m)" + exit 1 + ;; + esac + + filename="opendex-launcher-${os}-${arch}.zip" + unset os + unset arch + + LAUNCHER_DOWNLOAD_URL="https://github.com/opendexnetwork/opendex-launcher/releases/download/$LAUNCHER_VERSION/$filename" + unset filename +} + +assemble_launcher_download_url + +if [ "$(uname)" = "Darwin" ]; then + OPENDEX_DOCKER_HOME="$HOME/Library/Application Support/OpendexDocker" +else + OPENDEX_DOCKER_HOME="$HOME/.opendex-docker" +fi + +if ! [ -e "$OPENDEX_DOCKER_HOME" ]; then + mkdir "$OPENDEX_DOCKER_HOME" +fi + +DEFAULT_LAUNCHER="$OPENDEX_DOCKER_HOME/opendex-launcher" + +install_launcher() { + # Install opendex-launcher binary file into $OPENDEX_DOCKER_HOME folder + echo "Installing opendex-launcher $LAUNCHER_VERSION ..." + echo "$LAUNCHER_DOWNLOAD_URL" + zipfile="$OPENDEX_DOCKER_HOME/opendex-launcher.zip" + curl -sfL -o "$zipfile" "$LAUNCHER_DOWNLOAD_URL" + case $(uname) in + Darwin) + unzip -qo "$zipfile" -d "$OPENDEX_DOCKER_HOME" + ;; + Linux) + gunzip -qc -S .zip "$zipfile" > "$OPENDEX_DOCKER_HOME/opendex-launcher" + ;; + *) + echo "Unsupported kernel: $(uname)" + exit 1 + esac + rm "$zipfile" + unset zipfile + chmod u+x "$DEFAULT_LAUNCHER" +} + +ensure_launcher() { + LAUNCHER=${LAUNCHER:-"$DEFAULT_LAUNCHER"} + install=false + if [ -e "$LAUNCHER" ]; then + if ! "$LAUNCHER" version | grep "opendex-launcher" | grep -q "$LAUNCHER_VERSION"; then + install=true + fi + else + if [ "$LAUNCHER" = "$DEFAULT_LAUNCHER" ]; then + install=true + else + echo "opendex-launcher not found: $LAUNCHER" + exit 1 + fi + fi + $install && install_launcher + unset install +} + +ensure_network() { + while [ -z "${NETWORK:-}" ]; do + echo "1) Testnet" + echo "2) Mainnet" + read -p "Please choose the network: " REPLY + case $REPLY in + 1) NETWORK="testnet";; + 2) NETWORK="mainnet";; + esac + done +} + +ensure_launcher +ensure_network +export NETWORK=$NETWORK +"$LAUNCHER" setup --interactive