github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/p2p/simulations/adapters/docker.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-Ethereum库是免费软件:您可以重新分发它和/或修改
    13  //根据GNU发布的较低通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊图书馆的发行目的是希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU较低的通用公共许可证,了解更多详细信息。
    21  //
    22  //你应该收到一份GNU较低级别的公共许可证副本
    23  //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  package adapters
    26  
    27  import (
    28  	"errors"
    29  	"fmt"
    30  	"io"
    31  	"io/ioutil"
    32  	"os"
    33  	"os/exec"
    34  	"path/filepath"
    35  	"runtime"
    36  	"strings"
    37  
    38  	"github.com/docker/docker/pkg/reexec"
    39  	"github.com/ethereum/go-ethereum/node"
    40  	"github.com/ethereum/go-ethereum/p2p/discover"
    41  )
    42  
    43  var (
    44  	ErrLinuxOnly = errors.New("DockerAdapter can only be used on Linux as it uses the current binary (which must be a Linux binary)")
    45  )
    46  
    47  //Dockeradapter是在Docker中运行模拟节点的节点适配器。
    48  //容器。
    49  //
    50  //建立了一个包含当前二进制at/bin/p2p节点的Docker映像。
    51  //执行时运行基础服务(请参见说明
    52  //有关详细信息,请参阅execp2pnode函数)
    53  type DockerAdapter struct {
    54  	ExecAdapter
    55  }
    56  
    57  //newdockeradapter构建包含当前
    58  //二进制并返回dockeradapter
    59  func NewDockerAdapter() (*DockerAdapter, error) {
    60  //因为Docker容器在Linux上运行,而这个适配器运行
    61  //当前容器中的二进制文件,必须为Linux编译。
    62  //
    63  //要求这样做是合理的,因为打电话的人可以
    64  //在Docker容器中编译当前二进制文件。
    65  	if runtime.GOOS != "linux" {
    66  		return nil, ErrLinuxOnly
    67  	}
    68  
    69  	if err := buildDockerImage(); err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	return &DockerAdapter{
    74  		ExecAdapter{
    75  			nodes: make(map[discover.NodeID]*ExecNode),
    76  		},
    77  	}, nil
    78  }
    79  
    80  //name返回用于日志记录的适配器的名称
    81  func (d *DockerAdapter) Name() string {
    82  	return "docker-adapter"
    83  }
    84  
    85  //newnode使用给定的配置返回一个新的dockernode
    86  func (d *DockerAdapter) NewNode(config *NodeConfig) (Node, error) {
    87  	if len(config.Services) == 0 {
    88  		return nil, errors.New("node must have at least one service")
    89  	}
    90  	for _, service := range config.Services {
    91  		if _, exists := serviceFuncs[service]; !exists {
    92  			return nil, fmt.Errorf("unknown node service %q", service)
    93  		}
    94  	}
    95  
    96  //生成配置
    97  	conf := &execNodeConfig{
    98  		Stack: node.DefaultConfig,
    99  		Node:  config,
   100  	}
   101  	conf.Stack.DataDir = "/data"
   102  	conf.Stack.WSHost = "0.0.0.0"
   103  	conf.Stack.WSOrigins = []string{"*"}
   104  	conf.Stack.WSExposeAll = true
   105  	conf.Stack.P2P.EnableMsgEvents = false
   106  	conf.Stack.P2P.NoDiscovery = true
   107  	conf.Stack.P2P.NAT = nil
   108  	conf.Stack.NoUSB = true
   109  
   110  //监听给定端口上的所有接口,当我们
   111  //初始化nodeconfig(通常是随机端口)
   112  	conf.Stack.P2P.ListenAddr = fmt.Sprintf(":%d", config.Port)
   113  
   114  	node := &DockerNode{
   115  		ExecNode: ExecNode{
   116  			ID:      config.ID,
   117  			Config:  conf,
   118  			adapter: &d.ExecAdapter,
   119  		},
   120  	}
   121  	node.newCmd = node.dockerCommand
   122  	d.ExecAdapter.nodes[node.ID] = &node.ExecNode
   123  	return node, nil
   124  }
   125  
   126  //dockernode包装execnode,但exec的是docker中的当前二进制文件
   127  //容器而不是本地
   128  type DockerNode struct {
   129  	ExecNode
   130  }
   131  
   132  //docker command返回一个命令,exec是docker中的二进制文件
   133  //容器。
   134  //
   135  //它使用了一个shell,这样我们就可以通过
   136  //使用--env标志将变量转换为容器。
   137  func (n *DockerNode) dockerCommand() *exec.Cmd {
   138  	return exec.Command(
   139  		"sh", "-c",
   140  		fmt.Sprintf(
   141  			`exec docker run --interactive --env _P2P_NODE_CONFIG="${_P2P_NODE_CONFIG}" %s p2p-node %s %s`,
   142  			dockerImage, strings.Join(n.Config.Node.Services, ","), n.ID.String(),
   143  		),
   144  	)
   145  }
   146  
   147  //DockerImage是为运行
   148  //仿真节点
   149  const dockerImage = "p2p-node"
   150  
   151  //buildDockerImage构建用于运行模拟的Docker映像
   152  //Docker容器中的节点。
   153  //
   154  //它将当前二进制文件添加为“p2p node”,以便运行execp2pnode
   155  //执行时。
   156  func buildDockerImage() error {
   157  //创建用作生成上下文的目录
   158  	dir, err := ioutil.TempDir("", "p2p-docker")
   159  	if err != nil {
   160  		return err
   161  	}
   162  	defer os.RemoveAll(dir)
   163  
   164  //将当前二进制文件复制到生成上下文中
   165  	bin, err := os.Open(reexec.Self())
   166  	if err != nil {
   167  		return err
   168  	}
   169  	defer bin.Close()
   170  	dst, err := os.OpenFile(filepath.Join(dir, "self.bin"), os.O_WRONLY|os.O_CREATE, 0755)
   171  	if err != nil {
   172  		return err
   173  	}
   174  	defer dst.Close()
   175  	if _, err := io.Copy(dst, bin); err != nil {
   176  		return err
   177  	}
   178  
   179  //创建dockerfile
   180  	dockerfile := []byte(`
   181  FROM ubuntu:16.04
   182  RUN mkdir /data
   183  ADD self.bin /bin/p2p-node
   184  	`)
   185  	if err := ioutil.WriteFile(filepath.Join(dir, "Dockerfile"), dockerfile, 0644); err != nil {
   186  		return err
   187  	}
   188  
   189  //运行“docker build”
   190  	cmd := exec.Command("docker", "build", "-t", dockerImage, dir)
   191  	cmd.Stdout = os.Stdout
   192  	cmd.Stderr = os.Stderr
   193  	if err := cmd.Run(); err != nil {
   194  		return fmt.Errorf("error building docker image: %s", err)
   195  	}
   196  
   197  	return nil
   198  }