wizard_intro.go 5.29 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
// Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
27
	"sync"
28 29 30 31 32 33 34

	"github.com/ethereum/go-ethereum/log"
)

// makeWizard creates and returns a new puppeth wizard.
func makeWizard(network string) *wizard {
	return &wizard{
35 36 37 38
		network: network,
		conf: config{
			Servers: make(map[string][]byte),
		},
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
		servers:  make(map[string]*sshClient),
		services: make(map[string][]string),
		in:       bufio.NewReader(os.Stdin),
	}
}

// run displays some useful infos to the user, starting on the journey of
// setting up a new or managing an existing Ethereum private network.
func (w *wizard) run() {
	fmt.Println("+-----------------------------------------------------------+")
	fmt.Println("| Welcome to puppeth, your Ethereum private network manager |")
	fmt.Println("|                                                           |")
	fmt.Println("| This tool lets you create a new Ethereum network down to  |")
	fmt.Println("| the genesis block, bootnodes, miners and ethstats servers |")
	fmt.Println("| without the hassle that it would normally entail.         |")
	fmt.Println("|                                                           |")
	fmt.Println("| Puppeth uses SSH to dial in to remote servers, and builds |")
	fmt.Println("| its network components out of Docker containers using the |")
	fmt.Println("| docker-compose toolset.                                   |")
	fmt.Println("+-----------------------------------------------------------+")
	fmt.Println()

	// Make sure we have a good network name to work with	fmt.Println()
62
	// Docker accepts hyphens in image names, but doesn't like it for container names
63
	if w.network == "" {
64
		fmt.Println("Please specify a network name to administer (no spaces, hyphens or capital letters please)")
65 66
		for {
			w.network = w.readString()
67
			if !strings.Contains(w.network, " ") && !strings.Contains(w.network, "-") && strings.ToLower(w.network) == w.network {
68
				fmt.Printf("\nSweet, you can set this via --network=%s next time!\n\n", w.network)
69 70
				break
			}
71
			log.Error("I also like to live dangerously, still no spaces, hyphens or capital letters")
72 73 74 75 76 77 78 79 80 81 82 83 84
		}
	}
	log.Info("Administering Ethereum network", "name", w.network)

	// Load initial configurations and connect to all live servers
	w.conf.path = filepath.Join(os.Getenv("HOME"), ".puppeth", w.network)

	blob, err := ioutil.ReadFile(w.conf.path)
	if err != nil {
		log.Warn("No previous configurations found", "path", w.conf.path)
	} else if err := json.Unmarshal(blob, &w.conf); err != nil {
		log.Crit("Previous configuration corrupted", "path", w.conf.path, "err", err)
	} else {
85 86
		// Dial all previously known servers concurrently
		var pend sync.WaitGroup
87
		for server, pubkey := range w.conf.Servers {
88 89 90 91 92 93 94 95 96 97 98 99 100 101
			pend.Add(1)

			go func(server string, pubkey []byte) {
				defer pend.Done()

				log.Info("Dialing previously configured server", "server", server)
				client, err := dial(server, pubkey)
				if err != nil {
					log.Error("Previous server unreachable", "server", server, "err", err)
				}
				w.lock.Lock()
				w.servers[server] = client
				w.lock.Unlock()
			}(server, pubkey)
102
		}
103
		pend.Wait()
104
		w.networkStats()
105 106 107 108 109 110
	}
	// Basics done, loop ad infinitum about what to do
	for {
		fmt.Println()
		fmt.Println("What would you like to do? (default = stats)")
		fmt.Println(" 1. Show network stats")
111
		if w.conf.Genesis == nil {
112 113
			fmt.Println(" 2. Configure new genesis")
		} else {
114
			fmt.Println(" 2. Manage existing genesis")
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
		}
		if len(w.servers) == 0 {
			fmt.Println(" 3. Track new remote server")
		} else {
			fmt.Println(" 3. Manage tracked machines")
		}
		if len(w.services) == 0 {
			fmt.Println(" 4. Deploy network components")
		} else {
			fmt.Println(" 4. Manage network components")
		}

		choice := w.read()
		switch {
		case choice == "" || choice == "1":
130
			w.networkStats()
131 132

		case choice == "2":
133
			if w.conf.Genesis == nil {
134 135 136 137 138 139 140 141 142 143 144 145 146 147
				fmt.Println()
				fmt.Println("What would you like to do? (default = create)")
				fmt.Println(" 1. Create new genesis from scratch")
				fmt.Println(" 2. Import already existing genesis")

				choice := w.read()
				switch {
				case choice == "" || choice == "1":
					w.makeGenesis()
				case choice == "2":
					w.importGenesis()
				default:
					log.Error("That's not something I can do")
				}
148
			} else {
149
				w.manageGenesis()
150 151 152 153
			}
		case choice == "3":
			if len(w.servers) == 0 {
				if w.makeServer() != "" {
154
					w.networkStats()
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
				}
			} else {
				w.manageServers()
			}
		case choice == "4":
			if len(w.services) == 0 {
				w.deployComponent()
			} else {
				w.manageComponents()
			}
		default:
			log.Error("That's not something I can do")
		}
	}
}