update vendor

Signed-off-by: Roland.Ma <rolandma@yunify.com>
This commit is contained in:
Roland.Ma
2021-08-11 07:10:14 +00:00
parent a18f72b565
commit ea8f47c73a
2901 changed files with 269317 additions and 43103 deletions

View File

@@ -98,6 +98,9 @@ func ShortenConfig(config *Config) {
if len(authInfo.ClientCertificateData) > 0 {
authInfo.ClientCertificateData = redactedBytes
}
if len(authInfo.Token) > 0 {
authInfo.Token = "REDACTED"
}
config.AuthInfos[key] = authInfo
}
for key, cluster := range config.Clusters {

View File

@@ -82,6 +82,17 @@ type Cluster struct {
// CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority
// +optional
CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
// ProxyURL is the URL to the proxy to be used for all requests made by this
// client. URLs with "http", "https", and "socks5" schemes are supported. If
// this configuration is not provided or the empty string, the client
// attempts to construct a proxy configuration from http_proxy and
// https_proxy environment variables. If these environment variables are not
// set, the client does not attempt to proxy requests.
//
// socks5 proxying does not currently support spdy streaming endpoints (exec,
// attach, port forward).
// +optional
ProxyURL string `json:"proxy-url,omitempty"`
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
// +optional
Extensions map[string]runtime.Object `json:"extensions,omitempty"`
@@ -103,10 +114,10 @@ type AuthInfo struct {
ClientKey string `json:"client-key,omitempty"`
// ClientKeyData contains PEM-encoded data from a client key file for TLS. Overrides ClientKey
// +optional
ClientKeyData []byte `json:"client-key-data,omitempty"`
ClientKeyData []byte `json:"client-key-data,omitempty" datapolicy:"security-key"`
// Token is the bearer token for authentication to the kubernetes cluster.
// +optional
Token string `json:"token,omitempty"`
Token string `json:"token,omitempty" datapolicy:"token"`
// TokenFile is a pointer to a file that contains a bearer token (as described above). If both Token and TokenFile are present, Token takes precedence.
// +optional
TokenFile string `json:"tokenFile,omitempty"`
@@ -124,7 +135,7 @@ type AuthInfo struct {
Username string `json:"username,omitempty"`
// Password is the password for basic authentication to the kubernetes cluster.
// +optional
Password string `json:"password,omitempty"`
Password string `json:"password,omitempty" datapolicy:"password"`
// AuthProvider specifies a custom authentication plugin for the kubernetes cluster.
// +optional
AuthProvider *AuthProviderConfig `json:"auth-provider,omitempty"`
@@ -182,7 +193,7 @@ func (c AuthProviderConfig) String() string {
// ExecConfig specifies a command to provide client credentials. The command is exec'd
// and outputs structured stdout holding credentials.
//
// See the client.authentiction.k8s.io API group for specifications of the exact input
// See the client.authentication.k8s.io API group for specifications of the exact input
// and output format
type ExecConfig struct {
// Command to execute.
@@ -199,6 +210,41 @@ type ExecConfig struct {
// Preferred input version of the ExecInfo. The returned ExecCredentials MUST use
// the same encoding version as the input.
APIVersion string `json:"apiVersion,omitempty"`
// This text is shown to the user when the executable doesn't seem to be
// present. For example, `brew install foo-cli` might be a good InstallHint for
// foo-cli on Mac OS systems.
InstallHint string `json:"installHint,omitempty"`
// ProvideClusterInfo determines whether or not to provide cluster information,
// which could potentially contain very large CA data, to this exec plugin as a
// part of the KUBERNETES_EXEC_INFO environment variable. By default, it is set
// to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for
// reading this environment variable.
ProvideClusterInfo bool `json:"provideClusterInfo"`
// Config holds additional config data that is specific to the exec
// plugin with regards to the cluster being authenticated to.
//
// This data is sourced from the clientcmd Cluster object's extensions[exec] field:
//
// clusters:
// - name: my-cluster
// cluster:
// ...
// extensions:
// - name: client.authentication.k8s.io/exec # reserved extension name for per cluster exec config
// extension:
// audience: 06e3fbd18de8 # arbitrary config
//
// In some environments, the user config may be exactly the same across many clusters
// (i.e. call this exec plugin) minus some details that are specific to each cluster
// such as the audience. This field allows the per cluster config to be directly
// specified with the cluster info. Using this field to store secret data is not
// recommended as one of the prime benefits of exec plugins is that no secrets need
// to be stored directly in the kubeconfig.
// +k8s:conversion-gen=false
Config runtime.Object
}
var _ fmt.Stringer = new(ExecConfig)
@@ -221,7 +267,11 @@ func (c ExecConfig) String() string {
if len(c.Env) > 0 {
env = "[]ExecEnvVar{--- REDACTED ---}"
}
return fmt.Sprintf("api.AuthProviderConfig{Command: %q, Args: %#v, Env: %s, APIVersion: %q}", c.Command, args, env, c.APIVersion)
config := "runtime.Object(nil)"
if c.Config != nil {
config = "runtime.Object(--- REDACTED ---)"
}
return fmt.Sprintf("api.ExecConfig{Command: %q, Args: %#v, Env: %s, APIVersion: %q, ProvideClusterInfo: %t, Config: %s}", c.Command, args, env, c.APIVersion, c.ProvideClusterInfo, config)
}
// ExecEnvVar is used for setting environment variables when executing an exec-based

View File

@@ -75,6 +75,17 @@ type Cluster struct {
// CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority
// +optional
CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
// ProxyURL is the URL to the proxy to be used for all requests made by this
// client. URLs with "http", "https", and "socks5" schemes are supported. If
// this configuration is not provided or the empty string, the client
// attempts to construct a proxy configuration from http_proxy and
// https_proxy environment variables. If these environment variables are not
// set, the client does not attempt to proxy requests.
//
// socks5 proxying does not currently support spdy streaming endpoints (exec,
// attach, port forward).
// +optional
ProxyURL string `json:"proxy-url,omitempty"`
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
// +optional
Extensions []NamedExtension `json:"extensions,omitempty"`
@@ -93,10 +104,10 @@ type AuthInfo struct {
ClientKey string `json:"client-key,omitempty"`
// ClientKeyData contains PEM-encoded data from a client key file for TLS. Overrides ClientKey
// +optional
ClientKeyData []byte `json:"client-key-data,omitempty"`
ClientKeyData []byte `json:"client-key-data,omitempty" datapolicy:"security-key"`
// Token is the bearer token for authentication to the kubernetes cluster.
// +optional
Token string `json:"token,omitempty"`
Token string `json:"token,omitempty" datapolicy:"token"`
// TokenFile is a pointer to a file that contains a bearer token (as described above). If both Token and TokenFile are present, Token takes precedence.
// +optional
TokenFile string `json:"tokenFile,omitempty"`
@@ -114,7 +125,7 @@ type AuthInfo struct {
Username string `json:"username,omitempty"`
// Password is the password for basic authentication to the kubernetes cluster.
// +optional
Password string `json:"password,omitempty"`
Password string `json:"password,omitempty" datapolicy:"password"`
// AuthProvider specifies a custom authentication plugin for the kubernetes cluster.
// +optional
AuthProvider *AuthProviderConfig `json:"auth-provider,omitempty"`
@@ -181,7 +192,7 @@ type AuthProviderConfig struct {
// ExecConfig specifies a command to provide client credentials. The command is exec'd
// and outputs structured stdout holding credentials.
//
// See the client.authentiction.k8s.io API group for specifications of the exact input
// See the client.authentication.k8s.io API group for specifications of the exact input
// and output format
type ExecConfig struct {
// Command to execute.
@@ -198,6 +209,18 @@ type ExecConfig struct {
// Preferred input version of the ExecInfo. The returned ExecCredentials MUST use
// the same encoding version as the input.
APIVersion string `json:"apiVersion,omitempty"`
// This text is shown to the user when the executable doesn't seem to be
// present. For example, `brew install foo-cli` might be a good InstallHint for
// foo-cli on Mac OS systems.
InstallHint string `json:"installHint,omitempty"`
// ProvideClusterInfo determines whether or not to provide cluster information,
// which could potentially contain very large CA data, to this exec plugin as a
// part of the KUBERNETES_EXEC_INFO environment variable. By default, it is set
// to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for
// reading this environment variable.
ProvideClusterInfo bool `json:"provideClusterInfo"`
}
// ExecEnvVar is used for setting environment variables when executing an exec-based

View File

@@ -171,7 +171,15 @@ func autoConvert_v1_AuthInfo_To_api_AuthInfo(in *AuthInfo, out *api.AuthInfo, s
out.Username = in.Username
out.Password = in.Password
out.AuthProvider = (*api.AuthProviderConfig)(unsafe.Pointer(in.AuthProvider))
out.Exec = (*api.ExecConfig)(unsafe.Pointer(in.Exec))
if in.Exec != nil {
in, out := &in.Exec, &out.Exec
*out = new(api.ExecConfig)
if err := Convert_v1_ExecConfig_To_api_ExecConfig(*in, *out, s); err != nil {
return err
}
} else {
out.Exec = nil
}
if err := Convert_Slice_v1_NamedExtension_To_Map_string_To_runtime_Object(&in.Extensions, &out.Extensions, s); err != nil {
return err
}
@@ -197,7 +205,15 @@ func autoConvert_api_AuthInfo_To_v1_AuthInfo(in *api.AuthInfo, out *AuthInfo, s
out.Username = in.Username
out.Password = in.Password
out.AuthProvider = (*AuthProviderConfig)(unsafe.Pointer(in.AuthProvider))
out.Exec = (*ExecConfig)(unsafe.Pointer(in.Exec))
if in.Exec != nil {
in, out := &in.Exec, &out.Exec
*out = new(ExecConfig)
if err := Convert_api_ExecConfig_To_v1_ExecConfig(*in, *out, s); err != nil {
return err
}
} else {
out.Exec = nil
}
if err := Convert_Map_string_To_runtime_Object_To_Slice_v1_NamedExtension(&in.Extensions, &out.Extensions, s); err != nil {
return err
}
@@ -237,6 +253,7 @@ func autoConvert_v1_Cluster_To_api_Cluster(in *Cluster, out *api.Cluster, s conv
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
out.CertificateAuthority = in.CertificateAuthority
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
out.ProxyURL = in.ProxyURL
if err := Convert_Slice_v1_NamedExtension_To_Map_string_To_runtime_Object(&in.Extensions, &out.Extensions, s); err != nil {
return err
}
@@ -255,6 +272,7 @@ func autoConvert_api_Cluster_To_v1_Cluster(in *api.Cluster, out *Cluster, s conv
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
out.CertificateAuthority = in.CertificateAuthority
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
out.ProxyURL = in.ProxyURL
if err := Convert_Map_string_To_runtime_Object_To_Slice_v1_NamedExtension(&in.Extensions, &out.Extensions, s); err != nil {
return err
}
@@ -356,6 +374,8 @@ func autoConvert_v1_ExecConfig_To_api_ExecConfig(in *ExecConfig, out *api.ExecCo
out.Args = *(*[]string)(unsafe.Pointer(&in.Args))
out.Env = *(*[]api.ExecEnvVar)(unsafe.Pointer(&in.Env))
out.APIVersion = in.APIVersion
out.InstallHint = in.InstallHint
out.ProvideClusterInfo = in.ProvideClusterInfo
return nil
}
@@ -369,6 +389,9 @@ func autoConvert_api_ExecConfig_To_v1_ExecConfig(in *api.ExecConfig, out *ExecCo
out.Args = *(*[]string)(unsafe.Pointer(&in.Args))
out.Env = *(*[]ExecEnvVar)(unsafe.Pointer(&in.Env))
out.APIVersion = in.APIVersion
out.InstallHint = in.InstallHint
out.ProvideClusterInfo = in.ProvideClusterInfo
// INFO: in.Config opted out of conversion generation
return nil
}

View File

@@ -267,6 +267,9 @@ func (in *ExecConfig) DeepCopyInto(out *ExecConfig) {
*out = make([]ExecEnvVar, len(*in))
copy(*out, *in)
}
if in.Config != nil {
out.Config = in.Config.DeepCopyObject()
}
return
}

View File

@@ -23,7 +23,7 @@ import (
"io/ioutil"
"os"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/term"
clientauth "k8s.io/client-go/tools/auth"
)
@@ -90,8 +90,8 @@ func promptForString(field string, r io.Reader, show bool) (result string, err e
_, err = fmt.Fscan(r, &result)
} else {
var data []byte
if terminal.IsTerminal(int(os.Stdin.Fd())) {
data, err = terminal.ReadPassword(int(os.Stdin.Fd()))
if term.IsTerminal(int(os.Stdin.Fd())) {
data, err = term.ReadPassword(int(os.Stdin.Fd()))
result = string(data)
} else {
return "", fmt.Errorf("error reading input for %s", field)

View File

@@ -20,16 +20,23 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"github.com/imdario/mergo"
"k8s.io/klog"
"unicode"
restclient "k8s.io/client-go/rest"
clientauth "k8s.io/client-go/tools/auth"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/klog/v2"
"github.com/imdario/mergo"
)
const (
// clusterExtensionKey is reserved in the cluster extensions list for exec plugin config.
clusterExtensionKey = "client.authentication.k8s.io/exec"
)
var (
@@ -70,7 +77,7 @@ type PersistAuthProviderConfigForUser func(user string) restclient.AuthProviderC
type promptedCredentials struct {
username string
password string
password string `datapolicy:"password"`
}
// DirectClientConfig is a ClientConfig interface that is backed by a clientcmdapi.Config, options overrides, and an optional fallbackReader for auth information
@@ -150,8 +157,15 @@ func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {
clientConfig := &restclient.Config{}
clientConfig.Host = configClusterInfo.Server
if configClusterInfo.ProxyURL != "" {
u, err := parseProxyURL(configClusterInfo.ProxyURL)
if err != nil {
return nil, err
}
clientConfig.Proxy = http.ProxyURL(u)
}
if len(config.overrides.Timeout) > 0 {
if config.overrides != nil && len(config.overrides.Timeout) > 0 {
timeout, err := ParseTimeout(config.overrides.Timeout)
if err != nil {
return nil, err
@@ -180,17 +194,17 @@ func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {
authInfoName, _ := config.getAuthInfoName()
persister = PersisterForUser(config.configAccess, authInfoName)
}
userAuthPartialConfig, err := config.getUserIdentificationPartialConfig(configAuthInfo, config.fallbackReader, persister)
userAuthPartialConfig, err := config.getUserIdentificationPartialConfig(configAuthInfo, config.fallbackReader, persister, configClusterInfo)
if err != nil {
return nil, err
}
mergo.MergeWithOverwrite(clientConfig, userAuthPartialConfig)
mergo.Merge(clientConfig, userAuthPartialConfig, mergo.WithOverride)
serverAuthPartialConfig, err := getServerIdentificationPartialConfig(configAuthInfo, configClusterInfo)
if err != nil {
return nil, err
}
mergo.MergeWithOverwrite(clientConfig, serverAuthPartialConfig)
mergo.Merge(clientConfig, serverAuthPartialConfig, mergo.WithOverride)
}
return clientConfig, nil
@@ -211,7 +225,7 @@ func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo,
configClientConfig.CAData = configClusterInfo.CertificateAuthorityData
configClientConfig.Insecure = configClusterInfo.InsecureSkipTLSVerify
configClientConfig.ServerName = configClusterInfo.TLSServerName
mergo.MergeWithOverwrite(mergedConfig, configClientConfig)
mergo.Merge(mergedConfig, configClientConfig, mergo.WithOverride)
return mergedConfig, nil
}
@@ -223,7 +237,7 @@ func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo,
// 2. configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority)
// 3. if there is not enough information to identify the user, load try the ~/.kubernetes_auth file
// 4. if there is not enough information to identify the user, prompt if possible
func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fallbackReader io.Reader, persistAuthConfig restclient.AuthProviderConfigPersister) (*restclient.Config, error) {
func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fallbackReader io.Reader, persistAuthConfig restclient.AuthProviderConfigPersister, configClusterInfo clientcmdapi.Cluster) (*restclient.Config, error) {
mergedConfig := &restclient.Config{}
// blindly overwrite existing values based on precedence
@@ -261,6 +275,8 @@ func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthI
}
if configAuthInfo.Exec != nil {
mergedConfig.ExecProvider = configAuthInfo.Exec
mergedConfig.ExecProvider.InstallHint = cleanANSIEscapeCodes(mergedConfig.ExecProvider.InstallHint)
mergedConfig.ExecProvider.Config = configClusterInfo.Extensions[clusterExtensionKey]
}
// if there still isn't enough information to authenticate the user, try prompting
@@ -278,8 +294,8 @@ func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthI
promptedConfig := makeUserIdentificationConfig(*promptedAuthInfo)
previouslyMergedConfig := mergedConfig
mergedConfig = &restclient.Config{}
mergo.MergeWithOverwrite(mergedConfig, promptedConfig)
mergo.MergeWithOverwrite(mergedConfig, previouslyMergedConfig)
mergo.Merge(mergedConfig, promptedConfig, mergo.WithOverride)
mergo.Merge(mergedConfig, previouslyMergedConfig, mergo.WithOverride)
config.promptedCredentials.username = mergedConfig.Username
config.promptedCredentials.password = mergedConfig.Password
}
@@ -306,6 +322,41 @@ func canIdentifyUser(config restclient.Config) bool {
config.ExecProvider != nil
}
// cleanANSIEscapeCodes takes an arbitrary string and ensures that there are no
// ANSI escape sequences that could put the terminal in a weird state (e.g.,
// "\e[1m" bolds text)
func cleanANSIEscapeCodes(s string) string {
// spaceControlCharacters includes tab, new line, vertical tab, new page, and
// carriage return. These are in the unicode.Cc category, but that category also
// contains ESC (U+001B) which we don't want.
spaceControlCharacters := unicode.RangeTable{
R16: []unicode.Range16{
{Lo: 0x0009, Hi: 0x000D, Stride: 1},
},
}
// Why not make this deny-only (instead of allow-only)? Because unicode.C
// contains newline and tab characters that we want.
allowedRanges := []*unicode.RangeTable{
unicode.L,
unicode.M,
unicode.N,
unicode.P,
unicode.S,
unicode.Z,
&spaceControlCharacters,
}
builder := strings.Builder{}
for _, roon := range s {
if unicode.IsOneOf(allowedRanges, roon) {
builder.WriteRune(roon) // returns nil error, per go doc
} else {
fmt.Fprintf(&builder, "%U", roon)
}
}
return builder.String()
}
// Namespace implements ClientConfig
func (config *DirectClientConfig) Namespace() (string, bool, error) {
if config.overrides != nil && config.overrides.Context.Namespace != "" {
@@ -373,7 +424,7 @@ func (config *DirectClientConfig) ConfirmUsable() error {
// getContextName returns the default, or user-set context name, and a boolean that indicates
// whether the default context name has been overwritten by a user-set flag, or left as its default value
func (config *DirectClientConfig) getContextName() (string, bool) {
if len(config.overrides.CurrentContext) != 0 {
if config.overrides != nil && len(config.overrides.CurrentContext) != 0 {
return config.overrides.CurrentContext, true
}
if len(config.contextName) != 0 {
@@ -387,7 +438,7 @@ func (config *DirectClientConfig) getContextName() (string, bool) {
// and a boolean indicating whether the default authInfo name is overwritten by a user-set flag, or
// left as its default value
func (config *DirectClientConfig) getAuthInfoName() (string, bool) {
if len(config.overrides.Context.AuthInfo) != 0 {
if config.overrides != nil && len(config.overrides.Context.AuthInfo) != 0 {
return config.overrides.Context.AuthInfo, true
}
context, _ := config.getContext()
@@ -398,7 +449,7 @@ func (config *DirectClientConfig) getAuthInfoName() (string, bool) {
// indicating whether the default clusterName has been overwritten by a user-set flag, or left as
// its default value
func (config *DirectClientConfig) getClusterName() (string, bool) {
if len(config.overrides.Context.Cluster) != 0 {
if config.overrides != nil && len(config.overrides.Context.Cluster) != 0 {
return config.overrides.Context.Cluster, true
}
context, _ := config.getContext()
@@ -412,11 +463,13 @@ func (config *DirectClientConfig) getContext() (clientcmdapi.Context, error) {
mergedContext := clientcmdapi.NewContext()
if configContext, exists := contexts[contextName]; exists {
mergo.MergeWithOverwrite(mergedContext, configContext)
mergo.Merge(mergedContext, configContext, mergo.WithOverride)
} else if required {
return clientcmdapi.Context{}, fmt.Errorf("context %q does not exist", contextName)
}
mergo.MergeWithOverwrite(mergedContext, config.overrides.Context)
if config.overrides != nil {
mergo.Merge(mergedContext, config.overrides.Context, mergo.WithOverride)
}
return *mergedContext, nil
}
@@ -428,11 +481,13 @@ func (config *DirectClientConfig) getAuthInfo() (clientcmdapi.AuthInfo, error) {
mergedAuthInfo := clientcmdapi.NewAuthInfo()
if configAuthInfo, exists := authInfos[authInfoName]; exists {
mergo.MergeWithOverwrite(mergedAuthInfo, configAuthInfo)
mergo.Merge(mergedAuthInfo, configAuthInfo, mergo.WithOverride)
} else if required {
return clientcmdapi.AuthInfo{}, fmt.Errorf("auth info %q does not exist", authInfoName)
}
mergo.MergeWithOverwrite(mergedAuthInfo, config.overrides.AuthInfo)
if config.overrides != nil {
mergo.Merge(mergedAuthInfo, config.overrides.AuthInfo, mergo.WithOverride)
}
return *mergedAuthInfo, nil
}
@@ -443,30 +498,37 @@ func (config *DirectClientConfig) getCluster() (clientcmdapi.Cluster, error) {
clusterInfoName, required := config.getClusterName()
mergedClusterInfo := clientcmdapi.NewCluster()
mergo.MergeWithOverwrite(mergedClusterInfo, config.overrides.ClusterDefaults)
if config.overrides != nil {
mergo.Merge(mergedClusterInfo, config.overrides.ClusterDefaults, mergo.WithOverride)
}
if configClusterInfo, exists := clusterInfos[clusterInfoName]; exists {
mergo.MergeWithOverwrite(mergedClusterInfo, configClusterInfo)
mergo.Merge(mergedClusterInfo, configClusterInfo, mergo.WithOverride)
} else if required {
return clientcmdapi.Cluster{}, fmt.Errorf("cluster %q does not exist", clusterInfoName)
}
mergo.MergeWithOverwrite(mergedClusterInfo, config.overrides.ClusterInfo)
if config.overrides != nil {
mergo.Merge(mergedClusterInfo, config.overrides.ClusterInfo, mergo.WithOverride)
}
// * An override of --insecure-skip-tls-verify=true and no accompanying CA/CA data should clear already-set CA/CA data
// otherwise, a kubeconfig containing a CA reference would return an error that "CA and insecure-skip-tls-verify couldn't both be set".
// * An override of --certificate-authority should also override TLS skip settings and CA data, otherwise existing CA data will take precedence.
caLen := len(config.overrides.ClusterInfo.CertificateAuthority)
caDataLen := len(config.overrides.ClusterInfo.CertificateAuthorityData)
if config.overrides.ClusterInfo.InsecureSkipTLSVerify || caLen > 0 || caDataLen > 0 {
mergedClusterInfo.InsecureSkipTLSVerify = config.overrides.ClusterInfo.InsecureSkipTLSVerify
mergedClusterInfo.CertificateAuthority = config.overrides.ClusterInfo.CertificateAuthority
mergedClusterInfo.CertificateAuthorityData = config.overrides.ClusterInfo.CertificateAuthorityData
}
if config.overrides != nil {
caLen := len(config.overrides.ClusterInfo.CertificateAuthority)
caDataLen := len(config.overrides.ClusterInfo.CertificateAuthorityData)
if config.overrides.ClusterInfo.InsecureSkipTLSVerify || caLen > 0 || caDataLen > 0 {
mergedClusterInfo.InsecureSkipTLSVerify = config.overrides.ClusterInfo.InsecureSkipTLSVerify
mergedClusterInfo.CertificateAuthority = config.overrides.ClusterInfo.CertificateAuthority
mergedClusterInfo.CertificateAuthorityData = config.overrides.ClusterInfo.CertificateAuthorityData
}
// if the --tls-server-name has been set in overrides, use that value.
// if the --server has been set in overrides, then use the value of --tls-server-name specified on the CLI too. This gives the property
// that setting a --server will effectively clear the KUBECONFIG value of tls-server-name if it is specified on the command line which is
// usually correct.
if config.overrides.ClusterInfo.TLSServerName != "" || config.overrides.ClusterInfo.Server != "" {
mergedClusterInfo.TLSServerName = config.overrides.ClusterInfo.TLSServerName
// if the --tls-server-name has been set in overrides, use that value.
// if the --server has been set in overrides, then use the value of --tls-server-name specified on the CLI too. This gives the property
// that setting a --server will effectively clear the KUBECONFIG value of tls-server-name if it is specified on the command line which is
// usually correct.
if config.overrides.ClusterInfo.TLSServerName != "" || config.overrides.ClusterInfo.Server != "" {
mergedClusterInfo.TLSServerName = config.overrides.ClusterInfo.TLSServerName
}
}
return *mergedClusterInfo, nil
@@ -486,11 +548,12 @@ func (config *inClusterClientConfig) RawConfig() (clientcmdapi.Config, error) {
}
func (config *inClusterClientConfig) ClientConfig() (*restclient.Config, error) {
if config.inClusterConfigProvider == nil {
config.inClusterConfigProvider = restclient.InClusterConfig
inClusterConfigProvider := config.inClusterConfigProvider
if inClusterConfigProvider == nil {
inClusterConfigProvider = restclient.InClusterConfig
}
icc, err := config.inClusterConfigProvider()
icc, err := inClusterConfigProvider()
if err != nil {
return nil, err
}
@@ -510,7 +573,7 @@ func (config *inClusterClientConfig) ClientConfig() (*restclient.Config, error)
}
}
return icc, err
return icc, nil
}
func (config *inClusterClientConfig) Namespace() (string, bool, error) {
@@ -549,7 +612,7 @@ func (config *inClusterClientConfig) Possible() bool {
// to the default config.
func BuildConfigFromFlags(masterUrl, kubeconfigPath string) (*restclient.Config, error) {
if kubeconfigPath == "" && masterUrl == "" {
klog.Warningf("Neither --kubeconfig nor --master was specified. Using the inClusterConfig. This might not work.")
klog.Warning("Neither --kubeconfig nor --master was specified. Using the inClusterConfig. This might not work.")
kubeconfig, err := restclient.InClusterConfig()
if err == nil {
return kubeconfig, nil

View File

@@ -24,7 +24,7 @@ import (
"reflect"
"sort"
"k8s.io/klog"
"k8s.io/klog/v2"
restclient "k8s.io/client-go/rest"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
@@ -58,6 +58,15 @@ type PathOptions struct {
LoadingRules *ClientConfigLoadingRules
}
var (
// UseModifyConfigLock ensures that access to kubeconfig file using ModifyConfig method
// is being guarded by a lock file.
// This variable is intentionaly made public so other consumers of this library
// can modify its default behavior, but be caution when disabling it since
// this will make your code not threadsafe.
UseModifyConfigLock = true
)
func (o *PathOptions) GetEnvVarFiles() []string {
if len(o.EnvVar) == 0 {
return []string{}
@@ -74,10 +83,13 @@ func (o *PathOptions) GetEnvVarFiles() []string {
}
func (o *PathOptions) GetLoadingPrecedence() []string {
if o.IsExplicitFile() {
return []string{o.GetExplicitFile()}
}
if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 {
return envVarFiles
}
return []string{o.GlobalFile}
}
@@ -156,15 +168,17 @@ func NewDefaultPathOptions() *PathOptions {
// that means that this code will only write into a single file. If you want to relativizePaths, you must provide a fully qualified path in any
// modified element.
func ModifyConfig(configAccess ConfigAccess, newConfig clientcmdapi.Config, relativizePaths bool) error {
possibleSources := configAccess.GetLoadingPrecedence()
// sort the possible kubeconfig files so we always "lock" in the same order
// to avoid deadlock (note: this can fail w/ symlinks, but... come on).
sort.Strings(possibleSources)
for _, filename := range possibleSources {
if err := lockFile(filename); err != nil {
return err
if UseModifyConfigLock {
possibleSources := configAccess.GetLoadingPrecedence()
// sort the possible kubeconfig files so we always "lock" in the same order
// to avoid deadlock (note: this can fail w/ symlinks, but... come on).
sort.Strings(possibleSources)
for _, filename := range possibleSources {
if err := lockFile(filename); err != nil {
return err
}
defer unlockFile(filename)
}
defer unlockFile(filename)
}
startingConfig, err := configAccess.GetStartingConfig()
@@ -360,7 +374,7 @@ func (p *persister) Persist(config map[string]string) error {
authInfo, ok := newConfig.AuthInfos[p.user]
if ok && authInfo.AuthProvider != nil {
authInfo.AuthProvider.Config = config
ModifyConfig(p.configAccess, *newConfig, false)
return ModifyConfig(p.configAccess, *newConfig, false)
}
return nil
}

View File

@@ -18,6 +18,7 @@ package clientcmd
import (
"fmt"
"net/url"
"strconv"
"time"
)
@@ -33,3 +34,17 @@ func ParseTimeout(duration string) (time.Duration, error) {
}
return 0, fmt.Errorf("Invalid timeout value. Timeout must be a single integer in seconds, or an integer followed by a corresponding time unit (e.g. 1s | 2m | 3h)")
}
func parseProxyURL(proxyURL string) (*url.URL, error) {
u, err := url.Parse(proxyURL)
if err != nil {
return nil, fmt.Errorf("could not parse: %v", proxyURL)
}
switch u.Scheme {
case "http", "https", "socks5":
default:
return nil, fmt.Errorf("unsupported scheme %q, must be http, https, or socks5", u.Scheme)
}
return u, nil
}

View File

@@ -18,17 +18,15 @@ package clientcmd
import (
"fmt"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"reflect"
goruntime "runtime"
"strings"
"github.com/imdario/mergo"
"k8s.io/klog"
"k8s.io/klog/v2"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -48,24 +46,24 @@ const (
)
var (
RecommendedConfigDir = path.Join(homedir.HomeDir(), RecommendedHomeDir)
RecommendedHomeFile = path.Join(RecommendedConfigDir, RecommendedFileName)
RecommendedSchemaFile = path.Join(RecommendedConfigDir, RecommendedSchemaName)
RecommendedConfigDir = filepath.Join(homedir.HomeDir(), RecommendedHomeDir)
RecommendedHomeFile = filepath.Join(RecommendedConfigDir, RecommendedFileName)
RecommendedSchemaFile = filepath.Join(RecommendedConfigDir, RecommendedSchemaName)
)
// currentMigrationRules returns a map that holds the history of recommended home directories used in previous versions.
// Any future changes to RecommendedHomeFile and related are expected to add a migration rule here, in order to make
// sure existing config files are migrated to their new locations properly.
func currentMigrationRules() map[string]string {
oldRecommendedHomeFile := path.Join(os.Getenv("HOME"), "/.kube/.kubeconfig")
oldRecommendedWindowsHomeFile := path.Join(os.Getenv("HOME"), RecommendedHomeDir, RecommendedFileName)
migrationRules := map[string]string{}
migrationRules[RecommendedHomeFile] = oldRecommendedHomeFile
var oldRecommendedHomeFileName string
if goruntime.GOOS == "windows" {
migrationRules[RecommendedHomeFile] = oldRecommendedWindowsHomeFile
oldRecommendedHomeFileName = RecommendedFileName
} else {
oldRecommendedHomeFileName = ".kubeconfig"
}
return map[string]string{
RecommendedHomeFile: filepath.Join(os.Getenv("HOME"), RecommendedHomeDir, oldRecommendedHomeFileName),
}
return migrationRules
}
type ClientConfigLoader interface {
@@ -227,7 +225,7 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
mapConfig := clientcmdapi.NewConfig()
for _, kubeconfig := range kubeconfigs {
mergo.MergeWithOverwrite(mapConfig, kubeconfig)
mergo.Merge(mapConfig, kubeconfig, mergo.WithOverride)
}
// merge all of the struct values in the reverse order so that priority is given correctly
@@ -235,14 +233,14 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
nonMapConfig := clientcmdapi.NewConfig()
for i := len(kubeconfigs) - 1; i >= 0; i-- {
kubeconfig := kubeconfigs[i]
mergo.MergeWithOverwrite(nonMapConfig, kubeconfig)
mergo.Merge(nonMapConfig, kubeconfig, mergo.WithOverride)
}
// since values are overwritten, but maps values are not, we can merge the non-map config on top of the map config and
// get the values we expect.
config := clientcmdapi.NewConfig()
mergo.MergeWithOverwrite(config, mapConfig)
mergo.MergeWithOverwrite(config, nonMapConfig)
mergo.Merge(config, mapConfig, mergo.WithOverride)
mergo.Merge(config, nonMapConfig, mergo.WithOverride)
if rules.ResolvePaths() {
if err := ResolveLocalPaths(config); err != nil {
@@ -283,20 +281,15 @@ func (rules *ClientConfigLoadingRules) Migrate() error {
return fmt.Errorf("cannot migrate %v to %v because it is a directory", source, destination)
}
in, err := os.Open(source)
data, err := ioutil.ReadFile(source)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(destination)
// destination is created with mode 0666 before umask
err = ioutil.WriteFile(destination, data, 0666)
if err != nil {
return err
}
defer out.Close()
if _, err = io.Copy(out, in); err != nil {
return err
}
}
return nil
@@ -304,6 +297,10 @@ func (rules *ClientConfigLoadingRules) Migrate() error {
// GetLoadingPrecedence implements ConfigAccess
func (rules *ClientConfigLoadingRules) GetLoadingPrecedence() []string {
if len(rules.ExplicitPath) > 0 {
return []string{rules.ExplicitPath}
}
return rules.Precedence
}

View File

@@ -20,7 +20,7 @@ import (
"io"
"sync"
"k8s.io/klog"
"k8s.io/klog/v2"
restclient "k8s.io/client-go/rest"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
@@ -60,27 +60,26 @@ func NewInteractiveDeferredLoadingClientConfig(loader ClientConfigLoader, overri
}
func (config *DeferredLoadingClientConfig) createClientConfig() (ClientConfig, error) {
if config.clientConfig == nil {
config.loadingLock.Lock()
defer config.loadingLock.Unlock()
config.loadingLock.Lock()
defer config.loadingLock.Unlock()
if config.clientConfig == nil {
mergedConfig, err := config.loader.Load()
if err != nil {
return nil, err
}
var mergedClientConfig ClientConfig
if config.fallbackReader != nil {
mergedClientConfig = NewInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides, config.fallbackReader, config.loader)
} else {
mergedClientConfig = NewNonInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides, config.loader)
}
config.clientConfig = mergedClientConfig
}
if config.clientConfig != nil {
return config.clientConfig, nil
}
mergedConfig, err := config.loader.Load()
if err != nil {
return nil, err
}
var currentContext string
if config.overrides != nil {
currentContext = config.overrides.CurrentContext
}
if config.fallbackReader != nil {
config.clientConfig = NewInteractiveClientConfig(*mergedConfig, currentContext, config.overrides, config.fallbackReader, config.loader)
} else {
config.clientConfig = NewNonInteractiveClientConfig(*mergedConfig, currentContext, config.overrides, config.loader)
}
return config.clientConfig, nil
}

View File

@@ -30,11 +30,24 @@ import (
var (
ErrNoContext = errors.New("no context chosen")
ErrEmptyConfig = errors.New("no configuration has been provided, try setting KUBERNETES_MASTER environment variable")
ErrEmptyConfig = NewEmptyConfigError("no configuration has been provided, try setting KUBERNETES_MASTER environment variable")
// message is for consistency with old behavior
ErrEmptyCluster = errors.New("cluster has no server defined")
)
// NewEmptyConfigError returns an error wrapping the given message which IsEmptyConfig() will recognize as an empty config error
func NewEmptyConfigError(message string) error {
return &errEmptyConfig{message}
}
type errEmptyConfig struct {
message string
}
func (e *errEmptyConfig) Error() string {
return e.message
}
type errContextNotFound struct {
ContextName string
}
@@ -60,9 +73,14 @@ func IsContextNotFound(err error) bool {
func IsEmptyConfig(err error) bool {
switch t := err.(type) {
case errConfigurationInvalid:
return len(t) == 1 && t[0] == ErrEmptyConfig
if len(t) != 1 {
return false
}
_, ok := t[0].(*errEmptyConfig)
return ok
}
return err == ErrEmptyConfig
_, ok := err.(*errEmptyConfig)
return ok
}
// errConfigurationInvalid is a set of errors indicating the configuration is invalid.
@@ -209,6 +227,11 @@ func validateClusterInfo(clusterName string, clusterInfo clientcmdapi.Cluster) [
validationErrors = append(validationErrors, fmt.Errorf("no server found for cluster %q", clusterName))
}
}
if proxyURL := clusterInfo.ProxyURL; proxyURL != "" {
if _, err := parseProxyURL(proxyURL); err != nil {
validationErrors = append(validationErrors, fmt.Errorf("invalid 'proxy-url' %q for cluster %q: %v", proxyURL, clusterName, err))
}
}
// Make sure CA data and CA file aren't both specified
if len(clusterInfo.CertificateAuthority) != 0 && len(clusterInfo.CertificateAuthorityData) != 0 {
validationErrors = append(validationErrors, fmt.Errorf("certificate-authority-data and certificate-authority are both specified for %v. certificate-authority-data will override.", clusterName))