github.com/ali-iotechsys/cli@v20.10.0+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  }
    54  
    55  // WrapCli wraps command.Cli with kubernetes specifics
    56  func WrapCli(dockerCli command.Cli, opts Options) (*KubeCli, error) {
    57  	cli := &KubeCli{
    58  		Cli: dockerCli,
    59  	}
    60  	var (
    61  		clientConfig clientcmd.ClientConfig
    62  		err          error
    63  	)
    64  	if dockerCli.CurrentContext() == "" {
    65  		clientConfig = kubernetes.NewKubernetesConfig(opts.Config)
    66  	} else {
    67  		clientConfig, err = kubecontext.ConfigFromContext(dockerCli.CurrentContext(), dockerCli.ContextStore())
    68  	}
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	cli.kubeNamespace = opts.Namespace
    74  	if opts.Namespace == "" {
    75  		configNamespace, _, err := clientConfig.Namespace()
    76  		switch {
    77  		case os.IsNotExist(err), os.IsPermission(err):
    78  			return nil, errors.Wrap(err, "unable to load configuration file")
    79  		case err != nil:
    80  			return nil, err
    81  		}
    82  		cli.kubeNamespace = configNamespace
    83  	}
    84  
    85  	config, err := clientConfig.ClientConfig()
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	cli.kubeConfig = config
    90  
    91  	clientSet, err := kubeclient.NewForConfig(config)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	cli.clientSet = clientSet
    96  
    97  	if opts.Orchestrator.HasAll() {
    98  		if err := cli.checkHostsMatch(); err != nil {
    99  			return nil, err
   100  		}
   101  	}
   102  	return cli, nil
   103  }
   104  
   105  func (c *KubeCli) composeClient() (*Factory, error) {
   106  	return NewFactory(c.kubeNamespace, c.kubeConfig, c.clientSet)
   107  }
   108  
   109  func (c *KubeCli) checkHostsMatch() error {
   110  	daemonEndpoint, err := url.Parse(c.Client().DaemonHost())
   111  	if err != nil {
   112  		return err
   113  	}
   114  	kubeEndpoint, err := url.Parse(c.kubeConfig.Host)
   115  	if err != nil {
   116  		return err
   117  	}
   118  	if daemonEndpoint.Hostname() == kubeEndpoint.Hostname() {
   119  		return nil
   120  	}
   121  	// The daemon can be local in Docker for Desktop, e.g. "npipe", "unix", ...
   122  	if daemonEndpoint.Scheme != "tcp" {
   123  		ips, err := net.LookupIP(kubeEndpoint.Hostname())
   124  		if err != nil {
   125  			return err
   126  		}
   127  		for _, ip := range ips {
   128  			if ip.IsLoopback() {
   129  				return nil
   130  			}
   131  		}
   132  	}
   133  	fmt.Fprintf(c.Err(), "WARNING: Swarm and Kubernetes hosts do not match (docker host=%s, kubernetes host=%s).\n"+
   134  		"         Update $DOCKER_HOST (or pass -H), or use 'kubectl config use-context' to match.\n", daemonEndpoint.Hostname(), kubeEndpoint.Hostname())
   135  	return nil
   136  }
   137  
   138  func (c *KubeCli) stacksv1beta1() (cliv1beta1.StackInterface, error) {
   139  	raw, err := newStackV1Beta1(c.kubeConfig, c.kubeNamespace)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	return raw.stacks, nil
   144  }