Files
moby/integration-cli/daemon/daemon_swarm.go
Cory Snider 7ea066c8d1 client: add Filters type
Add a new type to use for building filter predicates for API requests,
replacing "./api/types/filters".Args in the client. Remove the now
unused api/types/filters package.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2025-10-08 12:06:31 -04:00

216 lines
6.6 KiB
Go

package daemon
import (
"context"
"errors"
"fmt"
"strings"
"testing"
cerrdefs "github.com/containerd/errdefs"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
)
// CheckServiceTasksInState returns the number of tasks with a matching state,
// and optional message substring.
func (d *Daemon) CheckServiceTasksInState(ctx context.Context, service string, state swarm.TaskState, message string) func(*testing.T) (any, string) {
return func(t *testing.T) (any, string) {
tasks := d.GetServiceTasks(ctx, t, service)
var count int
for _, task := range tasks {
if task.Status.State == state {
if message == "" || strings.Contains(task.Status.Message, message) {
count++
}
}
}
return count, ""
}
}
// CheckServiceTasksInStateWithError returns the number of tasks with a matching state,
// and optional message substring.
func (d *Daemon) CheckServiceTasksInStateWithError(ctx context.Context, service string, state swarm.TaskState, errorMessage string) func(*testing.T) (any, string) {
return func(t *testing.T) (any, string) {
tasks := d.GetServiceTasks(ctx, t, service)
var count int
for _, task := range tasks {
if task.Status.State == state {
if errorMessage == "" || strings.Contains(task.Status.Err, errorMessage) {
count++
}
}
}
return count, ""
}
}
// CheckServiceRunningTasks returns the number of running tasks for the specified service
func (d *Daemon) CheckServiceRunningTasks(ctx context.Context, service string) func(*testing.T) (any, string) {
return d.CheckServiceTasksInState(ctx, service, swarm.TaskStateRunning, "")
}
// CheckServiceUpdateState returns the current update state for the specified service
func (d *Daemon) CheckServiceUpdateState(ctx context.Context, service string) func(*testing.T) (any, string) {
return func(t *testing.T) (any, string) {
service := d.GetService(ctx, t, service)
if service.UpdateStatus == nil {
return "", ""
}
return service.UpdateStatus.State, ""
}
}
// CheckPluginRunning returns the runtime state of the plugin
func (d *Daemon) CheckPluginRunning(ctx context.Context, plugin string) func(c *testing.T) (any, string) {
return func(t *testing.T) (any, string) {
apiclient := d.NewClientT(t)
resp, _, err := apiclient.PluginInspectWithRaw(ctx, plugin)
if cerrdefs.IsNotFound(err) {
return false, fmt.Sprintf("%v", err)
}
assert.NilError(t, err)
return resp.Enabled, fmt.Sprintf("%+v", resp)
}
}
// CheckPluginImage returns the runtime state of the plugin
func (d *Daemon) CheckPluginImage(ctx context.Context, plugin string) func(c *testing.T) (any, string) {
return func(t *testing.T) (any, string) {
apiclient := d.NewClientT(t)
resp, _, err := apiclient.PluginInspectWithRaw(ctx, plugin)
if cerrdefs.IsNotFound(err) {
return false, fmt.Sprintf("%v", err)
}
assert.NilError(t, err)
return resp.PluginReference, fmt.Sprintf("%+v", resp)
}
}
// CheckServiceTasks returns the number of tasks for the specified service
func (d *Daemon) CheckServiceTasks(ctx context.Context, service string) func(*testing.T) (any, string) {
return func(t *testing.T) (any, string) {
tasks := d.GetServiceTasks(ctx, t, service)
return len(tasks), ""
}
}
// CheckRunningTaskNetworks returns the number of times each network is referenced from a task.
func (d *Daemon) CheckRunningTaskNetworks(ctx context.Context) func(t *testing.T) (any, string) {
return func(t *testing.T) (any, string) {
cli := d.NewClientT(t)
defer cli.Close()
tasks, err := cli.TaskList(ctx, client.TaskListOptions{
Filters: make(client.Filters).Add("desired-state", "running"),
})
assert.NilError(t, err)
result := make(map[string]int)
for _, task := range tasks {
for _, network := range task.Spec.Networks {
result[network.Target]++
}
}
return result, ""
}
}
// CheckRunningTaskImages returns the times each image is running as a task.
func (d *Daemon) CheckRunningTaskImages(ctx context.Context) func(t *testing.T) (any, string) {
return func(t *testing.T) (any, string) {
cli := d.NewClientT(t)
defer cli.Close()
tasks, err := cli.TaskList(ctx, client.TaskListOptions{
Filters: make(client.Filters).Add("desired-state", "running"),
})
assert.NilError(t, err)
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, ""
}
}
// CheckNodeReadyCount returns the number of ready node on the swarm
func (d *Daemon) CheckNodeReadyCount(ctx context.Context) func(t *testing.T) (any, string) {
return func(t *testing.T) (any, string) {
nodes := d.ListNodes(ctx, t)
var readyCount int
for _, node := range nodes {
if node.Status.State == swarm.NodeStateReady {
readyCount++
}
}
return readyCount, ""
}
}
// CheckLocalNodeState returns the current swarm node state
func (d *Daemon) CheckLocalNodeState(ctx context.Context) func(t *testing.T) (any, string) {
return func(t *testing.T) (any, string) {
info := d.SwarmInfo(ctx, t)
return info.LocalNodeState, ""
}
}
// CheckControlAvailable returns the current swarm control available
func (d *Daemon) CheckControlAvailable(ctx context.Context) func(t *testing.T) (any, string) {
return func(t *testing.T) (any, string) {
info := d.SwarmInfo(ctx, t)
assert.Equal(t, info.LocalNodeState, swarm.LocalNodeStateActive)
return info.ControlAvailable, ""
}
}
// CheckLeader returns whether there is a leader on the swarm or not
func (d *Daemon) CheckLeader(ctx context.Context) func(t *testing.T) (any, string) {
return func(t *testing.T) (any, string) {
cli := d.NewClientT(t)
defer cli.Close()
errList := "could not get node list"
ls, err := cli.NodeList(ctx, client.NodeListOptions{})
if err != nil {
return err, errList
}
for _, node := range ls {
if node.ManagerStatus != nil && node.ManagerStatus.Leader {
return nil, ""
}
}
return errors.New("no leader"), "could not find leader"
}
}
// CmdRetryOutOfSequence tries the specified command against the current daemon
// up to 10 times, retrying if it encounters an "update out of sequence" error.
func (d *Daemon) CmdRetryOutOfSequence(args ...string) (string, error) {
var (
output string
err error
)
for i := 0; i < 10; i++ {
output, err = d.Cmd(args...)
// error, no error, whatever. if we don't have "update out of
// sequence", we don't retry, we just return.
if !strings.Contains(output, "update out of sequence") {
return output, err
}
}
// otherwise, once all of our attempts have been exhausted, just return
// whatever the last values were.
return output, err
}