github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/cmd/puppeth/module_explorer.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:33</date> 10 //</624450069361332224> 11 12 13 package main 14 15 import ( 16 "bytes" 17 "fmt" 18 "html/template" 19 "math/rand" 20 "path/filepath" 21 "strconv" 22 "strings" 23 24 "github.com/ethereum/go-ethereum/log" 25 ) 26 27 //ExplorerDockerFile是运行块资源管理器所需的DockerFile。 28 var explorerDockerfile = ` 29 FROM puppeth/explorer:latest 30 31 ADD ethstats.json /ethstats.json 32 ADD chain.json /chain.json 33 34 RUN \ 35 echo '(cd ../eth-net-intelligence-api && pm2 start /ethstats.json)' > explorer.sh && \ 36 echo '(cd ../etherchain-light && npm start &)' >> explorer.sh && \ 37 echo 'exec /parity/parity --chain=/chain.json --port={{.NodePort}} --tracing=on --fat-db=on --pruning=archive' >> explorer.sh 38 39 ENTRYPOINT ["/bin/sh", "explorer.sh"] 40 ` 41 42 //explorerethstats是ethstats javascript客户机的配置文件。 43 var explorerEthstats = `[ 44 { 45 "name" : "node-app", 46 "script" : "app.js", 47 "log_date_format" : "YYYY-MM-DD HH:mm Z", 48 "merge_logs" : false, 49 "watch" : false, 50 "max_restarts" : 10, 51 "exec_interpreter" : "node", 52 "exec_mode" : "fork_mode", 53 "env": 54 { 55 "NODE_ENV" : "production", 56 "RPC_HOST" : "localhost", 57 "RPC_PORT" : "8545", 58 "LISTENING_PORT" : "{{.Port}}", 59 "INSTANCE_NAME" : "{{.Name}}", 60 "CONTACT_DETAILS" : "", 61 "WS_SERVER" : "{{.Host}}", 62 "WS_SECRET" : "{{.Secret}}", 63 "VERBOSITY" : 2 64 } 65 } 66 ]` 67 68 //explorer compose file是部署和 69 //维护块资源管理器。 70 var explorerComposefile = ` 71 version: '2' 72 services: 73 explorer: 74 build: . 75 image: {{.Network}}/explorer 76 container_name: {{.Network}}_explorer_1 77 ports: 78 - "{{.NodePort}}:{{.NodePort}}" 79 - "{{.NodePort}}:{{.NodePort}}/udp"{{if not .VHost}} 80 - "{{.WebPort}}:3000"{{end}} 81 volumes: 82 - {{.Datadir}}:/root/.local/share/io.parity.ethereum 83 environment: 84 - NODE_PORT={{.NodePort}}/tcp 85 - STATS={{.Ethstats}}{{if .VHost}} 86 - VIRTUAL_HOST={{.VHost}} 87 - VIRTUAL_PORT=3000{{end}} 88 logging: 89 driver: "json-file" 90 options: 91 max-size: "1m" 92 max-file: "10" 93 restart: always 94 ` 95 96 //deployexplorer通过将新的块资源管理器容器部署到远程计算机 97 //ssh、docker和docker撰写。如果具有指定网络名称的实例 98 //已经存在,将被覆盖! 99 func deployExplorer(client *sshClient, network string, chainspec []byte, config *explorerInfos, nocache bool) ([]byte, error) { 100 //生成要上载到服务器的内容 101 workdir := fmt.Sprintf("%d", rand.Int63()) 102 files := make(map[string][]byte) 103 104 dockerfile := new(bytes.Buffer) 105 template.Must(template.New("").Parse(explorerDockerfile)).Execute(dockerfile, map[string]interface{}{ 106 "NodePort": config.nodePort, 107 }) 108 files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() 109 110 ethstats := new(bytes.Buffer) 111 template.Must(template.New("").Parse(explorerEthstats)).Execute(ethstats, map[string]interface{}{ 112 "Port": config.nodePort, 113 "Name": config.ethstats[:strings.Index(config.ethstats, ":")], 114 "Secret": config.ethstats[strings.Index(config.ethstats, ":")+1 : strings.Index(config.ethstats, "@")], 115 "Host": config.ethstats[strings.Index(config.ethstats, "@")+1:], 116 }) 117 files[filepath.Join(workdir, "ethstats.json")] = ethstats.Bytes() 118 119 composefile := new(bytes.Buffer) 120 template.Must(template.New("").Parse(explorerComposefile)).Execute(composefile, map[string]interface{}{ 121 "Datadir": config.datadir, 122 "Network": network, 123 "NodePort": config.nodePort, 124 "VHost": config.webHost, 125 "WebPort": config.webPort, 126 "Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")], 127 }) 128 files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() 129 130 files[filepath.Join(workdir, "chain.json")] = chainspec 131 132 //将部署文件上载到远程服务器(然后清理) 133 if out, err := client.Upload(files); err != nil { 134 return out, err 135 } 136 defer client.Run("rm -rf " + workdir) 137 138 //构建和部署引导或密封节点服务 139 if nocache { 140 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)) 141 } 142 return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build --force-recreate --timeout 60", workdir, network)) 143 } 144 145 //ExplorerInfo从块资源管理器状态检查返回以允许报告 146 //各种配置参数。 147 type explorerInfos struct { 148 datadir string 149 ethstats string 150 nodePort int 151 webHost string 152 webPort int 153 } 154 155 //报表将类型化结构转换为纯字符串->字符串映射,其中包含 156 //大多数(但不是全部)字段用于向用户报告。 157 func (info *explorerInfos) Report() map[string]string { 158 report := map[string]string{ 159 "Data directory": info.datadir, 160 "Node listener port ": strconv.Itoa(info.nodePort), 161 "Ethstats username": info.ethstats, 162 "Website address ": info.webHost, 163 "Website listener port ": strconv.Itoa(info.webPort), 164 } 165 return report 166 } 167 168 //check explorer对块资源管理器服务器执行运行状况检查以验证 169 // 170 func checkExplorer(client *sshClient, network string) (*explorerInfos, error) { 171 //检查主机上可能的块资源管理器容器 172 infos, err := inspectContainer(client, fmt.Sprintf("%s_explorer_1", network)) 173 if err != nil { 174 return nil, err 175 } 176 if !infos.running { 177 return nil, ErrServiceOffline 178 } 179 //从主机或反向代理解析端口 180 webPort := infos.portmap["3000/tcp"] 181 if webPort == 0 { 182 if proxy, _ := checkNginx(client, network); proxy != nil { 183 webPort = proxy.port 184 } 185 } 186 if webPort == 0 { 187 return nil, ErrNotExposed 188 } 189 // 190 host := infos.envvars["VIRTUAL_HOST"] 191 if host == "" { 192 host = client.server 193 } 194 //运行健全性检查以查看是否可以访问devp2p 195 nodePort := infos.portmap[infos.envvars["NODE_PORT"]] 196 if err = checkPort(client.server, nodePort); err != nil { 197 log.Warn(fmt.Sprintf("Explorer devp2p port seems unreachable"), "server", client.server, "port", nodePort, "err", err) 198 } 199 //收集并返回有用的信息 200 stats := &explorerInfos{ 201 datadir: infos.volumes["/root/.local/share/io.parity.ethereum"], 202 nodePort: nodePort, 203 webHost: host, 204 webPort: webPort, 205 ethstats: infos.envvars["STATS"], 206 } 207 return stats, nil 208 } 209