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