ContainerImage.Pinniped/internal/execcredcache/cachefile_test.go

208 lines
6.1 KiB
Go
Raw Normal View History

// 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())
})
}