2017-11-20 09:30:52 -05:00
|
|
|
package kubernetes
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
|
|
|
|
"github.com/docker/cli/cli/command"
|
|
|
|
"github.com/docker/cli/cli/command/formatter"
|
2017-12-04 06:30:39 -05:00
|
|
|
"github.com/docker/cli/cli/command/stack/options"
|
2017-11-20 09:30:52 -05:00
|
|
|
"github.com/docker/cli/cli/command/task"
|
|
|
|
"github.com/docker/docker/api/types/swarm"
|
|
|
|
apiv1 "k8s.io/api/core/v1"
|
2018-04-24 06:28:08 -04:00
|
|
|
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
2017-11-20 09:30:52 -05:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
|
|
|
)
|
|
|
|
|
2018-04-24 06:28:08 -04:00
|
|
|
var supportedPSFilters = map[string]bool{
|
|
|
|
"name": true,
|
|
|
|
"service": true,
|
|
|
|
"node": true,
|
|
|
|
}
|
|
|
|
|
2017-12-04 06:30:39 -05:00
|
|
|
// RunPS is the kubernetes implementation of docker stack ps
|
|
|
|
func RunPS(dockerCli *KubeCli, options options.PS) error {
|
2018-04-24 06:28:08 -04:00
|
|
|
filters := options.Filter.Value()
|
|
|
|
if err := filters.Validate(supportedPSFilters); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-12-04 06:30:39 -05:00
|
|
|
client, err := dockerCli.composeClient()
|
2017-11-20 09:30:52 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-09 09:13:16 -04:00
|
|
|
stacks, err := client.Stacks(false)
|
2017-11-20 09:30:52 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-24 06:28:08 -04:00
|
|
|
stackName := options.Namespace
|
|
|
|
_, err = stacks.Get(stackName)
|
|
|
|
if apierrs.IsNotFound(err) {
|
|
|
|
return fmt.Errorf("nothing found in stack: %s", stackName)
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-24 06:28:08 -04:00
|
|
|
pods, err := fetchPods(stackName, client.Pods(), filters)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-11-20 09:30:52 -05:00
|
|
|
if len(pods) == 0 {
|
2018-04-24 06:28:08 -04:00
|
|
|
return fmt.Errorf("nothing found in stack: %s", stackName)
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
2018-04-24 06:28:08 -04:00
|
|
|
return printTasks(dockerCli, options, stackName, client, pods)
|
|
|
|
}
|
2017-11-20 09:30:52 -05:00
|
|
|
|
2018-04-24 06:28:08 -04:00
|
|
|
func printTasks(dockerCli command.Cli, options options.PS, namespace string, client corev1.NodesGetter, pods []apiv1.Pod) error {
|
2017-12-04 06:30:39 -05:00
|
|
|
format := options.Format
|
2018-04-24 06:28:08 -04:00
|
|
|
if format == "" {
|
2017-12-04 06:30:39 -05:00
|
|
|
format = task.DefaultFormat(dockerCli.ConfigFile(), options.Quiet)
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
tasks := make([]swarm.Task, len(pods))
|
|
|
|
for i, pod := range pods {
|
|
|
|
tasks[i] = podToTask(pod)
|
|
|
|
}
|
|
|
|
sort.Stable(tasksBySlot(tasks))
|
|
|
|
|
|
|
|
names := map[string]string{}
|
|
|
|
nodes := map[string]string{}
|
|
|
|
|
2018-05-30 09:19:45 -04:00
|
|
|
n, err := listNodes(client, options.NoResolve)
|
2018-04-24 06:28:08 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
|
|
|
for i, task := range tasks {
|
2018-04-24 06:28:08 -04:00
|
|
|
nodeValue, err := resolveNode(pods[i].Spec.NodeName, n, options.NoResolve)
|
2017-11-20 09:30:52 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-01-02 17:56:07 -05:00
|
|
|
names[task.ID] = fmt.Sprintf("%s_%s", namespace, pods[i].Name)
|
2017-11-20 09:30:52 -05:00
|
|
|
nodes[task.ID] = nodeValue
|
|
|
|
}
|
|
|
|
|
2018-04-24 06:28:08 -04:00
|
|
|
tasksCtx := formatter.Context{
|
|
|
|
Output: dockerCli.Out(),
|
|
|
|
Format: formatter.NewTaskFormat(format, options.Quiet),
|
|
|
|
Trunc: !options.NoTrunc,
|
|
|
|
}
|
|
|
|
|
2017-11-20 09:30:52 -05:00
|
|
|
return formatter.TaskWrite(tasksCtx, tasks, names, nodes)
|
|
|
|
}
|
|
|
|
|
2018-04-24 06:28:08 -04:00
|
|
|
func resolveNode(name string, nodes *apiv1.NodeList, noResolve bool) (string, error) {
|
2017-11-20 09:30:52 -05:00
|
|
|
// Here we have a name and we need to resolve its identifier. To mimic swarm behavior
|
2018-04-24 06:28:08 -04:00
|
|
|
// we need to resolve to the id when noResolve is set, otherwise we return the name.
|
2017-11-20 09:30:52 -05:00
|
|
|
if noResolve {
|
2018-04-24 06:28:08 -04:00
|
|
|
for _, node := range nodes.Items {
|
|
|
|
if node.Name == name {
|
|
|
|
return string(node.UID), nil
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
|
|
|
}
|
2018-04-24 06:28:08 -04:00
|
|
|
return "", fmt.Errorf("could not find node '%s'", name)
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
2018-04-24 06:28:08 -04:00
|
|
|
return name, nil
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
2018-05-30 09:19:45 -04:00
|
|
|
|
|
|
|
func listNodes(client corev1.NodesGetter, noResolve bool) (*apiv1.NodeList, error) {
|
|
|
|
if noResolve {
|
|
|
|
return client.Nodes().List(metav1.ListOptions{})
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|