198 lines
7.0 KiB
Go
198 lines
7.0 KiB
Go
|
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||
|
// SPDX-License-Identifier: Apache-2.0
|
||
|
|
||
|
package supervisorconfig
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/sclevine/spec"
|
||
|
"github.com/sclevine/spec/report"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
|
"k8s.io/apimachinery/pkg/util/clock"
|
||
|
|
||
|
"go.pinniped.dev/generated/1.19/apis/config/v1alpha1"
|
||
|
pinnipedfake "go.pinniped.dev/generated/1.19/client/clientset/versioned/fake"
|
||
|
pinnipedinformers "go.pinniped.dev/generated/1.19/client/informers/externalversions"
|
||
|
"go.pinniped.dev/internal/controllerlib"
|
||
|
"go.pinniped.dev/internal/oidc/provider"
|
||
|
"go.pinniped.dev/internal/testutil"
|
||
|
)
|
||
|
|
||
|
func TestInformerFilters(t *testing.T) {
|
||
|
spec.Run(t, "informer filters", func(t *testing.T, when spec.G, it spec.S) {
|
||
|
var r *require.Assertions
|
||
|
var observableWithInformerOption *testutil.ObservableWithInformerOption
|
||
|
var configMapInformerFilter controllerlib.Filter
|
||
|
|
||
|
it.Before(func() {
|
||
|
r = require.New(t)
|
||
|
observableWithInformerOption = testutil.NewObservableWithInformerOption()
|
||
|
opcInformer := pinnipedinformers.NewSharedInformerFactoryWithOptions(nil, 0).Config().V1alpha1().OIDCProviderConfigs()
|
||
|
_ = NewOIDCProviderConfigWatcherController(
|
||
|
nil,
|
||
|
nil,
|
||
|
nil,
|
||
|
opcInformer,
|
||
|
observableWithInformerOption.WithInformer, // make it possible to observe the behavior of the Filters
|
||
|
)
|
||
|
configMapInformerFilter = observableWithInformerOption.GetFilterForInformer(opcInformer)
|
||
|
})
|
||
|
|
||
|
when("watching OIDCProviderConfig objects", func() {
|
||
|
var subject controllerlib.Filter
|
||
|
var target, otherNamespace, otherName *v1alpha1.OIDCProviderConfig
|
||
|
|
||
|
it.Before(func() {
|
||
|
subject = configMapInformerFilter
|
||
|
target = &v1alpha1.OIDCProviderConfig{ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}}
|
||
|
otherNamespace = &v1alpha1.OIDCProviderConfig{ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "other-namespace"}}
|
||
|
otherName = &v1alpha1.OIDCProviderConfig{ObjectMeta: metav1.ObjectMeta{Name: "other-name", Namespace: "some-namespace"}}
|
||
|
})
|
||
|
|
||
|
when("any OIDCProviderConfig changes", func() {
|
||
|
it("returns true to trigger the sync method", func() {
|
||
|
r.True(subject.Add(target))
|
||
|
r.True(subject.Add(otherName))
|
||
|
r.True(subject.Add(otherNamespace))
|
||
|
r.True(subject.Update(target, otherName))
|
||
|
r.True(subject.Update(otherName, otherName))
|
||
|
r.True(subject.Update(otherNamespace, otherName))
|
||
|
r.True(subject.Update(otherName, target))
|
||
|
r.True(subject.Update(otherName, otherName))
|
||
|
r.True(subject.Update(otherName, otherNamespace))
|
||
|
r.True(subject.Delete(target))
|
||
|
r.True(subject.Delete(otherName))
|
||
|
r.True(subject.Delete(otherNamespace))
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
}, spec.Parallel(), spec.Report(report.Terminal{}))
|
||
|
}
|
||
|
|
||
|
type fakeProvidersSetter struct {
|
||
|
SetProvidersWasCalled bool
|
||
|
OIDCProvidersReceived []*provider.OIDCProvider
|
||
|
}
|
||
|
|
||
|
func (f *fakeProvidersSetter) SetProviders(oidcProviders ...*provider.OIDCProvider) {
|
||
|
f.SetProvidersWasCalled = true
|
||
|
f.OIDCProvidersReceived = oidcProviders
|
||
|
}
|
||
|
|
||
|
func TestSync(t *testing.T) {
|
||
|
spec.Run(t, "Sync", func(t *testing.T, when spec.G, it spec.S) {
|
||
|
var r *require.Assertions
|
||
|
|
||
|
var subject controllerlib.Controller
|
||
|
var opcInformerClient *pinnipedfake.Clientset
|
||
|
var opcInformers pinnipedinformers.SharedInformerFactory
|
||
|
var pinnipedAPIClient *pinnipedfake.Clientset
|
||
|
var timeoutContext context.Context
|
||
|
var timeoutContextCancel context.CancelFunc
|
||
|
var syncContext *controllerlib.Context
|
||
|
var frozenNow time.Time
|
||
|
var providersSetter *fakeProvidersSetter
|
||
|
|
||
|
// Defer starting the informers until the last possible moment so that the
|
||
|
// nested Before's can keep adding things to the informer caches.
|
||
|
var startInformersAndController = func() {
|
||
|
// Set this at the last second to allow for injection of server override.
|
||
|
subject = NewOIDCProviderConfigWatcherController(
|
||
|
providersSetter,
|
||
|
clock.NewFakeClock(frozenNow),
|
||
|
pinnipedAPIClient,
|
||
|
opcInformers.Config().V1alpha1().OIDCProviderConfigs(),
|
||
|
controllerlib.WithInformer,
|
||
|
)
|
||
|
|
||
|
// Set this at the last second to support calling subject.Name().
|
||
|
syncContext = &controllerlib.Context{
|
||
|
Context: timeoutContext,
|
||
|
Name: subject.Name(),
|
||
|
Key: controllerlib.Key{
|
||
|
Namespace: "some-namespace",
|
||
|
Name: "config-name",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
// Must start informers before calling TestRunSynchronously()
|
||
|
opcInformers.Start(timeoutContext.Done())
|
||
|
controllerlib.TestRunSynchronously(t, subject)
|
||
|
}
|
||
|
|
||
|
it.Before(func() {
|
||
|
r = require.New(t)
|
||
|
|
||
|
providersSetter = &fakeProvidersSetter{}
|
||
|
frozenNow = time.Date(2020, time.September, 23, 7, 42, 0, 0, time.Local)
|
||
|
|
||
|
timeoutContext, timeoutContextCancel = context.WithTimeout(context.Background(), time.Second*3)
|
||
|
|
||
|
opcInformerClient = pinnipedfake.NewSimpleClientset()
|
||
|
opcInformers = pinnipedinformers.NewSharedInformerFactory(opcInformerClient, 0)
|
||
|
pinnipedAPIClient = pinnipedfake.NewSimpleClientset()
|
||
|
})
|
||
|
|
||
|
it.After(func() {
|
||
|
timeoutContextCancel()
|
||
|
})
|
||
|
|
||
|
when("there are some valid OIDCProviderConfigs in the informer", func() {
|
||
|
it.Before(func() {
|
||
|
oidcProviderConfig1 := &v1alpha1.OIDCProviderConfig{
|
||
|
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: "some-namespace"},
|
||
|
Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://issuer1.com"},
|
||
|
}
|
||
|
r.NoError(pinnipedAPIClient.Tracker().Add(oidcProviderConfig1))
|
||
|
r.NoError(opcInformerClient.Tracker().Add(oidcProviderConfig1))
|
||
|
|
||
|
oidcProviderConfig2 := &v1alpha1.OIDCProviderConfig{
|
||
|
ObjectMeta: metav1.ObjectMeta{Name: "config2", Namespace: "some-namespace"},
|
||
|
Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://issuer2.com"},
|
||
|
}
|
||
|
r.NoError(pinnipedAPIClient.Tracker().Add(oidcProviderConfig2))
|
||
|
r.NoError(opcInformerClient.Tracker().Add(oidcProviderConfig2))
|
||
|
})
|
||
|
|
||
|
it("calls the ProvidersSetter and updates the OIDCProviderConfigs", func() {
|
||
|
startInformersAndController()
|
||
|
err := controllerlib.TestSync(t, subject, *syncContext)
|
||
|
r.NoError(err)
|
||
|
|
||
|
r.True(providersSetter.SetProvidersWasCalled)
|
||
|
r.Len(providersSetter.OIDCProvidersReceived, 2)
|
||
|
|
||
|
// TODO make more assertions about the OIDCProvidersReceived
|
||
|
// TODO make assertions about the expected pinnipedAPIClient.Actions()
|
||
|
})
|
||
|
|
||
|
when("there is a conflict while updating an OIDCProviderConfig", func() {
|
||
|
// TODO write this test
|
||
|
})
|
||
|
})
|
||
|
|
||
|
when("there are both valid and invalid OIDCProviderConfigs in the informer", func() {
|
||
|
// TODO write this test
|
||
|
})
|
||
|
|
||
|
when("they there are OIDCProviderConfigs with duplicate issuer names in the informer", func() {
|
||
|
// TODO write this test
|
||
|
})
|
||
|
|
||
|
when("there are no OIDCProviderConfigs in the informer", func() {
|
||
|
it("keeps waiting for one", func() {
|
||
|
startInformersAndController()
|
||
|
err := controllerlib.TestSync(t, subject, *syncContext)
|
||
|
r.NoError(err)
|
||
|
r.Empty(pinnipedAPIClient.Actions())
|
||
|
r.True(providersSetter.SetProvidersWasCalled)
|
||
|
r.Empty(providersSetter.OIDCProvidersReceived)
|
||
|
})
|
||
|
})
|
||
|
}, spec.Parallel(), spec.Report(report.Terminal{}))
|
||
|
}
|