mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-26 01:44:17 +00:00
Add CLI caching of cluster-specific credentials.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package execcredcache
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
||||
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
)
|
||||
|
||||
var (
|
||||
// validCache should be the same data as `testdata/valid.yaml`.
|
||||
validCache = credCache{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "config.supervisor.pinniped.dev/v1alpha1", Kind: "CredentialCache"},
|
||||
Entries: []entry{
|
||||
{
|
||||
Key: "test-key",
|
||||
CreationTimestamp: metav1.NewTime(time.Date(2020, 10, 20, 18, 42, 7, 0, time.UTC).Local()),
|
||||
LastUsedTimestamp: metav1.NewTime(time.Date(2020, 10, 20, 18, 45, 31, 0, time.UTC).Local()),
|
||||
Credential: &clientauthenticationv1beta1.ExecCredentialStatus{
|
||||
Token: "test-token",
|
||||
ExpirationTimestamp: &expTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
expTime = metav1.NewTime(time.Date(2020, 10, 20, 19, 46, 30, 0, time.UTC).Local())
|
||||
)
|
||||
|
||||
func TestReadCache(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
want *credCache
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "does not exist",
|
||||
path: "./testdata/does-not-exist.yaml",
|
||||
want: &credCache{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "config.supervisor.pinniped.dev/v1alpha1", Kind: "CredentialCache"},
|
||||
Entries: []entry{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "other file error",
|
||||
path: "./testdata/",
|
||||
wantErr: "could not read cache file: read ./testdata/: is a directory",
|
||||
},
|
||||
{
|
||||
name: "invalid YAML",
|
||||
path: "./testdata/invalid.yaml",
|
||||
wantErr: "invalid cache file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type execcredcache.credCache",
|
||||
},
|
||||
{
|
||||
name: "wrong version",
|
||||
path: "./testdata/wrong-version.yaml",
|
||||
wantErr: `unsupported credential cache version: v1.TypeMeta{Kind:"NotACredentialCache", APIVersion:"config.supervisor.pinniped.dev/v2alpha6"}`,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
path: "./testdata/valid.yaml",
|
||||
want: &validCache,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
got, err := readCache(tt.path)
|
||||
if tt.wantErr != "" {
|
||||
require.EqualError(t, err, tt.wantErr)
|
||||
require.Nil(t, got)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
require.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyCache(t *testing.T) {
|
||||
t.Parallel()
|
||||
got := emptyCache()
|
||||
require.Equal(t, metav1.TypeMeta{APIVersion: "config.supervisor.pinniped.dev/v1alpha1", Kind: "CredentialCache"}, got.TypeMeta)
|
||||
require.Equal(t, 0, len(got.Entries))
|
||||
require.Equal(t, 1, cap(got.Entries))
|
||||
}
|
||||
|
||||
func TestWriteTo(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("io error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
tmp := testutil.TempDir(t) + "/credentials.yaml"
|
||||
require.NoError(t, os.Mkdir(tmp, 0700))
|
||||
err := validCache.writeTo(tmp)
|
||||
require.EqualError(t, err, "open "+tmp+": is a directory")
|
||||
})
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.NoError(t, validCache.writeTo(testutil.TempDir(t)+"/credentials.yaml"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNormalized(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.Equal(t, emptyCache(), emptyCache().normalized())
|
||||
})
|
||||
|
||||
t.Run("nonempty", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
input := emptyCache()
|
||||
now := time.Now()
|
||||
oneMinuteAgo := metav1.NewTime(now.Add(-1 * time.Minute))
|
||||
oneHourFromNow := metav1.NewTime(now.Add(1 * time.Hour))
|
||||
input.Entries = []entry{
|
||||
// Credential is nil.
|
||||
{
|
||||
Key: "nil-credential-key",
|
||||
LastUsedTimestamp: metav1.NewTime(now),
|
||||
Credential: nil,
|
||||
},
|
||||
// Credential's expiration is nil.
|
||||
{
|
||||
Key: "nil-expiration-key",
|
||||
LastUsedTimestamp: metav1.NewTime(now),
|
||||
Credential: &clientauthenticationv1beta1.ExecCredentialStatus{},
|
||||
},
|
||||
// Credential is expired.
|
||||
{
|
||||
Key: "expired-key",
|
||||
LastUsedTimestamp: metav1.NewTime(now),
|
||||
Credential: &clientauthenticationv1beta1.ExecCredentialStatus{
|
||||
ExpirationTimestamp: &oneMinuteAgo,
|
||||
Token: "expired-token",
|
||||
},
|
||||
},
|
||||
// Credential is still valid but is older than maxCacheDuration.
|
||||
{
|
||||
Key: "too-old-key",
|
||||
LastUsedTimestamp: metav1.NewTime(now),
|
||||
CreationTimestamp: metav1.NewTime(now.Add(-3 * time.Hour)),
|
||||
Credential: &clientauthenticationv1beta1.ExecCredentialStatus{
|
||||
ExpirationTimestamp: &oneHourFromNow,
|
||||
Token: "too-old-token",
|
||||
},
|
||||
},
|
||||
// Two entries that are still valid but are out of order.
|
||||
{
|
||||
Key: "key-two",
|
||||
CreationTimestamp: metav1.NewTime(now.Add(-1 * time.Minute)),
|
||||
LastUsedTimestamp: metav1.NewTime(now),
|
||||
Credential: &clientauthenticationv1beta1.ExecCredentialStatus{
|
||||
ExpirationTimestamp: &oneHourFromNow,
|
||||
Token: "token-two",
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: "key-one",
|
||||
CreationTimestamp: metav1.NewTime(now.Add(-2 * time.Minute)),
|
||||
LastUsedTimestamp: metav1.NewTime(now),
|
||||
Credential: &clientauthenticationv1beta1.ExecCredentialStatus{
|
||||
ExpirationTimestamp: &oneHourFromNow,
|
||||
Token: "token-one",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Expect that all but the last two valid entries are pruned, and that they're sorted.
|
||||
require.Equal(t, &credCache{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "config.supervisor.pinniped.dev/v1alpha1", Kind: "CredentialCache"},
|
||||
Entries: []entry{
|
||||
{
|
||||
Key: "key-one",
|
||||
CreationTimestamp: metav1.NewTime(now.Add(-2 * time.Minute)),
|
||||
LastUsedTimestamp: metav1.NewTime(now),
|
||||
Credential: &clientauthenticationv1beta1.ExecCredentialStatus{
|
||||
ExpirationTimestamp: &oneHourFromNow,
|
||||
Token: "token-one",
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: "key-two",
|
||||
CreationTimestamp: metav1.NewTime(now.Add(-1 * time.Minute)),
|
||||
LastUsedTimestamp: metav1.NewTime(now),
|
||||
Credential: &clientauthenticationv1beta1.ExecCredentialStatus{
|
||||
ExpirationTimestamp: &oneHourFromNow,
|
||||
Token: "token-two",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, input.normalized())
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user