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:
Andrew Keesler
2021-06-14 17:15:36 -04:00
committed by Kubernetes Publisher
parent 1bccfc8c60
commit 37ed584bed
19 changed files with 659 additions and 83 deletions

View File

@@ -162,10 +162,10 @@ func (s *sometimes) Do(f func()) {
// GetAuthenticator returns an exec-based plugin for providing client credentials.
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)
if a, ok := c.get(key); ok {
return a, nil
@@ -196,11 +196,11 @@ func newAuthenticator(c *cache, config *api.ExecConfig, cluster *clientauthentic
clock: clock.RealClock{},
},
stdin: os.Stdin,
stderr: os.Stderr,
interactive: term.IsTerminal(int(os.Stdin.Fd())),
now: time.Now,
environ: os.Environ,
stdin: os.Stdin,
stderr: os.Stderr,
interactiveFunc: func() (bool, error) { return isInteractive(isTerminalFunc, config) },
now: time.Now,
environ: os.Environ,
defaultDialer: defaultDialer,
connTracker: connTracker,
@@ -213,6 +213,33 @@ func newAuthenticator(c *cache, config *api.ExecConfig, cluster *clientauthentic
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.
// The plugin input and output are defined by the API group client.authentication.k8s.io.
type Authenticator struct {
@@ -231,11 +258,11 @@ type Authenticator struct {
installHint string
// Stubbable for testing
stdin io.Reader
stderr io.Writer
interactive bool
now func() time.Time
environ func() []string
stdin io.Reader
stderr io.Writer
interactiveFunc func() (bool, error)
now func() time.Time
environ func() []string
// defaultDialer is used for clients which don't specify a custom dialer
defaultDialer *connrotation.Dialer
@@ -376,10 +403,15 @@ func (a *Authenticator) maybeRefreshCreds(creds *credentials, r *clientauthentic
// refreshCredsLocked executes the plugin and reads the credentials from
// stdout. It must be called while holding the Authenticator's mutex.
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{
Spec: clientauthentication.ExecCredentialSpec{
Response: r,
Interactive: a.interactive,
Interactive: interactive,
},
}
if a.provideClusterInfo {
@@ -398,7 +430,7 @@ func (a *Authenticator) refreshCredsLocked(r *clientauthentication.Response) err
cmd.Env = env
cmd.Stderr = a.stderr
cmd.Stdout = stdout
if a.interactive {
if interactive {
cmd.Stdin = a.stdin
}

View File

@@ -244,12 +244,25 @@ func TestCacheKey(t *testing.T) {
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)
key2 := cacheKey(c2, c2c)
key3 := cacheKey(c3, c3c)
key4 := cacheKey(c4, c4c)
key5 := cacheKey(c5, c5c)
key6 := cacheKey(c6, nil)
key7 := cacheKey(c7, nil)
if key1 != key2 {
t.Error("key1 and key2 didn't match")
}
@@ -268,6 +281,9 @@ func TestCacheKey(t *testing.T) {
if key6 == key4 {
t.Error("key6 and key4 matched")
}
if key6 == key7 {
t.Error("key6 and key7 matched")
}
}
func compJSON(t *testing.T, got, want []byte) {
@@ -290,23 +306,25 @@ func compJSON(t *testing.T, got, want []byte) {
func TestRefreshCreds(t *testing.T) {
tests := []struct {
name string
config api.ExecConfig
exitCode int
cluster *clientauthentication.Cluster
output string
interactive bool
response *clientauthentication.Response
wantInput string
wantCreds credentials
wantExpiry time.Time
wantErr bool
wantErrSubstr string
name string
config api.ExecConfig
stdinUnavailable bool
exitCode int
cluster *clientauthentication.Cluster
output string
isTerminal bool
response *clientauthentication.Response
wantInput string
wantCreds credentials
wantExpiry time.Time
wantErr bool
wantErrSubstr string
}{
{
name: "basic-request",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
wantInput: `{
"kind":"ExecCredential",
@@ -325,9 +343,10 @@ func TestRefreshCreds(t *testing.T) {
{
name: "interactive",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
interactive: true,
isTerminal: true,
wantInput: `{
"kind":"ExecCredential",
"apiVersion":"client.authentication.k8s.io/v1alpha1",
@@ -347,7 +366,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "response",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
response: &clientauthentication.Response{
Header: map[string][]string{
@@ -381,7 +401,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "expiry",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
wantInput: `{
"kind":"ExecCredential",
@@ -402,7 +423,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "no-group-version",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
wantInput: `{
"kind":"ExecCredential",
@@ -420,7 +442,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "no-status",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
wantInput: `{
"kind":"ExecCredential",
@@ -436,7 +459,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "no-creds",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
wantInput: `{
"kind":"ExecCredential",
@@ -453,7 +477,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "TLS credentials",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
wantInput: `{
"kind":"ExecCredential",
@@ -473,7 +498,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "bad TLS credentials",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
wantInput: `{
"kind":"ExecCredential",
@@ -493,7 +519,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "cert but no key",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
wantInput: `{
"kind":"ExecCredential",
@@ -512,12 +539,207 @@ func TestRefreshCreds(t *testing.T) {
{
name: "beta-basic-request",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1beta1",
APIVersion: "client.authentication.k8s.io/v1beta1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
wantInput: `{
"kind": "ExecCredential",
"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: `{
"kind": "ExecCredential",
@@ -531,12 +753,15 @@ func TestRefreshCreds(t *testing.T) {
{
name: "beta-expiry",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1beta1",
APIVersion: "client.authentication.k8s.io/v1beta1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
wantInput: `{
"kind": "ExecCredential",
"apiVersion": "client.authentication.k8s.io/v1beta1",
"spec": {}
"spec": {
"interactive": false
}
}`,
output: `{
"kind": "ExecCredential",
@@ -552,7 +777,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "beta-no-group-version",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1beta1",
APIVersion: "client.authentication.k8s.io/v1beta1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
output: `{
"kind": "ExecCredential",
@@ -565,7 +791,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "beta-no-status",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1beta1",
APIVersion: "client.authentication.k8s.io/v1beta1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
output: `{
"kind": "ExecCredential",
@@ -576,7 +803,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "beta-no-token",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1beta1",
APIVersion: "client.authentication.k8s.io/v1beta1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
output: `{
"kind": "ExecCredential",
@@ -588,9 +816,10 @@ func TestRefreshCreds(t *testing.T) {
{
name: "unknown-binary",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1beta1",
Command: "does not exist",
InstallHint: "some install hint",
APIVersion: "client.authentication.k8s.io/v1beta1",
Command: "does not exist",
InstallHint: "some install hint",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
wantErr: true,
wantErrSubstr: "some install hint",
@@ -598,7 +827,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "binary-fails",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1beta1",
APIVersion: "client.authentication.k8s.io/v1beta1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
exitCode: 73,
wantErr: true,
@@ -607,7 +837,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "alpha-with-cluster-is-ignored",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
cluster: &clientauthentication.Cluster{
Server: "foo",
@@ -657,6 +888,7 @@ func TestRefreshCreds(t *testing.T) {
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1beta1",
ProvideClusterInfo: true,
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
cluster: &clientauthentication.Cluster{
Server: "foo",
@@ -693,7 +925,8 @@ func TestRefreshCreds(t *testing.T) {
"audience": "snorlax"
}
}
}
},
"interactive": false
}
}`,
output: `{
@@ -708,7 +941,8 @@ func TestRefreshCreds(t *testing.T) {
{
name: "beta-with-cluster-and-without-provide-cluster-info-is-not-serialized",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1beta1",
APIVersion: "client.authentication.k8s.io/v1beta1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
},
cluster: &clientauthentication.Cluster{
Server: "foo",
@@ -733,7 +967,9 @@ func TestRefreshCreds(t *testing.T) {
wantInput: `{
"kind":"ExecCredential",
"apiVersion":"client.authentication.k8s.io/v1beta1",
"spec": {}
"spec": {
"interactive": false
}
}`,
output: `{
"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 {
t.Fatal(err)
}
stderr := &bytes.Buffer{}
a.stderr = stderr
a.interactive = test.interactive
a.environ = func() []string { return nil }
if err := a.refreshCredsLocked(test.response); err != nil {
@@ -837,10 +1072,11 @@ func TestRoundTripper(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(handler))
c := api.ExecConfig{
Command: "./testdata/test-plugin.sh",
APIVersion: "client.authentication.k8s.io/v1alpha1",
Command: "./testdata/test-plugin.sh",
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 {
t.Fatal(err)
}
@@ -943,7 +1179,7 @@ func TestAuthorizationHeaderPresentCancelsExecAction(t *testing.T) {
}
for _, test := range tests {
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",
APIVersion: "client.authentication.k8s.io/v1alpha1",
}, nil)
@@ -985,9 +1221,10 @@ func TestTLSCredentials(t *testing.T) {
server.StartTLS()
defer server.Close()
a, err := newAuthenticator(newCache(), &api.ExecConfig{
Command: "./testdata/test-plugin.sh",
APIVersion: "client.authentication.k8s.io/v1alpha1",
a, err := newAuthenticator(newCache(), func(_ int) bool { return false }, &api.ExecConfig{
Command: "./testdata/test-plugin.sh",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InteractiveMode: api.IfAvailableExecInteractiveMode,
}, nil)
if err != nil {
t.Fatal(err)
@@ -1078,7 +1315,7 @@ func TestConcurrentUpdateTransportConfig(t *testing.T) {
Command: "./testdata/test-plugin.sh",
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 {
t.Fatal(err)
}
@@ -1141,11 +1378,12 @@ func TestInstallHintRateLimit(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
c := api.ExecConfig{
Command: "does not exist",
APIVersion: "client.authentication.k8s.io/v1alpha1",
InstallHint: "some install hint",
Command: "does not exist",
APIVersion: "client.authentication.k8s.io/v1alpha1",
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 {
t.Fatal(err)
}

View File

@@ -140,9 +140,10 @@ func TestCallsMetric(t *testing.T) {
{Name: "TEST_EXIT_CODE", Value: fmt.Sprintf("%d", exitCode)},
{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 {
t.Fatal(err)
}
@@ -171,10 +172,11 @@ func TestCallsMetric(t *testing.T) {
// metric values.
refreshCreds := func(command string) {
c := api.ExecConfig{
Command: "does not exist",
APIVersion: "client.authentication.k8s.io/v1beta1",
Command: "does not exist",
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 {
t.Fatal(err)
}