code: func (m *Mercury) AddNode(ctx context.Context, name, endpoint, podname, ca, cert, key string, cpu, share int, memory int64, labels map[string]string) (*types.Node, error) {
  	// TODO VM branch
  	if !strings.HasPrefix(endpoint, nodeTCPPrefixKey) &&
  		!strings.HasPrefix(endpoint, nodeSockPrefixKey) &&
  		!strings.HasPrefix(endpoint, nodeMockPrefixKey) {
  		return nil, types.NewDetailedErr(types.ErrNodeFormat,
  			fmt.Sprintf("endpoint must starts with %s or %s %q",
  				nodeTCPPrefixKey, nodeSockPrefixKey, endpoint))
  	}
  
  	_, err := m.GetPod(ctx, podname)
  	if err != nil {
  		return nil, err
  	}
  
  	if _, err := m.doGetNode(ctx, podname, name); err == nil {
  		return nil, types.NewDetailedErr(types.ErrNodeExist,
  			fmt.Sprintf("node %s:%s already exists",
  				podname, name))
  	}
  
  	// 尝试加载的客户端
  	// 会自动判断是否是支持的 url
  	engine, err := m.doMakeClient(ctx, name, endpoint, ca, cert, key)
  	if err != nil {
  		return nil, err
  	}
  
  	// 判断这货是不是活着的
  	info, err := engine.Info(ctx)
  	if err != nil {
  		return nil, err
  	}
  	// 更新默认值
  	if cpu == 0 {
  		cpu = info.NCPU
  	}
  	if memory == 0 {
  		memory = info.MemTotal - types.GByte
  	}
  	if share == 0 {
  		share = m.config.Scheduler.ShareBase
  	}
  
  	return m.doAddNode(ctx, name, endpoint, podname, ca, cert, key, cpu, share, memory, labels)
  }
docs: // AddNode save it to etcd
  // storage path in etcd is `/pod/nodes/:podname/:nodename`
  // node->pod path in etcd is `/node/pod/:nodename`