swarm.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. package daemon
  2. import (
  3. "context"
  4. "fmt"
  5. "testing"
  6. "github.com/docker/docker/api/types/swarm"
  7. "github.com/pkg/errors"
  8. "gotest.tools/v3/assert"
  9. )
  10. const (
  11. // DefaultSwarmPort is the default port use for swarm in the tests
  12. DefaultSwarmPort = 2477
  13. defaultSwarmListenAddr = "0.0.0.0"
  14. )
  15. var startArgs = []string{"--iptables=false", "--swarm-default-advertise-addr=lo"}
  16. // StartNode (re)starts the daemon
  17. func (d *Daemon) StartNode(t testing.TB) {
  18. t.Helper()
  19. d.Start(t, startArgs...)
  20. }
  21. // StartNodeWithBusybox starts daemon to be used as a swarm node, and loads the busybox image
  22. func (d *Daemon) StartNodeWithBusybox(ctx context.Context, t testing.TB) {
  23. t.Helper()
  24. d.StartWithBusybox(ctx, t, startArgs...)
  25. }
  26. // RestartNode restarts a daemon to be used as a swarm node
  27. func (d *Daemon) RestartNode(t testing.TB) {
  28. t.Helper()
  29. // avoid networking conflicts
  30. d.Stop(t)
  31. d.Start(t, startArgs...)
  32. }
  33. // StartAndSwarmInit starts the daemon (with busybox) and init the swarm
  34. func (d *Daemon) StartAndSwarmInit(ctx context.Context, t testing.TB) {
  35. d.StartNodeWithBusybox(ctx, t)
  36. d.SwarmInit(ctx, t, swarm.InitRequest{})
  37. }
  38. // StartAndSwarmJoin starts the daemon (with busybox) and join the specified swarm as worker or manager
  39. func (d *Daemon) StartAndSwarmJoin(ctx context.Context, t testing.TB, leader *Daemon, manager bool) {
  40. t.Helper()
  41. d.StartNodeWithBusybox(ctx, t)
  42. tokens := leader.JoinTokens(t)
  43. token := tokens.Worker
  44. if manager {
  45. token = tokens.Manager
  46. }
  47. t.Logf("[%s] joining swarm manager [%s]@%s, swarm listen addr %s", d.id, leader.id, leader.SwarmListenAddr(), d.SwarmListenAddr())
  48. d.SwarmJoin(ctx, t, swarm.JoinRequest{
  49. RemoteAddrs: []string{leader.SwarmListenAddr()},
  50. JoinToken: token,
  51. })
  52. }
  53. // SpecConstructor defines a swarm spec constructor
  54. type SpecConstructor func(*swarm.Spec)
  55. // SwarmListenAddr returns the listen-addr used for the daemon
  56. func (d *Daemon) SwarmListenAddr() string {
  57. return fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort)
  58. }
  59. // NodeID returns the swarm mode node ID
  60. func (d *Daemon) NodeID() string {
  61. return d.CachedInfo.Swarm.NodeID
  62. }
  63. // SwarmInit initializes a new swarm cluster.
  64. func (d *Daemon) SwarmInit(ctx context.Context, t testing.TB, req swarm.InitRequest) {
  65. t.Helper()
  66. if req.ListenAddr == "" {
  67. req.ListenAddr = fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort)
  68. }
  69. if req.DefaultAddrPool == nil {
  70. req.DefaultAddrPool = d.DefaultAddrPool
  71. req.SubnetSize = d.SubnetSize
  72. }
  73. if d.DataPathPort > 0 {
  74. req.DataPathPort = d.DataPathPort
  75. }
  76. cli := d.NewClientT(t)
  77. defer cli.Close()
  78. _, err := cli.SwarmInit(ctx, req)
  79. assert.NilError(t, err, "initializing swarm")
  80. d.CachedInfo = d.Info(t)
  81. }
  82. // SwarmJoin joins a daemon to an existing cluster.
  83. func (d *Daemon) SwarmJoin(ctx context.Context, t testing.TB, req swarm.JoinRequest) {
  84. t.Helper()
  85. if req.ListenAddr == "" {
  86. req.ListenAddr = fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort)
  87. }
  88. cli := d.NewClientT(t)
  89. defer cli.Close()
  90. err := cli.SwarmJoin(ctx, req)
  91. assert.NilError(t, err, "[%s] joining swarm", d.id)
  92. d.CachedInfo = d.Info(t)
  93. }
  94. // SwarmLeave forces daemon to leave current cluster.
  95. //
  96. // The passed in testing.TB is only used to validate that the client was successfully created
  97. // Some tests rely on error checking the result of the actual unlock, so allow
  98. // the error to be returned.
  99. func (d *Daemon) SwarmLeave(ctx context.Context, t testing.TB, force bool) error {
  100. cli := d.NewClientT(t)
  101. defer cli.Close()
  102. return cli.SwarmLeave(ctx, force)
  103. }
  104. // SwarmInfo returns the swarm information of the daemon
  105. func (d *Daemon) SwarmInfo(ctx context.Context, t testing.TB) swarm.Info {
  106. t.Helper()
  107. cli := d.NewClientT(t)
  108. info, err := cli.Info(ctx)
  109. assert.NilError(t, err, "get swarm info")
  110. return info.Swarm
  111. }
  112. // SwarmUnlock tries to unlock a locked swarm
  113. //
  114. // The passed in testing.TB is only used to validate that the client was successfully created
  115. // Some tests rely on error checking the result of the actual unlock, so allow
  116. // the error to be returned.
  117. func (d *Daemon) SwarmUnlock(t testing.TB, req swarm.UnlockRequest) error {
  118. cli := d.NewClientT(t)
  119. defer cli.Close()
  120. err := cli.SwarmUnlock(context.Background(), req)
  121. if err != nil {
  122. err = errors.Wrap(err, "unlocking swarm")
  123. }
  124. return err
  125. }
  126. // GetSwarm returns the current swarm object
  127. func (d *Daemon) GetSwarm(t testing.TB) swarm.Swarm {
  128. t.Helper()
  129. cli := d.NewClientT(t)
  130. defer cli.Close()
  131. sw, err := cli.SwarmInspect(context.Background())
  132. assert.NilError(t, err)
  133. return sw
  134. }
  135. // UpdateSwarm updates the current swarm object with the specified spec constructors
  136. func (d *Daemon) UpdateSwarm(t testing.TB, f ...SpecConstructor) {
  137. t.Helper()
  138. cli := d.NewClientT(t)
  139. defer cli.Close()
  140. sw := d.GetSwarm(t)
  141. for _, fn := range f {
  142. fn(&sw.Spec)
  143. }
  144. err := cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, swarm.UpdateFlags{})
  145. assert.NilError(t, err)
  146. }
  147. // RotateTokens update the swarm to rotate tokens
  148. func (d *Daemon) RotateTokens(t testing.TB) {
  149. t.Helper()
  150. cli := d.NewClientT(t)
  151. defer cli.Close()
  152. sw, err := cli.SwarmInspect(context.Background())
  153. assert.NilError(t, err)
  154. flags := swarm.UpdateFlags{
  155. RotateManagerToken: true,
  156. RotateWorkerToken: true,
  157. }
  158. err = cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, flags)
  159. assert.NilError(t, err)
  160. }
  161. // JoinTokens returns the current swarm join tokens
  162. func (d *Daemon) JoinTokens(t testing.TB) swarm.JoinTokens {
  163. t.Helper()
  164. cli := d.NewClientT(t)
  165. defer cli.Close()
  166. sw, err := cli.SwarmInspect(context.Background())
  167. assert.NilError(t, err)
  168. return sw.JoinTokens
  169. }