2020-07-27 23:49:43 +00:00
|
|
|
/*
|
|
|
|
Copyright 2020 VMware, Inc.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2020-07-28 14:10:40 +00:00
|
|
|
"context"
|
2020-07-27 23:49:43 +00:00
|
|
|
"fmt"
|
|
|
|
"testing"
|
2020-07-28 14:10:40 +00:00
|
|
|
"time"
|
2020-07-27 23:49:43 +00:00
|
|
|
|
|
|
|
"github.com/sclevine/spec"
|
|
|
|
"github.com/sclevine/spec/report"
|
2020-07-28 13:42:25 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2020-08-25 15:48:14 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
2020-07-28 13:42:25 +00:00
|
|
|
|
2020-08-25 15:48:14 +00:00
|
|
|
"github.com/suzerain-io/pinniped/internal/testutil"
|
2020-07-28 15:44:43 +00:00
|
|
|
)
|
2020-07-28 13:42:25 +00:00
|
|
|
|
2020-07-27 23:49:43 +00:00
|
|
|
func TestRun(t *testing.T) {
|
2020-07-29 00:22:17 +00:00
|
|
|
spec.Run(t, "main.run", func(t *testing.T, when spec.G, it spec.S) {
|
|
|
|
var r *require.Assertions
|
2020-07-27 23:49:43 +00:00
|
|
|
var buffer *bytes.Buffer
|
|
|
|
var tokenExchanger tokenExchanger
|
|
|
|
var fakeEnv map[string]string
|
|
|
|
|
|
|
|
var envGetter envGetter = func(envVarName string) (string, bool) {
|
|
|
|
value, present := fakeEnv[envVarName]
|
|
|
|
if !present {
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
return value, true
|
|
|
|
}
|
|
|
|
|
|
|
|
it.Before(func() {
|
2020-07-29 00:22:17 +00:00
|
|
|
r = require.New(t)
|
2020-07-27 23:49:43 +00:00
|
|
|
buffer = new(bytes.Buffer)
|
|
|
|
fakeEnv = map[string]string{
|
2020-08-20 17:54:15 +00:00
|
|
|
"PINNIPED_TOKEN": "token from env",
|
|
|
|
"PINNIPED_CA_BUNDLE": "ca bundle from env",
|
|
|
|
"PINNIPED_K8S_API_ENDPOINT": "k8s api from env",
|
2020-07-27 23:49:43 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
when("env vars are missing", func() {
|
2020-08-20 17:54:15 +00:00
|
|
|
it("returns an error when PINNIPED_TOKEN is missing", func() {
|
|
|
|
delete(fakeEnv, "PINNIPED_TOKEN")
|
2020-07-28 14:10:40 +00:00
|
|
|
err := run(envGetter, tokenExchanger, buffer, 30*time.Second)
|
2020-08-20 17:54:15 +00:00
|
|
|
r.EqualError(err, "failed to get credential: environment variable not set: PINNIPED_TOKEN")
|
2020-07-27 23:49:43 +00:00
|
|
|
})
|
|
|
|
|
2020-08-20 17:54:15 +00:00
|
|
|
it("returns an error when PINNIPED_CA_BUNDLE is missing", func() {
|
|
|
|
delete(fakeEnv, "PINNIPED_CA_BUNDLE")
|
2020-07-28 14:10:40 +00:00
|
|
|
err := run(envGetter, tokenExchanger, buffer, 30*time.Second)
|
2020-08-20 17:54:15 +00:00
|
|
|
r.EqualError(err, "failed to get credential: environment variable not set: PINNIPED_CA_BUNDLE")
|
2020-07-27 23:49:43 +00:00
|
|
|
})
|
|
|
|
|
2020-08-20 17:54:15 +00:00
|
|
|
it("returns an error when PINNIPED_K8S_API_ENDPOINT is missing", func() {
|
|
|
|
delete(fakeEnv, "PINNIPED_K8S_API_ENDPOINT")
|
2020-07-28 14:10:40 +00:00
|
|
|
err := run(envGetter, tokenExchanger, buffer, 30*time.Second)
|
2020-08-20 17:54:15 +00:00
|
|
|
r.EqualError(err, "failed to get credential: environment variable not set: PINNIPED_K8S_API_ENDPOINT")
|
2020-07-27 23:49:43 +00:00
|
|
|
})
|
2020-07-30 01:18:42 +00:00
|
|
|
})
|
2020-07-27 23:49:43 +00:00
|
|
|
|
|
|
|
when("the token exchange fails", func() {
|
|
|
|
it.Before(func() {
|
2020-08-25 15:48:14 +00:00
|
|
|
tokenExchanger = func(ctx context.Context, token, caBundle, apiEndpoint string) (*clientauthenticationv1beta1.ExecCredential, error) {
|
2020-07-27 23:49:43 +00:00
|
|
|
return nil, fmt.Errorf("some error")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
it("returns an error", func() {
|
2020-07-28 14:10:40 +00:00
|
|
|
err := run(envGetter, tokenExchanger, buffer, 30*time.Second)
|
2020-08-14 14:11:14 +00:00
|
|
|
r.EqualError(err, "failed to get credential: some error")
|
2020-07-27 23:49:43 +00:00
|
|
|
})
|
2020-07-30 01:18:42 +00:00
|
|
|
})
|
2020-07-27 23:49:43 +00:00
|
|
|
|
2020-07-28 13:42:25 +00:00
|
|
|
when("the JSON encoder fails", func() {
|
|
|
|
it.Before(func() {
|
2020-08-25 15:48:14 +00:00
|
|
|
tokenExchanger = func(ctx context.Context, token, caBundle, apiEndpoint string) (*clientauthenticationv1beta1.ExecCredential, error) {
|
|
|
|
return &clientauthenticationv1beta1.ExecCredential{
|
|
|
|
Status: &clientauthenticationv1beta1.ExecCredentialStatus{
|
|
|
|
Token: "some token",
|
|
|
|
},
|
|
|
|
}, nil
|
2020-07-28 13:42:25 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
it("returns an error", func() {
|
2020-08-06 22:19:09 +00:00
|
|
|
err := run(envGetter, tokenExchanger, &testutil.ErrorWriter{ReturnError: fmt.Errorf("some IO error")}, 30*time.Second)
|
2020-07-29 00:22:17 +00:00
|
|
|
r.EqualError(err, "failed to marshal response to stdout: some IO error")
|
2020-07-28 13:42:25 +00:00
|
|
|
})
|
2020-07-30 01:18:42 +00:00
|
|
|
})
|
2020-07-28 13:42:25 +00:00
|
|
|
|
2020-07-28 14:10:40 +00:00
|
|
|
when("the token exchange times out", func() {
|
|
|
|
it.Before(func() {
|
2020-08-25 15:48:14 +00:00
|
|
|
tokenExchanger = func(ctx context.Context, token, caBundle, apiEndpoint string) (*clientauthenticationv1beta1.ExecCredential, error) {
|
2020-07-28 14:10:40 +00:00
|
|
|
select {
|
|
|
|
case <-time.After(100 * time.Millisecond):
|
2020-08-25 15:48:14 +00:00
|
|
|
return &clientauthenticationv1beta1.ExecCredential{
|
|
|
|
Status: &clientauthenticationv1beta1.ExecCredentialStatus{
|
|
|
|
Token: "some token",
|
|
|
|
},
|
|
|
|
}, nil
|
2020-07-28 14:10:40 +00:00
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
it("returns an error", func() {
|
|
|
|
err := run(envGetter, tokenExchanger, buffer, 1*time.Millisecond)
|
2020-08-14 14:11:14 +00:00
|
|
|
r.EqualError(err, "failed to get credential: context deadline exceeded")
|
2020-07-28 14:10:40 +00:00
|
|
|
})
|
2020-07-30 01:18:42 +00:00
|
|
|
})
|
2020-07-28 14:10:40 +00:00
|
|
|
|
2020-07-27 23:49:43 +00:00
|
|
|
when("the token exchange succeeds", func() {
|
|
|
|
var actualToken, actualCaBundle, actualAPIEndpoint string
|
|
|
|
|
|
|
|
it.Before(func() {
|
2020-08-25 15:48:14 +00:00
|
|
|
tokenExchanger = func(ctx context.Context, token, caBundle, apiEndpoint string) (*clientauthenticationv1beta1.ExecCredential, error) {
|
2020-07-27 23:49:43 +00:00
|
|
|
actualToken, actualCaBundle, actualAPIEndpoint = token, caBundle, apiEndpoint
|
2020-08-25 15:48:14 +00:00
|
|
|
now := metav1.NewTime(time.Date(2020, 7, 29, 1, 2, 3, 0, time.UTC))
|
|
|
|
return &clientauthenticationv1beta1.ExecCredential{
|
|
|
|
TypeMeta: metav1.TypeMeta{
|
|
|
|
Kind: "ExecCredential",
|
|
|
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
|
|
|
},
|
|
|
|
Status: &clientauthenticationv1beta1.ExecCredentialStatus{
|
|
|
|
ExpirationTimestamp: &now,
|
|
|
|
ClientCertificateData: "some certificate",
|
|
|
|
ClientKeyData: "some key",
|
|
|
|
Token: "some token",
|
|
|
|
},
|
2020-07-27 23:49:43 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
it("writes the execCredential to the given writer", func() {
|
2020-07-28 14:10:40 +00:00
|
|
|
err := run(envGetter, tokenExchanger, buffer, 30*time.Second)
|
2020-07-29 00:22:17 +00:00
|
|
|
r.NoError(err)
|
2020-08-20 17:54:15 +00:00
|
|
|
r.Equal(fakeEnv["PINNIPED_TOKEN"], actualToken)
|
|
|
|
r.Equal(fakeEnv["PINNIPED_CA_BUNDLE"], actualCaBundle)
|
|
|
|
r.Equal(fakeEnv["PINNIPED_K8S_API_ENDPOINT"], actualAPIEndpoint)
|
2020-07-29 00:22:17 +00:00
|
|
|
expected := `{
|
2020-07-28 20:59:16 +00:00
|
|
|
"kind": "ExecCredential",
|
|
|
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
|
|
"spec": {},
|
|
|
|
"status": {
|
2020-07-29 21:24:16 +00:00
|
|
|
"expirationTimestamp":"2020-07-29T01:02:03Z",
|
|
|
|
"clientCertificateData": "some certificate",
|
|
|
|
"clientKeyData":"some key",
|
2020-07-28 20:59:16 +00:00
|
|
|
"token": "some token"
|
|
|
|
}
|
2020-07-29 00:22:17 +00:00
|
|
|
}`
|
|
|
|
r.JSONEq(expected, buffer.String())
|
2020-07-27 23:49:43 +00:00
|
|
|
})
|
2020-07-30 01:18:42 +00:00
|
|
|
})
|
|
|
|
}, spec.Parallel(), spec.Report(report.Terminal{}))
|
2020-07-27 23:49:43 +00:00
|
|
|
}
|