208 lines
6.1 KiB
Go
208 lines
6.1 KiB
Go
|
// 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())
|
||
|
})
|
||
|
}
|