-
Notifications
You must be signed in to change notification settings - Fork 2.1k
[Kubernetes] stack services filters #1023
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
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 |
|---|---|---|
|
|
@@ -10,17 +10,23 @@ import ( | |
| "github.com/docker/cli/cli/command/task" | ||
| "github.com/docker/docker/api/types/swarm" | ||
| apiv1 "k8s.io/api/core/v1" | ||
| apierrs "k8s.io/apimachinery/pkg/api/errors" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| "k8s.io/apimachinery/pkg/fields" | ||
| corev1 "k8s.io/client-go/kubernetes/typed/core/v1" | ||
| "k8s.io/kubernetes/pkg/api" | ||
| ) | ||
|
|
||
| var supportedPSFilters = map[string]bool{ | ||
| "name": true, | ||
| "service": true, | ||
| "node": true, | ||
| } | ||
|
|
||
| // RunPS is the kubernetes implementation of docker stack ps | ||
| func RunPS(dockerCli *KubeCli, options options.PS) error { | ||
| namespace := options.Namespace | ||
|
|
||
| // Initialize clients | ||
| filters := options.Filter.Value() | ||
| if err := filters.Validate(supportedPSFilters); err != nil { | ||
| return err | ||
| } | ||
| client, err := dockerCli.composeClient() | ||
| if err != nil { | ||
| return err | ||
|
|
@@ -29,78 +35,71 @@ func RunPS(dockerCli *KubeCli, options options.PS) error { | |
| if err != nil { | ||
| return err | ||
| } | ||
| podsClient := client.Pods() | ||
|
|
||
| // Fetch pods | ||
| if _, err := stacks.Get(namespace); err != nil { | ||
| return fmt.Errorf("nothing found in stack: %s", namespace) | ||
| stackName := options.Namespace | ||
| _, err = stacks.Get(stackName) | ||
| if apierrs.IsNotFound(err) { | ||
| return fmt.Errorf("nothing found in stack: %s", stackName) | ||
| } | ||
|
|
||
| pods, err := fetchPods(namespace, podsClient) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| pods, err := fetchPods(stackName, client.Pods(), filters) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if len(pods) == 0 { | ||
| return fmt.Errorf("nothing found in stack: %s", namespace) | ||
| return fmt.Errorf("nothing found in stack: %s", stackName) | ||
| } | ||
| return printTasks(dockerCli, options, stackName, client, pods) | ||
| } | ||
|
|
||
| func printTasks(dockerCli command.Cli, options options.PS, namespace string, client corev1.NodesGetter, pods []apiv1.Pod) error { | ||
| format := options.Format | ||
| if len(format) == 0 { | ||
| if format == "" { | ||
| format = task.DefaultFormat(dockerCli.ConfigFile(), options.Quiet) | ||
| } | ||
| nodeResolver := makeNodeResolver(options.NoResolve, client.Nodes()) | ||
|
|
||
| tasks := make([]swarm.Task, len(pods)) | ||
| for i, pod := range pods { | ||
| tasks[i] = podToTask(pod) | ||
| } | ||
| return print(dockerCli, namespace, tasks, pods, nodeResolver, !options.NoTrunc, options.Quiet, format) | ||
| } | ||
|
|
||
| type idResolver func(name string) (string, error) | ||
|
|
||
| func print(dockerCli command.Cli, namespace string, tasks []swarm.Task, pods []apiv1.Pod, nodeResolver idResolver, trunc, quiet bool, format string) error { | ||
| sort.Stable(tasksBySlot(tasks)) | ||
|
|
||
| names := map[string]string{} | ||
| nodes := map[string]string{} | ||
|
|
||
| tasksCtx := formatter.Context{ | ||
| Output: dockerCli.Out(), | ||
| Format: formatter.NewTaskFormat(format, quiet), | ||
| Trunc: trunc, | ||
| n, err := client.Nodes().List(metav1.ListOptions{}) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| for i, task := range tasks { | ||
| nodeValue, err := nodeResolver(pods[i].Spec.NodeName) | ||
| nodeValue, err := resolveNode(pods[i].Spec.NodeName, n, options.NoResolve) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| names[task.ID] = fmt.Sprintf("%s_%s", namespace, pods[i].Name) | ||
| nodes[task.ID] = nodeValue | ||
| } | ||
|
|
||
| tasksCtx := formatter.Context{ | ||
| Output: dockerCli.Out(), | ||
| Format: formatter.NewTaskFormat(format, options.Quiet), | ||
| Trunc: !options.NoTrunc, | ||
| } | ||
|
|
||
| return formatter.TaskWrite(tasksCtx, tasks, names, nodes) | ||
| } | ||
|
|
||
| func makeNodeResolver(noResolve bool, nodes corev1.NodeInterface) func(string) (string, error) { | ||
| func resolveNode(name string, nodes *apiv1.NodeList, noResolve bool) (string, error) { | ||
| // Here we have a name and we need to resolve its identifier. To mimic swarm behavior | ||
| // we need to resolve the id when noresolve is set, otherwise we return the name. | ||
| // we need to resolve to the id when noResolve is set, otherwise we return the name. | ||
| if noResolve { | ||
| return func(name string) (string, error) { | ||
| n, err := nodes.List(metav1.ListOptions{ | ||
| FieldSelector: fields.OneTermEqualSelector(api.ObjectNameField, name).String(), | ||
| }) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
| if len(n.Items) != 1 { | ||
| return "", fmt.Errorf("could not find node '%s'", name) | ||
| for _, node := range nodes.Items { | ||
| if node.Name == name { | ||
| return string(node.UID), nil | ||
|
Contributor
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. Is the
Contributor
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. It is more about having the same UX as Swarm than being really useful... Sure we can remove the noResolve flag and simplify the code, but in that case we may break some Swarm scripts which use this flag? From the start we choose to stick to Swarm UX, even if it is not very relevant sometimes. But maybe we are wrong and we should simplify where we can, WDYT?
Contributor
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. Well, we can keep the flag, but I think when we translate Kube nodes to Swarm nodes, the The same goes for everywhere else we use
Contributor
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. |
||
| } | ||
| return string(n.Items[0].UID), nil | ||
| } | ||
| return "", fmt.Errorf("could not find node '%s'", name) | ||
| } | ||
| return func(name string) (string, error) { return name, nil } | ||
| return name, nil | ||
| } | ||
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.
So does this mean that we require the name field to be like
--filter name=<stack name prefix> + <pod name>?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.
This is a
FuzzyMatchmeaning with astackNamePrefixofdtccccccand apod.Nameofbackend-695899db65-tptbgall these--filterwill match:name=dtcname=dtccccccname=dtcccccc-backendname=dtcccccc-backend-695899db65-tptbgHowever this one won't:
name=backendThis seems to be consistent with the behavior with
--orchestrator=swarm, however @simonferquel initially putMatchhere thus allowing the following filters to match:name=backendname=cccetc…
I'm not sure which solution we want, the specification document was a bit vague and gave no example of expected outputs:
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.
Wait, aren't you just concatenating the
stackNamePrefixandpod.NamewithstackNamePrefix+pod.Name? You said thatdtcccccc-backendshould match, implying there should be a hyphen betweenstackNamePrefixandpod.Name, but that doesn't seem to be the case.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.
stackNamePrefixactually ends with an hyphen, it's defined a few lines before asThere 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.
Not sure if this is intentional, but that's an underscore, not a hyphen.
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.
Sorry for the confusion, I made an error when writing the examples in #1023 (comment): the hyphens should have been underscores i.e.