2017-11-20 09:30:52 -05:00
|
|
|
package kubernetes
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2018-04-24 06:28:08 -04:00
|
|
|
"strings"
|
2017-11-20 09:30:52 -05:00
|
|
|
|
2018-10-23 11:05:44 -04:00
|
|
|
"github.com/docker/cli/cli/command/service"
|
|
|
|
"github.com/docker/cli/cli/command/stack/formatter"
|
2017-12-04 06:30:39 -05:00
|
|
|
"github.com/docker/cli/cli/command/stack/options"
|
2017-12-04 03:44:06 -05:00
|
|
|
"github.com/docker/cli/kubernetes/labels"
|
2018-04-24 06:28:08 -04:00
|
|
|
"github.com/docker/docker/api/types/filters"
|
2018-05-31 18:18:26 -04:00
|
|
|
"github.com/docker/docker/api/types/swarm"
|
2018-04-24 06:28:08 -04:00
|
|
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
|
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
2017-11-20 09:30:52 -05:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
)
|
|
|
|
|
2018-04-24 06:28:08 -04:00
|
|
|
var supportedServicesFilters = map[string]bool{
|
|
|
|
"mode": true,
|
|
|
|
"name": true,
|
|
|
|
"label": true,
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateSelector(labels map[string][]string) []string {
|
|
|
|
var result []string
|
|
|
|
for k, v := range labels {
|
2018-05-24 08:14:37 -04:00
|
|
|
for _, val := range v {
|
|
|
|
result = append(result, fmt.Sprintf("%s=%s", k, val))
|
|
|
|
}
|
|
|
|
if len(v) == 0 {
|
2018-04-24 06:28:08 -04:00
|
|
|
result = append(result, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseLabelFilters(rawFilters []string) map[string][]string {
|
|
|
|
labels := map[string][]string{}
|
|
|
|
for _, rawLabel := range rawFilters {
|
|
|
|
v := strings.SplitN(rawLabel, "=", 2)
|
|
|
|
key := v[0]
|
|
|
|
if len(v) > 1 {
|
|
|
|
labels[key] = append(labels[key], v[1])
|
|
|
|
} else if _, ok := labels[key]; !ok {
|
|
|
|
labels[key] = []string{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return labels
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateLabelSelector(f filters.Args, stackName string) string {
|
2018-05-31 18:18:26 -04:00
|
|
|
selectors := append(generateSelector(parseLabelFilters(f.Get("label"))), labels.SelectorForStack(stackName))
|
2018-04-24 06:28:08 -04:00
|
|
|
return strings.Join(selectors, ",")
|
|
|
|
}
|
|
|
|
|
|
|
|
func getResourcesForServiceList(dockerCli *KubeCli, filters filters.Args, labelSelector string) (*appsv1beta2.ReplicaSetList, *appsv1beta2.DaemonSetList, *corev1.ServiceList, error) {
|
|
|
|
client, err := dockerCli.composeClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
|
|
|
modes := filters.Get("mode")
|
|
|
|
replicas := &appsv1beta2.ReplicaSetList{}
|
|
|
|
if len(modes) == 0 || filters.ExactMatch("mode", "replicated") {
|
|
|
|
if replicas, err = client.ReplicaSets().List(metav1.ListOptions{LabelSelector: labelSelector}); err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
daemons := &appsv1beta2.DaemonSetList{}
|
|
|
|
if len(modes) == 0 || filters.ExactMatch("mode", "global") {
|
|
|
|
if daemons, err = client.DaemonSets().List(metav1.ListOptions{LabelSelector: labelSelector}); err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
services, err := client.Services().List(metav1.ListOptions{LabelSelector: labelSelector})
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
|
|
|
return replicas, daemons, services, nil
|
|
|
|
}
|
|
|
|
|
2017-12-04 06:30:39 -05:00
|
|
|
// RunServices is the kubernetes implementation of docker stack services
|
|
|
|
func RunServices(dockerCli *KubeCli, opts options.Services) error {
|
2018-04-24 06:28:08 -04:00
|
|
|
filters := opts.Filter.Value()
|
|
|
|
if err := filters.Validate(supportedServicesFilters); 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 nil
|
|
|
|
}
|
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 nil
|
|
|
|
}
|
2018-04-24 06:28:08 -04:00
|
|
|
stackName := opts.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
|
|
|
labelSelector := generateLabelSelector(filters, stackName)
|
|
|
|
replicasList, daemonsList, servicesList, err := getResourcesForServiceList(dockerCli, filters, labelSelector)
|
2017-11-20 09:30:52 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-09-07 05:18:00 -04:00
|
|
|
// Convert Replicas sets and kubernetes services to swarm services and formatter information
|
2018-04-24 06:28:08 -04:00
|
|
|
services, info, err := convertToServices(replicasList, daemonsList, servicesList)
|
2017-11-20 09:30:52 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-05-31 18:18:26 -04:00
|
|
|
services = filterServicesByName(services, filters.Get("name"), stackName)
|
2017-11-20 09:30:52 -05:00
|
|
|
|
2017-12-04 06:30:39 -05:00
|
|
|
if opts.Quiet {
|
2018-10-23 11:05:44 -04:00
|
|
|
info = map[string]service.ListInfo{}
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
|
|
|
|
2017-12-04 06:30:39 -05:00
|
|
|
format := opts.Format
|
2017-11-20 09:30:52 -05:00
|
|
|
if len(format) == 0 {
|
2017-12-04 06:30:39 -05:00
|
|
|
if len(dockerCli.ConfigFile().ServicesFormat) > 0 && !opts.Quiet {
|
2017-11-20 09:30:52 -05:00
|
|
|
format = dockerCli.ConfigFile().ServicesFormat
|
|
|
|
} else {
|
|
|
|
format = formatter.TableFormatKey
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
servicesCtx := formatter.Context{
|
|
|
|
Output: dockerCli.Out(),
|
2018-10-23 11:05:44 -04:00
|
|
|
Format: service.NewListFormat(format, opts.Quiet),
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
2018-10-23 11:05:44 -04:00
|
|
|
return service.ListFormatWrite(servicesCtx, services, info)
|
2017-11-20 09:30:52 -05:00
|
|
|
}
|
2018-05-31 18:18:26 -04:00
|
|
|
|
|
|
|
func filterServicesByName(services []swarm.Service, names []string, stackName string) []swarm.Service {
|
|
|
|
if len(names) == 0 {
|
|
|
|
return services
|
|
|
|
}
|
|
|
|
prefix := stackName + "_"
|
|
|
|
// Accepts unprefixed service name (for compatibility with existing swarm scripts where service names are prefixed by stack names)
|
|
|
|
for i, n := range names {
|
|
|
|
if !strings.HasPrefix(n, prefix) {
|
|
|
|
names[i] = stackName + "_" + n
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Filter services
|
|
|
|
result := []swarm.Service{}
|
|
|
|
for _, s := range services {
|
|
|
|
for _, n := range names {
|
|
|
|
if strings.HasPrefix(s.Spec.Name, n) {
|
|
|
|
result = append(result, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|