github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/command/stack/kubernetes/list.go (about)

     1  package kubernetes
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/url"
     9  
    10  	"github.com/docker/cli/cli/command"
    11  	"github.com/docker/cli/cli/command/stack/formatter"
    12  	"github.com/docker/cli/cli/command/stack/options"
    13  	"github.com/docker/cli/cli/config/configfile"
    14  	"github.com/pkg/errors"
    15  	core_v1 "k8s.io/api/core/v1"
    16  	apierrs "k8s.io/apimachinery/pkg/api/errors"
    17  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    18  )
    19  
    20  // GetStacks lists the kubernetes stacks
    21  func GetStacks(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error) {
    22  	if opts.AllNamespaces || len(opts.Namespaces) == 0 {
    23  		if isAllNamespacesDisabled(kubeCli.ConfigFile().Kubernetes) {
    24  			opts.AllNamespaces = true
    25  		}
    26  		return getStacksWithAllNamespaces(kubeCli, opts)
    27  	}
    28  	return getStacksWithNamespaces(kubeCli, opts, removeDuplicates(opts.Namespaces))
    29  }
    30  
    31  func isAllNamespacesDisabled(kubeCliConfig *configfile.KubernetesConfig) bool {
    32  	return kubeCliConfig == nil || kubeCliConfig.AllNamespaces != "disabled"
    33  }
    34  
    35  func getStacks(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error) {
    36  	composeClient, err := kubeCli.composeClient()
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	stackSvc, err := composeClient.Stacks(opts.AllNamespaces)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	stacks, err := stackSvc.List(metav1.ListOptions{})
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	var formattedStacks []*formatter.Stack
    49  	for _, stack := range stacks {
    50  		formattedStacks = append(formattedStacks, &formatter.Stack{
    51  			Name:         stack.Name,
    52  			Services:     len(stack.getServices()),
    53  			Orchestrator: "Kubernetes",
    54  			Namespace:    stack.Namespace,
    55  		})
    56  	}
    57  	return formattedStacks, nil
    58  }
    59  
    60  func getStacksWithAllNamespaces(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error) {
    61  	stacks, err := getStacks(kubeCli, opts)
    62  	if !apierrs.IsForbidden(err) {
    63  		return stacks, err
    64  	}
    65  	namespaces, err2 := getUserVisibleNamespaces(*kubeCli)
    66  	if err2 != nil {
    67  		return nil, errors.Wrap(err2, "failed to query user visible namespaces")
    68  	}
    69  	if namespaces == nil {
    70  		// UCP API not present, fall back to Kubernetes error
    71  		return nil, err
    72  	}
    73  	opts.AllNamespaces = false
    74  	return getStacksWithNamespaces(kubeCli, opts, namespaces)
    75  }
    76  
    77  func getUserVisibleNamespaces(dockerCli command.Cli) ([]string, error) {
    78  	host := dockerCli.Client().DaemonHost()
    79  	endpoint, err := url.Parse(host)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	endpoint.Scheme = "https"
    84  	endpoint.Path = "/kubernetesNamespaces"
    85  	resp, err := dockerCli.Client().HTTPClient().Get(endpoint.String())
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	defer resp.Body.Close()
    90  	body, err := ioutil.ReadAll(resp.Body)
    91  	if err != nil {
    92  		return nil, errors.Wrapf(err, "received %d status and unable to read response", resp.StatusCode)
    93  	}
    94  	switch resp.StatusCode {
    95  	case http.StatusOK:
    96  		nms := &core_v1.NamespaceList{}
    97  		if err := json.Unmarshal(body, nms); err != nil {
    98  			return nil, errors.Wrapf(err, "unmarshal failed: %s", string(body))
    99  		}
   100  		namespaces := make([]string, len(nms.Items))
   101  		for i, namespace := range nms.Items {
   102  			namespaces[i] = namespace.Name
   103  		}
   104  		return namespaces, nil
   105  	case http.StatusNotFound:
   106  		// UCP API not present
   107  		return nil, nil
   108  	default:
   109  		return nil, fmt.Errorf("received %d status while retrieving namespaces: %s", resp.StatusCode, string(body))
   110  	}
   111  }
   112  
   113  func getStacksWithNamespaces(kubeCli *KubeCli, opts options.List, namespaces []string) ([]*formatter.Stack, error) {
   114  	stacks := []*formatter.Stack{}
   115  	for _, namespace := range namespaces {
   116  		kubeCli.kubeNamespace = namespace
   117  		ss, err := getStacks(kubeCli, opts)
   118  		if err != nil {
   119  			return nil, err
   120  		}
   121  		stacks = append(stacks, ss...)
   122  	}
   123  	return stacks, nil
   124  }
   125  
   126  func removeDuplicates(namespaces []string) []string {
   127  	found := make(map[string]bool)
   128  	results := namespaces[:0]
   129  	for _, n := range namespaces {
   130  		if !found[n] {
   131  			results = append(results, n)
   132  			found[n] = true
   133  		}
   134  	}
   135  	return results
   136  }