mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
Merge pull request #95489 from ankeesler/ankeesler/enj/f/exec_plugin_cluster
exec credential provider: wire in cluster info (superset of #91192)
This commit is contained in:
commit
53913a7c67
@ -468,6 +468,10 @@ API rule violation: names_match,k8s.io/apimachinery/pkg/runtime,Unknown,Raw
|
|||||||
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,IntVal
|
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,IntVal
|
||||||
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,StrVal
|
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,StrVal
|
||||||
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,Type
|
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,Type
|
||||||
|
API rule violation: names_match,k8s.io/client-go/pkg/apis/clientauthentication/v1beta1,Cluster,CertificateAuthorityData
|
||||||
|
API rule violation: names_match,k8s.io/client-go/pkg/apis/clientauthentication/v1beta1,Cluster,InsecureSkipTLSVerify
|
||||||
|
API rule violation: names_match,k8s.io/client-go/pkg/apis/clientauthentication/v1beta1,Cluster,ProxyURL
|
||||||
|
API rule violation: names_match,k8s.io/client-go/pkg/apis/clientauthentication/v1beta1,Cluster,TLSServerName
|
||||||
API rule violation: names_match,k8s.io/cloud-provider/app/apis/config/v1alpha1,CloudControllerManagerConfiguration,Generic
|
API rule violation: names_match,k8s.io/cloud-provider/app/apis/config/v1alpha1,CloudControllerManagerConfiguration,Generic
|
||||||
API rule violation: names_match,k8s.io/cloud-provider/app/apis/config/v1alpha1,CloudControllerManagerConfiguration,KubeCloudShared
|
API rule violation: names_match,k8s.io/cloud-provider/app/apis/config/v1alpha1,CloudControllerManagerConfiguration,KubeCloudShared
|
||||||
API rule violation: names_match,k8s.io/cloud-provider/app/apis/config/v1alpha1,CloudControllerManagerConfiguration,NodeStatusUpdateFrequency
|
API rule violation: names_match,k8s.io/cloud-provider/app/apis/config/v1alpha1,CloudControllerManagerConfiguration,NodeStatusUpdateFrequency
|
||||||
|
@ -18,11 +18,12 @@ package clientauthentication
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
// ExecCredentials is used by exec-based plugins to communicate credentials to
|
// ExecCredential is used by exec-based plugins to communicate credentials to
|
||||||
// HTTP transports.
|
// HTTP transports.
|
||||||
type ExecCredential struct {
|
type ExecCredential struct {
|
||||||
metav1.TypeMeta
|
metav1.TypeMeta
|
||||||
@ -37,7 +38,7 @@ type ExecCredential struct {
|
|||||||
Status *ExecCredentialStatus
|
Status *ExecCredentialStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecCredenitalSpec holds request and runtime specific information provided by
|
// ExecCredentialSpec holds request and runtime specific information provided by
|
||||||
// the transport.
|
// the transport.
|
||||||
type ExecCredentialSpec struct {
|
type ExecCredentialSpec struct {
|
||||||
// Response is populated when the transport encounters HTTP status codes, such as 401,
|
// Response is populated when the transport encounters HTTP status codes, such as 401,
|
||||||
@ -49,6 +50,13 @@ type ExecCredentialSpec struct {
|
|||||||
// interactive prompt.
|
// interactive prompt.
|
||||||
// +optional
|
// +optional
|
||||||
Interactive bool
|
Interactive bool
|
||||||
|
|
||||||
|
// Cluster contains information to allow an exec plugin to communicate with the
|
||||||
|
// kubernetes cluster being authenticated to. Note that Cluster is non-nil only
|
||||||
|
// when provideClusterInfo is set to true in the exec provider config (i.e.,
|
||||||
|
// ExecConfig.ProvideClusterInfo).
|
||||||
|
// +optional
|
||||||
|
Cluster *Cluster
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecCredentialStatus holds credentials for the transport to use.
|
// ExecCredentialStatus holds credentials for the transport to use.
|
||||||
@ -75,3 +83,56 @@ type Response struct {
|
|||||||
// Code is the HTTP status code returned by the server.
|
// Code is the HTTP status code returned by the server.
|
||||||
Code int32
|
Code int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cluster contains information to allow an exec plugin to communicate
|
||||||
|
// with the kubernetes cluster being authenticated to.
|
||||||
|
//
|
||||||
|
// To ensure that this struct contains everything someone would need to communicate
|
||||||
|
// with a kubernetes cluster (just like they would via a kubeconfig), the fields
|
||||||
|
// should shadow "k8s.io/client-go/tools/clientcmd/api/v1".Cluster, with the exception
|
||||||
|
// of CertificateAuthority, since CA data will always be passed to the plugin as bytes.
|
||||||
|
type Cluster struct {
|
||||||
|
// Server is the address of the kubernetes cluster (https://hostname:port).
|
||||||
|
Server string
|
||||||
|
// TLSServerName is passed to the server for SNI and is used in the client to
|
||||||
|
// check server certificates against. If ServerName is empty, the hostname
|
||||||
|
// used to contact the server is used.
|
||||||
|
// +optional
|
||||||
|
TLSServerName string
|
||||||
|
// InsecureSkipTLSVerify skips the validity check for the server's certificate.
|
||||||
|
// This will make your HTTPS connections insecure.
|
||||||
|
// +optional
|
||||||
|
InsecureSkipTLSVerify bool
|
||||||
|
// CAData contains PEM-encoded certificate authority certificates.
|
||||||
|
// If empty, system roots should be used.
|
||||||
|
// +listType=atomic
|
||||||
|
// +optional
|
||||||
|
CertificateAuthorityData []byte
|
||||||
|
// ProxyURL is the URL to the proxy to be used for all requests to this
|
||||||
|
// cluster.
|
||||||
|
// +optional
|
||||||
|
ProxyURL string
|
||||||
|
// 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[client.authentication.k8s.io/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.
|
||||||
|
// +optional
|
||||||
|
Config runtime.Object
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@ load(
|
|||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"conversion.go",
|
||||||
"doc.go",
|
"doc.go",
|
||||||
"register.go",
|
"register.go",
|
||||||
"types.go",
|
"types.go",
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/conversion"
|
||||||
|
"k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Convert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
||||||
|
// This conversion intentionally omits the Cluster field which is only supported in newer versions.
|
||||||
|
return autoConvert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec(in, out, s)
|
||||||
|
}
|
@ -37,7 +37,7 @@ type ExecCredential struct {
|
|||||||
Status *ExecCredentialStatus `json:"status,omitempty"`
|
Status *ExecCredentialStatus `json:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecCredenitalSpec holds request and runtime specific information provided by
|
// ExecCredentialSpec holds request and runtime specific information provided by
|
||||||
// the transport.
|
// the transport.
|
||||||
type ExecCredentialSpec struct {
|
type ExecCredentialSpec struct {
|
||||||
// Response is populated when the transport encounters HTTP status codes, such as 401,
|
// Response is populated when the transport encounters HTTP status codes, such as 401,
|
||||||
|
@ -51,11 +51,6 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.AddGeneratedConversionFunc((*clientauthentication.ExecCredentialSpec)(nil), (*ExecCredentialSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
|
||||||
return Convert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec(a.(*clientauthentication.ExecCredentialSpec), b.(*ExecCredentialSpec), scope)
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := s.AddGeneratedConversionFunc((*ExecCredentialStatus)(nil), (*clientauthentication.ExecCredentialStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
if err := s.AddGeneratedConversionFunc((*ExecCredentialStatus)(nil), (*clientauthentication.ExecCredentialStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
return Convert_v1alpha1_ExecCredentialStatus_To_clientauthentication_ExecCredentialStatus(a.(*ExecCredentialStatus), b.(*clientauthentication.ExecCredentialStatus), scope)
|
return Convert_v1alpha1_ExecCredentialStatus_To_clientauthentication_ExecCredentialStatus(a.(*ExecCredentialStatus), b.(*clientauthentication.ExecCredentialStatus), scope)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
@ -76,6 +71,11 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := s.AddConversionFunc((*clientauthentication.ExecCredentialSpec)(nil), (*ExecCredentialSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return Convert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec(a.(*clientauthentication.ExecCredentialSpec), b.(*ExecCredentialSpec), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,14 +119,10 @@ func Convert_v1alpha1_ExecCredentialSpec_To_clientauthentication_ExecCredentialS
|
|||||||
func autoConvert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
func autoConvert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
||||||
out.Response = (*Response)(unsafe.Pointer(in.Response))
|
out.Response = (*Response)(unsafe.Pointer(in.Response))
|
||||||
out.Interactive = in.Interactive
|
out.Interactive = in.Interactive
|
||||||
|
// WARNING: in.Cluster requires manual conversion: does not exist in peer-type
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec is an autogenerated conversion function.
|
|
||||||
func Convert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
|
||||||
return autoConvert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec(in, out, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func autoConvert_v1alpha1_ExecCredentialStatus_To_clientauthentication_ExecCredentialStatus(in *ExecCredentialStatus, out *clientauthentication.ExecCredentialStatus, s conversion.Scope) error {
|
func autoConvert_v1alpha1_ExecCredentialStatus_To_clientauthentication_ExecCredentialStatus(in *ExecCredentialStatus, out *clientauthentication.ExecCredentialStatus, s conversion.Scope) error {
|
||||||
out.ExpirationTimestamp = (*v1.Time)(unsafe.Pointer(in.ExpirationTimestamp))
|
out.ExpirationTimestamp = (*v1.Time)(unsafe.Pointer(in.ExpirationTimestamp))
|
||||||
out.Token = in.Token
|
out.Token = in.Token
|
||||||
|
@ -17,10 +17,12 @@ limitations under the License.
|
|||||||
package v1beta1
|
package v1beta1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
"k8s.io/apimachinery/pkg/conversion"
|
||||||
clientauthentication "k8s.io/client-go/pkg/apis/clientauthentication"
|
"k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Convert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
func Convert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
||||||
return nil
|
// This conversion intentionally omits the Response and Interactive fields, which were only
|
||||||
|
// supported in v1alpha1.
|
||||||
|
return autoConvert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in, out, s)
|
||||||
}
|
}
|
||||||
|
@ -18,17 +18,17 @@ package v1beta1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
// ExecCredentials is used by exec-based plugins to communicate credentials to
|
// ExecCredential is used by exec-based plugins to communicate credentials to
|
||||||
// HTTP transports.
|
// HTTP transports.
|
||||||
type ExecCredential struct {
|
type ExecCredential struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
|
||||||
// Spec holds information passed to the plugin by the transport. This contains
|
// Spec holds information passed to the plugin by the transport.
|
||||||
// request and runtime specific information, such as if the session is interactive.
|
|
||||||
Spec ExecCredentialSpec `json:"spec,omitempty"`
|
Spec ExecCredentialSpec `json:"spec,omitempty"`
|
||||||
|
|
||||||
// Status is filled in by the plugin and holds the credentials that the transport
|
// Status is filled in by the plugin and holds the credentials that the transport
|
||||||
@ -37,9 +37,16 @@ type ExecCredential struct {
|
|||||||
Status *ExecCredentialStatus `json:"status,omitempty"`
|
Status *ExecCredentialStatus `json:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecCredenitalSpec holds request and runtime specific information provided by
|
// ExecCredentialSpec holds request and runtime specific information provided by
|
||||||
// the transport.
|
// the transport.
|
||||||
type ExecCredentialSpec struct{}
|
type ExecCredentialSpec struct {
|
||||||
|
// Cluster contains information to allow an exec plugin to communicate with the
|
||||||
|
// kubernetes cluster being authenticated to. Note that Cluster is non-nil only
|
||||||
|
// when provideClusterInfo is set to true in the exec provider config (i.e.,
|
||||||
|
// ExecConfig.ProvideClusterInfo).
|
||||||
|
// +optional
|
||||||
|
Cluster *Cluster `json:"cluster,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// ExecCredentialStatus holds credentials for the transport to use.
|
// ExecCredentialStatus holds credentials for the transport to use.
|
||||||
//
|
//
|
||||||
@ -57,3 +64,56 @@ type ExecCredentialStatus struct {
|
|||||||
// PEM-encoded private key for the above certificate.
|
// PEM-encoded private key for the above certificate.
|
||||||
ClientKeyData string `json:"clientKeyData,omitempty"`
|
ClientKeyData string `json:"clientKeyData,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cluster contains information to allow an exec plugin to communicate
|
||||||
|
// with the kubernetes cluster being authenticated to.
|
||||||
|
//
|
||||||
|
// To ensure that this struct contains everything someone would need to communicate
|
||||||
|
// with a kubernetes cluster (just like they would via a kubeconfig), the fields
|
||||||
|
// should shadow "k8s.io/client-go/tools/clientcmd/api/v1".Cluster, with the exception
|
||||||
|
// of CertificateAuthority, since CA data will always be passed to the plugin as bytes.
|
||||||
|
type Cluster struct {
|
||||||
|
// Server is the address of the kubernetes cluster (https://hostname:port).
|
||||||
|
Server string `json:"server"`
|
||||||
|
// TLSServerName is passed to the server for SNI and is used in the client to
|
||||||
|
// check server certificates against. If ServerName is empty, the hostname
|
||||||
|
// used to contact the server is used.
|
||||||
|
// +optional
|
||||||
|
TLSServerName string `json:"tls-server-name,omitempty"`
|
||||||
|
// InsecureSkipTLSVerify skips the validity check for the server's certificate.
|
||||||
|
// This will make your HTTPS connections insecure.
|
||||||
|
// +optional
|
||||||
|
InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"`
|
||||||
|
// CAData contains PEM-encoded certificate authority certificates.
|
||||||
|
// If empty, system roots should be used.
|
||||||
|
// +listType=atomic
|
||||||
|
// +optional
|
||||||
|
CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
|
||||||
|
// ProxyURL is the URL to the proxy to be used for all requests to this
|
||||||
|
// cluster.
|
||||||
|
// +optional
|
||||||
|
ProxyURL string `json:"proxy-url,omitempty"`
|
||||||
|
// 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[client.authentication.k8s.io/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.
|
||||||
|
// +optional
|
||||||
|
Config runtime.RawExtension `json:"config,omitempty"`
|
||||||
|
}
|
||||||
|
@ -36,6 +36,16 @@ func init() {
|
|||||||
// RegisterConversions adds conversion functions to the given scheme.
|
// RegisterConversions adds conversion functions to the given scheme.
|
||||||
// Public to allow building arbitrary schemes.
|
// Public to allow building arbitrary schemes.
|
||||||
func RegisterConversions(s *runtime.Scheme) error {
|
func RegisterConversions(s *runtime.Scheme) error {
|
||||||
|
if err := s.AddGeneratedConversionFunc((*Cluster)(nil), (*clientauthentication.Cluster)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return Convert_v1beta1_Cluster_To_clientauthentication_Cluster(a.(*Cluster), b.(*clientauthentication.Cluster), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.AddGeneratedConversionFunc((*clientauthentication.Cluster)(nil), (*Cluster)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return Convert_clientauthentication_Cluster_To_v1beta1_Cluster(a.(*clientauthentication.Cluster), b.(*Cluster), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := s.AddGeneratedConversionFunc((*ExecCredential)(nil), (*clientauthentication.ExecCredential)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
if err := s.AddGeneratedConversionFunc((*ExecCredential)(nil), (*clientauthentication.ExecCredential)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
return Convert_v1beta1_ExecCredential_To_clientauthentication_ExecCredential(a.(*ExecCredential), b.(*clientauthentication.ExecCredential), scope)
|
return Convert_v1beta1_ExecCredential_To_clientauthentication_ExecCredential(a.(*ExecCredential), b.(*clientauthentication.ExecCredential), scope)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
@ -69,6 +79,40 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func autoConvert_v1beta1_Cluster_To_clientauthentication_Cluster(in *Cluster, out *clientauthentication.Cluster, s conversion.Scope) error {
|
||||||
|
out.Server = in.Server
|
||||||
|
out.TLSServerName = in.TLSServerName
|
||||||
|
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
|
||||||
|
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
|
||||||
|
out.ProxyURL = in.ProxyURL
|
||||||
|
if err := runtime.Convert_runtime_RawExtension_To_runtime_Object(&in.Config, &out.Config, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert_v1beta1_Cluster_To_clientauthentication_Cluster is an autogenerated conversion function.
|
||||||
|
func Convert_v1beta1_Cluster_To_clientauthentication_Cluster(in *Cluster, out *clientauthentication.Cluster, s conversion.Scope) error {
|
||||||
|
return autoConvert_v1beta1_Cluster_To_clientauthentication_Cluster(in, out, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func autoConvert_clientauthentication_Cluster_To_v1beta1_Cluster(in *clientauthentication.Cluster, out *Cluster, s conversion.Scope) error {
|
||||||
|
out.Server = in.Server
|
||||||
|
out.TLSServerName = in.TLSServerName
|
||||||
|
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
|
||||||
|
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
|
||||||
|
out.ProxyURL = in.ProxyURL
|
||||||
|
if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&in.Config, &out.Config, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert_clientauthentication_Cluster_To_v1beta1_Cluster is an autogenerated conversion function.
|
||||||
|
func Convert_clientauthentication_Cluster_To_v1beta1_Cluster(in *clientauthentication.Cluster, out *Cluster, s conversion.Scope) error {
|
||||||
|
return autoConvert_clientauthentication_Cluster_To_v1beta1_Cluster(in, out, s)
|
||||||
|
}
|
||||||
|
|
||||||
func autoConvert_v1beta1_ExecCredential_To_clientauthentication_ExecCredential(in *ExecCredential, out *clientauthentication.ExecCredential, s conversion.Scope) error {
|
func autoConvert_v1beta1_ExecCredential_To_clientauthentication_ExecCredential(in *ExecCredential, out *clientauthentication.ExecCredential, s conversion.Scope) error {
|
||||||
if err := Convert_v1beta1_ExecCredentialSpec_To_clientauthentication_ExecCredentialSpec(&in.Spec, &out.Spec, s); err != nil {
|
if err := Convert_v1beta1_ExecCredentialSpec_To_clientauthentication_ExecCredentialSpec(&in.Spec, &out.Spec, s); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -96,6 +140,15 @@ func Convert_clientauthentication_ExecCredential_To_v1beta1_ExecCredential(in *c
|
|||||||
}
|
}
|
||||||
|
|
||||||
func autoConvert_v1beta1_ExecCredentialSpec_To_clientauthentication_ExecCredentialSpec(in *ExecCredentialSpec, out *clientauthentication.ExecCredentialSpec, s conversion.Scope) error {
|
func autoConvert_v1beta1_ExecCredentialSpec_To_clientauthentication_ExecCredentialSpec(in *ExecCredentialSpec, out *clientauthentication.ExecCredentialSpec, s conversion.Scope) error {
|
||||||
|
if in.Cluster != nil {
|
||||||
|
in, out := &in.Cluster, &out.Cluster
|
||||||
|
*out = new(clientauthentication.Cluster)
|
||||||
|
if err := Convert_v1beta1_Cluster_To_clientauthentication_Cluster(*in, *out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.Cluster = nil
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +160,15 @@ func Convert_v1beta1_ExecCredentialSpec_To_clientauthentication_ExecCredentialSp
|
|||||||
func autoConvert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
func autoConvert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
||||||
// WARNING: in.Response requires manual conversion: does not exist in peer-type
|
// WARNING: in.Response requires manual conversion: does not exist in peer-type
|
||||||
// WARNING: in.Interactive requires manual conversion: does not exist in peer-type
|
// WARNING: in.Interactive requires manual conversion: does not exist in peer-type
|
||||||
|
if in.Cluster != nil {
|
||||||
|
in, out := &in.Cluster, &out.Cluster
|
||||||
|
*out = new(Cluster)
|
||||||
|
if err := Convert_clientauthentication_Cluster_To_v1beta1_Cluster(*in, *out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.Cluster = nil
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,11 +24,33 @@ import (
|
|||||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Cluster) DeepCopyInto(out *Cluster) {
|
||||||
|
*out = *in
|
||||||
|
if in.CertificateAuthorityData != nil {
|
||||||
|
in, out := &in.CertificateAuthorityData, &out.CertificateAuthorityData
|
||||||
|
*out = make([]byte, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
in.Config.DeepCopyInto(&out.Config)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cluster.
|
||||||
|
func (in *Cluster) DeepCopy() *Cluster {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Cluster)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ExecCredential) DeepCopyInto(out *ExecCredential) {
|
func (in *ExecCredential) DeepCopyInto(out *ExecCredential) {
|
||||||
*out = *in
|
*out = *in
|
||||||
out.TypeMeta = in.TypeMeta
|
out.TypeMeta = in.TypeMeta
|
||||||
out.Spec = in.Spec
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
if in.Status != nil {
|
if in.Status != nil {
|
||||||
in, out := &in.Status, &out.Status
|
in, out := &in.Status, &out.Status
|
||||||
*out = new(ExecCredentialStatus)
|
*out = new(ExecCredentialStatus)
|
||||||
@ -58,6 +80,11 @@ func (in *ExecCredential) DeepCopyObject() runtime.Object {
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ExecCredentialSpec) DeepCopyInto(out *ExecCredentialSpec) {
|
func (in *ExecCredentialSpec) DeepCopyInto(out *ExecCredentialSpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.Cluster != nil {
|
||||||
|
in, out := &in.Cluster, &out.Cluster
|
||||||
|
*out = new(Cluster)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,30 @@ import (
|
|||||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Cluster) DeepCopyInto(out *Cluster) {
|
||||||
|
*out = *in
|
||||||
|
if in.CertificateAuthorityData != nil {
|
||||||
|
in, out := &in.CertificateAuthorityData, &out.CertificateAuthorityData
|
||||||
|
*out = make([]byte, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.Config != nil {
|
||||||
|
out.Config = in.Config.DeepCopyObject()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cluster.
|
||||||
|
func (in *Cluster) DeepCopy() *Cluster {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Cluster)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ExecCredential) DeepCopyInto(out *ExecCredential) {
|
func (in *ExecCredential) DeepCopyInto(out *ExecCredential) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@ -63,6 +87,11 @@ func (in *ExecCredentialSpec) DeepCopyInto(out *ExecCredentialSpec) {
|
|||||||
*out = new(Response)
|
*out = new(Response)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
|
if in.Cluster != nil {
|
||||||
|
in, out := &in.Cluster, &out.Cluster
|
||||||
|
*out = new(Cluster)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,8 +87,15 @@ func newCache() *cache {
|
|||||||
|
|
||||||
var spewConfig = &spew.ConfigState{DisableMethods: true, Indent: " "}
|
var spewConfig = &spew.ConfigState{DisableMethods: true, Indent: " "}
|
||||||
|
|
||||||
func cacheKey(c *api.ExecConfig) string {
|
func cacheKey(conf *api.ExecConfig, cluster *clientauthentication.Cluster) string {
|
||||||
return spewConfig.Sprint(c)
|
key := struct {
|
||||||
|
conf *api.ExecConfig
|
||||||
|
cluster *clientauthentication.Cluster
|
||||||
|
}{
|
||||||
|
conf: conf,
|
||||||
|
cluster: cluster,
|
||||||
|
}
|
||||||
|
return spewConfig.Sprint(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
type cache struct {
|
type cache struct {
|
||||||
@ -155,12 +162,12 @@ func (s *sometimes) Do(f func()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAuthenticator returns an exec-based plugin for providing client credentials.
|
// GetAuthenticator returns an exec-based plugin for providing client credentials.
|
||||||
func GetAuthenticator(config *api.ExecConfig) (*Authenticator, error) {
|
func GetAuthenticator(config *api.ExecConfig, cluster *clientauthentication.Cluster) (*Authenticator, error) {
|
||||||
return newAuthenticator(globalCache, config)
|
return newAuthenticator(globalCache, config, cluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAuthenticator(c *cache, config *api.ExecConfig) (*Authenticator, error) {
|
func newAuthenticator(c *cache, config *api.ExecConfig, cluster *clientauthentication.Cluster) (*Authenticator, error) {
|
||||||
key := cacheKey(config)
|
key := cacheKey(config, cluster)
|
||||||
if a, ok := c.get(key); ok {
|
if a, ok := c.get(key); ok {
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -171,9 +178,11 @@ func newAuthenticator(c *cache, config *api.ExecConfig) (*Authenticator, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
a := &Authenticator{
|
a := &Authenticator{
|
||||||
cmd: config.Command,
|
cmd: config.Command,
|
||||||
args: config.Args,
|
args: config.Args,
|
||||||
group: gv,
|
group: gv,
|
||||||
|
cluster: cluster,
|
||||||
|
provideClusterInfo: config.ProvideClusterInfo,
|
||||||
|
|
||||||
installHint: config.InstallHint,
|
installHint: config.InstallHint,
|
||||||
sometimes: &sometimes{
|
sometimes: &sometimes{
|
||||||
@ -200,10 +209,12 @@ func newAuthenticator(c *cache, config *api.ExecConfig) (*Authenticator, error)
|
|||||||
// The plugin input and output are defined by the API group client.authentication.k8s.io.
|
// The plugin input and output are defined by the API group client.authentication.k8s.io.
|
||||||
type Authenticator struct {
|
type Authenticator struct {
|
||||||
// Set by the config
|
// Set by the config
|
||||||
cmd string
|
cmd string
|
||||||
args []string
|
args []string
|
||||||
group schema.GroupVersion
|
group schema.GroupVersion
|
||||||
env []string
|
env []string
|
||||||
|
cluster *clientauthentication.Cluster
|
||||||
|
provideClusterInfo bool
|
||||||
|
|
||||||
// Used to avoid log spew by rate limiting install hint printing. We didn't do
|
// Used to avoid log spew by rate limiting install hint printing. We didn't do
|
||||||
// this by interval based rate limiting alone since that way may have prevented
|
// this by interval based rate limiting alone since that way may have prevented
|
||||||
@ -367,19 +378,16 @@ func (a *Authenticator) refreshCredsLocked(r *clientauthentication.Response) err
|
|||||||
Interactive: a.interactive,
|
Interactive: a.interactive,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if a.provideClusterInfo {
|
||||||
|
cred.Spec.Cluster = a.cluster
|
||||||
|
}
|
||||||
|
|
||||||
env := append(a.environ(), a.env...)
|
env := append(a.environ(), a.env...)
|
||||||
if a.group == v1alpha1.SchemeGroupVersion {
|
data, err := runtime.Encode(codecs.LegacyCodec(a.group), cred)
|
||||||
// Input spec disabled for beta due to lack of use. Possibly re-enable this later if
|
if err != nil {
|
||||||
// someone wants it back.
|
return fmt.Errorf("encode ExecCredentials: %v", err)
|
||||||
//
|
|
||||||
// See: https://github.com/kubernetes/kubernetes/issues/61796
|
|
||||||
data, err := runtime.Encode(codecs.LegacyCodec(a.group), cred)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("encode ExecCredentials: %v", err)
|
|
||||||
}
|
|
||||||
env = append(env, fmt.Sprintf("%s=%s", execInfoEnv, data))
|
|
||||||
}
|
}
|
||||||
|
env = append(env, fmt.Sprintf("%s=%s", execInfoEnv, data))
|
||||||
|
|
||||||
stdout := &bytes.Buffer{}
|
stdout := &bytes.Buffer{}
|
||||||
cmd := exec.Command(a.cmd, a.args...)
|
cmd := exec.Command(a.cmd, a.args...)
|
||||||
|
@ -115,8 +115,24 @@ func TestCacheKey(t *testing.T) {
|
|||||||
{Name: "5", Value: "6"},
|
{Name: "5", Value: "6"},
|
||||||
{Name: "7", Value: "8"},
|
{Name: "7", Value: "8"},
|
||||||
},
|
},
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
ProvideClusterInfo: true,
|
||||||
}
|
}
|
||||||
|
c1c := &clientauthentication.Cluster{
|
||||||
|
Server: "foo",
|
||||||
|
TLSServerName: "bar",
|
||||||
|
CertificateAuthorityData: []byte("baz"),
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
TypeMeta: runtime.TypeMeta{
|
||||||
|
APIVersion: "",
|
||||||
|
Kind: "",
|
||||||
|
},
|
||||||
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"snorlax"}}`),
|
||||||
|
ContentEncoding: "",
|
||||||
|
ContentType: "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
c2 := &api.ExecConfig{
|
c2 := &api.ExecConfig{
|
||||||
Command: "foo-bar",
|
Command: "foo-bar",
|
||||||
Args: []string{"1", "2"},
|
Args: []string{"1", "2"},
|
||||||
@ -125,8 +141,24 @@ func TestCacheKey(t *testing.T) {
|
|||||||
{Name: "5", Value: "6"},
|
{Name: "5", Value: "6"},
|
||||||
{Name: "7", Value: "8"},
|
{Name: "7", Value: "8"},
|
||||||
},
|
},
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
ProvideClusterInfo: true,
|
||||||
}
|
}
|
||||||
|
c2c := &clientauthentication.Cluster{
|
||||||
|
Server: "foo",
|
||||||
|
TLSServerName: "bar",
|
||||||
|
CertificateAuthorityData: []byte("baz"),
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
TypeMeta: runtime.TypeMeta{
|
||||||
|
APIVersion: "",
|
||||||
|
Kind: "",
|
||||||
|
},
|
||||||
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"snorlax"}}`),
|
||||||
|
ContentEncoding: "",
|
||||||
|
ContentType: "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
c3 := &api.ExecConfig{
|
c3 := &api.ExecConfig{
|
||||||
Command: "foo-bar",
|
Command: "foo-bar",
|
||||||
Args: []string{"1", "2"},
|
Args: []string{"1", "2"},
|
||||||
@ -136,9 +168,88 @@ func TestCacheKey(t *testing.T) {
|
|||||||
},
|
},
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
}
|
}
|
||||||
key1 := cacheKey(c1)
|
c3c := &clientauthentication.Cluster{
|
||||||
key2 := cacheKey(c2)
|
Server: "foo",
|
||||||
key3 := cacheKey(c3)
|
TLSServerName: "bar",
|
||||||
|
CertificateAuthorityData: []byte("baz"),
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
TypeMeta: runtime.TypeMeta{
|
||||||
|
APIVersion: "",
|
||||||
|
Kind: "",
|
||||||
|
},
|
||||||
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"snorlax"}}`),
|
||||||
|
ContentEncoding: "",
|
||||||
|
ContentType: "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
c4 := &api.ExecConfig{
|
||||||
|
Command: "foo-bar",
|
||||||
|
Args: []string{"1", "2"},
|
||||||
|
Env: []api.ExecEnvVar{
|
||||||
|
{Name: "3", Value: "4"},
|
||||||
|
{Name: "5", Value: "6"},
|
||||||
|
},
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
}
|
||||||
|
c4c := &clientauthentication.Cluster{
|
||||||
|
Server: "foo",
|
||||||
|
TLSServerName: "bar",
|
||||||
|
CertificateAuthorityData: []byte("baz"),
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
TypeMeta: runtime.TypeMeta{
|
||||||
|
APIVersion: "",
|
||||||
|
Kind: "",
|
||||||
|
},
|
||||||
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"panda"}}`),
|
||||||
|
ContentEncoding: "",
|
||||||
|
ContentType: "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// c5/c5c should be the same as c4/c4c, except c5 has ProvideClusterInfo set to true.
|
||||||
|
c5 := &api.ExecConfig{
|
||||||
|
Command: "foo-bar",
|
||||||
|
Args: []string{"1", "2"},
|
||||||
|
Env: []api.ExecEnvVar{
|
||||||
|
{Name: "3", Value: "4"},
|
||||||
|
{Name: "5", Value: "6"},
|
||||||
|
},
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
}
|
||||||
|
c5c := &clientauthentication.Cluster{
|
||||||
|
Server: "foo",
|
||||||
|
TLSServerName: "bar",
|
||||||
|
CertificateAuthorityData: []byte("baz"),
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
TypeMeta: runtime.TypeMeta{
|
||||||
|
APIVersion: "",
|
||||||
|
Kind: "",
|
||||||
|
},
|
||||||
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"panda"}}`),
|
||||||
|
ContentEncoding: "",
|
||||||
|
ContentType: "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// c6 should be the same as c4, except c6 is passed with a nil cluster
|
||||||
|
c6 := &api.ExecConfig{
|
||||||
|
Command: "foo-bar",
|
||||||
|
Args: []string{"1", "2"},
|
||||||
|
Env: []api.ExecEnvVar{
|
||||||
|
{Name: "3", Value: "4"},
|
||||||
|
{Name: "5", Value: "6"},
|
||||||
|
},
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
}
|
||||||
|
|
||||||
|
key1 := cacheKey(c1, c1c)
|
||||||
|
key2 := cacheKey(c2, c2c)
|
||||||
|
key3 := cacheKey(c3, c3c)
|
||||||
|
key4 := cacheKey(c4, c4c)
|
||||||
|
key5 := cacheKey(c5, c5c)
|
||||||
|
key6 := cacheKey(c6, nil)
|
||||||
if key1 != key2 {
|
if key1 != key2 {
|
||||||
t.Error("key1 and key2 didn't match")
|
t.Error("key1 and key2 didn't match")
|
||||||
}
|
}
|
||||||
@ -148,6 +259,15 @@ func TestCacheKey(t *testing.T) {
|
|||||||
if key2 == key3 {
|
if key2 == key3 {
|
||||||
t.Error("key2 and key3 matched")
|
t.Error("key2 and key3 matched")
|
||||||
}
|
}
|
||||||
|
if key3 == key4 {
|
||||||
|
t.Error("key3 and key4 matched")
|
||||||
|
}
|
||||||
|
if key4 == key5 {
|
||||||
|
t.Error("key3 and key4 matched")
|
||||||
|
}
|
||||||
|
if key6 == key4 {
|
||||||
|
t.Error("key6 and key4 matched")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compJSON(t *testing.T, got, want []byte) {
|
func compJSON(t *testing.T, got, want []byte) {
|
||||||
@ -173,6 +293,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
config api.ExecConfig
|
config api.ExecConfig
|
||||||
exitCode int
|
exitCode int
|
||||||
|
cluster *clientauthentication.Cluster
|
||||||
output string
|
output string
|
||||||
interactive bool
|
interactive bool
|
||||||
response *clientauthentication.Response
|
response *clientauthentication.Response
|
||||||
@ -393,6 +514,11 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
},
|
},
|
||||||
|
wantInput: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"spec": {}
|
||||||
|
}`,
|
||||||
output: `{
|
output: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
@ -407,6 +533,11 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
},
|
},
|
||||||
|
wantInput: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"spec": {}
|
||||||
|
}`,
|
||||||
output: `{
|
output: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
@ -473,6 +604,146 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
wantErr: true,
|
wantErr: true,
|
||||||
wantErrSubstr: "73",
|
wantErrSubstr: "73",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "alpha-with-cluster-is-ignored",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
},
|
||||||
|
cluster: &clientauthentication.Cluster{
|
||||||
|
Server: "foo",
|
||||||
|
TLSServerName: "bar",
|
||||||
|
CertificateAuthorityData: []byte("baz"),
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
TypeMeta: runtime.TypeMeta{
|
||||||
|
APIVersion: "",
|
||||||
|
Kind: "",
|
||||||
|
},
|
||||||
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"panda"}}`),
|
||||||
|
ContentEncoding: "",
|
||||||
|
ContentType: "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
response: &clientauthentication.Response{
|
||||||
|
Header: map[string][]string{
|
||||||
|
"WWW-Authenticate": {`Basic realm="Access to the staging site", charset="UTF-8"`},
|
||||||
|
},
|
||||||
|
Code: 401,
|
||||||
|
},
|
||||||
|
wantInput: `{
|
||||||
|
"kind":"ExecCredential",
|
||||||
|
"apiVersion":"client.authentication.k8s.io/v1alpha1",
|
||||||
|
"spec": {
|
||||||
|
"response": {
|
||||||
|
"header": {
|
||||||
|
"WWW-Authenticate": [
|
||||||
|
"Basic realm=\"Access to the staging site\", charset=\"UTF-8\""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"code": 401
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
output: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1alpha1",
|
||||||
|
"status": {
|
||||||
|
"token": "foo-bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta-with-cluster-and-provide-cluster-info-is-serialized",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
},
|
||||||
|
cluster: &clientauthentication.Cluster{
|
||||||
|
Server: "foo",
|
||||||
|
TLSServerName: "bar",
|
||||||
|
CertificateAuthorityData: []byte("baz"),
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
TypeMeta: runtime.TypeMeta{
|
||||||
|
APIVersion: "",
|
||||||
|
Kind: "",
|
||||||
|
},
|
||||||
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"snorlax"}}`),
|
||||||
|
ContentEncoding: "",
|
||||||
|
ContentType: "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
response: &clientauthentication.Response{
|
||||||
|
Header: map[string][]string{
|
||||||
|
"WWW-Authenticate": {`Basic realm="Access to the staging site", charset="UTF-8"`},
|
||||||
|
},
|
||||||
|
Code: 401,
|
||||||
|
},
|
||||||
|
wantInput: `{
|
||||||
|
"kind":"ExecCredential",
|
||||||
|
"apiVersion":"client.authentication.k8s.io/v1beta1",
|
||||||
|
"spec": {
|
||||||
|
"cluster": {
|
||||||
|
"server": "foo",
|
||||||
|
"tls-server-name": "bar",
|
||||||
|
"certificate-authority-data": "YmF6",
|
||||||
|
"config": {
|
||||||
|
"apiVersion": "group/v1",
|
||||||
|
"kind": "PluginConfig",
|
||||||
|
"spec": {
|
||||||
|
"audience": "snorlax"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
output: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"status": {
|
||||||
|
"token": "foo-bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta-with-cluster-and-without-provide-cluster-info-is-not-serialized",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
},
|
||||||
|
cluster: &clientauthentication.Cluster{
|
||||||
|
Server: "foo",
|
||||||
|
TLSServerName: "bar",
|
||||||
|
CertificateAuthorityData: []byte("baz"),
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
TypeMeta: runtime.TypeMeta{
|
||||||
|
APIVersion: "",
|
||||||
|
Kind: "",
|
||||||
|
},
|
||||||
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"snorlax"}}`),
|
||||||
|
ContentEncoding: "",
|
||||||
|
ContentType: "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
response: &clientauthentication.Response{
|
||||||
|
Header: map[string][]string{
|
||||||
|
"WWW-Authenticate": {`Basic realm="Access to the staging site", charset="UTF-8"`},
|
||||||
|
},
|
||||||
|
Code: 401,
|
||||||
|
},
|
||||||
|
wantInput: `{
|
||||||
|
"kind":"ExecCredential",
|
||||||
|
"apiVersion":"client.authentication.k8s.io/v1beta1",
|
||||||
|
"spec": {}
|
||||||
|
}`,
|
||||||
|
output: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"status": {
|
||||||
|
"token": "foo-bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -491,7 +762,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
a, err := newAuthenticator(newCache(), &c)
|
a, err := newAuthenticator(newCache(), &c, test.cluster)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -569,7 +840,7 @@ func TestRoundTripper(t *testing.T) {
|
|||||||
Command: "./testdata/test-plugin.sh",
|
Command: "./testdata/test-plugin.sh",
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
}
|
}
|
||||||
a, err := newAuthenticator(newCache(), &c)
|
a, err := newAuthenticator(newCache(), &c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -655,7 +926,7 @@ func TestTokenPresentCancelsExecAction(t *testing.T) {
|
|||||||
a, err := newAuthenticator(newCache(), &api.ExecConfig{
|
a, err := newAuthenticator(newCache(), &api.ExecConfig{
|
||||||
Command: "./testdata/test-plugin.sh",
|
Command: "./testdata/test-plugin.sh",
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
})
|
}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -694,7 +965,7 @@ func TestTLSCredentials(t *testing.T) {
|
|||||||
a, err := newAuthenticator(newCache(), &api.ExecConfig{
|
a, err := newAuthenticator(newCache(), &api.ExecConfig{
|
||||||
Command: "./testdata/test-plugin.sh",
|
Command: "./testdata/test-plugin.sh",
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
})
|
}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -784,7 +1055,7 @@ func TestConcurrentUpdateTransportConfig(t *testing.T) {
|
|||||||
Command: "./testdata/test-plugin.sh",
|
Command: "./testdata/test-plugin.sh",
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
}
|
}
|
||||||
a, err := newAuthenticator(newCache(), &c)
|
a, err := newAuthenticator(newCache(), &c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -851,7 +1122,7 @@ func TestInstallHintRateLimit(t *testing.T) {
|
|||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
InstallHint: "some install hint",
|
InstallHint: "some install hint",
|
||||||
}
|
}
|
||||||
a, err := newAuthenticator(newCache(), &c)
|
a, err := newAuthenticator(newCache(), &c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,13 @@ go_test(
|
|||||||
srcs = [
|
srcs = [
|
||||||
"client_test.go",
|
"client_test.go",
|
||||||
"config_test.go",
|
"config_test.go",
|
||||||
|
"exec_test.go",
|
||||||
"plugin_test.go",
|
"plugin_test.go",
|
||||||
"request_test.go",
|
"request_test.go",
|
||||||
"url_utils_test.go",
|
"url_utils_test.go",
|
||||||
"urlbackoff_test.go",
|
"urlbackoff_test.go",
|
||||||
],
|
],
|
||||||
|
data = glob(["testdata/**"]),
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
@ -33,6 +35,7 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/pkg/apis/clientauthentication:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/rest/watch:go_default_library",
|
"//staging/src/k8s.io/client-go/rest/watch:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
"//staging/src/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/transport:go_default_library",
|
"//staging/src/k8s.io/client-go/transport:go_default_library",
|
||||||
@ -50,6 +53,7 @@ go_library(
|
|||||||
srcs = [
|
srcs = [
|
||||||
"client.go",
|
"client.go",
|
||||||
"config.go",
|
"config.go",
|
||||||
|
"exec.go",
|
||||||
"plugin.go",
|
"plugin.go",
|
||||||
"request.go",
|
"request.go",
|
||||||
"transport.go",
|
"transport.go",
|
||||||
@ -71,6 +75,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/pkg/apis/clientauthentication:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/pkg/version:go_default_library",
|
"//staging/src/k8s.io/client-go/pkg/version:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec:go_default_library",
|
"//staging/src/k8s.io/client-go/plugin/pkg/client/auth/exec:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/rest/watch:go_default_library",
|
"//staging/src/k8s.io/client-go/rest/watch:go_default_library",
|
||||||
|
@ -160,6 +160,15 @@ func (sanitizedAuthConfigPersister) String() string {
|
|||||||
return "rest.AuthProviderConfigPersister(--- REDACTED ---)"
|
return "rest.AuthProviderConfigPersister(--- REDACTED ---)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type sanitizedObject struct{ runtime.Object }
|
||||||
|
|
||||||
|
func (sanitizedObject) GoString() string {
|
||||||
|
return "runtime.Object(--- REDACTED ---)"
|
||||||
|
}
|
||||||
|
func (sanitizedObject) String() string {
|
||||||
|
return "runtime.Object(--- REDACTED ---)"
|
||||||
|
}
|
||||||
|
|
||||||
// GoString implements fmt.GoStringer and sanitizes sensitive fields of Config
|
// GoString implements fmt.GoStringer and sanitizes sensitive fields of Config
|
||||||
// to prevent accidental leaking via logs.
|
// to prevent accidental leaking via logs.
|
||||||
func (c *Config) GoString() string {
|
func (c *Config) GoString() string {
|
||||||
@ -183,7 +192,9 @@ func (c *Config) String() string {
|
|||||||
if cc.AuthConfigPersister != nil {
|
if cc.AuthConfigPersister != nil {
|
||||||
cc.AuthConfigPersister = sanitizedAuthConfigPersister{cc.AuthConfigPersister}
|
cc.AuthConfigPersister = sanitizedAuthConfigPersister{cc.AuthConfigPersister}
|
||||||
}
|
}
|
||||||
|
if cc.ExecProvider != nil && cc.ExecProvider.Config != nil {
|
||||||
|
cc.ExecProvider.Config = sanitizedObject{Object: cc.ExecProvider.Config}
|
||||||
|
}
|
||||||
return fmt.Sprintf("%#v", cc)
|
return fmt.Sprintf("%#v", cc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,7 +599,7 @@ func AnonymousClientConfig(config *Config) *Config {
|
|||||||
|
|
||||||
// CopyConfig returns a copy of the given config
|
// CopyConfig returns a copy of the given config
|
||||||
func CopyConfig(config *Config) *Config {
|
func CopyConfig(config *Config) *Config {
|
||||||
return &Config{
|
c := &Config{
|
||||||
Host: config.Host,
|
Host: config.Host,
|
||||||
APIPath: config.APIPath,
|
APIPath: config.APIPath,
|
||||||
ContentConfig: config.ContentConfig,
|
ContentConfig: config.ContentConfig,
|
||||||
@ -627,4 +638,8 @@ func CopyConfig(config *Config) *Config {
|
|||||||
Dial: config.Dial,
|
Dial: config.Dial,
|
||||||
Proxy: config.Proxy,
|
Proxy: config.Proxy,
|
||||||
}
|
}
|
||||||
|
if config.ExecProvider != nil && config.ExecProvider.Config != nil {
|
||||||
|
c.ExecProvider.Config = config.ExecProvider.Config.DeepCopyObject()
|
||||||
|
}
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
@ -337,6 +337,11 @@ func TestAnonymousConfig(t *testing.T) {
|
|||||||
func(r *func(*http.Request) (*url.URL, error), f fuzz.Continue) {
|
func(r *func(*http.Request) (*url.URL, error), f fuzz.Continue) {
|
||||||
*r = fakeProxyFunc
|
*r = fakeProxyFunc
|
||||||
},
|
},
|
||||||
|
func(r *runtime.Object, f fuzz.Continue) {
|
||||||
|
unknown := &runtime.Unknown{}
|
||||||
|
f.Fuzz(unknown)
|
||||||
|
*r = unknown
|
||||||
|
},
|
||||||
)
|
)
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
original := &Config{}
|
original := &Config{}
|
||||||
@ -428,6 +433,11 @@ func TestCopyConfig(t *testing.T) {
|
|||||||
func(r *func(*http.Request) (*url.URL, error), f fuzz.Continue) {
|
func(r *func(*http.Request) (*url.URL, error), f fuzz.Continue) {
|
||||||
*r = fakeProxyFunc
|
*r = fakeProxyFunc
|
||||||
},
|
},
|
||||||
|
func(r *runtime.Object, f fuzz.Continue) {
|
||||||
|
unknown := &runtime.Unknown{}
|
||||||
|
f.Fuzz(unknown)
|
||||||
|
*r = unknown
|
||||||
|
},
|
||||||
)
|
)
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
original := &Config{}
|
original := &Config{}
|
||||||
@ -525,8 +535,9 @@ func TestConfigStringer(t *testing.T) {
|
|||||||
Config: map[string]string{"secret": "s3cr3t"},
|
Config: map[string]string{"secret": "s3cr3t"},
|
||||||
},
|
},
|
||||||
ExecProvider: &clientcmdapi.ExecConfig{
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
Args: []string{"secret"},
|
Args: []string{"secret"},
|
||||||
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
|
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
|
||||||
|
Config: &runtime.Unknown{Raw: []byte("here is some config data")},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectContent: []string{
|
expectContent: []string{
|
||||||
@ -545,6 +556,8 @@ func TestConfigStringer(t *testing.T) {
|
|||||||
formatBytes([]byte("fake key")),
|
formatBytes([]byte("fake key")),
|
||||||
"secret",
|
"secret",
|
||||||
"s3cr3t",
|
"s3cr3t",
|
||||||
|
"here is some config data",
|
||||||
|
formatBytes([]byte("super secret password")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -588,9 +601,11 @@ func TestConfigSprint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
AuthConfigPersister: fakeAuthProviderConfigPersister{},
|
AuthConfigPersister: fakeAuthProviderConfigPersister{},
|
||||||
ExecProvider: &clientcmdapi.ExecConfig{
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
Command: "sudo",
|
Command: "sudo",
|
||||||
Args: []string{"secret"},
|
Args: []string{"secret"},
|
||||||
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
|
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
Config: &runtime.Unknown{Raw: []byte("super secret password")},
|
||||||
},
|
},
|
||||||
TLSClientConfig: TLSClientConfig{
|
TLSClientConfig: TLSClientConfig{
|
||||||
CertFile: "a.crt",
|
CertFile: "a.crt",
|
||||||
@ -611,7 +626,7 @@ func TestConfigSprint(t *testing.T) {
|
|||||||
Proxy: fakeProxyFunc,
|
Proxy: fakeProxyFunc,
|
||||||
}
|
}
|
||||||
want := fmt.Sprintf(
|
want := fmt.Sprintf(
|
||||||
`&rest.Config{Host:"localhost:8080", APIPath:"v1", ContentConfig:rest.ContentConfig{AcceptContentTypes:"application/json", ContentType:"application/json", GroupVersion:(*schema.GroupVersion)(nil), NegotiatedSerializer:runtime.NegotiatedSerializer(nil)}, Username:"gopher", Password:"--- REDACTED ---", BearerToken:"--- REDACTED ---", BearerTokenFile:"", Impersonate:rest.ImpersonationConfig{UserName:"gopher2", Groups:[]string(nil), Extra:map[string][]string(nil)}, AuthProvider:api.AuthProviderConfig{Name: "gopher", Config: map[string]string{--- REDACTED ---}}, AuthConfigPersister:rest.AuthProviderConfigPersister(--- REDACTED ---), ExecProvider:api.AuthProviderConfig{Command: "sudo", Args: []string{"--- REDACTED ---"}, Env: []ExecEnvVar{--- REDACTED ---}, APIVersion: ""}, TLSClientConfig:rest.sanitizedTLSClientConfig{Insecure:false, ServerName:"", CertFile:"a.crt", KeyFile:"a.key", CAFile:"", CertData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x41, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, KeyData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x52, 0x45, 0x44, 0x41, 0x43, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, CAData:[]uint8(nil), NextProtos:[]string{"h2", "http/1.1"}}, UserAgent:"gobot", DisableCompression:false, Transport:(*rest.fakeRoundTripper)(%p), WrapTransport:(transport.WrapperFunc)(%p), QPS:1, Burst:2, RateLimiter:(*rest.fakeLimiter)(%p), WarningHandler:rest.fakeWarningHandler{}, Timeout:3000000000, Dial:(func(context.Context, string, string) (net.Conn, error))(%p), Proxy:(func(*http.Request) (*url.URL, error))(%p)}`,
|
`&rest.Config{Host:"localhost:8080", APIPath:"v1", ContentConfig:rest.ContentConfig{AcceptContentTypes:"application/json", ContentType:"application/json", GroupVersion:(*schema.GroupVersion)(nil), NegotiatedSerializer:runtime.NegotiatedSerializer(nil)}, Username:"gopher", Password:"--- REDACTED ---", BearerToken:"--- REDACTED ---", BearerTokenFile:"", Impersonate:rest.ImpersonationConfig{UserName:"gopher2", Groups:[]string(nil), Extra:map[string][]string(nil)}, AuthProvider:api.AuthProviderConfig{Name: "gopher", Config: map[string]string{--- REDACTED ---}}, AuthConfigPersister:rest.AuthProviderConfigPersister(--- REDACTED ---), ExecProvider:api.ExecConfig{Command: "sudo", Args: []string{"--- REDACTED ---"}, Env: []ExecEnvVar{--- REDACTED ---}, APIVersion: "", ProvideClusterInfo: true, Config: runtime.Object(--- REDACTED ---)}, TLSClientConfig:rest.sanitizedTLSClientConfig{Insecure:false, ServerName:"", CertFile:"a.crt", KeyFile:"a.key", CAFile:"", CertData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x41, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, KeyData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x52, 0x45, 0x44, 0x41, 0x43, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, CAData:[]uint8(nil), NextProtos:[]string{"h2", "http/1.1"}}, UserAgent:"gobot", DisableCompression:false, Transport:(*rest.fakeRoundTripper)(%p), WrapTransport:(transport.WrapperFunc)(%p), QPS:1, Burst:2, RateLimiter:(*rest.fakeLimiter)(%p), WarningHandler:rest.fakeWarningHandler{}, Timeout:3000000000, Dial:(func(context.Context, string, string) (net.Conn, error))(%p), Proxy:(func(*http.Request) (*url.URL, error))(%p)}`,
|
||||||
c.Transport, fakeWrapperFunc, c.RateLimiter, fakeDialFunc, fakeProxyFunc,
|
c.Transport, fakeWrapperFunc, c.RateLimiter, fakeDialFunc, fakeProxyFunc,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
85
staging/src/k8s.io/client-go/rest/exec.go
Normal file
85
staging/src/k8s.io/client-go/rest/exec.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
|
clientauthenticationapi "k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file contains Config logic related to exec credential plugins.
|
||||||
|
|
||||||
|
// ConfigToExecCluster creates a clientauthenticationapi.Cluster with the corresponding fields from
|
||||||
|
// the provided Config.
|
||||||
|
func ConfigToExecCluster(config *Config) (*clientauthenticationapi.Cluster, error) {
|
||||||
|
caData, err := dataFromSliceOrFile(config.CAData, config.CAFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load CA bundle for execProvider: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var proxyURL string
|
||||||
|
if config.Proxy != nil {
|
||||||
|
req, err := http.NewRequest("", config.Host, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create proxy URL request for execProvider: %w", err)
|
||||||
|
}
|
||||||
|
url, err := config.Proxy(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get proxy URL for execProvider: %w", err)
|
||||||
|
}
|
||||||
|
if url != nil {
|
||||||
|
proxyURL = url.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &clientauthentication.Cluster{
|
||||||
|
Server: config.Host,
|
||||||
|
TLSServerName: config.ServerName,
|
||||||
|
InsecureSkipTLSVerify: config.Insecure,
|
||||||
|
CertificateAuthorityData: caData,
|
||||||
|
ProxyURL: proxyURL,
|
||||||
|
Config: config.ExecProvider.Config,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecClusterToConfig creates a Config with the corresponding fields from the provided
|
||||||
|
// clientauthenticationapi.Cluster. The returned Config will be anonymous (i.e., it will not have
|
||||||
|
// any authentication-related fields set).
|
||||||
|
func ExecClusterToConfig(cluster *clientauthentication.Cluster) (*Config, error) {
|
||||||
|
var proxy func(*http.Request) (*url.URL, error)
|
||||||
|
if cluster.ProxyURL != "" {
|
||||||
|
proxyURL, err := url.Parse(cluster.ProxyURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot parse proxy URL: %w", err)
|
||||||
|
}
|
||||||
|
proxy = http.ProxyURL(proxyURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Config{
|
||||||
|
Host: cluster.Server,
|
||||||
|
TLSClientConfig: TLSClientConfig{
|
||||||
|
Insecure: cluster.InsecureSkipTLSVerify,
|
||||||
|
ServerName: cluster.TLSServerName,
|
||||||
|
CAData: cluster.CertificateAuthorityData,
|
||||||
|
},
|
||||||
|
Proxy: proxy,
|
||||||
|
}, nil
|
||||||
|
}
|
384
staging/src/k8s.io/client-go/rest/exec_test.go
Normal file
384
staging/src/k8s.io/client-go/rest/exec_test.go
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
fuzz "github.com/google/gofuzz"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
|
clientauthenticationapi "k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
|
"k8s.io/client-go/transport"
|
||||||
|
"k8s.io/client-go/util/flowcontrol"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfigToExecCluster(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const proxyURL = "https://some-proxy-url.com/tuna/fish"
|
||||||
|
proxy := func(r *http.Request) (*url.URL, error) {
|
||||||
|
return url.Parse(proxyURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
in Config
|
||||||
|
wantOut clientauthenticationapi.Cluster
|
||||||
|
wantErrorPrefix string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "CA data from memory",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
Raw: []byte("stuff"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Host: "some-host",
|
||||||
|
TLSClientConfig: TLSClientConfig{
|
||||||
|
ServerName: "some-server-name",
|
||||||
|
Insecure: true,
|
||||||
|
CAData: []byte("some-ca-data"),
|
||||||
|
},
|
||||||
|
Proxy: proxy,
|
||||||
|
},
|
||||||
|
wantOut: clientauthenticationapi.Cluster{
|
||||||
|
Server: "some-host",
|
||||||
|
TLSServerName: "some-server-name",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
CertificateAuthorityData: []byte("some-ca-data"),
|
||||||
|
ProxyURL: proxyURL,
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
Raw: []byte("stuff"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CA data from file",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
Raw: []byte("stuff"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Host: "some-host",
|
||||||
|
TLSClientConfig: TLSClientConfig{
|
||||||
|
ServerName: "some-server-name",
|
||||||
|
Insecure: true,
|
||||||
|
CAFile: "testdata/ca.pem",
|
||||||
|
},
|
||||||
|
Proxy: proxy,
|
||||||
|
},
|
||||||
|
wantOut: clientauthenticationapi.Cluster{
|
||||||
|
Server: "some-host",
|
||||||
|
TLSServerName: "some-server-name",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
CertificateAuthorityData: []byte("a CA bundle lives here"),
|
||||||
|
ProxyURL: proxyURL,
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
Raw: []byte("stuff"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no CA data",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
},
|
||||||
|
TLSClientConfig: TLSClientConfig{
|
||||||
|
CAFile: "this-file-does-not-exist",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErrorPrefix: "failed to load CA bundle for execProvider: ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nil proxy",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
Raw: []byte("stuff"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Host: "some-host",
|
||||||
|
TLSClientConfig: TLSClientConfig{
|
||||||
|
ServerName: "some-server-name",
|
||||||
|
Insecure: true,
|
||||||
|
CAFile: "testdata/ca.pem",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantOut: clientauthenticationapi.Cluster{
|
||||||
|
Server: "some-host",
|
||||||
|
TLSServerName: "some-server-name",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
CertificateAuthorityData: []byte("a CA bundle lives here"),
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
Raw: []byte("stuff"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad proxy",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
},
|
||||||
|
Proxy: func(_ *http.Request) (*url.URL, error) {
|
||||||
|
return nil, errors.New("some proxy error")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErrorPrefix: "failed to get proxy URL for execProvider: some proxy error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "proxy returns nil",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
},
|
||||||
|
Proxy: func(_ *http.Request) (*url.URL, error) {
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
Host: "some-host",
|
||||||
|
TLSClientConfig: TLSClientConfig{
|
||||||
|
ServerName: "some-server-name",
|
||||||
|
Insecure: true,
|
||||||
|
CAFile: "testdata/ca.pem",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantOut: clientauthenticationapi.Cluster{
|
||||||
|
Server: "some-host",
|
||||||
|
TLSServerName: "some-server-name",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
CertificateAuthorityData: []byte("a CA bundle lives here"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid config host",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
},
|
||||||
|
Proxy: func(_ *http.Request) (*url.URL, error) {
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
Host: "invalid-config-host\n",
|
||||||
|
},
|
||||||
|
wantErrorPrefix: "failed to create proxy URL request for execProvider: ",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
out, err := ConfigToExecCluster(&test.in)
|
||||||
|
if test.wantErrorPrefix != "" {
|
||||||
|
if err == nil {
|
||||||
|
t.Error("wanted error")
|
||||||
|
} else if !strings.HasPrefix(err.Error(), test.wantErrorPrefix) {
|
||||||
|
t.Errorf("wanted error prefix %q, got %q", test.wantErrorPrefix, err.Error())
|
||||||
|
}
|
||||||
|
} else if diff := cmp.Diff(&test.wantOut, out); diff != "" {
|
||||||
|
t.Errorf("unexpected returned cluster: -got, +want:\n %s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigToExecClusterRoundtrip(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
f := fuzz.New().NilChance(0.5).NumElements(1, 1)
|
||||||
|
f.Funcs(
|
||||||
|
func(r *runtime.Codec, f fuzz.Continue) {
|
||||||
|
codec := &fakeCodec{}
|
||||||
|
f.Fuzz(codec)
|
||||||
|
*r = codec
|
||||||
|
},
|
||||||
|
func(r *http.RoundTripper, f fuzz.Continue) {
|
||||||
|
roundTripper := &fakeRoundTripper{}
|
||||||
|
f.Fuzz(roundTripper)
|
||||||
|
*r = roundTripper
|
||||||
|
},
|
||||||
|
func(fn *func(http.RoundTripper) http.RoundTripper, f fuzz.Continue) {
|
||||||
|
*fn = fakeWrapperFunc
|
||||||
|
},
|
||||||
|
func(fn *transport.WrapperFunc, f fuzz.Continue) {
|
||||||
|
*fn = fakeWrapperFunc
|
||||||
|
},
|
||||||
|
func(r *runtime.NegotiatedSerializer, f fuzz.Continue) {
|
||||||
|
serializer := &fakeNegotiatedSerializer{}
|
||||||
|
f.Fuzz(serializer)
|
||||||
|
*r = serializer
|
||||||
|
},
|
||||||
|
func(r *flowcontrol.RateLimiter, f fuzz.Continue) {
|
||||||
|
limiter := &fakeLimiter{}
|
||||||
|
f.Fuzz(limiter)
|
||||||
|
*r = limiter
|
||||||
|
},
|
||||||
|
func(h *WarningHandler, f fuzz.Continue) {
|
||||||
|
*h = &fakeWarningHandler{}
|
||||||
|
},
|
||||||
|
// Authentication does not require fuzzer
|
||||||
|
func(r *AuthProviderConfigPersister, f fuzz.Continue) {},
|
||||||
|
func(r *clientcmdapi.AuthProviderConfig, f fuzz.Continue) {
|
||||||
|
r.Config = map[string]string{}
|
||||||
|
},
|
||||||
|
func(r *func(ctx context.Context, network, addr string) (net.Conn, error), f fuzz.Continue) {
|
||||||
|
*r = fakeDialFunc
|
||||||
|
},
|
||||||
|
func(r *func(*http.Request) (*url.URL, error), f fuzz.Continue) {
|
||||||
|
*r = fakeProxyFunc
|
||||||
|
},
|
||||||
|
func(r *runtime.Object, f fuzz.Continue) {
|
||||||
|
unknown := &runtime.Unknown{}
|
||||||
|
f.Fuzz(unknown)
|
||||||
|
*r = unknown
|
||||||
|
},
|
||||||
|
)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
expected := &Config{}
|
||||||
|
f.Fuzz(expected)
|
||||||
|
|
||||||
|
// This is the list of known fields that this roundtrip doesn't care about. We should add new
|
||||||
|
// fields to this list if we don't want to roundtrip them on exec cluster conversion.
|
||||||
|
expected.APIPath = ""
|
||||||
|
expected.ContentConfig = ContentConfig{}
|
||||||
|
expected.Username = ""
|
||||||
|
expected.Password = ""
|
||||||
|
expected.BearerToken = ""
|
||||||
|
expected.BearerTokenFile = ""
|
||||||
|
expected.Impersonate = ImpersonationConfig{}
|
||||||
|
expected.AuthProvider = nil
|
||||||
|
expected.AuthConfigPersister = nil
|
||||||
|
expected.ExecProvider = &clientcmdapi.ExecConfig{} // ConfigToExecCluster assumes != nil.
|
||||||
|
expected.TLSClientConfig.CertFile = ""
|
||||||
|
expected.TLSClientConfig.KeyFile = ""
|
||||||
|
expected.TLSClientConfig.CAFile = ""
|
||||||
|
expected.TLSClientConfig.CertData = nil
|
||||||
|
expected.TLSClientConfig.KeyData = nil
|
||||||
|
expected.TLSClientConfig.NextProtos = nil
|
||||||
|
expected.UserAgent = ""
|
||||||
|
expected.DisableCompression = false
|
||||||
|
expected.Transport = nil
|
||||||
|
expected.WrapTransport = nil
|
||||||
|
expected.QPS = 0.0
|
||||||
|
expected.Burst = 0
|
||||||
|
expected.RateLimiter = nil
|
||||||
|
expected.WarningHandler = nil
|
||||||
|
expected.Timeout = 0
|
||||||
|
expected.Dial = nil
|
||||||
|
|
||||||
|
// Manually set URLs so we don't get an error when parsing these during the roundtrip.
|
||||||
|
if expected.Host != "" {
|
||||||
|
expected.Host = "https://some-server-url.com/tuna/fish"
|
||||||
|
}
|
||||||
|
if expected.Proxy != nil {
|
||||||
|
expected.Proxy = func(_ *http.Request) (*url.URL, error) {
|
||||||
|
return url.Parse("https://some-proxy-url.com/tuna/fish")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster, err := ConfigToExecCluster(expected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err := ExecClusterToConfig(cluster)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if actual.Proxy != nil {
|
||||||
|
actualURL, actualErr := actual.Proxy(nil)
|
||||||
|
expectedURL, expectedErr := expected.Proxy(nil)
|
||||||
|
if actualErr != nil {
|
||||||
|
t.Fatalf("failed to get url from actual proxy func: %s", actualErr.Error())
|
||||||
|
}
|
||||||
|
if expectedErr != nil {
|
||||||
|
t.Fatalf("failed to get url from expected proxy func: %s", actualErr.Error())
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(actualURL, expectedURL); diff != "" {
|
||||||
|
t.Fatal("we dropped the Config.Proxy field during conversion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actual.Proxy = nil
|
||||||
|
expected.Proxy = nil
|
||||||
|
|
||||||
|
if actual.ExecProvider != nil {
|
||||||
|
t.Fatal("expected actual Config.ExecProvider field to be set to nil")
|
||||||
|
}
|
||||||
|
actual.ExecProvider = nil
|
||||||
|
expected.ExecProvider = nil
|
||||||
|
|
||||||
|
if diff := cmp.Diff(actual, expected); diff != "" {
|
||||||
|
t.Fatalf("we dropped some Config fields during roundtrip, -got, +want:\n %s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecClusterToConfigRoundtrip(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
f := fuzz.New().NilChance(0.5).NumElements(1, 1)
|
||||||
|
f.Funcs(
|
||||||
|
func(r *runtime.Object, f fuzz.Continue) {
|
||||||
|
// We don't expect the clientauthentication.Cluster.Config to show up in the Config that
|
||||||
|
// comes back from the roundtrip, so just set it to nil.
|
||||||
|
*r = nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
expected := &clientauthentication.Cluster{}
|
||||||
|
f.Fuzz(expected)
|
||||||
|
|
||||||
|
// Manually set URLs so we don't get an error when parsing these during the roundtrip.
|
||||||
|
if expected.Server != "" {
|
||||||
|
expected.Server = "https://some-server-url.com/tuna/fish"
|
||||||
|
}
|
||||||
|
if expected.ProxyURL != "" {
|
||||||
|
expected.ProxyURL = "https://some-proxy-url.com/tuna/fish"
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := ExecClusterToConfig(expected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigToExecCluster assumes config.ExecProvider is not nil.
|
||||||
|
config.ExecProvider = &clientcmdapi.ExecConfig{}
|
||||||
|
|
||||||
|
actual, err := ConfigToExecCluster(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(actual, expected); diff != "" {
|
||||||
|
t.Fatalf("we dropped some Cluster fields during roundtrip: -got, +want:\n %s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
staging/src/k8s.io/client-go/rest/testdata/ca.pem
vendored
Normal file
1
staging/src/k8s.io/client-go/rest/testdata/ca.pem
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
a CA bundle lives here
|
@ -21,6 +21,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
"k8s.io/client-go/plugin/pkg/client/auth/exec"
|
"k8s.io/client-go/plugin/pkg/client/auth/exec"
|
||||||
"k8s.io/client-go/transport"
|
"k8s.io/client-go/transport"
|
||||||
)
|
)
|
||||||
@ -94,7 +95,15 @@ func (c *Config) TransportConfig() (*transport.Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.ExecProvider != nil {
|
if c.ExecProvider != nil {
|
||||||
provider, err := exec.GetAuthenticator(c.ExecProvider)
|
var cluster *clientauthentication.Cluster
|
||||||
|
if c.ExecProvider.ProvideClusterInfo {
|
||||||
|
var err error
|
||||||
|
cluster, err = ConfigToExecCluster(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
provider, err := exec.GetAuthenticator(c.ExecProvider, cluster)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,9 @@ filegroup(
|
|||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "all-srcs",
|
name = "all-srcs",
|
||||||
srcs = [":package-srcs"],
|
srcs = [
|
||||||
|
":package-srcs",
|
||||||
|
"//staging/src/k8s.io/client-go/tools/auth/exec:all-srcs",
|
||||||
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
)
|
)
|
||||||
|
54
staging/src/k8s.io/client-go/tools/auth/exec/BUILD
Normal file
54
staging/src/k8s.io/client-go/tools/auth/exec/BUILD
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["exec.go"],
|
||||||
|
importmap = "k8s.io/kubernetes/vendor/k8s.io/client-go/tools/auth/exec",
|
||||||
|
importpath = "k8s.io/client-go/tools/auth/exec",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/pkg/apis/clientauthentication:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/pkg/apis/clientauthentication/v1beta1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = [
|
||||||
|
"exec_test.go",
|
||||||
|
"types_test.go",
|
||||||
|
],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/pkg/apis/clientauthentication/v1beta1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/tools/clientcmd/api/v1:go_default_library",
|
||||||
|
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
110
staging/src/k8s.io/client-go/tools/auth/exec/exec.go
Normal file
110
staging/src/k8s.io/client-go/tools/auth/exec/exec.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package exec contains helper utilities for exec credential plugins.
|
||||||
|
package exec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
|
"k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1"
|
||||||
|
"k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
const execInfoEnv = "KUBERNETES_EXEC_INFO"
|
||||||
|
|
||||||
|
var scheme = runtime.NewScheme()
|
||||||
|
var codecs = serializer.NewCodecFactory(scheme)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
metav1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
|
||||||
|
utilruntime.Must(v1alpha1.AddToScheme(scheme))
|
||||||
|
utilruntime.Must(v1beta1.AddToScheme(scheme))
|
||||||
|
utilruntime.Must(clientauthentication.AddToScheme(scheme))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadExecCredentialFromEnv is a helper-wrapper around LoadExecCredential that loads from the
|
||||||
|
// well-known KUBERNETES_EXEC_INFO environment variable.
|
||||||
|
//
|
||||||
|
// When the KUBERNETES_EXEC_INFO environment variable is not set or is empty, then this function
|
||||||
|
// will immediately return an error.
|
||||||
|
func LoadExecCredentialFromEnv() (runtime.Object, *rest.Config, error) {
|
||||||
|
env := os.Getenv(execInfoEnv)
|
||||||
|
if env == "" {
|
||||||
|
return nil, nil, errors.New("KUBERNETES_EXEC_INFO env var is unset or empty")
|
||||||
|
}
|
||||||
|
return LoadExecCredential([]byte(env))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadExecCredential loads the configuration needed for an exec plugin to communicate with a
|
||||||
|
// cluster.
|
||||||
|
//
|
||||||
|
// LoadExecCredential expects the provided data to be a serialized client.authentication.k8s.io
|
||||||
|
// ExecCredential object (of any version). If the provided data is invalid (i.e., it cannot be
|
||||||
|
// unmarshalled into any known client.authentication.k8s.io ExecCredential version), an error will
|
||||||
|
// be returned. A successfully unmarshalled ExecCredential will be returned as the first return
|
||||||
|
// value.
|
||||||
|
//
|
||||||
|
// If the provided data is successfully unmarshalled, but it does not contain cluster information
|
||||||
|
// (i.e., ExecCredential.Spec.Cluster == nil), then the returned rest.Config and error will be nil.
|
||||||
|
//
|
||||||
|
// Note that the returned rest.Config will use anonymous authentication, since the exec plugin has
|
||||||
|
// not returned credentials for this cluster yet.
|
||||||
|
func LoadExecCredential(data []byte) (runtime.Object, *rest.Config, error) {
|
||||||
|
obj, gvk, err := codecs.UniversalDeserializer().Decode(data, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("decode: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedGK := schema.GroupKind{
|
||||||
|
Group: clientauthentication.SchemeGroupVersion.Group,
|
||||||
|
Kind: "ExecCredential",
|
||||||
|
}
|
||||||
|
if gvk.GroupKind() != expectedGK {
|
||||||
|
return nil, nil, fmt.Errorf(
|
||||||
|
"invalid group/kind: wanted %s, got %s",
|
||||||
|
expectedGK.String(),
|
||||||
|
gvk.GroupKind().String(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicitly convert object here so that we can return a nicer error message above for when the
|
||||||
|
// data represents an invalid type.
|
||||||
|
var execCredential clientauthentication.ExecCredential
|
||||||
|
if err := scheme.Convert(obj, &execCredential, nil); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("cannot convert to ExecCredential: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if execCredential.Spec.Cluster == nil {
|
||||||
|
return nil, nil, errors.New("ExecCredential does not contain cluster information")
|
||||||
|
}
|
||||||
|
|
||||||
|
restConfig, err := rest.ExecClusterToConfig(execCredential.Spec.Cluster)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("cannot create rest.Config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj, restConfig, nil
|
||||||
|
}
|
210
staging/src/k8s.io/client-go/tools/auth/exec/exec_test.go
Normal file
210
staging/src/k8s.io/client-go/tools/auth/exec/exec_test.go
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package exec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
clientauthenticationv1alpha1 "k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1"
|
||||||
|
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// restInfo holds the rest.Client fields that we care about for test assertions.
|
||||||
|
type restInfo struct {
|
||||||
|
host string
|
||||||
|
tlsClientConfig rest.TLSClientConfig
|
||||||
|
proxyURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadExecCredential(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
data []byte
|
||||||
|
wantExecCredential runtime.Object
|
||||||
|
wantRESTInfo restInfo
|
||||||
|
wantErrorPrefix string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "v1beta1 happy path",
|
||||||
|
data: marshal(t, clientauthenticationv1beta1.SchemeGroupVersion, &clientauthenticationv1beta1.ExecCredential{
|
||||||
|
Spec: clientauthenticationv1beta1.ExecCredentialSpec{
|
||||||
|
Cluster: &clientauthenticationv1beta1.Cluster{
|
||||||
|
Server: "https://some-server/some/path",
|
||||||
|
TLSServerName: "some-server-name",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
CertificateAuthorityData: []byte("some-ca-data"),
|
||||||
|
ProxyURL: "https://some-proxy-url:12345",
|
||||||
|
Config: runtime.RawExtension{
|
||||||
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"names":["marshmallow","zelda"]}}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
wantExecCredential: &clientauthenticationv1beta1.ExecCredential{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "ExecCredential",
|
||||||
|
APIVersion: clientauthenticationv1beta1.SchemeGroupVersion.String(),
|
||||||
|
},
|
||||||
|
Spec: clientauthenticationv1beta1.ExecCredentialSpec{
|
||||||
|
Cluster: &clientauthenticationv1beta1.Cluster{
|
||||||
|
Server: "https://some-server/some/path",
|
||||||
|
TLSServerName: "some-server-name",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
CertificateAuthorityData: []byte("some-ca-data"),
|
||||||
|
ProxyURL: "https://some-proxy-url:12345",
|
||||||
|
Config: runtime.RawExtension{
|
||||||
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"names":["marshmallow","zelda"]}}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantRESTInfo: restInfo{
|
||||||
|
host: "https://some-server/some/path",
|
||||||
|
tlsClientConfig: rest.TLSClientConfig{
|
||||||
|
Insecure: true,
|
||||||
|
ServerName: "some-server-name",
|
||||||
|
CAData: []byte("some-ca-data"),
|
||||||
|
},
|
||||||
|
proxyURL: "https://some-proxy-url:12345",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1beta1 nil config",
|
||||||
|
data: marshal(t, clientauthenticationv1beta1.SchemeGroupVersion, &clientauthenticationv1beta1.ExecCredential{
|
||||||
|
Spec: clientauthenticationv1beta1.ExecCredentialSpec{
|
||||||
|
Cluster: &clientauthenticationv1beta1.Cluster{
|
||||||
|
Server: "https://some-server/some/path",
|
||||||
|
TLSServerName: "some-server-name",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
CertificateAuthorityData: []byte("some-ca-data"),
|
||||||
|
ProxyURL: "https://some-proxy-url:12345",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
wantExecCredential: &clientauthenticationv1beta1.ExecCredential{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "ExecCredential",
|
||||||
|
APIVersion: clientauthenticationv1beta1.SchemeGroupVersion.String(),
|
||||||
|
},
|
||||||
|
Spec: clientauthenticationv1beta1.ExecCredentialSpec{
|
||||||
|
Cluster: &clientauthenticationv1beta1.Cluster{
|
||||||
|
Server: "https://some-server/some/path",
|
||||||
|
TLSServerName: "some-server-name",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
CertificateAuthorityData: []byte("some-ca-data"),
|
||||||
|
ProxyURL: "https://some-proxy-url:12345",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantRESTInfo: restInfo{
|
||||||
|
host: "https://some-server/some/path",
|
||||||
|
tlsClientConfig: rest.TLSClientConfig{
|
||||||
|
Insecure: true,
|
||||||
|
ServerName: "some-server-name",
|
||||||
|
CAData: []byte("some-ca-data"),
|
||||||
|
},
|
||||||
|
proxyURL: "https://some-proxy-url:12345",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1beta1 invalid cluster",
|
||||||
|
data: marshal(t, clientauthenticationv1beta1.SchemeGroupVersion, &clientauthenticationv1beta1.ExecCredential{
|
||||||
|
Spec: clientauthenticationv1beta1.ExecCredentialSpec{
|
||||||
|
Cluster: &clientauthenticationv1beta1.Cluster{
|
||||||
|
ProxyURL: "invalid- url\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
wantErrorPrefix: "cannot create rest.Config",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1beta1 nil cluster",
|
||||||
|
data: marshal(t, clientauthenticationv1beta1.SchemeGroupVersion, &clientauthenticationv1beta1.ExecCredential{}),
|
||||||
|
wantErrorPrefix: "ExecCredential does not contain cluster information",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1alpha1",
|
||||||
|
data: marshal(t, clientauthenticationv1alpha1.SchemeGroupVersion, &clientauthenticationv1alpha1.ExecCredential{}),
|
||||||
|
wantErrorPrefix: "ExecCredential does not contain cluster information",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid object kind",
|
||||||
|
data: marshal(t, metav1.SchemeGroupVersion, &metav1.Status{}),
|
||||||
|
wantErrorPrefix: "invalid group/kind: wanted ExecCredential.client.authentication.k8s.io, got Status",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad data",
|
||||||
|
data: []byte("bad data"),
|
||||||
|
wantErrorPrefix: "decode: ",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
execCredential, restConfig, err := LoadExecCredential(test.data)
|
||||||
|
if test.wantErrorPrefix != "" {
|
||||||
|
if err == nil {
|
||||||
|
t.Error("wanted error, got success")
|
||||||
|
} else if !strings.HasPrefix(err.Error(), test.wantErrorPrefix) {
|
||||||
|
t.Errorf("wanted '%s', got '%s'", test.wantErrorPrefix, err.Error())
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else {
|
||||||
|
if diff := cmp.Diff(test.wantExecCredential, execCredential); diff != "" {
|
||||||
|
t.Error(diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(test.wantRESTInfo.host, restConfig.Host); diff != "" {
|
||||||
|
t.Error(diff)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(test.wantRESTInfo.tlsClientConfig, restConfig.TLSClientConfig); diff != "" {
|
||||||
|
t.Error(diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyURL, err := restConfig.Proxy(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(test.wantRESTInfo.proxyURL, proxyURL.String()); diff != "" {
|
||||||
|
t.Error(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshal(t *testing.T, gv schema.GroupVersion, obj runtime.Object) []byte {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
data, err := runtime.Encode(codecs.LegacyCodec(gv), obj)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
151
staging/src/k8s.io/client-go/tools/auth/exec/types_test.go
Normal file
151
staging/src/k8s.io/client-go/tools/auth/exec/types_test.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package exec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
clientauthenticationv1alpha1 "k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1"
|
||||||
|
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
||||||
|
clientcmdv1 "k8s.io/client-go/tools/clientcmd/api/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestV1beta1ClusterTypesAreSynced ensures that clientauthenticationv1beta1.Cluster stays in sync
|
||||||
|
// with clientcmdv1.Cluster.
|
||||||
|
//
|
||||||
|
// We want clientauthenticationv1beta1.Cluster to offer the same knobs as clientcmdv1.Cluster to
|
||||||
|
// allow someone to connect to the kubernetes API. This test should fail if a new field is added to
|
||||||
|
// one of the structs without updating the other.
|
||||||
|
func TestV1beta1ClusterTypesAreSynced(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
execType := reflect.TypeOf(clientauthenticationv1beta1.Cluster{})
|
||||||
|
clientcmdType := reflect.TypeOf(clientcmdv1.Cluster{})
|
||||||
|
|
||||||
|
t.Run("exec cluster fields match clientcmd cluster fields", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// These are fields that are specific to Cluster and shouldn't be in clientcmdv1.Cluster.
|
||||||
|
execSkippedFieldNames := sets.NewString(
|
||||||
|
// Cluster uses Config to provide its cluster-specific configuration object.
|
||||||
|
"Config",
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := 0; i < execType.NumField(); i++ {
|
||||||
|
execField := execType.Field(i)
|
||||||
|
if execSkippedFieldNames.Has(execField.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run(execField.Name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
clientcmdField, ok := clientcmdType.FieldByName(execField.Name)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("unknown field (please add field to clientcmdv1.Cluster): '%s'", execField.Name)
|
||||||
|
} else if execField.Type != clientcmdField.Type {
|
||||||
|
t.Errorf(
|
||||||
|
"type mismatch (please update Cluster.%s field type to match clientcmdv1.Cluster.%s field type): %q != %q",
|
||||||
|
execField.Name,
|
||||||
|
clientcmdField.Name,
|
||||||
|
execField.Type,
|
||||||
|
clientcmdField.Type,
|
||||||
|
)
|
||||||
|
} else if execField.Tag != clientcmdField.Tag {
|
||||||
|
t.Errorf(
|
||||||
|
"tag mismatch (please update Cluster.%s tag to match clientcmdv1.Cluster.%s tag): %q != %q",
|
||||||
|
execField.Name,
|
||||||
|
clientcmdField.Name,
|
||||||
|
execField.Tag,
|
||||||
|
clientcmdField.Tag,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("clientcmd cluster fields match exec cluster fields", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// These are the fields that we don't want to shadow from clientcmdv1.Cluster.
|
||||||
|
clientcmdSkippedFieldNames := sets.NewString(
|
||||||
|
// CA data will be passed via CertificateAuthorityData, so we don't need this field.
|
||||||
|
"CertificateAuthority",
|
||||||
|
// Cluster uses Config to provide its cluster-specific configuration object.
|
||||||
|
"Extensions",
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := 0; i < clientcmdType.NumField(); i++ {
|
||||||
|
clientcmdField := clientcmdType.Field(i)
|
||||||
|
if clientcmdSkippedFieldNames.Has(clientcmdField.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run(clientcmdField.Name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
execField, ok := execType.FieldByName(clientcmdField.Name)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("unknown field (please add field to Cluster): '%s'", clientcmdField.Name)
|
||||||
|
} else if clientcmdField.Type != execField.Type {
|
||||||
|
t.Errorf(
|
||||||
|
"type mismatch (please update clientcmdv1.Cluster.%s field type to match Cluster.%s field type): %q != %q",
|
||||||
|
clientcmdField.Name,
|
||||||
|
execField.Name,
|
||||||
|
clientcmdField.Type,
|
||||||
|
execField.Type,
|
||||||
|
)
|
||||||
|
} else if clientcmdField.Tag != execField.Tag {
|
||||||
|
t.Errorf(
|
||||||
|
"tag mismatch (please update clientcmdv1.Cluster.%s tag to match Cluster.%s tag): %q != %q",
|
||||||
|
clientcmdField.Name,
|
||||||
|
execField.Name,
|
||||||
|
clientcmdField.Tag,
|
||||||
|
execField.Tag,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAllClusterTypesAreSynced is a TODO so that we remember to write a test similar to
|
||||||
|
// TestV1beta1ClusterTypesAreSynced for any future ExecCredential version. It should start failing
|
||||||
|
// when someone adds support for any other ExecCredential type to this package.
|
||||||
|
func TestAllClusterTypesAreSynced(t *testing.T) {
|
||||||
|
versionsThatDontNeedTests := sets.NewString(
|
||||||
|
// The internal Cluster type should only be used...internally...and therefore doesn't
|
||||||
|
// necessarily need to be synced with clientcmdv1.
|
||||||
|
runtime.APIVersionInternal,
|
||||||
|
// V1alpha1 does not contain a Cluster type.
|
||||||
|
clientauthenticationv1alpha1.SchemeGroupVersion.Version,
|
||||||
|
// We have a test for v1beta1 above.
|
||||||
|
clientauthenticationv1beta1.SchemeGroupVersion.Version,
|
||||||
|
)
|
||||||
|
for gvk := range scheme.AllKnownTypes() {
|
||||||
|
if gvk.Group == clientauthenticationv1beta1.SchemeGroupVersion.Group &&
|
||||||
|
gvk.Kind == "ExecCredential" {
|
||||||
|
if !versionsThatDontNeedTests.Has(gvk.Version) {
|
||||||
|
t.Errorf(
|
||||||
|
"TODO: add test similar to TestV1beta1ClusterTypesAreSynced for client.authentication.k8s.io/%s",
|
||||||
|
gvk.Version,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -215,6 +215,36 @@ type ExecConfig struct {
|
|||||||
// present. For example, `brew install foo-cli` might be a good InstallHint for
|
// present. For example, `brew install foo-cli` might be a good InstallHint for
|
||||||
// foo-cli on Mac OS systems.
|
// foo-cli on Mac OS systems.
|
||||||
InstallHint string `json:"installHint,omitempty"`
|
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)
|
var _ fmt.Stringer = new(ExecConfig)
|
||||||
@ -237,7 +267,11 @@ func (c ExecConfig) String() string {
|
|||||||
if len(c.Env) > 0 {
|
if len(c.Env) > 0 {
|
||||||
env = "[]ExecEnvVar{--- REDACTED ---}"
|
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
|
// ExecEnvVar is used for setting environment variables when executing an exec-based
|
||||||
|
@ -214,6 +214,13 @@ type ExecConfig struct {
|
|||||||
// present. For example, `brew install foo-cli` might be a good InstallHint for
|
// present. For example, `brew install foo-cli` might be a good InstallHint for
|
||||||
// foo-cli on Mac OS systems.
|
// foo-cli on Mac OS systems.
|
||||||
InstallHint string `json:"installHint,omitempty"`
|
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
|
// ExecEnvVar is used for setting environment variables when executing an exec-based
|
||||||
|
@ -171,7 +171,15 @@ func autoConvert_v1_AuthInfo_To_api_AuthInfo(in *AuthInfo, out *api.AuthInfo, s
|
|||||||
out.Username = in.Username
|
out.Username = in.Username
|
||||||
out.Password = in.Password
|
out.Password = in.Password
|
||||||
out.AuthProvider = (*api.AuthProviderConfig)(unsafe.Pointer(in.AuthProvider))
|
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 {
|
if err := Convert_Slice_v1_NamedExtension_To_Map_string_To_runtime_Object(&in.Extensions, &out.Extensions, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -197,7 +205,15 @@ func autoConvert_api_AuthInfo_To_v1_AuthInfo(in *api.AuthInfo, out *AuthInfo, s
|
|||||||
out.Username = in.Username
|
out.Username = in.Username
|
||||||
out.Password = in.Password
|
out.Password = in.Password
|
||||||
out.AuthProvider = (*AuthProviderConfig)(unsafe.Pointer(in.AuthProvider))
|
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 {
|
if err := Convert_Map_string_To_runtime_Object_To_Slice_v1_NamedExtension(&in.Extensions, &out.Extensions, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -359,6 +375,7 @@ func autoConvert_v1_ExecConfig_To_api_ExecConfig(in *ExecConfig, out *api.ExecCo
|
|||||||
out.Env = *(*[]api.ExecEnvVar)(unsafe.Pointer(&in.Env))
|
out.Env = *(*[]api.ExecEnvVar)(unsafe.Pointer(&in.Env))
|
||||||
out.APIVersion = in.APIVersion
|
out.APIVersion = in.APIVersion
|
||||||
out.InstallHint = in.InstallHint
|
out.InstallHint = in.InstallHint
|
||||||
|
out.ProvideClusterInfo = in.ProvideClusterInfo
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,6 +390,8 @@ func autoConvert_api_ExecConfig_To_v1_ExecConfig(in *api.ExecConfig, out *ExecCo
|
|||||||
out.Env = *(*[]ExecEnvVar)(unsafe.Pointer(&in.Env))
|
out.Env = *(*[]ExecEnvVar)(unsafe.Pointer(&in.Env))
|
||||||
out.APIVersion = in.APIVersion
|
out.APIVersion = in.APIVersion
|
||||||
out.InstallHint = in.InstallHint
|
out.InstallHint = in.InstallHint
|
||||||
|
out.ProvideClusterInfo = in.ProvideClusterInfo
|
||||||
|
// INFO: in.Config opted out of conversion generation
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,6 +267,9 @@ func (in *ExecConfig) DeepCopyInto(out *ExecConfig) {
|
|||||||
*out = make([]ExecEnvVar, len(*in))
|
*out = make([]ExecEnvVar, len(*in))
|
||||||
copy(*out, *in)
|
copy(*out, *in)
|
||||||
}
|
}
|
||||||
|
if in.Config != nil {
|
||||||
|
out.Config = in.Config.DeepCopyObject()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,11 @@ import (
|
|||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// clusterExtensionKey is reserved in the cluster extensions list for exec plugin config.
|
||||||
|
clusterExtensionKey = "client.authentication.k8s.io/exec"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields
|
// ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields
|
||||||
// DEPRECATED will be replaced
|
// DEPRECATED will be replaced
|
||||||
@ -189,7 +194,7 @@ func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {
|
|||||||
authInfoName, _ := config.getAuthInfoName()
|
authInfoName, _ := config.getAuthInfoName()
|
||||||
persister = PersisterForUser(config.configAccess, authInfoName)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -232,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)
|
// 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
|
// 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
|
// 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{}
|
mergedConfig := &restclient.Config{}
|
||||||
|
|
||||||
// blindly overwrite existing values based on precedence
|
// blindly overwrite existing values based on precedence
|
||||||
@ -271,6 +276,7 @@ func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthI
|
|||||||
if configAuthInfo.Exec != nil {
|
if configAuthInfo.Exec != nil {
|
||||||
mergedConfig.ExecProvider = configAuthInfo.Exec
|
mergedConfig.ExecProvider = configAuthInfo.Exec
|
||||||
mergedConfig.ExecProvider.InstallHint = cleanANSIEscapeCodes(mergedConfig.ExecProvider.InstallHint)
|
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
|
// if there still isn't enough information to authenticate the user, try prompting
|
||||||
|
@ -23,10 +23,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/imdario/mergo"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMergoSemantics(t *testing.T) {
|
func TestMergoSemantics(t *testing.T) {
|
||||||
@ -834,6 +835,11 @@ apiVersion: v1
|
|||||||
clusters:
|
clusters:
|
||||||
- cluster:
|
- cluster:
|
||||||
server: https://localhost:8080
|
server: https://localhost:8080
|
||||||
|
extensions:
|
||||||
|
- name: client.authentication.k8s.io/exec
|
||||||
|
extension:
|
||||||
|
audience: foo
|
||||||
|
other: bar
|
||||||
name: foo-cluster
|
name: foo-cluster
|
||||||
contexts:
|
contexts:
|
||||||
- context:
|
- context:
|
||||||
@ -852,6 +858,7 @@ users:
|
|||||||
- arg-1
|
- arg-1
|
||||||
- arg-2
|
- arg-2
|
||||||
command: foo-command
|
command: foo-command
|
||||||
|
provideClusterInfo: true
|
||||||
`
|
`
|
||||||
tmpfile, err := ioutil.TempFile("", "kubeconfig")
|
tmpfile, err := ioutil.TempFile("", "kubeconfig")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -868,7 +875,16 @@ users:
|
|||||||
if !reflect.DeepEqual(config.ExecProvider.Args, []string{"arg-1", "arg-2"}) {
|
if !reflect.DeepEqual(config.ExecProvider.Args, []string{"arg-1", "arg-2"}) {
|
||||||
t.Errorf("Got args %v when they should be %v\n", config.ExecProvider.Args, []string{"arg-1", "arg-2"})
|
t.Errorf("Got args %v when they should be %v\n", config.ExecProvider.Args, []string{"arg-1", "arg-2"})
|
||||||
}
|
}
|
||||||
|
if !config.ExecProvider.ProvideClusterInfo {
|
||||||
|
t.Error("Wanted provider cluster info to be true")
|
||||||
|
}
|
||||||
|
want := &runtime.Unknown{
|
||||||
|
Raw: []byte(`{"audience":"foo","other":"bar"}`),
|
||||||
|
ContentType: "application/json",
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(config.ExecProvider.Config, want) {
|
||||||
|
t.Errorf("Got config %v when it should be %v\n", config.ExecProvider.Config, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCleanANSIEscapeCodes(t *testing.T) {
|
func TestCleanANSIEscapeCodes(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user