github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/cmd/puppeth/module_explorer.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 //版权所有2017 Go Ethereum作者 10 //此文件是Go以太坊的一部分。 11 // 12 //Go以太坊是免费软件:您可以重新发布和/或修改它 13 //根据GNU通用公共许可证的条款 14 //自由软件基金会,或者许可证的第3版,或者 15 //(由您选择)任何更高版本。 16 // 17 //Go以太坊的分布希望它会有用, 18 //但没有任何保证;甚至没有 19 //适销性或特定用途的适用性。见 20 //GNU通用公共许可证了解更多详细信息。 21 // 22 //你应该已经收到一份GNU通用公共许可证的副本 23 //一起去以太坊吧。如果没有,请参见<http://www.gnu.org/licenses/>。 24 25 package main 26 27 import ( 28 "bytes" 29 "fmt" 30 "html/template" 31 "math/rand" 32 "path/filepath" 33 "strconv" 34 "strings" 35 36 "github.com/ethereum/go-ethereum/log" 37 ) 38 39 //ExplorerDockerFile是运行块资源管理器所需的DockerFile。 40 var explorerDockerfile = ` 41 FROM puppeth/explorer:latest 42 43 ADD ethstats.json /ethstats.json 44 ADD chain.json /chain.json 45 46 RUN \ 47 echo '(cd ../eth-net-intelligence-api && pm2 start /ethstats.json)' > explorer.sh && \ 48 echo '(cd ../etherchain-light && npm start &)' >> explorer.sh && \ 49 echo 'exec /parity/parity --chain=/chain.json --port={{.NodePort}} --tracing=on --fat-db=on --pruning=archive' >> explorer.sh 50 51 ENTRYPOINT ["/bin/sh", "explorer.sh"] 52 ` 53 54 //explorerethstats是ethstats javascript客户机的配置文件。 55 var explorerEthstats = `[ 56 { 57 "name" : "node-app", 58 "script" : "app.js", 59 "log_date_format" : "YYYY-MM-DD HH:mm Z", 60 "merge_logs" : false, 61 "watch" : false, 62 "max_restarts" : 10, 63 "exec_interpreter" : "node", 64 "exec_mode" : "fork_mode", 65 "env": 66 { 67 "NODE_ENV" : "production", 68 "RPC_HOST" : "localhost", 69 "RPC_PORT" : "8545", 70 "LISTENING_PORT" : "{{.Port}}", 71 "INSTANCE_NAME" : "{{.Name}}", 72 "CONTACT_DETAILS" : "", 73 "WS_SERVER" : "{{.Host}}", 74 "WS_SECRET" : "{{.Secret}}", 75 "VERBOSITY" : 2 76 } 77 } 78 ]` 79 80 //explorer compose file是部署和 81 //维护块资源管理器。 82 var explorerComposefile = ` 83 version: '2' 84 services: 85 explorer: 86 build: . 87 image: {{.Network}}/explorer 88 ports: 89 - "{{.NodePort}}:{{.NodePort}}" 90 - "{{.NodePort}}:{{.NodePort}}/udp"{{if not .VHost}} 91 - "{{.WebPort}}:3000"{{end}} 92 volumes: 93 - {{.Datadir}}:/root/.local/share/io.parity.ethereum 94 environment: 95 - NODE_PORT={{.NodePort}}/tcp 96 - STATS={{.Ethstats}}{{if .VHost}} 97 - VIRTUAL_HOST={{.VHost}} 98 - VIRTUAL_PORT=3000{{end}} 99 logging: 100 driver: "json-file" 101 options: 102 max-size: "1m" 103 max-file: "10" 104 restart: always 105 ` 106 107 //deployexplorer通过将新的块资源管理器容器部署到远程计算机 108 //ssh、docker和docker撰写。如果具有指定网络名称的实例 109 //已经存在,将被覆盖! 110 func deployExplorer(client *sshClient, network string, chainspec []byte, config *explorerInfos, nocache bool) ([]byte, error) { 111 //生成要上载到服务器的内容 112 workdir := fmt.Sprintf("%d", rand.Int63()) 113 files := make(map[string][]byte) 114 115 dockerfile := new(bytes.Buffer) 116 template.Must(template.New("").Parse(explorerDockerfile)).Execute(dockerfile, map[string]interface{}{ 117 "NodePort": config.nodePort, 118 }) 119 files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() 120 121 ethstats := new(bytes.Buffer) 122 template.Must(template.New("").Parse(explorerEthstats)).Execute(ethstats, map[string]interface{}{ 123 "Port": config.nodePort, 124 "Name": config.ethstats[:strings.Index(config.ethstats, ":")], 125 "Secret": config.ethstats[strings.Index(config.ethstats, ":")+1 : strings.Index(config.ethstats, "@")], 126 "Host": config.ethstats[strings.Index(config.ethstats, "@")+1:], 127 }) 128 files[filepath.Join(workdir, "ethstats.json")] = ethstats.Bytes() 129 130 composefile := new(bytes.Buffer) 131 template.Must(template.New("").Parse(explorerComposefile)).Execute(composefile, map[string]interface{}{ 132 "Datadir": config.datadir, 133 "Network": network, 134 "NodePort": config.nodePort, 135 "VHost": config.webHost, 136 "WebPort": config.webPort, 137 "Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")], 138 }) 139 files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() 140 141 files[filepath.Join(workdir, "chain.json")] = chainspec 142 143 //将部署文件上载到远程服务器(然后清理) 144 if out, err := client.Upload(files); err != nil { 145 return out, err 146 } 147 defer client.Run("rm -rf " + workdir) 148 149 // 150 if nocache { 151 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)) 152 } 153 return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build --force-recreate --timeout 60", workdir, network)) 154 } 155 156 // 157 //各种配置参数。 158 type explorerInfos struct { 159 datadir string 160 ethstats string 161 nodePort int 162 webHost string 163 webPort int 164 } 165 166 //报表将类型化结构转换为纯字符串->字符串映射,其中包含 167 //大多数(但不是全部)字段用于向用户报告。 168 func (info *explorerInfos) Report() map[string]string { 169 report := map[string]string{ 170 "Data directory": info.datadir, 171 "Node listener port ": strconv.Itoa(info.nodePort), 172 "Ethstats username": info.ethstats, 173 "Website address ": info.webHost, 174 "Website listener port ": strconv.Itoa(info.webPort), 175 } 176 return report 177 } 178 179 // 180 // 181 func checkExplorer(client *sshClient, network string) (*explorerInfos, error) { 182 // 183 infos, err := inspectContainer(client, fmt.Sprintf("%s_explorer_1", network)) 184 if err != nil { 185 return nil, err 186 } 187 if !infos.running { 188 return nil, ErrServiceOffline 189 } 190 //从主机或反向代理解析端口 191 webPort := infos.portmap["3000/tcp"] 192 if webPort == 0 { 193 if proxy, _ := checkNginx(client, network); proxy != nil { 194 webPort = proxy.port 195 } 196 } 197 if webPort == 0 { 198 return nil, ErrNotExposed 199 } 200 // 201 host := infos.envvars["VIRTUAL_HOST"] 202 if host == "" { 203 host = client.server 204 } 205 // 206 nodePort := infos.portmap[infos.envvars["NODE_PORT"]] 207 if err = checkPort(client.server, nodePort); err != nil { 208 log.Warn(fmt.Sprintf("Explorer devp2p port seems unreachable"), "server", client.server, "port", nodePort, "err", err) 209 } 210 //收集并返回有用的信息 211 stats := &explorerInfos{ 212 datadir: infos.volumes["/root/.local/share/io.parity.ethereum"], 213 nodePort: nodePort, 214 webHost: host, 215 webPort: webPort, 216 ethstats: infos.envvars["STATS"], 217 } 218 return stats, nil 219 }