github.com/reapchain/go-reapchain@v0.2.15-0.20210609012950-9735c110c705/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 "strings" 25 "text/template" 26 27 "github.com/ethereum/go-ethereum/log" 28 ) 29 30 // ethstatsDockerfile is the Dockerfile required to build an ethstats backend 31 // and associated monitoring site. 32 var ethstatsDockerfile = ` 33 FROM mhart/alpine-node:latest 34 35 RUN \ 36 apk add --update git && \ 37 git clone --depth=1 https://github.com/karalabe/eth-netstats && \ 38 apk del git && rm -rf /var/cache/apk/* && \ 39 \ 40 cd /eth-netstats && npm install && npm install -g grunt-cli && grunt 41 42 WORKDIR /eth-netstats 43 EXPOSE 3000 44 45 RUN echo 'module.exports = {trusted: [{{.Trusted}}], banned: []};' > lib/utils/config.js 46 47 CMD ["npm", "start"] 48 ` 49 50 // ethstatsComposefile is the docker-compose.yml file required to deploy and 51 // maintain an ethstats monitoring site. 52 var ethstatsComposefile = ` 53 version: '2' 54 services: 55 ethstats: 56 build: . 57 image: {{.Network}}/ethstats{{if not .VHost}} 58 ports: 59 - "{{.Port}}:3000"{{end}} 60 environment: 61 - WS_SECRET={{.Secret}}{{if .VHost}} 62 - VIRTUAL_HOST={{.VHost}}{{end}} 63 restart: always 64 ` 65 66 // deployEthstats deploys a new ethstats container to a remote machine via SSH, 67 // docker and docker-compose. If an instance with the specified network name 68 // already exists there, it will be overwritten! 69 func deployEthstats(client *sshClient, network string, port int, secret string, vhost string, trusted []string) ([]byte, error) { 70 // Generate the content to upload to the server 71 workdir := fmt.Sprintf("%d", rand.Int63()) 72 files := make(map[string][]byte) 73 74 for i, address := range trusted { 75 trusted[i] = fmt.Sprintf("\"%s\"", address) 76 } 77 78 dockerfile := new(bytes.Buffer) 79 template.Must(template.New("").Parse(ethstatsDockerfile)).Execute(dockerfile, map[string]interface{}{ 80 "Trusted": strings.Join(trusted, ", "), 81 }) 82 files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() 83 84 composefile := new(bytes.Buffer) 85 template.Must(template.New("").Parse(ethstatsComposefile)).Execute(composefile, map[string]interface{}{ 86 "Network": network, 87 "Port": port, 88 "Secret": secret, 89 "VHost": vhost, 90 }) 91 files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() 92 93 // Upload the deployment files to the remote server (and clean up afterwards) 94 if out, err := client.Upload(files); err != nil { 95 return out, err 96 } 97 defer client.Run("rm -rf " + workdir) 98 99 // Build and deploy the ethstats service 100 return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build", workdir, network)) 101 } 102 103 // ethstatsInfos is returned from an ethstats status check to allow reporting 104 // various configuration parameters. 105 type ethstatsInfos struct { 106 host string 107 port int 108 secret string 109 config string 110 } 111 112 // String implements the stringer interface. 113 func (info *ethstatsInfos) String() string { 114 return fmt.Sprintf("host=%s, port=%d, secret=%s", info.host, info.port, info.secret) 115 } 116 117 // checkEthstats does a health-check against an ethstats server to verify whether 118 // it's running, and if yes, gathering a collection of useful infos about it. 119 func checkEthstats(client *sshClient, network string) (*ethstatsInfos, error) { 120 // Inspect a possible ethstats container on the host 121 infos, err := inspectContainer(client, fmt.Sprintf("%s_ethstats_1", network)) 122 if err != nil { 123 return nil, err 124 } 125 if !infos.running { 126 return nil, ErrServiceOffline 127 } 128 // Resolve the port from the host, or the reverse proxy 129 port := infos.portmap["3000/tcp"] 130 if port == 0 { 131 if proxy, _ := checkNginx(client, network); proxy != nil { 132 port = proxy.port 133 } 134 } 135 if port == 0 { 136 return nil, ErrNotExposed 137 } 138 // Resolve the host from the reverse-proxy and configure the connection string 139 host := infos.envvars["VIRTUAL_HOST"] 140 if host == "" { 141 host = client.server 142 } 143 secret := infos.envvars["WS_SECRET"] 144 config := fmt.Sprintf("%s@%s", secret, host) 145 if port != 80 && port != 443 { 146 config += fmt.Sprintf(":%d", port) 147 } 148 // Run a sanity check to see if the port is reachable 149 if err = checkPort(host, port); err != nil { 150 log.Warn("Ethstats service seems unreachable", "server", host, "port", port, "err", err) 151 } 152 // Container available, assemble and return the useful infos 153 return ðstatsInfos{ 154 host: host, 155 port: port, 156 secret: secret, 157 config: config, 158 }, nil 159 }