ContainerImage.Pinniped/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go

198 lines
7.0 KiB
Go
Raw Normal View History

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