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 }