github.com/justincormack/cli@v0.0.0-20201215022714-831ebeae9675/cli/command/context/options.go (about)

     1  package context
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/docker/cli/cli/command"
    11  	"github.com/docker/cli/cli/context"
    12  	"github.com/docker/cli/cli/context/docker"
    13  	"github.com/docker/cli/cli/context/kubernetes"
    14  	"github.com/docker/cli/cli/context/store"
    15  	"github.com/docker/docker/client"
    16  	"github.com/docker/docker/pkg/homedir"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  const (
    21  	keyFrom          = "from"
    22  	keyHost          = "host"
    23  	keyCA            = "ca"
    24  	keyCert          = "cert"
    25  	keyKey           = "key"
    26  	keySkipTLSVerify = "skip-tls-verify"
    27  	keyKubeconfig    = "config-file"
    28  	keyKubecontext   = "context-override"
    29  	keyKubenamespace = "namespace-override"
    30  )
    31  
    32  type configKeyDescription struct {
    33  	name        string
    34  	description string
    35  }
    36  
    37  var (
    38  	allowedDockerConfigKeys = map[string]struct{}{
    39  		keyFrom:          {},
    40  		keyHost:          {},
    41  		keyCA:            {},
    42  		keyCert:          {},
    43  		keyKey:           {},
    44  		keySkipTLSVerify: {},
    45  	}
    46  	allowedKubernetesConfigKeys = map[string]struct{}{
    47  		keyFrom:          {},
    48  		keyKubeconfig:    {},
    49  		keyKubecontext:   {},
    50  		keyKubenamespace: {},
    51  	}
    52  	dockerConfigKeysDescriptions = []configKeyDescription{
    53  		{
    54  			name:        keyFrom,
    55  			description: "Copy named context's Docker endpoint configuration",
    56  		},
    57  		{
    58  			name:        keyHost,
    59  			description: "Docker endpoint on which to connect",
    60  		},
    61  		{
    62  			name:        keyCA,
    63  			description: "Trust certs signed only by this CA",
    64  		},
    65  		{
    66  			name:        keyCert,
    67  			description: "Path to TLS certificate file",
    68  		},
    69  		{
    70  			name:        keyKey,
    71  			description: "Path to TLS key file",
    72  		},
    73  		{
    74  			name:        keySkipTLSVerify,
    75  			description: "Skip TLS certificate validation",
    76  		},
    77  	}
    78  	kubernetesConfigKeysDescriptions = []configKeyDescription{
    79  		{
    80  			name:        keyFrom,
    81  			description: "Copy named context's Kubernetes endpoint configuration",
    82  		},
    83  		{
    84  			name:        keyKubeconfig,
    85  			description: "Path to a Kubernetes config file",
    86  		},
    87  		{
    88  			name:        keyKubecontext,
    89  			description: "Overrides the context set in the kubernetes config file",
    90  		},
    91  		{
    92  			name:        keyKubenamespace,
    93  			description: "Overrides the namespace set in the kubernetes config file",
    94  		},
    95  	}
    96  )
    97  
    98  func parseBool(config map[string]string, name string) (bool, error) {
    99  	strVal, ok := config[name]
   100  	if !ok {
   101  		return false, nil
   102  	}
   103  	res, err := strconv.ParseBool(strVal)
   104  	return res, errors.Wrap(err, name)
   105  }
   106  
   107  func validateConfig(config map[string]string, allowedKeys map[string]struct{}) error {
   108  	var errs []string
   109  	for k := range config {
   110  		if _, ok := allowedKeys[k]; !ok {
   111  			errs = append(errs, fmt.Sprintf("%s: unrecognized config key", k))
   112  		}
   113  	}
   114  	if len(errs) == 0 {
   115  		return nil
   116  	}
   117  	return errors.New(strings.Join(errs, "\n"))
   118  }
   119  
   120  func getDockerEndpoint(dockerCli command.Cli, config map[string]string) (docker.Endpoint, error) {
   121  	if err := validateConfig(config, allowedDockerConfigKeys); err != nil {
   122  		return docker.Endpoint{}, err
   123  	}
   124  	if contextName, ok := config[keyFrom]; ok {
   125  		metadata, err := dockerCli.ContextStore().GetMetadata(contextName)
   126  		if err != nil {
   127  			return docker.Endpoint{}, err
   128  		}
   129  		if ep, ok := metadata.Endpoints[docker.DockerEndpoint].(docker.EndpointMeta); ok {
   130  			return docker.Endpoint{EndpointMeta: ep}, nil
   131  		}
   132  		return docker.Endpoint{}, errors.Errorf("unable to get endpoint from context %q", contextName)
   133  	}
   134  	tlsData, err := context.TLSDataFromFiles(config[keyCA], config[keyCert], config[keyKey])
   135  	if err != nil {
   136  		return docker.Endpoint{}, err
   137  	}
   138  	skipTLSVerify, err := parseBool(config, keySkipTLSVerify)
   139  	if err != nil {
   140  		return docker.Endpoint{}, err
   141  	}
   142  	ep := docker.Endpoint{
   143  		EndpointMeta: docker.EndpointMeta{
   144  			Host:          config[keyHost],
   145  			SkipTLSVerify: skipTLSVerify,
   146  		},
   147  		TLSData: tlsData,
   148  	}
   149  	// try to resolve a docker client, validating the configuration
   150  	opts, err := ep.ClientOpts()
   151  	if err != nil {
   152  		return docker.Endpoint{}, errors.Wrap(err, "invalid docker endpoint options")
   153  	}
   154  	if _, err := client.NewClientWithOpts(opts...); err != nil {
   155  		return docker.Endpoint{}, errors.Wrap(err, "unable to apply docker endpoint options")
   156  	}
   157  	return ep, nil
   158  }
   159  
   160  func getDockerEndpointMetadataAndTLS(dockerCli command.Cli, config map[string]string) (docker.EndpointMeta, *store.EndpointTLSData, error) {
   161  	ep, err := getDockerEndpoint(dockerCli, config)
   162  	if err != nil {
   163  		return docker.EndpointMeta{}, nil, err
   164  	}
   165  	return ep.EndpointMeta, ep.TLSData.ToStoreTLSData(), nil
   166  }
   167  
   168  func getKubernetesEndpoint(dockerCli command.Cli, config map[string]string) (*kubernetes.Endpoint, error) {
   169  	if err := validateConfig(config, allowedKubernetesConfigKeys); err != nil {
   170  		return nil, err
   171  	}
   172  	if len(config) == 0 {
   173  		return nil, nil
   174  	}
   175  	if contextName, ok := config[keyFrom]; ok {
   176  		ctxMeta, err := dockerCli.ContextStore().GetMetadata(contextName)
   177  		if err != nil {
   178  			return nil, err
   179  		}
   180  		endpointMeta := kubernetes.EndpointFromContext(ctxMeta)
   181  		if endpointMeta != nil {
   182  			res, err := endpointMeta.WithTLSData(dockerCli.ContextStore(), dockerCli.CurrentContext())
   183  			if err != nil {
   184  				return nil, err
   185  			}
   186  			return &res, nil
   187  		}
   188  
   189  		// fallback to env-based kubeconfig
   190  		kubeconfig := os.Getenv("KUBECONFIG")
   191  		if kubeconfig == "" {
   192  			kubeconfig = filepath.Join(homedir.Get(), ".kube/config")
   193  		}
   194  		ep, err := kubernetes.FromKubeConfig(kubeconfig, "", "")
   195  		if err != nil {
   196  			return nil, err
   197  		}
   198  		return &ep, nil
   199  	}
   200  	if config[keyKubeconfig] != "" {
   201  		ep, err := kubernetes.FromKubeConfig(config[keyKubeconfig], config[keyKubecontext], config[keyKubenamespace])
   202  		if err != nil {
   203  			return nil, err
   204  		}
   205  		return &ep, nil
   206  	}
   207  	return nil, nil
   208  }
   209  
   210  func getKubernetesEndpointMetadataAndTLS(dockerCli command.Cli, config map[string]string) (*kubernetes.EndpointMeta, *store.EndpointTLSData, error) {
   211  	ep, err := getKubernetesEndpoint(dockerCli, config)
   212  	if err != nil {
   213  		return nil, nil, err
   214  	}
   215  	if ep == nil {
   216  		return nil, nil, err
   217  	}
   218  	return &ep.EndpointMeta, ep.TLSData.ToStoreTLSData(), nil
   219  }