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