github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/integration/node.go (about)

     1  package integration
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  
    12  	"google.golang.org/grpc"
    13  
    14  	agentutils "github.com/docker/swarmkit/agent/testutils"
    15  	"github.com/docker/swarmkit/api"
    16  	"github.com/docker/swarmkit/ca"
    17  	"github.com/docker/swarmkit/node"
    18  	"github.com/docker/swarmkit/testutils"
    19  )
    20  
    21  // TestNode is representation of *agent.Node. It stores listeners, connections,
    22  // config for later access from tests.
    23  type testNode struct {
    24  	config   *node.Config
    25  	node     *node.Node
    26  	stateDir string
    27  }
    28  
    29  // generateCerts generates/overwrites TLS certificates for a node in a particular directory
    30  func generateCerts(tmpDir string, rootCA *ca.RootCA, nodeID, role, org string, writeKey bool) error {
    31  	signer, err := rootCA.Signer()
    32  	if err != nil {
    33  		return err
    34  	}
    35  	certDir := filepath.Join(tmpDir, "certificates")
    36  	if err := os.MkdirAll(certDir, 0700); err != nil {
    37  		return err
    38  	}
    39  	certPaths := ca.NewConfigPaths(certDir)
    40  	if err := ioutil.WriteFile(certPaths.RootCA.Cert, signer.Cert, 0644); err != nil {
    41  		return err
    42  	}
    43  	if writeKey {
    44  		if err := ioutil.WriteFile(certPaths.RootCA.Key, signer.Key, 0600); err != nil {
    45  			return err
    46  		}
    47  	}
    48  	_, _, err = rootCA.IssueAndSaveNewCertificates(
    49  		ca.NewKeyReadWriter(certPaths.Node, nil, nil), nodeID, role, org)
    50  	return err
    51  }
    52  
    53  // newNode creates new node with specific role(manager or agent) and joins to
    54  // existing cluster. if joinAddr is empty string, then new cluster will be initialized.
    55  // It uses TestExecutor as executor. If lateBind is set, the remote API port is not
    56  // bound.  If rootCA is set, this root is used to bootstrap the node's TLS certs.
    57  func newTestNode(joinAddr, joinToken string, lateBind bool, fips bool) (*testNode, error) {
    58  	tmpDir, err := ioutil.TempDir("", "swarmkit-integration-")
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	cAddr := filepath.Join(tmpDir, "control.sock")
    64  	cfg := &node.Config{
    65  		ListenControlAPI: cAddr,
    66  		JoinAddr:         joinAddr,
    67  		StateDir:         tmpDir,
    68  		Executor:         &agentutils.TestExecutor{},
    69  		JoinToken:        joinToken,
    70  		FIPS:             fips,
    71  	}
    72  	if !lateBind {
    73  		cfg.ListenRemoteAPI = "127.0.0.1:0"
    74  	}
    75  
    76  	node, err := node.New(cfg)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	return &testNode{
    81  		config:   cfg,
    82  		node:     node,
    83  		stateDir: tmpDir,
    84  	}, nil
    85  }
    86  
    87  // Pause stops the node, and creates a new swarm node while keeping all the state
    88  func (n *testNode) Pause(forceNewCluster bool) error {
    89  	rAddr, err := n.node.RemoteAPIAddr()
    90  	if err != nil {
    91  		rAddr = "127.0.0.1:0"
    92  	}
    93  
    94  	if err := n.stop(); err != nil {
    95  		return err
    96  	}
    97  
    98  	cfg := n.config
    99  	cfg.ListenRemoteAPI = rAddr
   100  	// If JoinAddr is set, the node will connect to the join addr and ignore any
   101  	// other remotes that are stored in the raft directory.
   102  	cfg.JoinAddr = ""
   103  	cfg.JoinToken = ""
   104  	cfg.ForceNewCluster = forceNewCluster
   105  
   106  	node, err := node.New(cfg)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	n.node = node
   111  	return nil
   112  }
   113  
   114  func (n *testNode) stop() error {
   115  	ctx, cancel := context.WithTimeout(context.Background(), opsTimeout)
   116  	defer cancel()
   117  	isManager := n.IsManager()
   118  	if err := n.node.Stop(ctx); err != nil {
   119  		// if the error is from trying to stop an already stopped stopped node, ignore the error
   120  		if strings.Contains(err.Error(), "node: not started") {
   121  			return nil
   122  		}
   123  		// TODO(aaronl): This stack dumping may be removed in the
   124  		// future once context deadline issues while shutting down
   125  		// nodes are resolved.
   126  		buf := make([]byte, 1024)
   127  		for {
   128  			n := runtime.Stack(buf, true)
   129  			if n < len(buf) {
   130  				buf = buf[:n]
   131  				break
   132  			}
   133  			buf = make([]byte, 2*len(buf))
   134  		}
   135  		os.Stderr.Write(buf)
   136  
   137  		if isManager {
   138  			return fmt.Errorf("error stop manager %s: %v", n.node.NodeID(), err)
   139  		}
   140  		return fmt.Errorf("error stop worker %s: %v", n.node.NodeID(), err)
   141  	}
   142  	return nil
   143  }
   144  
   145  // Stop stops the node and removes its state directory.
   146  func (n *testNode) Stop() error {
   147  	if err := n.stop(); err != nil {
   148  		return err
   149  	}
   150  	return os.RemoveAll(n.stateDir)
   151  }
   152  
   153  // ControlClient returns grpc client to ControlAPI of node. It will panic for
   154  // non-manager nodes.
   155  func (n *testNode) ControlClient(ctx context.Context) (api.ControlClient, error) {
   156  	ctx, cancel := context.WithTimeout(ctx, opsTimeout)
   157  	defer cancel()
   158  	connChan := n.node.ListenControlSocket(ctx)
   159  	var controlConn *grpc.ClientConn
   160  	if err := testutils.PollFuncWithTimeout(nil, func() error {
   161  		select {
   162  		case controlConn = <-connChan:
   163  		default:
   164  		}
   165  		if controlConn == nil {
   166  			return fmt.Errorf("didn't get control api connection")
   167  		}
   168  		return nil
   169  	}, opsTimeout); err != nil {
   170  		return nil, err
   171  	}
   172  	return api.NewControlClient(controlConn), nil
   173  }
   174  
   175  func (n *testNode) IsManager() bool {
   176  	return n.node.Manager() != nil
   177  }