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 }