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 }