Skip to content

Commit 0bf781e

Browse files
committed
Query UCP Kubernetes user namespaces endpoint
Signed-off-by: Mathieu Champlon <[email protected]>
1 parent 685e0dd commit 0bf781e

2 files changed

Lines changed: 82 additions & 1 deletion

File tree

cli/command/cli.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error {
174174
DefaultVersion: cli.client.ClientVersion(),
175175
HasExperimental: hasExperimental,
176176
Orchestrator: orchestrator,
177+
TLSOptions: opts.Common.TLSOptions,
177178
}
178179
cli.initializeFromClient()
179180
return nil
@@ -240,6 +241,7 @@ type ClientInfo struct {
240241
HasExperimental bool
241242
DefaultVersion string
242243
Orchestrator Orchestrator
244+
TLSOptions *tlsconfig.Options
243245
}
244246

245247
// HasKubernetes checks if kubernetes orchestrator is enabled

cli/command/stack/kubernetes/list.go

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
package kubernetes
22

33
import (
4+
"encoding/json"
5+
"fmt"
6+
"io/ioutil"
7+
"net/http"
8+
"net/url"
9+
410
"github.com/docker/cli/cli/command"
511
"github.com/docker/cli/cli/command/formatter"
612
"github.com/docker/cli/cli/command/stack/options"
13+
"github.com/docker/go-connections/tlsconfig"
14+
"github.com/pkg/errors"
15+
"github.com/sirupsen/logrus"
16+
core_v1 "k8s.io/api/core/v1"
17+
apierrs "k8s.io/apimachinery/pkg/api/errors"
718
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
819
)
920

@@ -17,7 +28,7 @@ func GetStacks(dockerCli command.Cli, opts options.List, kopts Options) ([]*form
1728
if dockerCli.ClientInfo().HasAll() {
1829
opts.AllNamespaces = true
1930
}
20-
return getStacks(kubeCli, opts)
31+
return getStacksWithAllNamespaces(dockerCli, kubeCli, opts)
2132
}
2233
return getStacksWithNamespaces(kubeCli, opts)
2334
}
@@ -47,6 +58,74 @@ func getStacks(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error)
4758
return formattedStacks, nil
4859
}
4960

61+
func getStacksWithAllNamespaces(dockerCli command.Cli, kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error) {
62+
stacks, err := getStacks(kubeCli, opts)
63+
if err == nil || !apierrs.IsForbidden(err) {
64+
return stacks, err
65+
}
66+
nms, err2 := getUserVisibleNamespaces(dockerCli)
67+
if err2 != nil {
68+
logrus.Debugf("Failed to query user visible namespaces: %s", err2)
69+
return nil, err
70+
}
71+
opts.AllNamespaces = false
72+
for _, namespace := range nms.Items {
73+
kubeCli.kubeNamespace = namespace.Name
74+
ss, err := getStacks(kubeCli, opts)
75+
if err != nil {
76+
return nil, err
77+
}
78+
stacks = append(stacks, ss...)
79+
}
80+
return stacks, nil
81+
}
82+
83+
func getUserVisibleNamespaces(dockerCli command.Cli) (*core_v1.NamespaceList, error) {
84+
host := dockerCli.Client().DaemonHost()
85+
endpoint, err := url.Parse(host)
86+
if err != nil {
87+
return nil, err
88+
}
89+
endpoint.Scheme = "https"
90+
endpoint.Path = "/kubernetesNamespaces"
91+
res := core_v1.NamespaceList{}
92+
return &res, makeRequest(dockerCli, *endpoint, func(resp http.Response) error {
93+
body, err := ioutil.ReadAll(resp.Body)
94+
if err != nil {
95+
return errors.Wrapf(err, "received %d status and unable to read response", resp.StatusCode)
96+
}
97+
if resp.StatusCode != http.StatusOK {
98+
return fmt.Errorf(string(body))
99+
}
100+
if err := json.Unmarshal(body, &res); err != nil {
101+
return errors.Wrapf(err, "unmarshal failed: %s", string(body))
102+
}
103+
return nil
104+
})
105+
}
106+
107+
func makeRequest(dockerCli command.Cli, endpoint url.URL, handler func(resp http.Response) error) error {
108+
tlsOptions := dockerCli.ClientInfo().TLSOptions
109+
if tlsOptions == nil {
110+
return fmt.Errorf("missing TLS options for https")
111+
}
112+
tlsConfig, err := tlsconfig.Client(*tlsOptions)
113+
if err != nil {
114+
return err
115+
}
116+
httpClient := http.Client{
117+
Transport: &http.Transport{
118+
TLSClientConfig: tlsConfig,
119+
},
120+
}
121+
resp, err := httpClient.Get(endpoint.String())
122+
if err != nil {
123+
return err
124+
}
125+
defer resp.Body.Close()
126+
return handler(*resp)
127+
}
128+
50129
func getStacksWithNamespaces(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error) {
51130
stacks := []*formatter.Stack{}
52131
for _, namespace := range removeDuplicates(opts.Namespaces) {

0 commit comments

Comments
 (0)