forked from docker/cli
/
cli.go
144 lines (130 loc) · 3.76 KB
/
cli.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
137
138
139
140
141
142
143
144
package kubernetes
import (
"fmt"
"net"
"net/url"
"os"
"github.com/docker/cli/cli/command"
kubecontext "github.com/docker/cli/cli/context/kubernetes"
kubernetes "github.com/docker/compose-on-kubernetes/api"
cliv1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1"
"github.com/pkg/errors"
flag "github.com/spf13/pflag"
kubeclient "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
// KubeCli holds kubernetes specifics (client, namespace) with the command.Cli
type KubeCli struct {
command.Cli
kubeConfig *restclient.Config
kubeNamespace string
clientSet *kubeclient.Clientset
}
// Options contains resolved parameters to initialize kubernetes clients
type Options struct {
Namespace string
Config string
Orchestrator command.Orchestrator
}
// NewOptions returns an Options initialized with command line flags
func NewOptions(flags *flag.FlagSet, orchestrator command.Orchestrator) Options {
opts := Options{
Orchestrator: orchestrator,
}
if namespace, err := flags.GetString("namespace"); err == nil {
opts.Namespace = namespace
}
if kubeConfig, err := flags.GetString("kubeconfig"); err == nil {
opts.Config = kubeConfig
}
return opts
}
// AddNamespaceFlag adds the namespace flag to the given flag set
func AddNamespaceFlag(flags *flag.FlagSet) {
flags.String("namespace", "", "Kubernetes namespace to use")
flags.SetAnnotation("namespace", "kubernetes", nil)
}
// WrapCli wraps command.Cli with kubernetes specifics
func WrapCli(dockerCli command.Cli, opts Options) (*KubeCli, error) {
cli := &KubeCli{
Cli: dockerCli,
}
var (
clientConfig clientcmd.ClientConfig
err error
)
if dockerCli.CurrentContext() == "" {
clientConfig = kubernetes.NewKubernetesConfig(opts.Config)
} else {
clientConfig, err = kubecontext.ConfigFromContext(dockerCli.CurrentContext(), dockerCli.ContextStore())
}
if err != nil {
return nil, err
}
cli.kubeNamespace = opts.Namespace
if opts.Namespace == "" {
configNamespace, _, err := clientConfig.Namespace()
switch {
case os.IsNotExist(err), os.IsPermission(err):
return nil, errors.Wrap(err, "unable to load configuration file")
case err != nil:
return nil, err
}
cli.kubeNamespace = configNamespace
}
config, err := clientConfig.ClientConfig()
if err != nil {
return nil, err
}
cli.kubeConfig = config
clientSet, err := kubeclient.NewForConfig(config)
if err != nil {
return nil, err
}
cli.clientSet = clientSet
if opts.Orchestrator.HasAll() {
if err := cli.checkHostsMatch(); err != nil {
return nil, err
}
}
return cli, nil
}
func (c *KubeCli) composeClient() (*Factory, error) {
return NewFactory(c.kubeNamespace, c.kubeConfig, c.clientSet)
}
func (c *KubeCli) checkHostsMatch() error {
daemonEndpoint, err := url.Parse(c.Client().DaemonHost())
if err != nil {
return err
}
kubeEndpoint, err := url.Parse(c.kubeConfig.Host)
if err != nil {
return err
}
if daemonEndpoint.Hostname() == kubeEndpoint.Hostname() {
return nil
}
// The daemon can be local in Docker for Desktop, e.g. "npipe", "unix", ...
if daemonEndpoint.Scheme != "tcp" {
ips, err := net.LookupIP(kubeEndpoint.Hostname())
if err != nil {
return err
}
for _, ip := range ips {
if ip.IsLoopback() {
return nil
}
}
}
fmt.Fprintf(c.Err(), "WARNING: Swarm and Kubernetes hosts do not match (docker host=%s, kubernetes host=%s).\n"+
" Update $DOCKER_HOST (or pass -H), or use 'kubectl config use-context' to match.\n", daemonEndpoint.Hostname(), kubeEndpoint.Hostname())
return nil
}
func (c *KubeCli) stacksv1beta1() (cliv1beta1.StackInterface, error) {
raw, err := newStackV1Beta1(c.kubeConfig, c.kubeNamespace)
if err != nil {
return nil, err
}
return raw.stacks, nil
}