github.com/duglin/docker@v1.13.1/integration-cli/daemon_swarm.go (about)

     1  package main
     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/pkg/integration/checker"
    14  	"github.com/go-check/check"
    15  )
    16  
    17  // SwarmDaemon is a test daemon with helpers for participating in a swarm.
    18  type SwarmDaemon struct {
    19  	*Daemon
    20  	swarm.Info
    21  	port       int
    22  	listenAddr string
    23  }
    24  
    25  // Init initializes a new swarm cluster.
    26  func (d *SwarmDaemon) Init(req swarm.InitRequest) error {
    27  	if req.ListenAddr == "" {
    28  		req.ListenAddr = d.listenAddr
    29  	}
    30  	status, out, err := d.SockRequest("POST", "/swarm/init", req)
    31  	if status != http.StatusOK {
    32  		return fmt.Errorf("initializing swarm: invalid statuscode %v, %q", status, out)
    33  	}
    34  	if err != nil {
    35  		return fmt.Errorf("initializing swarm: %v", err)
    36  	}
    37  	info, err := d.info()
    38  	if err != nil {
    39  		return err
    40  	}
    41  	d.Info = info
    42  	return nil
    43  }
    44  
    45  // Join joins a daemon to an existing cluster.
    46  func (d *SwarmDaemon) Join(req swarm.JoinRequest) error {
    47  	if req.ListenAddr == "" {
    48  		req.ListenAddr = d.listenAddr
    49  	}
    50  	status, out, err := d.SockRequest("POST", "/swarm/join", req)
    51  	if status != http.StatusOK {
    52  		return fmt.Errorf("joining swarm: invalid statuscode %v, %q", status, out)
    53  	}
    54  	if err != nil {
    55  		return fmt.Errorf("joining swarm: %v", err)
    56  	}
    57  	info, err := d.info()
    58  	if err != nil {
    59  		return err
    60  	}
    61  	d.Info = info
    62  	return nil
    63  }
    64  
    65  // Leave forces daemon to leave current cluster.
    66  func (d *SwarmDaemon) Leave(force bool) error {
    67  	url := "/swarm/leave"
    68  	if force {
    69  		url += "?force=1"
    70  	}
    71  	status, out, err := d.SockRequest("POST", url, nil)
    72  	if status != http.StatusOK {
    73  		return fmt.Errorf("leaving swarm: invalid statuscode %v, %q", status, out)
    74  	}
    75  	if err != nil {
    76  		err = fmt.Errorf("leaving swarm: %v", err)
    77  	}
    78  	return err
    79  }
    80  
    81  func (d *SwarmDaemon) info() (swarm.Info, error) {
    82  	var info struct {
    83  		Swarm swarm.Info
    84  	}
    85  	status, dt, err := d.SockRequest("GET", "/info", nil)
    86  	if status != http.StatusOK {
    87  		return info.Swarm, fmt.Errorf("get swarm info: invalid statuscode %v", status)
    88  	}
    89  	if err != nil {
    90  		return info.Swarm, fmt.Errorf("get swarm info: %v", err)
    91  	}
    92  	if err := json.Unmarshal(dt, &info); err != nil {
    93  		return info.Swarm, err
    94  	}
    95  	return info.Swarm, nil
    96  }
    97  
    98  type serviceConstructor func(*swarm.Service)
    99  type nodeConstructor func(*swarm.Node)
   100  type specConstructor func(*swarm.Spec)
   101  
   102  func (d *SwarmDaemon) createService(c *check.C, f ...serviceConstructor) string {
   103  	var service swarm.Service
   104  	for _, fn := range f {
   105  		fn(&service)
   106  	}
   107  	status, out, err := d.SockRequest("POST", "/services/create", service.Spec)
   108  
   109  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   110  	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf("output: %q", string(out)))
   111  
   112  	var scr types.ServiceCreateResponse
   113  	c.Assert(json.Unmarshal(out, &scr), checker.IsNil)
   114  	return scr.ID
   115  }
   116  
   117  func (d *SwarmDaemon) getService(c *check.C, id string) *swarm.Service {
   118  	var service swarm.Service
   119  	status, out, err := d.SockRequest("GET", "/services/"+id, nil)
   120  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   121  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   122  	c.Assert(json.Unmarshal(out, &service), checker.IsNil)
   123  	return &service
   124  }
   125  
   126  func (d *SwarmDaemon) getServiceTasks(c *check.C, service string) []swarm.Task {
   127  	var tasks []swarm.Task
   128  
   129  	filterArgs := filters.NewArgs()
   130  	filterArgs.Add("desired-state", "running")
   131  	filterArgs.Add("service", service)
   132  	filters, err := filters.ToParam(filterArgs)
   133  	c.Assert(err, checker.IsNil)
   134  
   135  	status, out, err := d.SockRequest("GET", "/tasks?filters="+filters, nil)
   136  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   137  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   138  	c.Assert(json.Unmarshal(out, &tasks), checker.IsNil)
   139  	return tasks
   140  }
   141  
   142  func (d *SwarmDaemon) checkServiceTasksInState(service string, state swarm.TaskState, message string) func(*check.C) (interface{}, check.CommentInterface) {
   143  	return func(c *check.C) (interface{}, check.CommentInterface) {
   144  		tasks := d.getServiceTasks(c, service)
   145  		var count int
   146  		for _, task := range tasks {
   147  			if task.Status.State == state {
   148  				if message == "" || strings.Contains(task.Status.Message, message) {
   149  					count++
   150  				}
   151  			}
   152  		}
   153  		return count, nil
   154  	}
   155  }
   156  
   157  func (d *SwarmDaemon) checkServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
   158  	return d.checkServiceTasksInState(service, swarm.TaskStateRunning, "")
   159  }
   160  
   161  func (d *SwarmDaemon) checkServiceUpdateState(service string) func(*check.C) (interface{}, check.CommentInterface) {
   162  	return func(c *check.C) (interface{}, check.CommentInterface) {
   163  		service := d.getService(c, service)
   164  		return service.UpdateStatus.State, nil
   165  	}
   166  }
   167  
   168  func (d *SwarmDaemon) checkServiceTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
   169  	return func(c *check.C) (interface{}, check.CommentInterface) {
   170  		tasks := d.getServiceTasks(c, service)
   171  		return len(tasks), nil
   172  	}
   173  }
   174  
   175  func (d *SwarmDaemon) checkRunningTaskImages(c *check.C) (interface{}, check.CommentInterface) {
   176  	var tasks []swarm.Task
   177  
   178  	filterArgs := filters.NewArgs()
   179  	filterArgs.Add("desired-state", "running")
   180  	filters, err := filters.ToParam(filterArgs)
   181  	c.Assert(err, checker.IsNil)
   182  
   183  	status, out, err := d.SockRequest("GET", "/tasks?filters="+filters, nil)
   184  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   185  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   186  	c.Assert(json.Unmarshal(out, &tasks), checker.IsNil)
   187  
   188  	result := make(map[string]int)
   189  	for _, task := range tasks {
   190  		if task.Status.State == swarm.TaskStateRunning {
   191  			result[task.Spec.ContainerSpec.Image]++
   192  		}
   193  	}
   194  	return result, nil
   195  }
   196  
   197  func (d *SwarmDaemon) checkNodeReadyCount(c *check.C) (interface{}, check.CommentInterface) {
   198  	nodes := d.listNodes(c)
   199  	var readyCount int
   200  	for _, node := range nodes {
   201  		if node.Status.State == swarm.NodeStateReady {
   202  			readyCount++
   203  		}
   204  	}
   205  	return readyCount, nil
   206  }
   207  
   208  func (d *SwarmDaemon) getTask(c *check.C, id string) swarm.Task {
   209  	var task swarm.Task
   210  
   211  	status, out, err := d.SockRequest("GET", "/tasks/"+id, nil)
   212  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   213  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   214  	c.Assert(json.Unmarshal(out, &task), checker.IsNil)
   215  	return task
   216  }
   217  
   218  func (d *SwarmDaemon) updateService(c *check.C, service *swarm.Service, f ...serviceConstructor) {
   219  	for _, fn := range f {
   220  		fn(service)
   221  	}
   222  	url := fmt.Sprintf("/services/%s/update?version=%d", service.ID, service.Version.Index)
   223  	status, out, err := d.SockRequest("POST", url, service.Spec)
   224  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   225  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   226  }
   227  
   228  func (d *SwarmDaemon) removeService(c *check.C, id string) {
   229  	status, out, err := d.SockRequest("DELETE", "/services/"+id, nil)
   230  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   231  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   232  }
   233  
   234  func (d *SwarmDaemon) getNode(c *check.C, id string) *swarm.Node {
   235  	var node swarm.Node
   236  	status, out, err := d.SockRequest("GET", "/nodes/"+id, nil)
   237  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   238  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   239  	c.Assert(json.Unmarshal(out, &node), checker.IsNil)
   240  	c.Assert(node.ID, checker.Equals, id)
   241  	return &node
   242  }
   243  
   244  func (d *SwarmDaemon) removeNode(c *check.C, id string, force bool) {
   245  	url := "/nodes/" + id
   246  	if force {
   247  		url += "?force=1"
   248  	}
   249  
   250  	status, out, err := d.SockRequest("DELETE", url, nil)
   251  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   252  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   253  }
   254  
   255  func (d *SwarmDaemon) updateNode(c *check.C, id string, f ...nodeConstructor) {
   256  	for i := 0; ; i++ {
   257  		node := d.getNode(c, id)
   258  		for _, fn := range f {
   259  			fn(node)
   260  		}
   261  		url := fmt.Sprintf("/nodes/%s/update?version=%d", node.ID, node.Version.Index)
   262  		status, out, err := d.SockRequest("POST", url, node.Spec)
   263  		if i < 10 && strings.Contains(string(out), "update out of sequence") {
   264  			time.Sleep(100 * time.Millisecond)
   265  			continue
   266  		}
   267  		c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   268  		c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   269  		return
   270  	}
   271  }
   272  
   273  func (d *SwarmDaemon) listNodes(c *check.C) []swarm.Node {
   274  	status, out, err := d.SockRequest("GET", "/nodes", nil)
   275  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   276  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   277  
   278  	nodes := []swarm.Node{}
   279  	c.Assert(json.Unmarshal(out, &nodes), checker.IsNil)
   280  	return nodes
   281  }
   282  
   283  func (d *SwarmDaemon) listServices(c *check.C) []swarm.Service {
   284  	status, out, err := d.SockRequest("GET", "/services", nil)
   285  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   286  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   287  
   288  	services := []swarm.Service{}
   289  	c.Assert(json.Unmarshal(out, &services), checker.IsNil)
   290  	return services
   291  }
   292  
   293  func (d *SwarmDaemon) createSecret(c *check.C, secretSpec swarm.SecretSpec) string {
   294  	status, out, err := d.SockRequest("POST", "/secrets/create", secretSpec)
   295  
   296  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   297  	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf("output: %q", string(out)))
   298  
   299  	var scr types.SecretCreateResponse
   300  	c.Assert(json.Unmarshal(out, &scr), checker.IsNil)
   301  	return scr.ID
   302  }
   303  
   304  func (d *SwarmDaemon) listSecrets(c *check.C) []swarm.Secret {
   305  	status, out, err := d.SockRequest("GET", "/secrets", nil)
   306  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   307  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   308  
   309  	secrets := []swarm.Secret{}
   310  	c.Assert(json.Unmarshal(out, &secrets), checker.IsNil)
   311  	return secrets
   312  }
   313  
   314  func (d *SwarmDaemon) getSecret(c *check.C, id string) *swarm.Secret {
   315  	var secret swarm.Secret
   316  	status, out, err := d.SockRequest("GET", "/secrets/"+id, nil)
   317  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   318  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   319  	c.Assert(json.Unmarshal(out, &secret), checker.IsNil)
   320  	return &secret
   321  }
   322  
   323  func (d *SwarmDaemon) deleteSecret(c *check.C, id string) {
   324  	status, out, err := d.SockRequest("DELETE", "/secrets/"+id, nil)
   325  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   326  	c.Assert(status, checker.Equals, http.StatusNoContent, check.Commentf("output: %q", string(out)))
   327  }
   328  
   329  func (d *SwarmDaemon) getSwarm(c *check.C) swarm.Swarm {
   330  	var sw swarm.Swarm
   331  	status, out, err := d.SockRequest("GET", "/swarm", nil)
   332  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   333  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   334  	c.Assert(json.Unmarshal(out, &sw), checker.IsNil)
   335  	return sw
   336  }
   337  
   338  func (d *SwarmDaemon) updateSwarm(c *check.C, f ...specConstructor) {
   339  	sw := d.getSwarm(c)
   340  	for _, fn := range f {
   341  		fn(&sw.Spec)
   342  	}
   343  	url := fmt.Sprintf("/swarm/update?version=%d", sw.Version.Index)
   344  	status, out, err := d.SockRequest("POST", url, sw.Spec)
   345  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   346  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   347  }
   348  
   349  func (d *SwarmDaemon) rotateTokens(c *check.C) {
   350  	var sw swarm.Swarm
   351  	status, out, err := d.SockRequest("GET", "/swarm", nil)
   352  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   353  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   354  	c.Assert(json.Unmarshal(out, &sw), checker.IsNil)
   355  
   356  	url := fmt.Sprintf("/swarm/update?version=%d&rotateWorkerToken=true&rotateManagerToken=true", sw.Version.Index)
   357  	status, out, err = d.SockRequest("POST", url, sw.Spec)
   358  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   359  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   360  }
   361  
   362  func (d *SwarmDaemon) joinTokens(c *check.C) swarm.JoinTokens {
   363  	var sw swarm.Swarm
   364  	status, out, err := d.SockRequest("GET", "/swarm", nil)
   365  	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
   366  	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
   367  	c.Assert(json.Unmarshal(out, &sw), checker.IsNil)
   368  	return sw.JoinTokens
   369  }
   370  
   371  func (d *SwarmDaemon) checkLocalNodeState(c *check.C) (interface{}, check.CommentInterface) {
   372  	info, err := d.info()
   373  	c.Assert(err, checker.IsNil)
   374  	return info.LocalNodeState, nil
   375  }
   376  
   377  func (d *SwarmDaemon) checkControlAvailable(c *check.C) (interface{}, check.CommentInterface) {
   378  	info, err := d.info()
   379  	c.Assert(err, checker.IsNil)
   380  	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
   381  	return info.ControlAvailable, nil
   382  }
   383  
   384  func (d *SwarmDaemon) checkLeader(c *check.C) (interface{}, check.CommentInterface) {
   385  	errList := check.Commentf("could not get node list")
   386  	status, out, err := d.SockRequest("GET", "/nodes", nil)
   387  	if err != nil {
   388  		return err, errList
   389  	}
   390  	if status != http.StatusOK {
   391  		return fmt.Errorf("expected http status OK, got: %d", status), errList
   392  	}
   393  
   394  	var ls []swarm.Node
   395  	if err := json.Unmarshal(out, &ls); err != nil {
   396  		return err, errList
   397  	}
   398  
   399  	for _, node := range ls {
   400  		if node.ManagerStatus != nil && node.ManagerStatus.Leader {
   401  			return nil, nil
   402  		}
   403  	}
   404  	return fmt.Errorf("no leader"), check.Commentf("could not find leader")
   405  }
   406  
   407  func (d *SwarmDaemon) cmdRetryOutOfSequence(args ...string) (string, error) {
   408  	for i := 0; ; i++ {
   409  		out, err := d.Cmd(args...)
   410  		if err != nil {
   411  			if strings.Contains(out, "update out of sequence") {
   412  				if i < 10 {
   413  					continue
   414  				}
   415  			}
   416  		}
   417  		return out, err
   418  	}
   419  }