88 "github.com/docker/cli/cli/command/formatter"
99 flagsHelper "github.com/docker/cli/cli/flags"
1010 "github.com/docker/cli/opts"
11- "github.com/moby/moby/api/types/swarm"
1211 "github.com/moby/moby/client"
1312 "github.com/spf13/cobra"
1413)
@@ -43,44 +42,19 @@ func newListCommand(dockerCLI command.Cli) *cobra.Command {
4342}
4443
4544func runList (ctx context.Context , dockerCLI command.Cli , options listOptions ) error {
46- var (
47- apiClient = dockerCLI .Client ()
48- err error
49- )
50-
51- listOpts := client.ServiceListOptions {
45+ apiClient := dockerCLI .Client ()
46+ services , err := apiClient .ServiceList (ctx , client.ServiceListOptions {
5247 Filters : options .filter .Value (),
5348 // When not running "quiet", also get service status (number of running
5449 // and desired tasks). Note that this is only supported on API v1.41 and
5550 // up; older API versions ignore this option, and we will have to collect
5651 // the information manually below.
5752 Status : ! options .quiet ,
58- }
59-
60- services , err := apiClient .ServiceList (ctx , listOpts )
53+ })
6154 if err != nil {
6255 return err
6356 }
6457
65- if listOpts .Status {
66- // Now that a request was made, we know what API version was used (either
67- // through configuration, or after client and daemon negotiated a version).
68- // If API version v1.41 or up was used; the daemon should already have done
69- // the legwork for us, and we don't have to calculate the number of desired
70- // and running tasks. On older API versions, we need to do some extra requests
71- // to get that information.
72- //
73- // So theoretically, this step can be skipped based on API version, however,
74- // some of our unit tests don't set the API version, and there may be other
75- // situations where the client uses the "default" version. To account for
76- // these situations, we do a quick check for services that do not have
77- // a ServiceStatus set, and perform a lookup for those.
78- services , err = AppendServiceStatus (ctx , apiClient , services )
79- if err != nil {
80- return err
81- }
82- }
83-
8458 format := options .format
8559 if len (format ) == 0 {
8660 if len (dockerCLI .ConfigFile ().ServicesFormat ) > 0 && ! options .quiet {
@@ -96,94 +70,3 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er
9670 }
9771 return ListFormatWrite (servicesCtx , services )
9872}
99-
100- // AppendServiceStatus propagates the ServiceStatus field for "services".
101- //
102- // If API version v1.41 or up is used, this information is already set by the
103- // daemon. On older API versions, we need to do some extra requests to get
104- // that information. Theoretically, this function can be skipped based on API
105- // version, however, some of our unit tests don't set the API version, and
106- // there may be other situations where the client uses the "default" version.
107- // To take these situations into account, we do a quick check for services
108- // that don't have ServiceStatus set, and perform a lookup for those.
109- func AppendServiceStatus (ctx context.Context , c client.APIClient , services []swarm.Service ) ([]swarm.Service , error ) {
110- status := map [string ]* swarm.ServiceStatus {}
111- taskFilter := make (client.Filters )
112- for i , s := range services {
113- // there is no need in this switch to check for job modes. jobs are not
114- // supported until after ServiceStatus was introduced.
115- switch {
116- case s .ServiceStatus != nil :
117- // Server already returned service-status, so we don't
118- // have to look-up tasks for this service.
119- continue
120- case s .Spec .Mode .Replicated != nil :
121- // For replicated services, set the desired number of tasks;
122- // that way we can present this information in case we're unable
123- // to get a list of tasks from the server.
124- services [i ].ServiceStatus = & swarm.ServiceStatus {DesiredTasks : * s .Spec .Mode .Replicated .Replicas }
125- status [s .ID ] = & swarm.ServiceStatus {}
126- taskFilter .Add ("service" , s .ID )
127- case s .Spec .Mode .Global != nil :
128- // No such thing as number of desired tasks for global services
129- services [i ].ServiceStatus = & swarm.ServiceStatus {}
130- status [s .ID ] = & swarm.ServiceStatus {}
131- taskFilter .Add ("service" , s .ID )
132- default :
133- // Unknown task type
134- }
135- }
136- if len (status ) == 0 {
137- // All services have their ServiceStatus set, so we're done
138- return services , nil
139- }
140-
141- tasks , err := c .TaskList (ctx , client.TaskListOptions {Filters : taskFilter })
142- if err != nil {
143- return nil , err
144- }
145- if len (tasks ) == 0 {
146- return services , nil
147- }
148- activeNodes , err := getActiveNodes (ctx , c )
149- if err != nil {
150- return nil , err
151- }
152-
153- for _ , task := range tasks {
154- if status [task .ServiceID ] == nil {
155- // This should not happen in practice; either all services have
156- // a ServiceStatus set, or none of them.
157- continue
158- }
159- // TODO: this should only be needed for "global" services. Replicated
160- // services have `Spec.Mode.Replicated.Replicas`, which should give this value.
161- if task .DesiredState != swarm .TaskStateShutdown {
162- status [task .ServiceID ].DesiredTasks ++
163- }
164- if _ , nodeActive := activeNodes [task .NodeID ]; nodeActive && task .Status .State == swarm .TaskStateRunning {
165- status [task .ServiceID ].RunningTasks ++
166- }
167- }
168-
169- for i , service := range services {
170- if s := status [service .ID ]; s != nil {
171- services [i ].ServiceStatus = s
172- }
173- }
174- return services , nil
175- }
176-
177- func getActiveNodes (ctx context.Context , c client.NodeAPIClient ) (map [string ]struct {}, error ) {
178- nodes , err := c .NodeList (ctx , client.NodeListOptions {})
179- if err != nil {
180- return nil , err
181- }
182- activeNodes := make (map [string ]struct {})
183- for _ , n := range nodes {
184- if n .Status .State != swarm .NodeStateDown {
185- activeNodes [n .ID ] = struct {}{}
186- }
187- }
188- return activeNodes , nil
189- }
0 commit comments