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