mirror of
https://github.com/kubernetes/client-go.git
synced 2025-06-25 22:51:40 +00:00
exec credential provider: InteractiveMode support
The value here is that the exec plugin author can use the kubeconfig to assert how standard input is treated with respect to the exec plugin, e.g., - an exec plugin author can ensure that kubectl fails if it cannot provide standard input to an exec plugin that needs it (Always) - an exec plugin author can ensure that an client-go process will still call an exec plugin that prefers standard input even if standard input is not available (IfAvailable) Signed-off-by: Andrew Keesler <akeesler@vmware.com> Kubernetes-commit: cd83d89ac94c5b61fdd38840098e7223e5af0d34
This commit is contained in:
parent
1bccfc8c60
commit
37ed584bed
@ -47,7 +47,7 @@ type ExecCredentialSpec struct {
|
|||||||
Response *Response
|
Response *Response
|
||||||
|
|
||||||
// Interactive is true when the transport detects the command is being called from an
|
// Interactive is true when the transport detects the command is being called from an
|
||||||
// interactive prompt.
|
// interactive prompt, i.e., when stdin has been passed to this exec plugin.
|
||||||
// +optional
|
// +optional
|
||||||
Interactive bool
|
Interactive bool
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
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 {
|
||||||
// This conversion intentionally omits the Response and Interactive fields, which were only
|
// This conversion intentionally omits the Response field, which were only
|
||||||
// supported in v1alpha1.
|
// supported in v1alpha1.
|
||||||
return autoConvert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in, out, s)
|
return autoConvert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in, out, s)
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,9 @@ type ExecCredentialSpec struct {
|
|||||||
// ExecConfig.ProvideClusterInfo).
|
// ExecConfig.ProvideClusterInfo).
|
||||||
// +optional
|
// +optional
|
||||||
Cluster *Cluster `json:"cluster,omitempty"`
|
Cluster *Cluster `json:"cluster,omitempty"`
|
||||||
|
|
||||||
|
// Interactive declares whether stdin has been passed to this exec plugin.
|
||||||
|
Interactive bool `json:"interactive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecCredentialStatus holds credentials for the transport to use.
|
// ExecCredentialStatus holds credentials for the transport to use.
|
||||||
|
@ -149,6 +149,7 @@ func autoConvert_v1beta1_ExecCredentialSpec_To_clientauthentication_ExecCredenti
|
|||||||
} else {
|
} else {
|
||||||
out.Cluster = nil
|
out.Cluster = nil
|
||||||
}
|
}
|
||||||
|
out.Interactive = in.Interactive
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +160,7 @@ 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
|
out.Interactive = in.Interactive
|
||||||
if in.Cluster != nil {
|
if in.Cluster != nil {
|
||||||
in, out := &in.Cluster, &out.Cluster
|
in, out := &in.Cluster, &out.Cluster
|
||||||
*out = new(Cluster)
|
*out = new(Cluster)
|
||||||
|
@ -162,10 +162,10 @@ 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, cluster *clientauthentication.Cluster) (*Authenticator, error) {
|
func GetAuthenticator(config *api.ExecConfig, cluster *clientauthentication.Cluster) (*Authenticator, error) {
|
||||||
return newAuthenticator(globalCache, config, cluster)
|
return newAuthenticator(globalCache, term.IsTerminal, config, cluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAuthenticator(c *cache, config *api.ExecConfig, cluster *clientauthentication.Cluster) (*Authenticator, error) {
|
func newAuthenticator(c *cache, isTerminalFunc func(int) bool, config *api.ExecConfig, cluster *clientauthentication.Cluster) (*Authenticator, error) {
|
||||||
key := cacheKey(config, cluster)
|
key := cacheKey(config, cluster)
|
||||||
if a, ok := c.get(key); ok {
|
if a, ok := c.get(key); ok {
|
||||||
return a, nil
|
return a, nil
|
||||||
@ -198,7 +198,7 @@ func newAuthenticator(c *cache, config *api.ExecConfig, cluster *clientauthentic
|
|||||||
|
|
||||||
stdin: os.Stdin,
|
stdin: os.Stdin,
|
||||||
stderr: os.Stderr,
|
stderr: os.Stderr,
|
||||||
interactive: term.IsTerminal(int(os.Stdin.Fd())),
|
interactiveFunc: func() (bool, error) { return isInteractive(isTerminalFunc, config) },
|
||||||
now: time.Now,
|
now: time.Now,
|
||||||
environ: os.Environ,
|
environ: os.Environ,
|
||||||
|
|
||||||
@ -213,6 +213,33 @@ func newAuthenticator(c *cache, config *api.ExecConfig, cluster *clientauthentic
|
|||||||
return c.put(key, a), nil
|
return c.put(key, a), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isInteractive(isTerminalFunc func(int) bool, config *api.ExecConfig) (bool, error) {
|
||||||
|
var shouldBeInteractive bool
|
||||||
|
switch config.InteractiveMode {
|
||||||
|
case api.NeverExecInteractiveMode:
|
||||||
|
shouldBeInteractive = false
|
||||||
|
case api.IfAvailableExecInteractiveMode:
|
||||||
|
shouldBeInteractive = !config.StdinUnavailable && isTerminalFunc(int(os.Stdin.Fd()))
|
||||||
|
case api.AlwaysExecInteractiveMode:
|
||||||
|
if !isTerminalFunc(int(os.Stdin.Fd())) {
|
||||||
|
return false, errors.New("standard input is not a terminal")
|
||||||
|
}
|
||||||
|
if config.StdinUnavailable {
|
||||||
|
suffix := ""
|
||||||
|
if len(config.StdinUnavailableMessage) > 0 {
|
||||||
|
// only print extra ": <message>" if the user actually specified a message
|
||||||
|
suffix = fmt.Sprintf(": %s", config.StdinUnavailableMessage)
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("standard input is unavailable%s", suffix)
|
||||||
|
}
|
||||||
|
shouldBeInteractive = true
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("unknown interactiveMode: %q", config.InteractiveMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return shouldBeInteractive, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Authenticator is a client credential provider that rotates credentials by executing a plugin.
|
// Authenticator is a client credential provider that rotates credentials by executing a plugin.
|
||||||
// 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 {
|
||||||
@ -233,7 +260,7 @@ type Authenticator struct {
|
|||||||
// Stubbable for testing
|
// Stubbable for testing
|
||||||
stdin io.Reader
|
stdin io.Reader
|
||||||
stderr io.Writer
|
stderr io.Writer
|
||||||
interactive bool
|
interactiveFunc func() (bool, error)
|
||||||
now func() time.Time
|
now func() time.Time
|
||||||
environ func() []string
|
environ func() []string
|
||||||
|
|
||||||
@ -376,10 +403,15 @@ func (a *Authenticator) maybeRefreshCreds(creds *credentials, r *clientauthentic
|
|||||||
// refreshCredsLocked executes the plugin and reads the credentials from
|
// refreshCredsLocked executes the plugin and reads the credentials from
|
||||||
// stdout. It must be called while holding the Authenticator's mutex.
|
// stdout. It must be called while holding the Authenticator's mutex.
|
||||||
func (a *Authenticator) refreshCredsLocked(r *clientauthentication.Response) error {
|
func (a *Authenticator) refreshCredsLocked(r *clientauthentication.Response) error {
|
||||||
|
interactive, err := a.interactiveFunc()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("exec plugin cannot support interactive mode: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
cred := &clientauthentication.ExecCredential{
|
cred := &clientauthentication.ExecCredential{
|
||||||
Spec: clientauthentication.ExecCredentialSpec{
|
Spec: clientauthentication.ExecCredentialSpec{
|
||||||
Response: r,
|
Response: r,
|
||||||
Interactive: a.interactive,
|
Interactive: interactive,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if a.provideClusterInfo {
|
if a.provideClusterInfo {
|
||||||
@ -398,7 +430,7 @@ func (a *Authenticator) refreshCredsLocked(r *clientauthentication.Response) err
|
|||||||
cmd.Env = env
|
cmd.Env = env
|
||||||
cmd.Stderr = a.stderr
|
cmd.Stderr = a.stderr
|
||||||
cmd.Stdout = stdout
|
cmd.Stdout = stdout
|
||||||
if a.interactive {
|
if interactive {
|
||||||
cmd.Stdin = a.stdin
|
cmd.Stdin = a.stdin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,12 +244,25 @@ func TestCacheKey(t *testing.T) {
|
|||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// c7 should be the same as c6, except c7 has stdin marked as unavailable
|
||||||
|
c7 := &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",
|
||||||
|
StdinUnavailable: true,
|
||||||
|
}
|
||||||
|
|
||||||
key1 := cacheKey(c1, c1c)
|
key1 := cacheKey(c1, c1c)
|
||||||
key2 := cacheKey(c2, c2c)
|
key2 := cacheKey(c2, c2c)
|
||||||
key3 := cacheKey(c3, c3c)
|
key3 := cacheKey(c3, c3c)
|
||||||
key4 := cacheKey(c4, c4c)
|
key4 := cacheKey(c4, c4c)
|
||||||
key5 := cacheKey(c5, c5c)
|
key5 := cacheKey(c5, c5c)
|
||||||
key6 := cacheKey(c6, nil)
|
key6 := cacheKey(c6, nil)
|
||||||
|
key7 := cacheKey(c7, nil)
|
||||||
if key1 != key2 {
|
if key1 != key2 {
|
||||||
t.Error("key1 and key2 didn't match")
|
t.Error("key1 and key2 didn't match")
|
||||||
}
|
}
|
||||||
@ -268,6 +281,9 @@ func TestCacheKey(t *testing.T) {
|
|||||||
if key6 == key4 {
|
if key6 == key4 {
|
||||||
t.Error("key6 and key4 matched")
|
t.Error("key6 and key4 matched")
|
||||||
}
|
}
|
||||||
|
if key6 == key7 {
|
||||||
|
t.Error("key6 and key7 matched")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compJSON(t *testing.T, got, want []byte) {
|
func compJSON(t *testing.T, got, want []byte) {
|
||||||
@ -292,10 +308,11 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
config api.ExecConfig
|
config api.ExecConfig
|
||||||
|
stdinUnavailable bool
|
||||||
exitCode int
|
exitCode int
|
||||||
cluster *clientauthentication.Cluster
|
cluster *clientauthentication.Cluster
|
||||||
output string
|
output string
|
||||||
interactive bool
|
isTerminal bool
|
||||||
response *clientauthentication.Response
|
response *clientauthentication.Response
|
||||||
wantInput string
|
wantInput string
|
||||||
wantCreds credentials
|
wantCreds credentials
|
||||||
@ -307,6 +324,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "basic-request",
|
name: "basic-request",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind":"ExecCredential",
|
"kind":"ExecCredential",
|
||||||
@ -326,8 +344,9 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "interactive",
|
name: "interactive",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
interactive: true,
|
isTerminal: true,
|
||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind":"ExecCredential",
|
"kind":"ExecCredential",
|
||||||
"apiVersion":"client.authentication.k8s.io/v1alpha1",
|
"apiVersion":"client.authentication.k8s.io/v1alpha1",
|
||||||
@ -348,6 +367,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "response",
|
name: "response",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
response: &clientauthentication.Response{
|
response: &clientauthentication.Response{
|
||||||
Header: map[string][]string{
|
Header: map[string][]string{
|
||||||
@ -382,6 +402,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "expiry",
|
name: "expiry",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind":"ExecCredential",
|
"kind":"ExecCredential",
|
||||||
@ -403,6 +424,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "no-group-version",
|
name: "no-group-version",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind":"ExecCredential",
|
"kind":"ExecCredential",
|
||||||
@ -421,6 +443,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "no-status",
|
name: "no-status",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind":"ExecCredential",
|
"kind":"ExecCredential",
|
||||||
@ -437,6 +460,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "no-creds",
|
name: "no-creds",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind":"ExecCredential",
|
"kind":"ExecCredential",
|
||||||
@ -454,6 +478,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "TLS credentials",
|
name: "TLS credentials",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind":"ExecCredential",
|
"kind":"ExecCredential",
|
||||||
@ -474,6 +499,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "bad TLS credentials",
|
name: "bad TLS credentials",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind":"ExecCredential",
|
"kind":"ExecCredential",
|
||||||
@ -494,6 +520,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "cert but no key",
|
name: "cert but no key",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind":"ExecCredential",
|
"kind":"ExecCredential",
|
||||||
@ -513,11 +540,206 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "beta-basic-request",
|
name: "beta-basic-request",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
"spec": {}
|
"spec": {
|
||||||
|
"interactive": false
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
output: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"status": {
|
||||||
|
"token": "foo-bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta-basic-request-with-never-interactive-mode",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.NeverExecInteractiveMode,
|
||||||
|
},
|
||||||
|
wantInput: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"spec": {
|
||||||
|
"interactive": false
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
output: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"status": {
|
||||||
|
"token": "foo-bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta-basic-request-with-never-interactive-mode-and-stdin-unavailable",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.NeverExecInteractiveMode,
|
||||||
|
StdinUnavailable: true,
|
||||||
|
},
|
||||||
|
wantInput: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"spec": {
|
||||||
|
"interactive": false
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
output: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"status": {
|
||||||
|
"token": "foo-bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta-basic-request-with-if-available-interactive-mode",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
|
},
|
||||||
|
wantInput: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"spec": {
|
||||||
|
"interactive": false
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
output: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"status": {
|
||||||
|
"token": "foo-bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta-basic-request-with-if-available-interactive-mode-and-stdin-unavailable",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
|
StdinUnavailable: true,
|
||||||
|
},
|
||||||
|
wantInput: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"spec": {
|
||||||
|
"interactive": false
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
output: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"status": {
|
||||||
|
"token": "foo-bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta-basic-request-with-if-available-interactive-mode-and-terminal",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
|
},
|
||||||
|
isTerminal: true,
|
||||||
|
wantInput: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"spec": {
|
||||||
|
"interactive": true
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
output: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"status": {
|
||||||
|
"token": "foo-bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta-basic-request-with-if-available-interactive-mode-and-terminal-and-stdin-unavailable",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
|
StdinUnavailable: true,
|
||||||
|
},
|
||||||
|
isTerminal: true,
|
||||||
|
wantInput: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"spec": {
|
||||||
|
"interactive": false
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
output: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"status": {
|
||||||
|
"token": "foo-bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta-basic-request-with-always-interactive-mode",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.AlwaysExecInteractiveMode,
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
wantErrSubstr: "exec plugin cannot support interactive mode: standard input is not a terminal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta-basic-request-with-always-interactive-mode-and-terminal-and-stdin-unavailable",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.AlwaysExecInteractiveMode,
|
||||||
|
StdinUnavailable: true,
|
||||||
|
},
|
||||||
|
isTerminal: true,
|
||||||
|
wantErr: true,
|
||||||
|
wantErrSubstr: "exec plugin cannot support interactive mode: standard input is unavailable",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta-basic-request-with-always-interactive-mode-and-terminal-and-stdin-unavailable-with-message",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.AlwaysExecInteractiveMode,
|
||||||
|
StdinUnavailable: true,
|
||||||
|
StdinUnavailableMessage: "some message",
|
||||||
|
},
|
||||||
|
isTerminal: true,
|
||||||
|
wantErr: true,
|
||||||
|
wantErrSubstr: "exec plugin cannot support interactive mode: standard input is unavailable: some message",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta-basic-request-with-always-interactive-mode-and-terminal",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.AlwaysExecInteractiveMode,
|
||||||
|
},
|
||||||
|
isTerminal: true,
|
||||||
|
wantInput: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"spec": {
|
||||||
|
"interactive": true
|
||||||
|
}
|
||||||
}`,
|
}`,
|
||||||
output: `{
|
output: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
@ -532,11 +754,14 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "beta-expiry",
|
name: "beta-expiry",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
"spec": {}
|
"spec": {
|
||||||
|
"interactive": false
|
||||||
|
}
|
||||||
}`,
|
}`,
|
||||||
output: `{
|
output: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
@ -553,6 +778,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "beta-no-group-version",
|
name: "beta-no-group-version",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
output: `{
|
output: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
@ -566,6 +792,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "beta-no-status",
|
name: "beta-no-status",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
output: `{
|
output: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
@ -577,6 +804,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "beta-no-token",
|
name: "beta-no-token",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
output: `{
|
output: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
@ -591,6 +819,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
Command: "does not exist",
|
Command: "does not exist",
|
||||||
InstallHint: "some install hint",
|
InstallHint: "some install hint",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
wantErrSubstr: "some install hint",
|
wantErrSubstr: "some install hint",
|
||||||
@ -599,6 +828,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "binary-fails",
|
name: "binary-fails",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
exitCode: 73,
|
exitCode: 73,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
@ -608,6 +838,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "alpha-with-cluster-is-ignored",
|
name: "alpha-with-cluster-is-ignored",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
cluster: &clientauthentication.Cluster{
|
cluster: &clientauthentication.Cluster{
|
||||||
Server: "foo",
|
Server: "foo",
|
||||||
@ -657,6 +888,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
ProvideClusterInfo: true,
|
ProvideClusterInfo: true,
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
cluster: &clientauthentication.Cluster{
|
cluster: &clientauthentication.Cluster{
|
||||||
Server: "foo",
|
Server: "foo",
|
||||||
@ -693,7 +925,8 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
"audience": "snorlax"
|
"audience": "snorlax"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"interactive": false
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
output: `{
|
output: `{
|
||||||
@ -709,6 +942,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name: "beta-with-cluster-and-without-provide-cluster-info-is-not-serialized",
|
name: "beta-with-cluster-and-without-provide-cluster-info-is-not-serialized",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
cluster: &clientauthentication.Cluster{
|
cluster: &clientauthentication.Cluster{
|
||||||
Server: "foo",
|
Server: "foo",
|
||||||
@ -733,7 +967,9 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind":"ExecCredential",
|
"kind":"ExecCredential",
|
||||||
"apiVersion":"client.authentication.k8s.io/v1beta1",
|
"apiVersion":"client.authentication.k8s.io/v1beta1",
|
||||||
"spec": {}
|
"spec": {
|
||||||
|
"interactive": false
|
||||||
|
}
|
||||||
}`,
|
}`,
|
||||||
output: `{
|
output: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
@ -762,14 +998,13 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
a, err := newAuthenticator(newCache(), &c, test.cluster)
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return test.isTerminal }, &c, test.cluster)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stderr := &bytes.Buffer{}
|
stderr := &bytes.Buffer{}
|
||||||
a.stderr = stderr
|
a.stderr = stderr
|
||||||
a.interactive = test.interactive
|
|
||||||
a.environ = func() []string { return nil }
|
a.environ = func() []string { return nil }
|
||||||
|
|
||||||
if err := a.refreshCredsLocked(test.response); err != nil {
|
if err := a.refreshCredsLocked(test.response); err != nil {
|
||||||
@ -839,8 +1074,9 @@ func TestRoundTripper(t *testing.T) {
|
|||||||
c := api.ExecConfig{
|
c := api.ExecConfig{
|
||||||
Command: "./testdata/test-plugin.sh",
|
Command: "./testdata/test-plugin.sh",
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
}
|
}
|
||||||
a, err := newAuthenticator(newCache(), &c, nil)
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -943,7 +1179,7 @@ func TestAuthorizationHeaderPresentCancelsExecAction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
a, err := newAuthenticator(newCache(), &api.ExecConfig{
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &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)
|
}, nil)
|
||||||
@ -985,9 +1221,10 @@ func TestTLSCredentials(t *testing.T) {
|
|||||||
server.StartTLS()
|
server.StartTLS()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
a, err := newAuthenticator(newCache(), &api.ExecConfig{
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &api.ExecConfig{
|
||||||
Command: "./testdata/test-plugin.sh",
|
Command: "./testdata/test-plugin.sh",
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
}, nil)
|
}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -1078,7 +1315,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, nil)
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -1144,8 +1381,9 @@ func TestInstallHintRateLimit(t *testing.T) {
|
|||||||
Command: "does not exist",
|
Command: "does not exist",
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
InstallHint: "some install hint",
|
InstallHint: "some install hint",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
}
|
}
|
||||||
a, err := newAuthenticator(newCache(), &c, nil)
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -140,9 +140,10 @@ func TestCallsMetric(t *testing.T) {
|
|||||||
{Name: "TEST_EXIT_CODE", Value: fmt.Sprintf("%d", exitCode)},
|
{Name: "TEST_EXIT_CODE", Value: fmt.Sprintf("%d", exitCode)},
|
||||||
{Name: "TEST_OUTPUT", Value: goodOutput},
|
{Name: "TEST_OUTPUT", Value: goodOutput},
|
||||||
},
|
},
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
a, err := newAuthenticator(newCache(), &c, nil)
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -173,8 +174,9 @@ func TestCallsMetric(t *testing.T) {
|
|||||||
c := api.ExecConfig{
|
c := api.ExecConfig{
|
||||||
Command: "does not exist",
|
Command: "does not exist",
|
||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: api.IfAvailableExecInteractiveMode,
|
||||||
}
|
}
|
||||||
a, err := newAuthenticator(newCache(), &c, nil)
|
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -626,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.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)}`,
|
`&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 ---), StdinUnavailable: false}, 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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -245,6 +245,33 @@ type ExecConfig struct {
|
|||||||
// to be stored directly in the kubeconfig.
|
// to be stored directly in the kubeconfig.
|
||||||
// +k8s:conversion-gen=false
|
// +k8s:conversion-gen=false
|
||||||
Config runtime.Object
|
Config runtime.Object
|
||||||
|
|
||||||
|
// InteractiveMode determines this plugin's relationship with standard input. Valid
|
||||||
|
// values are "Never" (this exec plugin never uses standard input), "IfAvailable" (this
|
||||||
|
// exec plugin wants to use standard input if it is available), or "Always" (this exec
|
||||||
|
// plugin requires standard input to function). See ExecInteractiveMode values for more
|
||||||
|
// details.
|
||||||
|
//
|
||||||
|
// If APIVersion is client.authentication.k8s.io/v1alpha1 or
|
||||||
|
// client.authentication.k8s.io/v1beta1, then this field is optional and defaults
|
||||||
|
// to "IfAvailable" when unset. Otherwise, this field is required.
|
||||||
|
// +optional
|
||||||
|
InteractiveMode ExecInteractiveMode
|
||||||
|
|
||||||
|
// StdinUnavailable indicates whether the exec authenticator can pass standard
|
||||||
|
// input through to this exec plugin. For example, a higher level entity might be using
|
||||||
|
// standard input for something else and therefore it would not be safe for the exec
|
||||||
|
// plugin to use standard input. This is kept here in order to keep all of the exec configuration
|
||||||
|
// together, but it is never serialized.
|
||||||
|
// +k8s:conversion-gen=false
|
||||||
|
StdinUnavailable bool
|
||||||
|
|
||||||
|
// StdinUnavailableMessage is an optional message to be displayed when the exec authenticator
|
||||||
|
// cannot successfully run this exec plugin because it needs to use standard input and
|
||||||
|
// StdinUnavailable is true. For example, a process that is already using standard input to
|
||||||
|
// read user instructions might set this to "used by my-program to read user instructions".
|
||||||
|
// +k8s:conversion-gen=false
|
||||||
|
StdinUnavailableMessage string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ fmt.Stringer = new(ExecConfig)
|
var _ fmt.Stringer = new(ExecConfig)
|
||||||
@ -271,7 +298,7 @@ func (c ExecConfig) String() string {
|
|||||||
if c.Config != nil {
|
if c.Config != nil {
|
||||||
config = "runtime.Object(--- REDACTED ---)"
|
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)
|
return fmt.Sprintf("api.ExecConfig{Command: %q, Args: %#v, Env: %s, APIVersion: %q, ProvideClusterInfo: %t, Config: %s, StdinUnavailable: %t}", c.Command, args, env, c.APIVersion, c.ProvideClusterInfo, config, c.StdinUnavailable)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecEnvVar is used for setting environment variables when executing an exec-based
|
// ExecEnvVar is used for setting environment variables when executing an exec-based
|
||||||
@ -281,6 +308,26 @@ type ExecEnvVar struct {
|
|||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecInteractiveMode is a string that describes an exec plugin's relationship with standard input.
|
||||||
|
type ExecInteractiveMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NeverExecInteractiveMode declares that this exec plugin never needs to use standard
|
||||||
|
// input, and therefore the exec plugin will be run regardless of whether standard input is
|
||||||
|
// available for user input.
|
||||||
|
NeverExecInteractiveMode ExecInteractiveMode = "Never"
|
||||||
|
// IfAvailableExecInteractiveMode declares that this exec plugin would like to use standard input
|
||||||
|
// if it is available, but can still operate if standard input is not available. Therefore, the
|
||||||
|
// exec plugin will be run regardless of whether stdin is available for user input. If standard
|
||||||
|
// input is available for user input, then it will be provided to this exec plugin.
|
||||||
|
IfAvailableExecInteractiveMode ExecInteractiveMode = "IfAvailable"
|
||||||
|
// AlwaysExecInteractiveMode declares that this exec plugin requires standard input in order to
|
||||||
|
// run, and therefore the exec plugin will only be run if standard input is available for user
|
||||||
|
// input. If standard input is not available for user input, then the exec plugin will not be run
|
||||||
|
// and an error will be returned by the exec plugin runner.
|
||||||
|
AlwaysExecInteractiveMode ExecInteractiveMode = "Always"
|
||||||
|
)
|
||||||
|
|
||||||
// NewConfig is a convenience function that returns a new Config object with non-nil maps
|
// NewConfig is a convenience function that returns a new Config object with non-nil maps
|
||||||
func NewConfig() *Config {
|
func NewConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
|
37
tools/clientcmd/api/v1/defaults.go
Normal file
37
tools/clientcmd/api/v1/defaults.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||||
|
return RegisterDefaults(scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetDefaults_ExecConfig(exec *ExecConfig) {
|
||||||
|
if len(exec.InteractiveMode) == 0 {
|
||||||
|
switch exec.APIVersion {
|
||||||
|
case "client.authentication.k8s.io/v1beta1", "client.authentication.k8s.io/v1alpha1":
|
||||||
|
// default to IfAvailableExecInteractiveMode for backwards compatibility
|
||||||
|
exec.InteractiveMode = IfAvailableExecInteractiveMode
|
||||||
|
default:
|
||||||
|
// require other versions to explicitly declare whether they want stdin or not
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
84
tools/clientcmd/api/v1/defaults_test.go
Normal file
84
tools/clientcmd/api/v1/defaults_test.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetDefaults_Config(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
in, wantOut *ExecConfig
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "alpha exec API with empty interactive mode",
|
||||||
|
in: &ExecConfig{APIVersion: "client.authentication.k8s.io/v1alpha1"},
|
||||||
|
wantOut: &ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: IfAvailableExecInteractiveMode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta exec API with empty interactive mode",
|
||||||
|
in: &ExecConfig{APIVersion: "client.authentication.k8s.io/v1beta1"},
|
||||||
|
wantOut: &ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: IfAvailableExecInteractiveMode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "alpha exec API with set interactive mode",
|
||||||
|
in: &ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: NeverExecInteractiveMode,
|
||||||
|
},
|
||||||
|
wantOut: &ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: NeverExecInteractiveMode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beta exec API with set interactive mode",
|
||||||
|
in: &ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: NeverExecInteractiveMode,
|
||||||
|
},
|
||||||
|
wantOut: &ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
InteractiveMode: NeverExecInteractiveMode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 exec API with empty interactive mode",
|
||||||
|
in: &ExecConfig{APIVersion: "client.authentication.k8s.io/v1"},
|
||||||
|
wantOut: &ExecConfig{APIVersion: "client.authentication.k8s.io/v1"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
gotOut := test.in.DeepCopy()
|
||||||
|
SetDefaults_ExecConfig(gotOut)
|
||||||
|
if diff := cmp.Diff(test.wantOut, gotOut); diff != "" {
|
||||||
|
t.Errorf("unexpected defaulting; -want, +got:\n %s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -16,5 +16,6 @@ limitations under the License.
|
|||||||
|
|
||||||
// +k8s:conversion-gen=k8s.io/client-go/tools/clientcmd/api
|
// +k8s:conversion-gen=k8s.io/client-go/tools/clientcmd/api
|
||||||
// +k8s:deepcopy-gen=package
|
// +k8s:deepcopy-gen=package
|
||||||
|
// +k8s:defaulter-gen=Kind
|
||||||
|
|
||||||
package v1
|
package v1
|
||||||
|
@ -37,7 +37,7 @@ func init() {
|
|||||||
// We only register manually written functions here. The registration of the
|
// We only register manually written functions here. The registration of the
|
||||||
// generated functions takes place in the generated files. The separation
|
// generated functions takes place in the generated files. The separation
|
||||||
// makes the code compile even when the generated files are missing.
|
// makes the code compile even when the generated files are missing.
|
||||||
localSchemeBuilder.Register(addKnownTypes)
|
localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||||
|
@ -221,6 +221,18 @@ type ExecConfig struct {
|
|||||||
// to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for
|
// to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for
|
||||||
// reading this environment variable.
|
// reading this environment variable.
|
||||||
ProvideClusterInfo bool `json:"provideClusterInfo"`
|
ProvideClusterInfo bool `json:"provideClusterInfo"`
|
||||||
|
|
||||||
|
// InteractiveMode determines this plugin's relationship with standard input. Valid
|
||||||
|
// values are "Never" (this exec plugin never uses standard input), "IfAvailable" (this
|
||||||
|
// exec plugin wants to use standard input if it is available), or "Always" (this exec
|
||||||
|
// plugin requires standard input to function). See ExecInteractiveMode values for more
|
||||||
|
// details.
|
||||||
|
//
|
||||||
|
// If APIVersion is client.authentication.k8s.io/v1alpha1 or
|
||||||
|
// client.authentication.k8s.io/v1beta1, then this field is optional and defaults
|
||||||
|
// to "IfAvailable" when unset. Otherwise, this field is required.
|
||||||
|
//+optional
|
||||||
|
InteractiveMode ExecInteractiveMode `json:"interactiveMode,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecEnvVar is used for setting environment variables when executing an exec-based
|
// ExecEnvVar is used for setting environment variables when executing an exec-based
|
||||||
@ -229,3 +241,23 @@ type ExecEnvVar struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecInteractiveMode is a string that describes an exec plugin's relationship with standard input.
|
||||||
|
type ExecInteractiveMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NeverExecInteractiveMode declares that this exec plugin never needs to use standard
|
||||||
|
// input, and therefore the exec plugin will be run regardless of whether standard input is
|
||||||
|
// available for user input.
|
||||||
|
NeverExecInteractiveMode ExecInteractiveMode = "Never"
|
||||||
|
// IfAvailableExecInteractiveMode declares that this exec plugin would like to use standard input
|
||||||
|
// if it is available, but can still operate if standard input is not available. Therefore, the
|
||||||
|
// exec plugin will be run regardless of whether stdin is available for user input. If standard
|
||||||
|
// input is available for user input, then it will be provided to this exec plugin.
|
||||||
|
IfAvailableExecInteractiveMode ExecInteractiveMode = "IfAvailable"
|
||||||
|
// AlwaysExecInteractiveMode declares that this exec plugin requires standard input in order to
|
||||||
|
// run, and therefore the exec plugin will only be run if standard input is available for user
|
||||||
|
// input. If standard input is not available for user input, then the exec plugin will not be run
|
||||||
|
// and an error will be returned by the exec plugin runner.
|
||||||
|
AlwaysExecInteractiveMode ExecInteractiveMode = "Always"
|
||||||
|
)
|
||||||
|
@ -376,6 +376,7 @@ func autoConvert_v1_ExecConfig_To_api_ExecConfig(in *ExecConfig, out *api.ExecCo
|
|||||||
out.APIVersion = in.APIVersion
|
out.APIVersion = in.APIVersion
|
||||||
out.InstallHint = in.InstallHint
|
out.InstallHint = in.InstallHint
|
||||||
out.ProvideClusterInfo = in.ProvideClusterInfo
|
out.ProvideClusterInfo = in.ProvideClusterInfo
|
||||||
|
out.InteractiveMode = api.ExecInteractiveMode(in.InteractiveMode)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,6 +393,9 @@ func autoConvert_api_ExecConfig_To_v1_ExecConfig(in *api.ExecConfig, out *ExecCo
|
|||||||
out.InstallHint = in.InstallHint
|
out.InstallHint = in.InstallHint
|
||||||
out.ProvideClusterInfo = in.ProvideClusterInfo
|
out.ProvideClusterInfo = in.ProvideClusterInfo
|
||||||
// INFO: in.Config opted out of conversion generation
|
// INFO: in.Config opted out of conversion generation
|
||||||
|
out.InteractiveMode = ExecInteractiveMode(in.InteractiveMode)
|
||||||
|
// INFO: in.StdinUnavailable opted out of conversion generation
|
||||||
|
// INFO: in.StdinUnavailableMessage opted out of conversion generation
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
tools/clientcmd/api/v1/zz_generated.defaults.go
Normal file
42
tools/clientcmd/api/v1/zz_generated.defaults.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by defaulter-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterDefaults adds defaulters functions to the given scheme.
|
||||||
|
// Public to allow building arbitrary schemes.
|
||||||
|
// All generated defaulters are covering - they call all nested defaulters.
|
||||||
|
func RegisterDefaults(scheme *runtime.Scheme) error {
|
||||||
|
scheme.AddTypeDefaultingFunc(&Config{}, func(obj interface{}) { SetObjectDefaults_Config(obj.(*Config)) })
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetObjectDefaults_Config(in *Config) {
|
||||||
|
for i := range in.AuthInfos {
|
||||||
|
a := &in.AuthInfos[i]
|
||||||
|
if a.AuthInfo.Exec != nil {
|
||||||
|
SetDefaults_ExecConfig(a.AuthInfo.Exec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -633,6 +633,7 @@ func TestCreateAuthConfigExecInstallHintCleanup(t *testing.T) {
|
|||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
Command: "some-command",
|
Command: "some-command",
|
||||||
InstallHint: "some install hint with \x1b[1mcontrol chars\x1b[0m\nand a newline",
|
InstallHint: "some install hint with \x1b[1mcontrol chars\x1b[0m\nand a newline",
|
||||||
|
InteractiveMode: clientcmdapi.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
@ -308,6 +308,14 @@ func validateAuthInfo(authInfoName string, authInfo clientcmdapi.AuthInfo) []err
|
|||||||
validationErrors = append(validationErrors, fmt.Errorf("env variable name must be specified for %v to use exec authentication plugin", authInfoName))
|
validationErrors = append(validationErrors, fmt.Errorf("env variable name must be specified for %v to use exec authentication plugin", authInfoName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
switch authInfo.Exec.InteractiveMode {
|
||||||
|
case "":
|
||||||
|
validationErrors = append(validationErrors, fmt.Errorf("interactiveMode must be specified for %v to use exec authentication plugin", authInfoName))
|
||||||
|
case clientcmdapi.NeverExecInteractiveMode, clientcmdapi.IfAvailableExecInteractiveMode, clientcmdapi.AlwaysExecInteractiveMode:
|
||||||
|
// These are valid
|
||||||
|
default:
|
||||||
|
validationErrors = append(validationErrors, fmt.Errorf("invalid interactiveMode for %v: %q", authInfoName, authInfo.Exec.InteractiveMode))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// authPath also provides information for the client to identify the server, so allow multiple auth methods in that case
|
// authPath also provides information for the client to identify the server, so allow multiple auth methods in that case
|
||||||
|
@ -377,6 +377,7 @@ func TestValidateAuthInfoExec(t *testing.T) {
|
|||||||
Env: []clientcmdapi.ExecEnvVar{
|
Env: []clientcmdapi.ExecEnvVar{
|
||||||
{Name: "foo", Value: "bar"},
|
{Name: "foo", Value: "bar"},
|
||||||
},
|
},
|
||||||
|
InteractiveMode: clientcmdapi.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
test := configValidationTest{
|
test := configValidationTest{
|
||||||
@ -392,6 +393,7 @@ func TestValidateAuthInfoExecNoVersion(t *testing.T) {
|
|||||||
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||||
Exec: &clientcmdapi.ExecConfig{
|
Exec: &clientcmdapi.ExecConfig{
|
||||||
Command: "/bin/example",
|
Command: "/bin/example",
|
||||||
|
InteractiveMode: clientcmdapi.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
test := configValidationTest{
|
test := configValidationTest{
|
||||||
@ -410,6 +412,7 @@ func TestValidateAuthInfoExecNoCommand(t *testing.T) {
|
|||||||
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||||
Exec: &clientcmdapi.ExecConfig{
|
Exec: &clientcmdapi.ExecConfig{
|
||||||
APIVersion: "clientauthentication.k8s.io/v1alpha1",
|
APIVersion: "clientauthentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: clientcmdapi.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
test := configValidationTest{
|
test := configValidationTest{
|
||||||
@ -432,6 +435,7 @@ func TestValidateAuthInfoExecWithAuthProvider(t *testing.T) {
|
|||||||
Exec: &clientcmdapi.ExecConfig{
|
Exec: &clientcmdapi.ExecConfig{
|
||||||
Command: "/bin/example",
|
Command: "/bin/example",
|
||||||
APIVersion: "clientauthentication.k8s.io/v1alpha1",
|
APIVersion: "clientauthentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: clientcmdapi.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
test := configValidationTest{
|
test := configValidationTest{
|
||||||
@ -454,6 +458,7 @@ func TestValidateAuthInfoExecNoEnv(t *testing.T) {
|
|||||||
Env: []clientcmdapi.ExecEnvVar{
|
Env: []clientcmdapi.ExecEnvVar{
|
||||||
{Name: "foo", Value: ""},
|
{Name: "foo", Value: ""},
|
||||||
},
|
},
|
||||||
|
InteractiveMode: clientcmdapi.IfAvailableExecInteractiveMode,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
test := configValidationTest{
|
test := configValidationTest{
|
||||||
@ -464,6 +469,45 @@ func TestValidateAuthInfoExecNoEnv(t *testing.T) {
|
|||||||
test.testConfig(t)
|
test.testConfig(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateAuthInfoExecInteractiveModeMissing(t *testing.T) {
|
||||||
|
config := clientcmdapi.NewConfig()
|
||||||
|
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||||
|
Exec: &clientcmdapi.ExecConfig{
|
||||||
|
Command: "/bin/example",
|
||||||
|
APIVersion: "clientauthentication.k8s.io/v1alpha1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
test := configValidationTest{
|
||||||
|
config: config,
|
||||||
|
expectedErrorSubstring: []string{
|
||||||
|
"interactiveMode must be specified for user to use exec authentication plugin",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
test.testAuthInfo("user", t)
|
||||||
|
test.testConfig(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateAuthInfoExecInteractiveModeInvalid(t *testing.T) {
|
||||||
|
config := clientcmdapi.NewConfig()
|
||||||
|
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||||
|
Exec: &clientcmdapi.ExecConfig{
|
||||||
|
Command: "/bin/example",
|
||||||
|
APIVersion: "clientauthentication.k8s.io/v1alpha1",
|
||||||
|
InteractiveMode: "invalid",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
test := configValidationTest{
|
||||||
|
config: config,
|
||||||
|
expectedErrorSubstring: []string{
|
||||||
|
`invalid interactiveMode for user: "invalid"`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
test.testAuthInfo("user", t)
|
||||||
|
test.testConfig(t)
|
||||||
|
}
|
||||||
|
|
||||||
type configValidationTest struct {
|
type configValidationTest struct {
|
||||||
config *clientcmdapi.Config
|
config *clientcmdapi.Config
|
||||||
expectedErrorSubstring []string
|
expectedErrorSubstring []string
|
||||||
|
Loading…
Reference in New Issue
Block a user