package service // import "github.com/docker/docker/integration/service" import ( "context" "fmt" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" swarmtypes "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/versions" "github.com/docker/docker/integration/internal/swarm" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" "gotest.tools/v3/poll" "gotest.tools/v3/skip" ) // TestServiceListWithStatuses tests that performing a ServiceList operation // correctly uses the Status parameter, and that the resulting response // contains correct service statuses. // // NOTE(dperny): because it's a pain to elicit the behavior of an unconverged // service reliably, I'm not testing that an unconverged service returns X // running and Y desired tasks. Instead, I'm just going to trust that I can // successfully assign a value to another value without screwing it up. The // logic for computing service statuses is in swarmkit anyway, not in the // engine, and is well-tested there, so this test just needs to make sure that // statuses get correctly associated with the right services. func TestServiceListWithStatuses(t *testing.T) { skip.If(t, testEnv.IsRemoteDaemon) skip.If(t, testEnv.DaemonInfo.OSType == "windows") // statuses were added in API version 1.41 skip.If(t, versions.LessThan(testEnv.DaemonInfo.ServerVersion, "1.41")) defer setupTest(t)() d := swarm.NewSwarm(t, testEnv) defer d.Stop(t) client := d.NewClientT(t) defer client.Close() ctx := context.Background() serviceCount := 3 // create some services. for i := 0; i < serviceCount; i++ { spec := fullSwarmServiceSpec(fmt.Sprintf("test-list-%d", i), uint64(i+1)) // for whatever reason, the args "-u root", when included, cause these // tasks to fail and exit. instead, we'll just pass no args, which // works. spec.TaskTemplate.ContainerSpec.Args = []string{} resp, err := client.ServiceCreate(ctx, spec, types.ServiceCreateOptions{ QueryRegistry: false, }) assert.NilError(t, err) id := resp.ID // we need to wait specifically for the tasks to be running, which the // serviceContainerCount function does not do. instead, we'll use a // bespoke closure right here. poll.WaitOn(t, func(log poll.LogT) poll.Result { filter := filters.NewArgs() filter.Add("service", id) tasks, err := client.TaskList(context.Background(), types.TaskListOptions{ Filters: filter, }) running := 0 for _, task := range tasks { if task.Status.State == swarmtypes.TaskStateRunning { running++ } } switch { case err != nil: return poll.Error(err) case running == i+1: return poll.Success() default: return poll.Continue( "running task count %d (%d total), waiting for %d", running, len(tasks), i+1, ) } }) } // now, let's do the list operation with no status arg set. resp, err := client.ServiceList(ctx, types.ServiceListOptions{}) assert.NilError(t, err) assert.Check(t, is.Len(resp, serviceCount)) for _, service := range resp { assert.Check(t, is.Nil(service.ServiceStatus)) } // now try again, but with Status: true. This time, we should have statuses resp, err = client.ServiceList(ctx, types.ServiceListOptions{Status: true}) assert.NilError(t, err) assert.Check(t, is.Len(resp, serviceCount)) for _, service := range resp { replicas := *service.Spec.Mode.Replicated.Replicas assert.Assert(t, service.ServiceStatus != nil) // Use assert.Check to not fail out of the test if this fails assert.Check(t, is.Equal(service.ServiceStatus.DesiredTasks, replicas)) assert.Check(t, is.Equal(service.ServiceStatus.RunningTasks, replicas)) } }