Files
docker-cli/cli/command/stack/kubernetes/cli.go
Sebastiaan van Stijn 977d3ae046 Always enable experimental features
The CLI disabled experimental features by default, requiring users
to set a configuration option to enable them.

Disabling experimental features was a request from Enterprise users
that did not want experimental features to be accessible.

We are changing this policy, and now enable experimental features
by default. Experimental features may still change and/or removed,
and will be highlighted in the documentation and "usage" output.

For example, the `docker manifest inspect --help` output now shows:

    EXPERIMENTAL:
      docker manifest inspect is an experimental feature.

      Experimental features provide early access to product functionality. These features
      may change between releases without warning or can be removed entirely from a future
      release. Learn more about experimental features: https://docs.docker.com/go/experimental/

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-10-02 15:59:42 +02:00

145 lines
3.8 KiB
Go

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
}