github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/command/stack/kubernetes/cli.go (about) 1 package kubernetes 2 3 import ( 4 "fmt" 5 "net" 6 "net/url" 7 "os" 8 9 "github.com/docker/cli/cli/command" 10 kubecontext "github.com/docker/cli/cli/context/kubernetes" 11 kubernetes "github.com/docker/compose-on-kubernetes/api" 12 cliv1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1" 13 "github.com/pkg/errors" 14 flag "github.com/spf13/pflag" 15 kubeclient "k8s.io/client-go/kubernetes" 16 restclient "k8s.io/client-go/rest" 17 "k8s.io/client-go/tools/clientcmd" 18 ) 19 20 // KubeCli holds kubernetes specifics (client, namespace) with the command.Cli 21 type KubeCli struct { 22 command.Cli 23 kubeConfig *restclient.Config 24 kubeNamespace string 25 clientSet *kubeclient.Clientset 26 } 27 28 // Options contains resolved parameters to initialize kubernetes clients 29 type Options struct { 30 Namespace string 31 Config string 32 Orchestrator command.Orchestrator 33 } 34 35 // NewOptions returns an Options initialized with command line flags 36 func NewOptions(flags *flag.FlagSet, orchestrator command.Orchestrator) Options { 37 opts := Options{ 38 Orchestrator: orchestrator, 39 } 40 if namespace, err := flags.GetString("namespace"); err == nil { 41 opts.Namespace = namespace 42 } 43 if kubeConfig, err := flags.GetString("kubeconfig"); err == nil { 44 opts.Config = kubeConfig 45 } 46 return opts 47 } 48 49 // AddNamespaceFlag adds the namespace flag to the given flag set 50 func AddNamespaceFlag(flags *flag.FlagSet) { 51 flags.String("namespace", "", "Kubernetes namespace to use") 52 flags.SetAnnotation("namespace", "kubernetes", nil) 53 flags.SetAnnotation("namespace", "deprecated", nil) 54 } 55 56 // WrapCli wraps command.Cli with kubernetes specifics 57 func WrapCli(dockerCli command.Cli, opts Options) (*KubeCli, error) { 58 cli := &KubeCli{ 59 Cli: dockerCli, 60 } 61 var ( 62 clientConfig clientcmd.ClientConfig 63 err error 64 ) 65 if dockerCli.CurrentContext() == "" { 66 clientConfig = kubernetes.NewKubernetesConfig(opts.Config) 67 } else { 68 clientConfig, err = kubecontext.ConfigFromContext(dockerCli.CurrentContext(), dockerCli.ContextStore()) 69 } 70 if err != nil { 71 return nil, err 72 } 73 74 cli.kubeNamespace = opts.Namespace 75 if opts.Namespace == "" { 76 configNamespace, _, err := clientConfig.Namespace() 77 switch { 78 case os.IsNotExist(err), os.IsPermission(err): 79 return nil, errors.Wrap(err, "unable to load configuration file") 80 case err != nil: 81 return nil, err 82 } 83 cli.kubeNamespace = configNamespace 84 } 85 86 config, err := clientConfig.ClientConfig() 87 if err != nil { 88 return nil, err 89 } 90 cli.kubeConfig = config 91 92 clientSet, err := kubeclient.NewForConfig(config) 93 if err != nil { 94 return nil, err 95 } 96 cli.clientSet = clientSet 97 98 if opts.Orchestrator.HasAll() { 99 if err := cli.checkHostsMatch(); err != nil { 100 return nil, err 101 } 102 } 103 return cli, nil 104 } 105 106 func (c *KubeCli) composeClient() (*Factory, error) { 107 return NewFactory(c.kubeNamespace, c.kubeConfig, c.clientSet) 108 } 109 110 func (c *KubeCli) checkHostsMatch() error { 111 daemonEndpoint, err := url.Parse(c.Client().DaemonHost()) 112 if err != nil { 113 return err 114 } 115 kubeEndpoint, err := url.Parse(c.kubeConfig.Host) 116 if err != nil { 117 return err 118 } 119 if daemonEndpoint.Hostname() == kubeEndpoint.Hostname() { 120 return nil 121 } 122 // The daemon can be local in Docker for Desktop, e.g. "npipe", "unix", ... 123 if daemonEndpoint.Scheme != "tcp" { 124 ips, err := net.LookupIP(kubeEndpoint.Hostname()) 125 if err != nil { 126 return err 127 } 128 for _, ip := range ips { 129 if ip.IsLoopback() { 130 return nil 131 } 132 } 133 } 134 fmt.Fprintf(c.Err(), "WARNING: Swarm and Kubernetes hosts do not match (docker host=%s, kubernetes host=%s).\n"+ 135 " Update $DOCKER_HOST (or pass -H), or use 'kubectl config use-context' to match.\n", daemonEndpoint.Hostname(), kubeEndpoint.Hostname()) 136 return nil 137 } 138 139 func (c *KubeCli) stacksv1beta1() (cliv1beta1.StackInterface, error) { 140 raw, err := newStackV1Beta1(c.kubeConfig, c.kubeNamespace) 141 if err != nil { 142 return nil, err 143 } 144 return raw.stacks, nil 145 }