test secret and configmap filtering in concierge authenticator controllers

Signed-off-by: Ashish Amarnath <ashish.amarnath@broadcom.com>
This commit is contained in:
Ashish Amarnath
2024-07-10 00:53:06 -07:00
committed by Ryan Richard
parent cb4b63f8b3
commit 1b7a26d932
7 changed files with 330 additions and 7 deletions

View File

@@ -18,6 +18,7 @@ import (
coreosoidc "github.com/coreos/go-oidc/v3/oidc"
"github.com/go-jose/go-jose/v4"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -137,6 +138,7 @@ func New(
jwtAuthenticators authinformers.JWTAuthenticatorInformer,
secretInformer corev1informers.SecretInformer,
configMapInformer corev1informers.ConfigMapInformer,
withInformer pinnipedcontroller.WithInformerOptionFunc,
clock clock.Clock,
log plog.Logger,
) controllerlib.Controller {
@@ -154,11 +156,27 @@ func New(
log: log.WithName(controllerName),
},
},
controllerlib.WithInformer(
withInformer(
jwtAuthenticators,
pinnipedcontroller.MatchAnythingFilter(nil), // nil parent func is fine because each event is distinct
controllerlib.InformerOption{},
),
withInformer(
secretInformer,
pinnipedcontroller.MatchAnySecretOfTypesFilter(
[]corev1.SecretType{
corev1.SecretTypeOpaque,
corev1.SecretTypeTLS,
},
pinnipedcontroller.SingletonQueue(),
), // nil parent func is fine because each event is distinct
controllerlib.InformerOption{},
),
withInformer(
configMapInformer,
pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()),
controllerlib.InformerOption{},
),
)
}

View File

@@ -26,12 +26,14 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
k8sinformers "k8s.io/client-go/informers"
kubeinformers "k8s.io/client-go/informers"
kubernetesfake "k8s.io/client-go/kubernetes/fake"
coretesting "k8s.io/client-go/testing"
@@ -1845,6 +1847,7 @@ func TestController(t *testing.T) {
}
pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0)
kubeInformers := kubeinformers.NewSharedInformerFactory(kubernetesfake.NewSimpleClientset(), 0)
observableInformers := testutil.NewObservableWithInformerOption()
cache := authncache.New()
var log bytes.Buffer
@@ -1861,6 +1864,7 @@ func TestController(t *testing.T) {
pinnipedInformers.Authentication().V1alpha1().JWTAuthenticators(),
kubeInformers.Core().V1().Secrets(),
kubeInformers.Core().V1().ConfigMaps(),
observableInformers.WithInformer,
frozenClock,
logger)
@@ -1868,6 +1872,8 @@ func TestController(t *testing.T) {
defer cancel()
pinnipedInformers.Start(ctx.Done())
kubeInformers.Start(ctx.Done())
controllerlib.TestRunSynchronously(t, controller)
syncCtx := controllerlib.Context{Context: ctx, Key: tt.syncKey}
@@ -2275,3 +2281,149 @@ func newCacheValue(t *testing.T, spec authenticationv1alpha1.JWTAuthenticatorSpe
},
}
}
func TestControllerFilterSecret(t *testing.T) {
tests := []struct {
name string
secret metav1.Object
wantAdd bool
wantUpdate bool
wantDelete bool
}{
{
name: "should return true for a secret of the type Opaque",
secret: &corev1.Secret{
Type: corev1.SecretTypeOpaque,
ObjectMeta: metav1.ObjectMeta{
Name: "some-name",
},
},
wantAdd: true,
wantUpdate: true,
wantDelete: true,
},
{
name: "should return true for a secret of the type TLS",
secret: &corev1.Secret{
Type: corev1.SecretTypeTLS,
ObjectMeta: metav1.ObjectMeta{
Name: "some-name",
},
},
wantAdd: true,
wantUpdate: true,
wantDelete: true,
},
{
name: "should return false for a secret of the wrong type",
secret: &corev1.Secret{
Type: "other-type",
ObjectMeta: metav1.ObjectMeta{
Name: "some-name",
},
},
},
{
name: "should return false for a resource of wrong data type",
secret: &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var log bytes.Buffer
logger := plog.TestLogger(t, &log)
nowDoesntMatter := time.Date(1122, time.September, 33, 4, 55, 56, 778899, time.Local)
frozenClock := clocktesting.NewFakeClock(nowDoesntMatter)
kubeInformers := k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0)
secretInformer := kubeInformers.Core().V1().Secrets()
configMapInformer := kubeInformers.Core().V1().ConfigMaps()
pinnipedAPIClient := conciergefake.NewSimpleClientset()
pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0)
observableInformers := testutil.NewObservableWithInformerOption()
_ = New(
"concierge", // namespace for the controller
authncache.New(),
pinnipedAPIClient,
pinnipedInformers.Authentication().V1alpha1().JWTAuthenticators(),
secretInformer,
configMapInformer,
observableInformers.WithInformer,
frozenClock,
logger)
unrelated := &corev1.Secret{}
filter := observableInformers.GetFilterForInformer(secretInformer)
require.Equal(t, tt.wantAdd, filter.Add(tt.secret))
require.Equal(t, tt.wantUpdate, filter.Update(unrelated, tt.secret))
require.Equal(t, tt.wantUpdate, filter.Update(tt.secret, unrelated))
require.Equal(t, tt.wantDelete, filter.Delete(tt.secret))
})
}
}
func TestControllerFilterConfigMap(t *testing.T) {
namespace := "some-namespace"
goodCM := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
},
}
tests := []struct {
name string
cm metav1.Object
wantAdd bool
wantUpdate bool
wantDelete bool
}{
{
name: "a configMap in the right namespace",
cm: goodCM,
wantAdd: true,
wantUpdate: true,
wantDelete: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var log bytes.Buffer
logger := plog.TestLogger(t, &log)
nowDoesntMatter := time.Date(1122, time.September, 33, 4, 55, 56, 778899, time.Local)
frozenClock := clocktesting.NewFakeClock(nowDoesntMatter)
kubeInformers := k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0)
secretInformer := kubeInformers.Core().V1().Secrets()
configMapInformer := kubeInformers.Core().V1().ConfigMaps()
pinnipedAPIClient := conciergefake.NewSimpleClientset()
pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0)
observableInformers := testutil.NewObservableWithInformerOption()
_ = New(
"concierge", // namespace for the controller
authncache.New(),
pinnipedAPIClient,
pinnipedInformers.Authentication().V1alpha1().JWTAuthenticators(),
secretInformer,
configMapInformer,
observableInformers.WithInformer,
frozenClock,
logger)
unrelated := &corev1.ConfigMap{}
filter := observableInformers.GetFilterForInformer(configMapInformer)
require.Equal(t, tt.wantAdd, filter.Add(tt.cm))
require.Equal(t, tt.wantUpdate, filter.Update(unrelated, tt.cm))
require.Equal(t, tt.wantUpdate, filter.Update(tt.cm, unrelated))
require.Equal(t, tt.wantDelete, filter.Delete(tt.cm))
})
}
}

