github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/integration-cli/daemon/daemon_swarm.go (about)

     1  package daemon
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/docker/docker/api/types"
    11  	"github.com/docker/docker/api/types/filters"
    12  	"github.com/docker/docker/api/types/swarm"
    13  	"github.com/docker/docker/integration-cli/checker"
    14  	"github.com/go-check/check"
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  // Swarm is a test daemon with helpers for participating in a swarm.
    19  type Swarm struct {
    20  	*Daemon
    21  	swarm.Info
    22  	Port       int
    23  	ListenAddr string
    24  }
    25  
    26  // Init initializes a new swarm cluster.
    27  func (d *Swarm) Init(req swarm.InitRequest) error {
    28  	if req.ListenAddr == "" {
    29  		req.ListenAddr = d.ListenAddr
    30  	}
    31  	status, out, err := d.SockRequest("POST", "/swarm/init", req)
    32  	if status != http.StatusOK {
    33  		return fmt.Errorf("initializing swarm: invalid statuscode %v, %q", status, out)
    34  	}
    35  	if err != nil {
    36  		return fmt.Errorf("initializing swarm: %v", err)
    37  	}
    38  	info, err := d.SwarmInfo()
    39  	if err != nil {
    40  		return err
    41  	}
    42  	d.Info = info
    43  	return nil
    44  }
    45  
    46  // Join joins a daemon to an existing cluster.
    47  func (d *Swarm) Join(req swarm.JoinRequest) error {
    48  	if req.ListenAddr == "" {
    49  		req.ListenAddr = d.ListenAddr
    50  	}
    51  	status, out, err := d.SockRequest("POST", "/swarm/join", req)
    52  	if status != http.StatusOK {
    53  		return fmt.Errorf("joining swarm: invalid statuscode %v, %q", status, out)
    54  	}
    55  	if err != nil {
    56  		return fmt.Errorf("joining swarm: %v", err)
    57  	}
    58  	info, err := d.SwarmInfo()
    59  	if err != nil {
    60  		return err
    61  	}
    62  	d.Info = info
    63  	return nil
    64  }
    65  
    66  // Leave forces daemon to leave current cluster.
    67  func (d *Swarm) Leave(force bool) error {
    68  	url := "/swarm/leave"
    69  	if force {
    70  		url += "?force=1"
    71  	}
    72  	status, out, err := d.SockRequest("POST", url, nil)
    73  	if status != http.StatusOK {
    74  		return fmt.Errorf("leaving swarm: invalid statuscode %v, %q", status, out)
    75  	}
    76  	if err != nil {
    77  		err = fmt.Errorf("leaving swarm: %v", err)
    78  	}
    79  	return err
    80  }
    81  
    82  // SwarmInfo returns the swarm information of the daemon
    83  func (d *Swarm) SwarmInfo() (swarm.Info, error) {
    84  	var info struct {
    85  		Swarm swarm.Info
    86  	}
    87  	status, dt, err := d.SockRequest("GET", "/info", nil)
    88  	if status != http.StatusOK {
    89  		return info.Swarm, fmt.Errorf("get swarm info: invalid statuscode %v", status)
    90  	}
    91  	if err != nil {
    92  		return info.Swarm, fmt.Errorf("get swarm info: %v", err)
    93  	}
    94  	if err := json.Unmarshal(dt, &info); err != nil {
    95  		return info.Swarm, err
    96  	}
    97  	return info.Swarm, nil
    98  }
    99  
   100  // Unlock tries to unlock a locked swarm
   101  func (d *Swarm) Unlock(req swarm.UnlockRequest) error {
   102  	status, out, err := d.SockRequest("POST", "/swarm/unlock", req)
   103  	if status != http.StatusOK {
   104  		return fmt.Errorf("unlocking swarm: invalid statuscode %v, %q", status, out)
   105  	}
   106  	if err != nil {
   107  		err = errors.Wrap(err, "unlocking swarm")
   108  	}
   109  	return err
   110  }
   111  
   112  // ServiceConstructor defines a swarm service constructor function
   113  type ServiceConstructor func(*swarm.Service)
   114  
   115  // NodeConstructor defines a swarm node constructor
   116  type NodeConstructor func(*swarm.Node)
   117  
   118  // SpecConstructor defines a swarm spec constructor
   119  type SpecConstructor func(*swarm.Spec)
   120  
   121  // CreateService creates a swarm service given the specified service constructor
   122  func (d *Swarm) CreateService(c *check.C, f ...ServiceConstructor) string {
   123  	var service swarm.Service
   124  	for _, fn := range f {
   125  		fn(&service)
   126  	}
   127  	status, out, err := d.SockRequest("POST", "/services/create", service.Spec)
   128  
   129  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   130  	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf("output: %q", string(out)))
   131  
   132  	var scr types.ServiceCreateResponse
   133  	c.Assert(json.Unmarshal(out, &scr), checker.IsNil)
   134  	return scr.ID
   135  }
   136  
   137  // GetService returns the swarm service corresponding to the specified id
   138  func (d *Swarm) GetService(c *check.C, id string) *swarm.Service {
   139  	var service swarm.Service
   140  	status, out, err := d.SockRequest("GET", "/services/"+id, nil)
   141  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   142  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   143  	c.Assert(json.Unmarshal(out, &service), checker.IsNil)
   144  	return &service
   145  }
   146  
   147  // GetServiceTasks returns the swarm tasks for the specified service
   148  func (d *Swarm) GetServiceTasks(c *check.C, service string) []swarm.Task {
   149  	var tasks []swarm.Task
   150  
   151  	filterArgs := filters.NewArgs()
   152  	filterArgs.Add("desired-state", "running")
   153  	filterArgs.Add("service", service)
   154  	filters, err := filters.ToParam(filterArgs)
   155  	c.Assert(err, checker.IsNil)
   156  
   157  	status, out, err := d.SockRequest("GET", "/tasks?filters="+filters, nil)
   158  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   159  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   160  	c.Assert(json.Unmarshal(out, &tasks), checker.IsNil)
   161  	return tasks
   162  }
   163  
   164  // CheckServiceTasksInState returns the number of tasks with a matching state,
   165  // and optional message substring.
   166  func (d *Swarm) CheckServiceTasksInState(service string, state swarm.TaskState, message string) func(*check.C) (interface{}, check.CommentInterface) {
   167  	return func(c *check.C) (interface{}, check.CommentInterface) {
   168  		tasks := d.GetServiceTasks(c, service)
   169  		var count int
   170  		for _, task := range tasks {
   171  			if task.Status.State == state {
   172  				if message == "" || strings.Contains(task.Status.Message, message) {
   173  					count++
   174  				}
   175  			}
   176  		}
   177  		return count, nil
   178  	}
   179  }
   180  
   181  // CheckServiceRunningTasks returns the number of running tasks for the specified service
   182  func (d *Swarm) CheckServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
   183  	return d.CheckServiceTasksInState(service, swarm.TaskStateRunning, "")
   184  }
   185  
   186  // CheckServiceUpdateState returns the current update state for the specified service
   187  func (d *Swarm) CheckServiceUpdateState(service string) func(*check.C) (interface{}, check.CommentInterface) {
   188  	return func(c *check.C) (interface{}, check.CommentInterface) {
   189  		service := d.GetService(c, service)
   190  		if service.UpdateStatus == nil {
   191  			return "", nil
   192  		}
   193  		return service.UpdateStatus.State, nil
   194  	}
   195  }
   196  
   197  // CheckServiceTasks returns the number of tasks for the specified service
   198  func (d *Swarm) CheckServiceTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
   199  	return func(c *check.C) (interface{}, check.CommentInterface) {
   200  		tasks := d.GetServiceTasks(c, service)
   201  		return len(tasks), nil
   202  	}
   203  }
   204  
   205  // CheckRunningTaskImages returns the number of different images attached to a running task
   206  func (d *Swarm) CheckRunningTaskImages(c *check.C) (interface{}, check.CommentInterface) {
   207  	var tasks []swarm.Task
   208  
   209  	filterArgs := filters.NewArgs()
   210  	filterArgs.Add("desired-state", "running")
   211  	filters, err := filters.ToParam(filterArgs)
   212  	c.Assert(err, checker.IsNil)
   213  
   214  	status, out, err := d.SockRequest("GET", "/tasks?filters="+filters, nil)
   215  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   216  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   217  	c.Assert(json.Unmarshal(out, &tasks), checker.IsNil)
   218  
   219  	result := make(map[string]int)
   220  	for _, task := range tasks {
   221  		if task.Status.State == swarm.TaskStateRunning {
   222  			result[task.Spec.ContainerSpec.Image]++
   223  		}
   224  	}
   225  	return result, nil
   226  }
   227  
   228  // CheckNodeReadyCount returns the number of ready node on the swarm
   229  func (d *Swarm) CheckNodeReadyCount(c *check.C) (interface{}, check.CommentInterface) {
   230  	nodes := d.ListNodes(c)
   231  	var readyCount int
   232  	for _, node := range nodes {
   233  		if node.Status.State == swarm.NodeStateReady {
   234  			readyCount++
   235  		}
   236  	}
   237  	return readyCount, nil
   238  }
   239  
   240  // GetTask returns the swarm task identified by the specified id
   241  func (d *Swarm) GetTask(c *check.C, id string) swarm.Task {
   242  	var task swarm.Task
   243  
   244  	status, out, err := d.SockRequest("GET", "/tasks/"+id, nil)
   245  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   246  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   247  	c.Assert(json.Unmarshal(out, &task), checker.IsNil)
   248  	return task
   249  }
   250  
   251  // UpdateService updates a swarm service with the specified service constructor
   252  func (d *Swarm) UpdateService(c *check.C, service *swarm.Service, f ...ServiceConstructor) {
   253  	for _, fn := range f {
   254  		fn(service)
   255  	}
   256  	url := fmt.Sprintf("/services/%s/update?version=%d", service.ID, service.Version.Index)
   257  	status, out, err := d.SockRequest("POST", url, service.Spec)
   258  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   259  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   260  }
   261  
   262  // RemoveService removes the specified service
   263  func (d *Swarm) RemoveService(c *check.C, id string) {
   264  	status, out, err := d.SockRequest("DELETE", "/services/"+id, nil)
   265  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   266  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   267  }
   268  
   269  // GetNode returns a swarm node identified by the specified id
   270  func (d *Swarm) GetNode(c *check.C, id string) *swarm.Node {
   271  	var node swarm.Node
   272  	status, out, err := d.SockRequest("GET", "/nodes/"+id, nil)
   273  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   274  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   275  	c.Assert(json.Unmarshal(out, &node), checker.IsNil)
   276  	c.Assert(node.ID, checker.Equals, id)
   277  	return &node
   278  }
   279  
   280  // RemoveNode removes the specified node
   281  func (d *Swarm) RemoveNode(c *check.C, id string, force bool) {
   282  	url := "/nodes/" + id
   283  	if force {
   284  		url += "?force=1"
   285  	}
   286  
   287  	status, out, err := d.SockRequest("DELETE", url, nil)
   288  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   289  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   290  }
   291  
   292  // UpdateNode updates a swarm node with the specified node constructor
   293  func (d *Swarm) UpdateNode(c *check.C, id string, f ...NodeConstructor) {
   294  	for i := 0; ; i++ {
   295  		node := d.GetNode(c, id)
   296  		for _, fn := range f {
   297  			fn(node)
   298  		}
   299  		url := fmt.Sprintf("/nodes/%s/update?version=%d", node.ID, node.Version.Index)
   300  		status, out, err := d.SockRequest("POST", url, node.Spec)
   301  		if i < 10 && strings.Contains(string(out), "update out of sequence") {
   302  			time.Sleep(100 * time.Millisecond)
   303  			continue
   304  		}
   305  		c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   306  		c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   307  		return
   308  	}
   309  }
   310  
   311  // ListNodes returns the list of the current swarm nodes
   312  func (d *Swarm) ListNodes(c *check.C) []swarm.Node {
   313  	status, out, err := d.SockRequest("GET", "/nodes", nil)
   314  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   315  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   316  
   317  	nodes := []swarm.Node{}
   318  	c.Assert(json.Unmarshal(out, &nodes), checker.IsNil)
   319  	return nodes
   320  }
   321  
   322  // ListServices return the list of the current swarm services
   323  func (d *Swarm) ListServices(c *check.C) []swarm.Service {
   324  	status, out, err := d.SockRequest("GET", "/services", nil)
   325  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   326  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   327  
   328  	services := []swarm.Service{}
   329  	c.Assert(json.Unmarshal(out, &services), checker.IsNil)
   330  	return services
   331  }
   332  
   333  // CreateSecret creates a secret given the specified spec
   334  func (d *Swarm) CreateSecret(c *check.C, secretSpec swarm.SecretSpec) string {
   335  	status, out, err := d.SockRequest("POST", "/secrets/create", secretSpec)
   336  
   337  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   338  	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf("output: %q", string(out)))
   339  
   340  	var scr types.SecretCreateResponse
   341  	c.Assert(json.Unmarshal(out, &scr), checker.IsNil)
   342  	return scr.ID
   343  }
   344  
   345  // ListSecrets returns the list of the current swarm secrets
   346  func (d *Swarm) ListSecrets(c *check.C) []swarm.Secret {
   347  	status, out, err := d.SockRequest("GET", "/secrets", nil)
   348  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   349  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   350  
   351  	secrets := []swarm.Secret{}
   352  	c.Assert(json.Unmarshal(out, &secrets), checker.IsNil)
   353  	return secrets
   354  }
   355  
   356  // GetSecret returns a swarm secret identified by the specified id
   357  func (d *Swarm) GetSecret(c *check.C, id string) *swarm.Secret {
   358  	var secret swarm.Secret
   359  	status, out, err := d.SockRequest("GET", "/secrets/"+id, nil)
   360  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   361  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   362  	c.Assert(json.Unmarshal(out, &secret), checker.IsNil)
   363  	return &secret
   364  }
   365  
   366  // DeleteSecret removes the swarm secret identified by the specified id
   367  func (d *Swarm) DeleteSecret(c *check.C, id string) {
   368  	status, out, err := d.SockRequest("DELETE", "/secrets/"+id, nil)
   369  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   370  	c.Assert(status, checker.Equals, http.StatusNoContent, check.Commentf("output: %q", string(out)))
   371  }
   372  
   373  // GetSwarm return the current swarm object
   374  func (d *Swarm) GetSwarm(c *check.C) swarm.Swarm {
   375  	var sw swarm.Swarm
   376  	status, out, err := d.SockRequest("GET", "/swarm", nil)
   377  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   378  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   379  	c.Assert(json.Unmarshal(out, &sw), checker.IsNil)
   380  	return sw
   381  }
   382  
   383  // UpdateSwarm updates the current swarm object with the specified spec constructors
   384  func (d *Swarm) UpdateSwarm(c *check.C, f ...SpecConstructor) {
   385  	sw := d.GetSwarm(c)
   386  	for _, fn := range f {
   387  		fn(&sw.Spec)
   388  	}
   389  	url := fmt.Sprintf("/swarm/update?version=%d", sw.Version.Index)
   390  	status, out, err := d.SockRequest("POST", url, sw.Spec)
   391  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   392  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   393  }
   394  
   395  // RotateTokens update the swarm to rotate tokens
   396  func (d *Swarm) RotateTokens(c *check.C) {
   397  	var sw swarm.Swarm
   398  	status, out, err := d.SockRequest("GET", "/swarm", nil)
   399  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   400  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   401  	c.Assert(json.Unmarshal(out, &sw), checker.IsNil)
   402  
   403  	url := fmt.Sprintf("/swarm/update?version=%d&rotateWorkerToken=true&rotateManagerToken=true", sw.Version.Index)
   404  	status, out, err = d.SockRequest("POST", url, sw.Spec)
   405  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   406  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   407  }
   408  
   409  // JoinTokens returns the current swarm join tokens
   410  func (d *Swarm) JoinTokens(c *check.C) swarm.JoinTokens {
   411  	var sw swarm.Swarm
   412  	status, out, err := d.SockRequest("GET", "/swarm", nil)
   413  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   414  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   415  	c.Assert(json.Unmarshal(out, &sw), checker.IsNil)
   416  	return sw.JoinTokens
   417  }
   418  
   419  // CheckLocalNodeState returns the current swarm node state
   420  func (d *Swarm) CheckLocalNodeState(c *check.C) (interface{}, check.CommentInterface) {
   421  	info, err := d.SwarmInfo()
   422  	c.Assert(err, checker.IsNil)
   423  	return info.LocalNodeState, nil
   424  }
   425  
   426  // CheckControlAvailable returns the current swarm control available
   427  func (d *Swarm) CheckControlAvailable(c *check.C) (interface{}, check.CommentInterface) {
   428  	info, err := d.SwarmInfo()
   429  	c.Assert(err, checker.IsNil)
   430  	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
   431  	return info.ControlAvailable, nil
   432  }
   433  
   434  // CheckLeader returns whether there is a leader on the swarm or not
   435  func (d *Swarm) CheckLeader(c *check.C) (interface{}, check.CommentInterface) {
   436  	errList := check.Commentf("could not get node list")
   437  	status, out, err := d.SockRequest("GET", "/nodes", nil)
   438  	if err != nil {
   439  		return err, errList
   440  	}
   441  	if status != http.StatusOK {
   442  		return fmt.Errorf("expected http status OK, got: %d", status), errList
   443  	}
   444  
   445  	var ls []swarm.Node
   446  	if err := json.Unmarshal(out, &ls); err != nil {
   447  		return err, errList
   448  	}
   449  
   450  	for _, node := range ls {
   451  		if node.ManagerStatus != nil && node.ManagerStatus.Leader {
   452  			return nil, nil
   453  		}
   454  	}
   455  	return fmt.Errorf("no leader"), check.Commentf("could not find leader")
   456  }
   457  
   458  // CmdRetryOutOfSequence tries the specified command against the current daemon for 10 times
   459  func (d *Swarm) CmdRetryOutOfSequence(args ...string) (string, error) {
   460  	for i := 0; ; i++ {
   461  		out, err := d.Cmd(args...)
   462  		if err != nil {
   463  			if strings.Contains(out, "update out of sequence") {
   464  				if i < 10 {
   465  					continue
   466  				}
   467  			}
   468  		}
   469  		return out, err
   470  	}
   471  }