-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor the stack services
command to be uniform
#2131
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -5,7 +5,6 @@ import ( | |||||||||
"strings" | ||||||||||
|
||||||||||
"github.com/docker/cli/cli/command/service" | ||||||||||
"github.com/docker/cli/cli/command/stack/formatter" | ||||||||||
"github.com/docker/cli/cli/command/stack/options" | ||||||||||
"github.com/docker/compose-on-kubernetes/api/labels" | ||||||||||
"github.com/docker/docker/api/types/filters" | ||||||||||
|
@@ -79,60 +78,47 @@ func getResourcesForServiceList(dockerCli *KubeCli, filters filters.Args, labelS | |||||||||
return replicas, daemons, services, nil | ||||||||||
} | ||||||||||
|
||||||||||
// RunServices is the kubernetes implementation of docker stack services | ||||||||||
func RunServices(dockerCli *KubeCli, opts options.Services) error { | ||||||||||
// GetServices is the kubernetes implementation of listing stack services | ||||||||||
func GetServices(dockerCli *KubeCli, opts options.Services) ([]swarm.Service, map[string]service.ListInfo, error) { | ||||||||||
filters := opts.Filter.Value() | ||||||||||
if err := filters.Validate(supportedServicesFilters); err != nil { | ||||||||||
return err | ||||||||||
return nil, nil, err | ||||||||||
} | ||||||||||
client, err := dockerCli.composeClient() | ||||||||||
if err != nil { | ||||||||||
return nil | ||||||||||
return nil, nil, err | ||||||||||
} | ||||||||||
stacks, err := client.Stacks(false) | ||||||||||
if err != nil { | ||||||||||
return nil | ||||||||||
return nil, nil, err | ||||||||||
} | ||||||||||
stackName := opts.Namespace | ||||||||||
_, err = stacks.Get(stackName) | ||||||||||
if apierrs.IsNotFound(err) { | ||||||||||
return fmt.Errorf("nothing found in stack: %s", stackName) | ||||||||||
return []swarm.Service{}, nil, nil | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was looking if we should return the error here, and have it handled by the caller 🤔. That way, this function (when used in different situations) would not hide this information. I think that's ok for a follow-up, because we should have a function to convert Kubernetes API errors to cli/vendor/k8s.io/apimachinery/pkg/api/errors/errors.go Lines 456 to 459 in 0b6685b
// statusCodeFromContainerdError returns status code for containerd errors when
// consumed directly (not through gRPC)
func statusCodeFromContainerdError(err error) int {
switch {
case containerderrors.IsInvalidArgument(err):
return http.StatusBadRequest
case containerderrors.IsNotFound(err):
return http.StatusNotFound
case containerderrors.IsAlreadyExists(err):
return http.StatusConflict
case containerderrors.IsFailedPrecondition(err):
return http.StatusPreconditionFailed
case containerderrors.IsUnavailable(err):
return http.StatusServiceUnavailable
case containerderrors.IsNotImplemented(err):
return http.StatusNotImplemented
default:
return http.StatusInternalServerError
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason I didn't return an error here is because the same code for swarm returns an empty slice when no services are found. We can do it one way or the other, we should just make sure that errors are returned in the same places for kubernetes and swarm. |
||||||||||
} | ||||||||||
if err != nil { | ||||||||||
return err | ||||||||||
return nil, nil, err | ||||||||||
} | ||||||||||
|
||||||||||
labelSelector := generateLabelSelector(filters, stackName) | ||||||||||
replicasList, daemonsList, servicesList, err := getResourcesForServiceList(dockerCli, filters, labelSelector) | ||||||||||
if err != nil { | ||||||||||
return err | ||||||||||
return nil, nil, err | ||||||||||
} | ||||||||||
|
||||||||||
// Convert Replicas sets and kubernetes services to swarm services and formatter information | ||||||||||
services, info, err := convertToServices(replicasList, daemonsList, servicesList) | ||||||||||
if err != nil { | ||||||||||
return err | ||||||||||
return nil, nil, err | ||||||||||
} | ||||||||||
services = filterServicesByName(services, filters.Get("name"), stackName) | ||||||||||
|
||||||||||
if opts.Quiet { | ||||||||||
info = map[string]service.ListInfo{} | ||||||||||
} | ||||||||||
|
||||||||||
format := opts.Format | ||||||||||
if len(format) == 0 { | ||||||||||
if len(dockerCli.ConfigFile().ServicesFormat) > 0 && !opts.Quiet { | ||||||||||
format = dockerCli.ConfigFile().ServicesFormat | ||||||||||
} else { | ||||||||||
format = formatter.TableFormatKey | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
servicesCtx := formatter.Context{ | ||||||||||
Output: dockerCli.Out(), | ||||||||||
Format: service.NewListFormat(format, opts.Quiet), | ||||||||||
} | ||||||||||
return service.ListFormatWrite(servicesCtx, services, info) | ||||||||||
return services, info, nil | ||||||||||
} | ||||||||||
|
||||||||||
func filterServicesByName(services []swarm.Service, names []string, stackName string) []swarm.Service { | ||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm looking at the extra return here; the
service.ListInfo
is basically generated from information that's in[]swarm.Service
, combined with information about the number of tasks running / desired. If I see it correctly, theservice.ListInfo
here is needed, becauseswarm.Service
has no field for task status ("mode" is already part of the Service spec).I just (finally) merged moby/moby#39231, which is a PR that adds exact those fields to the
Service
struct, which means we could just return([]swarmService, error)
, and have the presentation code either convert it to aListInfo
or adjust that code to use[]swarm.Service
directly.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me prepare a vendor bump to bring in the changes from moby/moby#39231
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I can wait for the vendor bump, thanks!