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  }