123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647 |
- package daemon
- import (
- "encoding/json"
- "fmt"
- "net/http"
- "strings"
- "time"
- "github.com/docker/docker/api/types"
- "github.com/docker/docker/api/types/filters"
- "github.com/docker/docker/api/types/swarm"
- "github.com/docker/docker/integration-cli/checker"
- "github.com/go-check/check"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- )
- // Swarm is a test daemon with helpers for participating in a swarm.
- type Swarm struct {
- *Daemon
- swarm.Info
- Port int
- ListenAddr string
- }
- // Init initializes a new swarm cluster.
- func (d *Swarm) Init(req swarm.InitRequest) error {
- if req.ListenAddr == "" {
- req.ListenAddr = d.ListenAddr
- }
- cli, err := d.NewClient()
- if err != nil {
- return fmt.Errorf("initializing swarm: failed to create client %v", err)
- }
- defer cli.Close()
- _, err = cli.SwarmInit(context.Background(), req)
- if err != nil {
- return fmt.Errorf("initializing swarm: %v", err)
- }
- info, err := d.SwarmInfo()
- if err != nil {
- return err
- }
- d.Info = info
- return nil
- }
- // Join joins a daemon to an existing cluster.
- func (d *Swarm) Join(req swarm.JoinRequest) error {
- if req.ListenAddr == "" {
- req.ListenAddr = d.ListenAddr
- }
- cli, err := d.NewClient()
- if err != nil {
- return fmt.Errorf("joining swarm: failed to create client %v", err)
- }
- defer cli.Close()
- err = cli.SwarmJoin(context.Background(), req)
- if err != nil {
- return fmt.Errorf("joining swarm: %v", err)
- }
- info, err := d.SwarmInfo()
- if err != nil {
- return err
- }
- d.Info = info
- return nil
- }
- // Leave forces daemon to leave current cluster.
- func (d *Swarm) Leave(force bool) error {
- cli, err := d.NewClient()
- if err != nil {
- return fmt.Errorf("leaving swarm: failed to create client %v", err)
- }
- defer cli.Close()
- err = cli.SwarmLeave(context.Background(), force)
- if err != nil {
- err = fmt.Errorf("leaving swarm: %v", err)
- }
- return err
- }
- // SwarmInfo returns the swarm information of the daemon
- func (d *Swarm) SwarmInfo() (swarm.Info, error) {
- cli, err := d.NewClient()
- if err != nil {
- return swarm.Info{}, fmt.Errorf("get swarm info: %v", err)
- }
- info, err := cli.Info(context.Background())
- if err != nil {
- return swarm.Info{}, fmt.Errorf("get swarm info: %v", err)
- }
- return info.Swarm, nil
- }
- // Unlock tries to unlock a locked swarm
- func (d *Swarm) Unlock(req swarm.UnlockRequest) error {
- cli, err := d.NewClient()
- if err != nil {
- return fmt.Errorf("unlocking swarm: failed to create client %v", err)
- }
- defer cli.Close()
- err = cli.SwarmUnlock(context.Background(), req)
- if err != nil {
- err = errors.Wrap(err, "unlocking swarm")
- }
- return err
- }
- // ServiceConstructor defines a swarm service constructor function
- type ServiceConstructor func(*swarm.Service)
- // NodeConstructor defines a swarm node constructor
- type NodeConstructor func(*swarm.Node)
- // SecretConstructor defines a swarm secret constructor
- type SecretConstructor func(*swarm.Secret)
- // ConfigConstructor defines a swarm config constructor
- type ConfigConstructor func(*swarm.Config)
- // SpecConstructor defines a swarm spec constructor
- type SpecConstructor func(*swarm.Spec)
- // CreateServiceWithOptions creates a swarm service given the specified service constructors
- // and auth config
- func (d *Swarm) CreateServiceWithOptions(c *check.C, opts types.ServiceCreateOptions, f ...ServiceConstructor) string {
- var service swarm.Service
- for _, fn := range f {
- fn(&service)
- }
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
- defer cancel()
- res, err := cli.ServiceCreate(ctx, service.Spec, opts)
- c.Assert(err, checker.IsNil)
- return res.ID
- }
- // CreateService creates a swarm service given the specified service constructor
- func (d *Swarm) CreateService(c *check.C, f ...ServiceConstructor) string {
- return d.CreateServiceWithOptions(c, types.ServiceCreateOptions{}, f...)
- }
- // GetService returns the swarm service corresponding to the specified id
- func (d *Swarm) GetService(c *check.C, id string) *swarm.Service {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- service, _, err := cli.ServiceInspectWithRaw(context.Background(), id, types.ServiceInspectOptions{})
- c.Assert(err, checker.IsNil)
- return &service
- }
- // GetServiceTasks returns the swarm tasks for the specified service
- func (d *Swarm) GetServiceTasks(c *check.C, service string) []swarm.Task {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- filterArgs := filters.NewArgs()
- filterArgs.Add("desired-state", "running")
- filterArgs.Add("service", service)
- options := types.TaskListOptions{
- Filters: filterArgs,
- }
- tasks, err := cli.TaskList(context.Background(), options)
- c.Assert(err, checker.IsNil)
- return tasks
- }
- // CheckServiceTasksInState returns the number of tasks with a matching state,
- // and optional message substring.
- func (d *Swarm) CheckServiceTasksInState(service string, state swarm.TaskState, message string) func(*check.C) (interface{}, check.CommentInterface) {
- return func(c *check.C) (interface{}, check.CommentInterface) {
- tasks := d.GetServiceTasks(c, service)
- var count int
- for _, task := range tasks {
- if task.Status.State == state {
- if message == "" || strings.Contains(task.Status.Message, message) {
- count++
- }
- }
- }
- return count, nil
- }
- }
- // CheckServiceRunningTasks returns the number of running tasks for the specified service
- func (d *Swarm) CheckServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
- return d.CheckServiceTasksInState(service, swarm.TaskStateRunning, "")
- }
- // CheckServiceUpdateState returns the current update state for the specified service
- func (d *Swarm) CheckServiceUpdateState(service string) func(*check.C) (interface{}, check.CommentInterface) {
- return func(c *check.C) (interface{}, check.CommentInterface) {
- service := d.GetService(c, service)
- if service.UpdateStatus == nil {
- return "", nil
- }
- return service.UpdateStatus.State, nil
- }
- }
- // CheckPluginRunning returns the runtime state of the plugin
- func (d *Swarm) CheckPluginRunning(plugin string) func(c *check.C) (interface{}, check.CommentInterface) {
- return func(c *check.C) (interface{}, check.CommentInterface) {
- status, out, err := d.SockRequest("GET", "/plugins/"+plugin+"/json", nil)
- c.Assert(err, checker.IsNil, check.Commentf(string(out)))
- if status != http.StatusOK {
- return false, nil
- }
- var p types.Plugin
- c.Assert(json.Unmarshal(out, &p), checker.IsNil, check.Commentf(string(out)))
- return p.Enabled, check.Commentf("%+v", p)
- }
- }
- // CheckPluginImage returns the runtime state of the plugin
- func (d *Swarm) CheckPluginImage(plugin string) func(c *check.C) (interface{}, check.CommentInterface) {
- return func(c *check.C) (interface{}, check.CommentInterface) {
- status, out, err := d.SockRequest("GET", "/plugins/"+plugin+"/json", nil)
- c.Assert(err, checker.IsNil, check.Commentf(string(out)))
- if status != http.StatusOK {
- return false, nil
- }
- var p types.Plugin
- c.Assert(json.Unmarshal(out, &p), checker.IsNil, check.Commentf(string(out)))
- return p.PluginReference, check.Commentf("%+v", p)
- }
- }
- // CheckServiceTasks returns the number of tasks for the specified service
- func (d *Swarm) CheckServiceTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
- return func(c *check.C) (interface{}, check.CommentInterface) {
- tasks := d.GetServiceTasks(c, service)
- return len(tasks), nil
- }
- }
- // CheckRunningTaskNetworks returns the number of times each network is referenced from a task.
- func (d *Swarm) CheckRunningTaskNetworks(c *check.C) (interface{}, check.CommentInterface) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- filterArgs := filters.NewArgs()
- filterArgs.Add("desired-state", "running")
- options := types.TaskListOptions{
- Filters: filterArgs,
- }
- tasks, err := cli.TaskList(context.Background(), options)
- c.Assert(err, checker.IsNil)
- result := make(map[string]int)
- for _, task := range tasks {
- for _, network := range task.Spec.Networks {
- result[network.Target]++
- }
- }
- return result, nil
- }
- // CheckRunningTaskImages returns the times each image is running as a task.
- func (d *Swarm) CheckRunningTaskImages(c *check.C) (interface{}, check.CommentInterface) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- filterArgs := filters.NewArgs()
- filterArgs.Add("desired-state", "running")
- options := types.TaskListOptions{
- Filters: filterArgs,
- }
- tasks, err := cli.TaskList(context.Background(), options)
- c.Assert(err, checker.IsNil)
- result := make(map[string]int)
- for _, task := range tasks {
- if task.Status.State == swarm.TaskStateRunning && task.Spec.ContainerSpec != nil {
- result[task.Spec.ContainerSpec.Image]++
- }
- }
- return result, nil
- }
- // CheckNodeReadyCount returns the number of ready node on the swarm
- func (d *Swarm) CheckNodeReadyCount(c *check.C) (interface{}, check.CommentInterface) {
- nodes := d.ListNodes(c)
- var readyCount int
- for _, node := range nodes {
- if node.Status.State == swarm.NodeStateReady {
- readyCount++
- }
- }
- return readyCount, nil
- }
- // GetTask returns the swarm task identified by the specified id
- func (d *Swarm) GetTask(c *check.C, id string) swarm.Task {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- task, _, err := cli.TaskInspectWithRaw(context.Background(), id)
- c.Assert(err, checker.IsNil)
- return task
- }
- // UpdateService updates a swarm service with the specified service constructor
- func (d *Swarm) UpdateService(c *check.C, service *swarm.Service, f ...ServiceConstructor) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- for _, fn := range f {
- fn(service)
- }
- _, err = cli.ServiceUpdate(context.Background(), service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{})
- c.Assert(err, checker.IsNil)
- }
- // RemoveService removes the specified service
- func (d *Swarm) RemoveService(c *check.C, id string) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- err = cli.ServiceRemove(context.Background(), id)
- c.Assert(err, checker.IsNil)
- }
- // GetNode returns a swarm node identified by the specified id
- func (d *Swarm) GetNode(c *check.C, id string) *swarm.Node {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- node, _, err := cli.NodeInspectWithRaw(context.Background(), id)
- c.Assert(err, checker.IsNil)
- c.Assert(node.ID, checker.Equals, id)
- return &node
- }
- // RemoveNode removes the specified node
- func (d *Swarm) RemoveNode(c *check.C, id string, force bool) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- options := types.NodeRemoveOptions{
- Force: force,
- }
- err = cli.NodeRemove(context.Background(), id, options)
- c.Assert(err, checker.IsNil)
- }
- // UpdateNode updates a swarm node with the specified node constructor
- func (d *Swarm) UpdateNode(c *check.C, id string, f ...NodeConstructor) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- for i := 0; ; i++ {
- node := d.GetNode(c, id)
- for _, fn := range f {
- fn(node)
- }
- err = cli.NodeUpdate(context.Background(), node.ID, node.Version, node.Spec)
- if i < 10 && err != nil && strings.Contains(err.Error(), "update out of sequence") {
- time.Sleep(100 * time.Millisecond)
- continue
- }
- c.Assert(err, checker.IsNil)
- return
- }
- }
- // ListNodes returns the list of the current swarm nodes
- func (d *Swarm) ListNodes(c *check.C) []swarm.Node {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- nodes, err := cli.NodeList(context.Background(), types.NodeListOptions{})
- c.Assert(err, checker.IsNil)
- return nodes
- }
- // ListServices returns the list of the current swarm services
- func (d *Swarm) ListServices(c *check.C) []swarm.Service {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- services, err := cli.ServiceList(context.Background(), types.ServiceListOptions{})
- c.Assert(err, checker.IsNil)
- return services
- }
- // CreateSecret creates a secret given the specified spec
- func (d *Swarm) CreateSecret(c *check.C, secretSpec swarm.SecretSpec) string {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- scr, err := cli.SecretCreate(context.Background(), secretSpec)
- c.Assert(err, checker.IsNil)
- return scr.ID
- }
- // ListSecrets returns the list of the current swarm secrets
- func (d *Swarm) ListSecrets(c *check.C) []swarm.Secret {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- secrets, err := cli.SecretList(context.Background(), types.SecretListOptions{})
- c.Assert(err, checker.IsNil)
- return secrets
- }
- // GetSecret returns a swarm secret identified by the specified id
- func (d *Swarm) GetSecret(c *check.C, id string) *swarm.Secret {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- secret, _, err := cli.SecretInspectWithRaw(context.Background(), id)
- c.Assert(err, checker.IsNil)
- return &secret
- }
- // DeleteSecret removes the swarm secret identified by the specified id
- func (d *Swarm) DeleteSecret(c *check.C, id string) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- err = cli.SecretRemove(context.Background(), id)
- c.Assert(err, checker.IsNil)
- }
- // UpdateSecret updates the swarm secret identified by the specified id
- // Currently, only label update is supported.
- func (d *Swarm) UpdateSecret(c *check.C, id string, f ...SecretConstructor) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- secret := d.GetSecret(c, id)
- for _, fn := range f {
- fn(secret)
- }
- err = cli.SecretUpdate(context.Background(), secret.ID, secret.Version, secret.Spec)
- c.Assert(err, checker.IsNil)
- }
- // CreateConfig creates a config given the specified spec
- func (d *Swarm) CreateConfig(c *check.C, configSpec swarm.ConfigSpec) string {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- scr, err := cli.ConfigCreate(context.Background(), configSpec)
- c.Assert(err, checker.IsNil)
- return scr.ID
- }
- // ListConfigs returns the list of the current swarm configs
- func (d *Swarm) ListConfigs(c *check.C) []swarm.Config {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- configs, err := cli.ConfigList(context.Background(), types.ConfigListOptions{})
- c.Assert(err, checker.IsNil)
- return configs
- }
- // GetConfig returns a swarm config identified by the specified id
- func (d *Swarm) GetConfig(c *check.C, id string) *swarm.Config {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- config, _, err := cli.ConfigInspectWithRaw(context.Background(), id)
- c.Assert(err, checker.IsNil)
- return &config
- }
- // DeleteConfig removes the swarm config identified by the specified id
- func (d *Swarm) DeleteConfig(c *check.C, id string) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- err = cli.ConfigRemove(context.Background(), id)
- c.Assert(err, checker.IsNil)
- }
- // UpdateConfig updates the swarm config identified by the specified id
- // Currently, only label update is supported.
- func (d *Swarm) UpdateConfig(c *check.C, id string, f ...ConfigConstructor) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- config := d.GetConfig(c, id)
- for _, fn := range f {
- fn(config)
- }
- err = cli.ConfigUpdate(context.Background(), config.ID, config.Version, config.Spec)
- c.Assert(err, checker.IsNil)
- }
- // GetSwarm returns the current swarm object
- func (d *Swarm) GetSwarm(c *check.C) swarm.Swarm {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- sw, err := cli.SwarmInspect(context.Background())
- c.Assert(err, checker.IsNil)
- return sw
- }
- // UpdateSwarm updates the current swarm object with the specified spec constructors
- func (d *Swarm) UpdateSwarm(c *check.C, f ...SpecConstructor) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- sw := d.GetSwarm(c)
- for _, fn := range f {
- fn(&sw.Spec)
- }
- err = cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, swarm.UpdateFlags{})
- c.Assert(err, checker.IsNil)
- }
- // RotateTokens update the swarm to rotate tokens
- func (d *Swarm) RotateTokens(c *check.C) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- sw, err := cli.SwarmInspect(context.Background())
- c.Assert(err, checker.IsNil)
- flags := swarm.UpdateFlags{
- RotateManagerToken: true,
- RotateWorkerToken: true,
- }
- err = cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, flags)
- c.Assert(err, checker.IsNil)
- }
- // JoinTokens returns the current swarm join tokens
- func (d *Swarm) JoinTokens(c *check.C) swarm.JoinTokens {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- sw, err := cli.SwarmInspect(context.Background())
- c.Assert(err, checker.IsNil)
- return sw.JoinTokens
- }
- // CheckLocalNodeState returns the current swarm node state
- func (d *Swarm) CheckLocalNodeState(c *check.C) (interface{}, check.CommentInterface) {
- info, err := d.SwarmInfo()
- c.Assert(err, checker.IsNil)
- return info.LocalNodeState, nil
- }
- // CheckControlAvailable returns the current swarm control available
- func (d *Swarm) CheckControlAvailable(c *check.C) (interface{}, check.CommentInterface) {
- info, err := d.SwarmInfo()
- c.Assert(err, checker.IsNil)
- c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
- return info.ControlAvailable, nil
- }
- // CheckLeader returns whether there is a leader on the swarm or not
- func (d *Swarm) CheckLeader(c *check.C) (interface{}, check.CommentInterface) {
- cli, err := d.NewClient()
- c.Assert(err, checker.IsNil)
- defer cli.Close()
- errList := check.Commentf("could not get node list")
- ls, err := cli.NodeList(context.Background(), types.NodeListOptions{})
- if err != nil {
- return err, errList
- }
- for _, node := range ls {
- if node.ManagerStatus != nil && node.ManagerStatus.Leader {
- return nil, nil
- }
- }
- return fmt.Errorf("no leader"), check.Commentf("could not find leader")
- }
- // CmdRetryOutOfSequence tries the specified command against the current daemon for 10 times
- func (d *Swarm) CmdRetryOutOfSequence(args ...string) (string, error) {
- for i := 0; ; i++ {
- out, err := d.Cmd(args...)
- if err != nil {
- if strings.Contains(out, "update out of sequence") {
- if i < 10 {
- continue
- }
- }
- }
- return out, err
- }
- }
|