Matteo Scandolo | a428586 | 2020-12-01 18:10:10 -0800 | [diff] [blame] | 1 | /* |
| 2 | Copyright 2014 The Kubernetes Authors. |
| 3 | |
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | you may not use this file except in compliance with the License. |
| 6 | You may obtain a copy of the License at |
| 7 | |
| 8 | http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | Unless required by applicable law or agreed to in writing, software |
| 11 | distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | See the License for the specific language governing permissions and |
| 14 | limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package clientcmd |
| 18 | |
| 19 | import ( |
| 20 | "strconv" |
| 21 | "strings" |
| 22 | |
| 23 | "github.com/spf13/pflag" |
| 24 | |
| 25 | clientcmdapi "k8s.io/client-go/tools/clientcmd/api" |
| 26 | ) |
| 27 | |
| 28 | // ConfigOverrides holds values that should override whatever information is pulled from the actual Config object. You can't |
| 29 | // simply use an actual Config object, because Configs hold maps, but overrides are restricted to "at most one" |
| 30 | type ConfigOverrides struct { |
| 31 | AuthInfo clientcmdapi.AuthInfo |
| 32 | // ClusterDefaults are applied before the configured cluster info is loaded. |
| 33 | ClusterDefaults clientcmdapi.Cluster |
| 34 | ClusterInfo clientcmdapi.Cluster |
| 35 | Context clientcmdapi.Context |
| 36 | CurrentContext string |
| 37 | Timeout string |
| 38 | } |
| 39 | |
| 40 | // ConfigOverrideFlags holds the flag names to be used for binding command line flags. Notice that this structure tightly |
| 41 | // corresponds to ConfigOverrides |
| 42 | type ConfigOverrideFlags struct { |
| 43 | AuthOverrideFlags AuthOverrideFlags |
| 44 | ClusterOverrideFlags ClusterOverrideFlags |
| 45 | ContextOverrideFlags ContextOverrideFlags |
| 46 | CurrentContext FlagInfo |
| 47 | Timeout FlagInfo |
| 48 | } |
| 49 | |
| 50 | // AuthOverrideFlags holds the flag names to be used for binding command line flags for AuthInfo objects |
| 51 | type AuthOverrideFlags struct { |
| 52 | ClientCertificate FlagInfo |
| 53 | ClientKey FlagInfo |
| 54 | Token FlagInfo |
| 55 | Impersonate FlagInfo |
| 56 | ImpersonateGroups FlagInfo |
| 57 | Username FlagInfo |
| 58 | Password FlagInfo |
| 59 | } |
| 60 | |
| 61 | // ContextOverrideFlags holds the flag names to be used for binding command line flags for Cluster objects |
| 62 | type ContextOverrideFlags struct { |
| 63 | ClusterName FlagInfo |
| 64 | AuthInfoName FlagInfo |
| 65 | Namespace FlagInfo |
| 66 | } |
| 67 | |
| 68 | // ClusterOverride holds the flag names to be used for binding command line flags for Cluster objects |
| 69 | type ClusterOverrideFlags struct { |
| 70 | APIServer FlagInfo |
| 71 | APIVersion FlagInfo |
| 72 | CertificateAuthority FlagInfo |
| 73 | InsecureSkipTLSVerify FlagInfo |
| 74 | TLSServerName FlagInfo |
| 75 | } |
| 76 | |
| 77 | // FlagInfo contains information about how to register a flag. This struct is useful if you want to provide a way for an extender to |
| 78 | // get back a set of recommended flag names, descriptions, and defaults, but allow for customization by an extender. This makes for |
| 79 | // coherent extension, without full prescription |
| 80 | type FlagInfo struct { |
| 81 | // LongName is the long string for a flag. If this is empty, then the flag will not be bound |
| 82 | LongName string |
| 83 | // ShortName is the single character for a flag. If this is empty, then there will be no short flag |
| 84 | ShortName string |
| 85 | // Default is the default value for the flag |
| 86 | Default string |
| 87 | // Description is the description for the flag |
| 88 | Description string |
| 89 | } |
| 90 | |
| 91 | // AddSecretAnnotation add secret flag to Annotation. |
| 92 | func (f FlagInfo) AddSecretAnnotation(flags *pflag.FlagSet) FlagInfo { |
| 93 | flags.SetAnnotation(f.LongName, "classified", []string{"true"}) |
| 94 | return f |
| 95 | } |
| 96 | |
| 97 | // BindStringFlag binds the flag based on the provided info. If LongName == "", nothing is registered |
| 98 | func (f FlagInfo) BindStringFlag(flags *pflag.FlagSet, target *string) FlagInfo { |
| 99 | // you can't register a flag without a long name |
| 100 | if len(f.LongName) > 0 { |
| 101 | flags.StringVarP(target, f.LongName, f.ShortName, f.Default, f.Description) |
| 102 | } |
| 103 | return f |
| 104 | } |
| 105 | |
| 106 | // BindTransformingStringFlag binds the flag based on the provided info. If LongName == "", nothing is registered |
| 107 | func (f FlagInfo) BindTransformingStringFlag(flags *pflag.FlagSet, target *string, transformer func(string) (string, error)) FlagInfo { |
| 108 | // you can't register a flag without a long name |
| 109 | if len(f.LongName) > 0 { |
| 110 | flags.VarP(newTransformingStringValue(f.Default, target, transformer), f.LongName, f.ShortName, f.Description) |
| 111 | } |
| 112 | return f |
| 113 | } |
| 114 | |
| 115 | // BindStringSliceFlag binds the flag based on the provided info. If LongName == "", nothing is registered |
| 116 | func (f FlagInfo) BindStringArrayFlag(flags *pflag.FlagSet, target *[]string) FlagInfo { |
| 117 | // you can't register a flag without a long name |
| 118 | if len(f.LongName) > 0 { |
| 119 | sliceVal := []string{} |
| 120 | if len(f.Default) > 0 { |
| 121 | sliceVal = []string{f.Default} |
| 122 | } |
| 123 | flags.StringArrayVarP(target, f.LongName, f.ShortName, sliceVal, f.Description) |
| 124 | } |
| 125 | return f |
| 126 | } |
| 127 | |
| 128 | // BindBoolFlag binds the flag based on the provided info. If LongName == "", nothing is registered |
| 129 | func (f FlagInfo) BindBoolFlag(flags *pflag.FlagSet, target *bool) FlagInfo { |
| 130 | // you can't register a flag without a long name |
| 131 | if len(f.LongName) > 0 { |
| 132 | // try to parse Default as a bool. If it fails, assume false |
| 133 | boolVal, err := strconv.ParseBool(f.Default) |
| 134 | if err != nil { |
| 135 | boolVal = false |
| 136 | } |
| 137 | |
| 138 | flags.BoolVarP(target, f.LongName, f.ShortName, boolVal, f.Description) |
| 139 | } |
| 140 | return f |
| 141 | } |
| 142 | |
| 143 | const ( |
| 144 | FlagClusterName = "cluster" |
| 145 | FlagAuthInfoName = "user" |
| 146 | FlagContext = "context" |
| 147 | FlagNamespace = "namespace" |
| 148 | FlagAPIServer = "server" |
| 149 | FlagTLSServerName = "tls-server-name" |
| 150 | FlagInsecure = "insecure-skip-tls-verify" |
| 151 | FlagCertFile = "client-certificate" |
| 152 | FlagKeyFile = "client-key" |
| 153 | FlagCAFile = "certificate-authority" |
| 154 | FlagEmbedCerts = "embed-certs" |
| 155 | FlagBearerToken = "token" |
| 156 | FlagImpersonate = "as" |
| 157 | FlagImpersonateGroup = "as-group" |
| 158 | FlagUsername = "username" |
| 159 | FlagPassword = "password" |
| 160 | FlagTimeout = "request-timeout" |
| 161 | ) |
| 162 | |
| 163 | // RecommendedConfigOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing |
| 164 | func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags { |
| 165 | return ConfigOverrideFlags{ |
| 166 | AuthOverrideFlags: RecommendedAuthOverrideFlags(prefix), |
| 167 | ClusterOverrideFlags: RecommendedClusterOverrideFlags(prefix), |
| 168 | ContextOverrideFlags: RecommendedContextOverrideFlags(prefix), |
| 169 | |
| 170 | CurrentContext: FlagInfo{prefix + FlagContext, "", "", "The name of the kubeconfig context to use"}, |
| 171 | Timeout: FlagInfo{prefix + FlagTimeout, "", "0", "The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests."}, |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | // RecommendedAuthOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing |
| 176 | func RecommendedAuthOverrideFlags(prefix string) AuthOverrideFlags { |
| 177 | return AuthOverrideFlags{ |
| 178 | ClientCertificate: FlagInfo{prefix + FlagCertFile, "", "", "Path to a client certificate file for TLS"}, |
| 179 | ClientKey: FlagInfo{prefix + FlagKeyFile, "", "", "Path to a client key file for TLS"}, |
| 180 | Token: FlagInfo{prefix + FlagBearerToken, "", "", "Bearer token for authentication to the API server"}, |
| 181 | Impersonate: FlagInfo{prefix + FlagImpersonate, "", "", "Username to impersonate for the operation"}, |
| 182 | ImpersonateGroups: FlagInfo{prefix + FlagImpersonateGroup, "", "", "Group to impersonate for the operation, this flag can be repeated to specify multiple groups."}, |
| 183 | Username: FlagInfo{prefix + FlagUsername, "", "", "Username for basic authentication to the API server"}, |
| 184 | Password: FlagInfo{prefix + FlagPassword, "", "", "Password for basic authentication to the API server"}, |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | // RecommendedClusterOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing |
| 189 | func RecommendedClusterOverrideFlags(prefix string) ClusterOverrideFlags { |
| 190 | return ClusterOverrideFlags{ |
| 191 | APIServer: FlagInfo{prefix + FlagAPIServer, "", "", "The address and port of the Kubernetes API server"}, |
| 192 | CertificateAuthority: FlagInfo{prefix + FlagCAFile, "", "", "Path to a cert file for the certificate authority"}, |
| 193 | InsecureSkipTLSVerify: FlagInfo{prefix + FlagInsecure, "", "false", "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure"}, |
| 194 | TLSServerName: FlagInfo{prefix + FlagTLSServerName, "", "", "If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used."}, |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | // RecommendedContextOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing |
| 199 | func RecommendedContextOverrideFlags(prefix string) ContextOverrideFlags { |
| 200 | return ContextOverrideFlags{ |
| 201 | ClusterName: FlagInfo{prefix + FlagClusterName, "", "", "The name of the kubeconfig cluster to use"}, |
| 202 | AuthInfoName: FlagInfo{prefix + FlagAuthInfoName, "", "", "The name of the kubeconfig user to use"}, |
| 203 | Namespace: FlagInfo{prefix + FlagNamespace, "n", "", "If present, the namespace scope for this CLI request"}, |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | // BindOverrideFlags is a convenience method to bind the specified flags to their associated variables |
| 208 | func BindOverrideFlags(overrides *ConfigOverrides, flags *pflag.FlagSet, flagNames ConfigOverrideFlags) { |
| 209 | BindAuthInfoFlags(&overrides.AuthInfo, flags, flagNames.AuthOverrideFlags) |
| 210 | BindClusterFlags(&overrides.ClusterInfo, flags, flagNames.ClusterOverrideFlags) |
| 211 | BindContextFlags(&overrides.Context, flags, flagNames.ContextOverrideFlags) |
| 212 | flagNames.CurrentContext.BindStringFlag(flags, &overrides.CurrentContext) |
| 213 | flagNames.Timeout.BindStringFlag(flags, &overrides.Timeout) |
| 214 | } |
| 215 | |
| 216 | // BindAuthInfoFlags is a convenience method to bind the specified flags to their associated variables |
| 217 | func BindAuthInfoFlags(authInfo *clientcmdapi.AuthInfo, flags *pflag.FlagSet, flagNames AuthOverrideFlags) { |
| 218 | flagNames.ClientCertificate.BindStringFlag(flags, &authInfo.ClientCertificate).AddSecretAnnotation(flags) |
| 219 | flagNames.ClientKey.BindStringFlag(flags, &authInfo.ClientKey).AddSecretAnnotation(flags) |
| 220 | flagNames.Token.BindStringFlag(flags, &authInfo.Token).AddSecretAnnotation(flags) |
| 221 | flagNames.Impersonate.BindStringFlag(flags, &authInfo.Impersonate).AddSecretAnnotation(flags) |
| 222 | flagNames.ImpersonateGroups.BindStringArrayFlag(flags, &authInfo.ImpersonateGroups).AddSecretAnnotation(flags) |
| 223 | flagNames.Username.BindStringFlag(flags, &authInfo.Username).AddSecretAnnotation(flags) |
| 224 | flagNames.Password.BindStringFlag(flags, &authInfo.Password).AddSecretAnnotation(flags) |
| 225 | } |
| 226 | |
| 227 | // BindClusterFlags is a convenience method to bind the specified flags to their associated variables |
| 228 | func BindClusterFlags(clusterInfo *clientcmdapi.Cluster, flags *pflag.FlagSet, flagNames ClusterOverrideFlags) { |
| 229 | flagNames.APIServer.BindStringFlag(flags, &clusterInfo.Server) |
| 230 | flagNames.CertificateAuthority.BindStringFlag(flags, &clusterInfo.CertificateAuthority) |
| 231 | flagNames.InsecureSkipTLSVerify.BindBoolFlag(flags, &clusterInfo.InsecureSkipTLSVerify) |
| 232 | flagNames.TLSServerName.BindStringFlag(flags, &clusterInfo.TLSServerName) |
| 233 | } |
| 234 | |
| 235 | // BindFlags is a convenience method to bind the specified flags to their associated variables |
| 236 | func BindContextFlags(contextInfo *clientcmdapi.Context, flags *pflag.FlagSet, flagNames ContextOverrideFlags) { |
| 237 | flagNames.ClusterName.BindStringFlag(flags, &contextInfo.Cluster) |
| 238 | flagNames.AuthInfoName.BindStringFlag(flags, &contextInfo.AuthInfo) |
| 239 | flagNames.Namespace.BindTransformingStringFlag(flags, &contextInfo.Namespace, RemoveNamespacesPrefix) |
| 240 | } |
| 241 | |
| 242 | // RemoveNamespacesPrefix is a transformer that strips "ns/", "namespace/" and "namespaces/" prefixes case-insensitively |
| 243 | func RemoveNamespacesPrefix(value string) (string, error) { |
| 244 | for _, prefix := range []string{"namespaces/", "namespace/", "ns/"} { |
| 245 | if len(value) > len(prefix) && strings.EqualFold(value[0:len(prefix)], prefix) { |
| 246 | value = value[len(prefix):] |
| 247 | break |
| 248 | } |
| 249 | } |
| 250 | return value, nil |
| 251 | } |