View File

@@ -73,6 +73,7 @@ func New(
webhooks authinformers.WebhookAuthenticatorInformer,
secretInformer corev1informers.SecretInformer,
configMapInformer corev1informers.ConfigMapInformer,
withInformer pinnipedcontroller.WithInformerOptionFunc,
clock clock.Clock,
log plog.Logger,
) controllerlib.Controller {
@@ -90,12 +91,12 @@ func New(
log: log.WithName(controllerName),
},
},
controllerlib.WithInformer(
withInformer(
webhooks,
pinnipedcontroller.MatchAnythingFilter(nil), // nil parent func is fine because each event is distinct
controllerlib.InformerOption{},
),
controllerlib.WithInformer(
withInformer(
secretInformer,
pinnipedcontroller.MatchAnySecretOfTypesFilter(
[]corev1.SecretType{
@@ -106,7 +107,7 @@ func New(
), // nil parent func is fine because each event is distinct
controllerlib.InformerOption{},
),
controllerlib.WithInformer(
withInformer(
configMapInformer,
pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()),
controllerlib.InformerOption{},

View File

@@ -21,10 +21,12 @@ import (
"github.com/stretchr/testify/require"
authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/authentication/authenticator"
k8sinformers "k8s.io/client-go/informers"
kubeinformers "k8s.io/client-go/informers"
kubernetesfake "k8s.io/client-go/kubernetes/fake"
coretesting "k8s.io/client-go/testing"
@@ -1491,6 +1493,7 @@ func TestController(t *testing.T) {
}
informers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0)
kubeInformers := kubeinformers.NewSharedInformerFactory(kubernetesfake.NewSimpleClientset(), 0)
observableInformers := testutil.NewObservableWithInformerOption()
cache := authncache.New()
var log bytes.Buffer
@@ -1507,6 +1510,7 @@ func TestController(t *testing.T) {
informers.Authentication().V1alpha1().WebhookAuthenticators(),
kubeInformers.Core().V1().Secrets(),
kubeInformers.Core().V1().ConfigMaps(),
observableInformers.WithInformer,
frozenClock,
logger)
@@ -1514,6 +1518,7 @@ func TestController(t *testing.T) {
defer cancel()
informers.Start(ctx.Done())
kubeInformers.Start(ctx.Done())
controllerlib.TestRunSynchronously(t, controller)
syncCtx := controllerlib.Context{Context: ctx, Key: tt.syncKey}
@@ -1677,3 +1682,149 @@ func TestNewWebhookAuthenticator(t *testing.T) {
})
}
}
func TestControllerFilterSecret(t *testing.T) {
tests := []struct {
name string
secret metav1.Object
wantAdd bool
wantUpdate bool
wantDelete bool
}{
{
name: "should return true for a secret of the type Opaque",
secret: &corev1.Secret{
Type: corev1.SecretTypeOpaque,
ObjectMeta: metav1.ObjectMeta{
Name: "some-name",
},
},
wantAdd: true,
wantUpdate: true,
wantDelete: true,
},
{
name: "should return true for a secret of the type TLS",
secret: &corev1.Secret{
Type: corev1.SecretTypeTLS,
ObjectMeta: metav1.ObjectMeta{
Name: "some-name",
},
},
wantAdd: true,
wantUpdate: true,
wantDelete: true,
},
{
name: "should return false for a secret of the wrong type",
secret: &corev1.Secret{
Type: "other-type",
ObjectMeta: metav1.ObjectMeta{
Name: "some-name",
},
},
},
{
name: "should return false for a resource of wrong data type",
secret: &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var log bytes.Buffer
logger := plog.TestLogger(t, &log)
nowDoesntMatter := time.Date(1122, time.September, 33, 4, 55, 56, 778899, time.Local)
frozenClock := clocktesting.NewFakeClock(nowDoesntMatter)
kubeInformers := k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0)
secretInformer := kubeInformers.Core().V1().Secrets()
configMapInformer := kubeInformers.Core().V1().ConfigMaps()
pinnipedAPIClient := conciergefake.NewSimpleClientset()
pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0)
observableInformers := testutil.NewObservableWithInformerOption()
_ = New(
"concierge", // namespace for controller
authncache.New(),
pinnipedAPIClient,
pinnipedInformers.Authentication().V1alpha1().WebhookAuthenticators(),
secretInformer,
configMapInformer,
observableInformers.WithInformer,
frozenClock,
logger)
unrelated := &corev1.Secret{}
filter := observableInformers.GetFilterForInformer(secretInformer)
require.Equal(t, tt.wantAdd, filter.Add(tt.secret))
require.Equal(t, tt.wantUpdate, filter.Update(unrelated, tt.secret))
require.Equal(t, tt.wantUpdate, filter.Update(tt.secret, unrelated))
require.Equal(t, tt.wantDelete, filter.Delete(tt.secret))
})
}
}
func TestControllerFilterConfigMap(t *testing.T) {
namespace := "some-namespace"
goodCM := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
},
}
tests := []struct {
name string
cm metav1.Object
wantAdd bool
wantUpdate bool
wantDelete bool
}{
{
name: "a configMap in the right namespace",
cm: goodCM,
wantAdd: true,
wantUpdate: true,
wantDelete: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var log bytes.Buffer
logger := plog.TestLogger(t, &log)
nowDoesntMatter := time.Date(1122, time.September, 33, 4, 55, 56, 778899, time.Local)
frozenClock := clocktesting.NewFakeClock(nowDoesntMatter)
kubeInformers := k8sinformers.NewSharedInformerFactoryWithOptions(kubernetesfake.NewSimpleClientset(), 0)
secretInformer := kubeInformers.Core().V1().Secrets()
configMapInformer := kubeInformers.Core().V1().ConfigMaps()
pinnipedAPIClient := conciergefake.NewSimpleClientset()
pinnipedInformers := conciergeinformers.NewSharedInformerFactory(pinnipedAPIClient, 0)
observableInformers := testutil.NewObservableWithInformerOption()
_ = New(
"concierge", // namespace for the controller
authncache.New(),
pinnipedAPIClient,
pinnipedInformers.Authentication().V1alpha1().WebhookAuthenticators(),
secretInformer,
configMapInformer,
observableInformers.WithInformer,
frozenClock,
logger)
unrelated := &corev1.ConfigMap{}
filter := observableInformers.GetFilterForInformer(configMapInformer)
require.Equal(t, tt.wantAdd, filter.Add(tt.cm))
require.Equal(t, tt.wantUpdate, filter.Update(unrelated, tt.cm))
require.Equal(t, tt.wantUpdate, filter.Update(tt.cm, unrelated))
require.Equal(t, tt.wantDelete, filter.Delete(tt.cm))
})
}
}