github.com/cooperstudy/ethereum@v1.9.7/cmd/puppeth/module_ethstats.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "bytes" 21 "fmt" 22 "math/rand" 23 "path/filepath" 24 "strconv" 25 "strings" 26 "text/template" 27 28 "github.com/ethereum/go-ethereum/log" 29 ) 30 31 // ethstatsDockerfile is the Dockerfile required to build an ethstats backend 32 // and associated monitoring site. 33 var ethstatsDockerfile = ` 34 FROM puppeth/ethstats:latest 35 36 RUN echo 'module.exports = {trusted: [{{.Trusted}}], banned: [{{.Banned}}], reserved: ["yournode"]};' > lib/utils/config.js 37 ` 38 39 // ethstatsComposefile is the docker-compose.yml file required to deploy and 40 // maintain an ethstats monitoring site. 41 var ethstatsComposefile = ` 42 version: '2' 43 services: 44 ethstats: 45 build: . 46 image: {{.Network}}/ethstats 47 container_name: {{.Network}}_ethstats_1{{if not .VHost}} 48 ports: 49 - "{{.Port}}:3000"{{end}} 50 environment: 51 - WS_SECRET={{.Secret}}{{if .VHost}} 52 - VIRTUAL_HOST={{.VHost}}{{end}}{{if .Banned}} 53 - BANNED={{.Banned}}{{end}} 54 logging: 55 driver: "json-file" 56 options: 57 max-size: "1m" 58 max-file: "10" 59 restart: always 60 ` 61 62 // deployEthstats deploys a new ethstats container to a remote machine via SSH, 63 // docker and docker-compose. If an instance with the specified network name 64 // already exists there, it will be overwritten! 65 func deployEthstats(client *sshClient, network string, port int, secret string, vhost string, trusted []string, banned []string, nocache bool) ([]byte, error) { 66 // Generate the content to upload to the server 67 workdir := fmt.Sprintf("%d", rand.Int63()) 68 files := make(map[string][]byte) 69 70 trustedLabels := make([]string, len(trusted)) 71 for i, address := range trusted { 72 trustedLabels[i] = fmt.Sprintf("\"%s\"", address) 73 } 74 bannedLabels := make([]string, len(banned)) 75 for i, address := range banned { 76 bannedLabels[i] = fmt.Sprintf("\"%s\"", address) 77 } 78 79 dockerfile := new(bytes.Buffer) 80 template.Must(template.New("").Parse(ethstatsDockerfile)).Execute(dockerfile, map[string]interface{}{ 81 "Trusted": strings.Join(trustedLabels, ", "), 82 "Banned": strings.Join(bannedLabels, ", "), 83 }) 84 files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() 85 86 composefile := new(bytes.Buffer) 87 template.Must(template.New("").Parse(ethstatsComposefile)).Execute(composefile, map[string]interface{}{ 88 "Network": network, 89 "Port": port, 90 "Secret": secret, 91 "VHost": vhost, 92 "Banned": strings.Join(banned, ","), 93 }) 94 files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() 95 96 // Upload the deployment files to the remote server (and clean up afterwards) 97 if out, err := client.Upload(files); err != nil { 98 return out, err 99 } 100 defer client.Run("rm -rf " + workdir) 101 102 // Build and deploy the ethstats service 103 if nocache { 104 return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s build --pull --no-cache && docker-compose -p %s up -d --force-recreate --timeout 60", workdir, network, network)) 105 } 106 return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build --force-recreate --timeout 60", workdir, network)) 107 } 108 109 // ethstatsInfos is returned from an ethstats status check to allow reporting 110 // various configuration parameters. 111 type ethstatsInfos struct { 112 host string 113 port int 114 secret string 115 config string 116 banned []string 117 } 118 119 // Report converts the typed struct into a plain string->string map, containing 120 // most - but not all - fields for reporting to the user. 121 func (info *ethstatsInfos) Report() map[string]string { 122 return map[string]string{ 123 "Website address": info.host, 124 "Website listener port": strconv.Itoa(info.port), 125 "Login secret": info.secret, 126 "Banned addresses": strings.Join(info.banned, "\n"), 127 } 128 } 129 130 // checkEthstats does a health-check against an ethstats server to verify whether 131 // it's running, and if yes, gathering a collection of useful infos about it. 132 func checkEthstats(client *sshClient, network string) (*ethstatsInfos, error) { 133 // Inspect a possible ethstats container on the host 134 infos, err := inspectContainer(client, fmt.Sprintf("%s_ethstats_1", network)) 135 if err != nil { 136 return nil, err 137 } 138 if !infos.running { 139 return nil, ErrServiceOffline 140 } 141 // Resolve the port from the host, or the reverse proxy 142 port := infos.portmap["3000/tcp"] 143 if port == 0 { 144 if proxy, _ := checkNginx(client, network); proxy != nil { 145 port = proxy.port 146 } 147 } 148 if port == 0 { 149 return nil, ErrNotExposed 150 } 151 // Resolve the host from the reverse-proxy and configure the connection string 152 host := infos.envvars["VIRTUAL_HOST"] 153 if host == "" { 154 host = client.server 155 } 156 secret := infos.envvars["WS_SECRET"] 157 config := fmt.Sprintf("%s@%s", secret, host) 158 if port != 80 && port != 443 { 159 config += fmt.Sprintf(":%d", port) 160 } 161 // Retrieve the IP blacklist 162 banned := strings.Split(infos.envvars["BANNED"], ",") 163 164 // Run a sanity check to see if the port is reachable 165 if err = checkPort(host, port); err != nil { 166 log.Warn("Ethstats service seems unreachable", "server", host, "port", port, "err", err) 167 } 168 // Container available, assemble and return the useful infos 169 return ðstatsInfos{ 170 host: host, 171 port: port, 172 secret: secret, 173 config: config, 174 banned: banned, 175 }, nil 176 }