From a261e0683f2d88ff0d2e8f545404ec5131841c88 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 19 Oct 2023 11:03:45 -0400 Subject: [PATCH] OIDC - Support auto generation of PingSource identity service account and expose in AuthStatus (#7344) * Implement the OIDC service account for PingSource * Adding the tests * Fixed th failing unit tests * lint fix * Fix the failed unit tests * Instead of having a new bool var, we mark oidc status when running the test * Fixing the comments regarding the serviceaccountInformer --- pkg/adapter/mtping/pingsource_test.go | 6 + pkg/apis/sources/v1/ping_lifecycle.go | 22 ++- pkg/apis/sources/v1/ping_lifecycle_test.go | 65 +++++++- pkg/reconciler/pingsource/controller.go | 29 +++- pkg/reconciler/pingsource/pingsource.go | 24 +++ pkg/reconciler/pingsource/pingsource_test.go | 149 ++++++++++++++++++- pkg/reconciler/testing/v1/pingsource.go | 31 ++++ 7 files changed, 315 insertions(+), 11 deletions(-) diff --git a/pkg/adapter/mtping/pingsource_test.go b/pkg/adapter/mtping/pingsource_test.go index 8038d018a1b..f3fb45ee129 100644 --- a/pkg/adapter/mtping/pingsource_test.go +++ b/pkg/adapter/mtping/pingsource_test.go @@ -89,6 +89,7 @@ func TestAllCases(t *testing.T) { rtv1.WithPingSourceDeployed, rtv1.WithPingSourceSink(sinkAddr), rtv1.WithPingSourceCloudEventAttributes, + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }, WantErr: false, @@ -112,6 +113,7 @@ func TestAllCases(t *testing.T) { rtv1.WithPingSourceDeployed, rtv1.WithPingSourceSink(sinkAddr), rtv1.WithPingSourceCloudEventAttributes, + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }, WantErr: false, @@ -137,6 +139,7 @@ func TestAllCases(t *testing.T) { rtv1.WithPingSourceDeployed, rtv1.WithPingSourceSink(sinkAddr), rtv1.WithPingSourceCloudEventAttributes, + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }, WantErr: false, @@ -162,6 +165,7 @@ func TestAllCases(t *testing.T) { rtv1.WithPingSourceDeployed, rtv1.WithPingSourceSink(sinkAddr), rtv1.WithPingSourceCloudEventAttributes, + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }, WantErr: false, @@ -188,6 +192,7 @@ func TestAllCases(t *testing.T) { rtv1.WithPingSourceSink(sinkAddr), rtv1.WithPingSourceCloudEventAttributes, rtv1.WithPingSourceDeleted, + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }, WantErr: false, @@ -210,6 +215,7 @@ func TestAllCases(t *testing.T) { rtv1.WithPingSourceSink(sinkAddr), rtv1.WithPingSourceCloudEventAttributes, rtv1.WithPingSourceDeleted, + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }, WantErr: false, diff --git a/pkg/apis/sources/v1/ping_lifecycle.go b/pkg/apis/sources/v1/ping_lifecycle.go index 24222d62112..8fa7cea6fae 100644 --- a/pkg/apis/sources/v1/ping_lifecycle.go +++ b/pkg/apis/sources/v1/ping_lifecycle.go @@ -35,11 +35,15 @@ const ( // PingSourceConditionDeployed has status True when the PingSource has had it's receive adapter deployment created. PingSourceConditionDeployed apis.ConditionType = "Deployed" + + // PingSourceConditionOIDCIdentityCreated has status True when the PingSource has had it's OIDC identity created. + PingSourceConditionOIDCIdentityCreated apis.ConditionType = "OIDCIdentityCreated" ) var PingSourceCondSet = apis.NewLivingConditionSet( PingSourceConditionSinkProvided, - PingSourceConditionDeployed) + PingSourceConditionDeployed, + PingSourceConditionOIDCIdentityCreated) const ( // PingSourceEventType is the default PingSource CloudEvent type. @@ -122,3 +126,19 @@ func (s *PingSourceStatus) PropagateDeploymentAvailability(d *appsv1.Deployment) PingSourceCondSet.Manage(s).MarkUnknown(PingSourceConditionDeployed, "DeploymentUnavailable", "The Deployment '%s' is unavailable.", d.Name) } } + +func (s *PingSourceStatus) MarkOIDCIdentityCreatedSucceeded() { + PingSourceCondSet.Manage(s).MarkTrue(PingSourceConditionOIDCIdentityCreated) +} + +func (s *PingSourceStatus) MarkOIDCIdentityCreatedSucceededWithReason(reason, messageFormat string, messageA ...interface{}) { + PingSourceCondSet.Manage(s).MarkTrueWithReason(PingSourceConditionOIDCIdentityCreated, reason, messageFormat, messageA...) +} + +func (s *PingSourceStatus) MarkOIDCIdentityCreatedFailed(reason, messageFormat string, messageA ...interface{}) { + PingSourceCondSet.Manage(s).MarkFalse(PingSourceConditionOIDCIdentityCreated, reason, messageFormat, messageA...) +} + +func (s *PingSourceStatus) MarkOIDCIdentityCreatedUnknown(reason, messageFormat string, messageA ...interface{}) { + PingSourceCondSet.Manage(s).MarkUnknown(PingSourceConditionOIDCIdentityCreated, reason, messageFormat, messageA...) +} diff --git a/pkg/apis/sources/v1/ping_lifecycle_test.go b/pkg/apis/sources/v1/ping_lifecycle_test.go index e01ce800eb7..1c5bd337d26 100644 --- a/pkg/apis/sources/v1/ping_lifecycle_test.go +++ b/pkg/apis/sources/v1/ping_lifecycle_test.go @@ -72,6 +72,7 @@ func TestPingSourceStatusIsReady(t *testing.T) { s: func() *PingSourceStatus { s := &PingSourceStatus{} s.InitializeConditions() + s.MarkOIDCIdentityCreatedSucceeded() return s }(), wantConditionStatus: corev1.ConditionUnknown, @@ -81,6 +82,7 @@ func TestPingSourceStatusIsReady(t *testing.T) { s: func() *PingSourceStatus { s := &PingSourceStatus{} s.InitializeConditions() + s.MarkOIDCIdentityCreatedSucceeded() s.PropagateDeploymentAvailability(availableDeployment) return s }(), @@ -91,7 +93,7 @@ func TestPingSourceStatusIsReady(t *testing.T) { s: func() *PingSourceStatus { s := &PingSourceStatus{} s.InitializeConditions() - + s.MarkOIDCIdentityCreatedSucceeded() s.MarkSink(exampleAddr) return s }(), @@ -102,16 +104,32 @@ func TestPingSourceStatusIsReady(t *testing.T) { s: func() *PingSourceStatus { s := &PingSourceStatus{} s.InitializeConditions() + s.MarkOIDCIdentityCreatedSucceeded() s.MarkSink(exampleAddr) s.PropagateDeploymentAvailability(availableDeployment) return s }(), wantConditionStatus: corev1.ConditionTrue, want: true, - }} + }, + { + name: "oidc status false", + s: func() *PingSourceStatus { + s := &PingSourceStatus{} + s.InitializeConditions() + s.MarkOIDCIdentityCreatedFailed("Unable to create the OIDC identity", "") + s.MarkSink(exampleAddr) + s.PropagateDeploymentAvailability(availableDeployment) + return s + }(), + wantConditionStatus: corev1.ConditionFalse, + want: false, + }, + } for _, test := range tests { t.Run(test.name, func(t *testing.T) { + if test.wantConditionStatus != "" { gotConditionStatus := test.s.GetTopLevelCondition().Status if gotConditionStatus != test.wantConditionStatus { @@ -122,6 +140,7 @@ func TestPingSourceStatusIsReady(t *testing.T) { if got != test.want { t.Errorf("unexpected readiness: want %v, got %v", test.want, got) } + }) } } @@ -145,6 +164,7 @@ func TestPingSourceStatusGetTopLevelCondition(t *testing.T) { s: func() *PingSourceStatus { s := &PingSourceStatus{} s.InitializeConditions() + s.MarkOIDCIdentityCreatedSucceeded() return s }(), want: &apis.Condition{ @@ -156,6 +176,7 @@ func TestPingSourceStatusGetTopLevelCondition(t *testing.T) { s: func() *PingSourceStatus { s := &PingSourceStatus{} s.InitializeConditions() + s.MarkOIDCIdentityCreatedSucceeded() s.PropagateDeploymentAvailability(availableDeployment) return s }(), @@ -168,6 +189,7 @@ func TestPingSourceStatusGetTopLevelCondition(t *testing.T) { s: func() *PingSourceStatus { s := &PingSourceStatus{} s.InitializeConditions() + s.MarkOIDCIdentityCreatedSucceeded() s.MarkSink(exampleAddr) return s }(), @@ -180,6 +202,7 @@ func TestPingSourceStatusGetTopLevelCondition(t *testing.T) { s: func() *PingSourceStatus { s := &PingSourceStatus{} s.InitializeConditions() + s.MarkOIDCIdentityCreatedSucceeded() s.MarkSink(exampleAddr) s.PropagateDeploymentAvailability(availableDeployment) return s @@ -188,10 +211,28 @@ func TestPingSourceStatusGetTopLevelCondition(t *testing.T) { Type: PingSourceConditionReady, Status: corev1.ConditionTrue, }, - }} + }, + { + name: "oidc fail", + s: func() *PingSourceStatus { + s := &PingSourceStatus{} + s.InitializeConditions() + s.MarkOIDCIdentityCreatedFailed("Unable to create the OIDC identity", "") + s.MarkSink(exampleAddr) + s.PropagateDeploymentAvailability(availableDeployment) + return s + }(), + want: &apis.Condition{ + Type: PingSourceConditionReady, + Status: corev1.ConditionFalse, + Reason: "Unable to create the OIDC identity", + }, + }, + } for _, test := range tests { t.Run(test.name, func(t *testing.T) { + got := test.s.GetTopLevelCondition() ignoreTime := cmpopts.IgnoreFields(apis.Condition{}, "LastTransitionTime", "Severity") @@ -223,6 +264,7 @@ func TestPingSourceStatusGetCondition(t *testing.T) { s: func() *PingSourceStatus { s := &PingSourceStatus{} s.InitializeConditions() + s.MarkOIDCIdentityCreatedSucceeded() return s }(), condQuery: PingSourceConditionReady, @@ -235,6 +277,7 @@ func TestPingSourceStatusGetCondition(t *testing.T) { s: func() *PingSourceStatus { s := &PingSourceStatus{} s.InitializeConditions() + s.MarkOIDCIdentityCreatedSucceeded() s.PropagateDeploymentAvailability(availableDeployment) return s }(), @@ -248,6 +291,7 @@ func TestPingSourceStatusGetCondition(t *testing.T) { s: func() *PingSourceStatus { s := &PingSourceStatus{} s.InitializeConditions() + s.MarkOIDCIdentityCreatedSucceeded() s.MarkSink(exampleAddr) return s }(), @@ -256,6 +300,21 @@ func TestPingSourceStatusGetCondition(t *testing.T) { Type: PingSourceConditionReady, Status: corev1.ConditionUnknown, }, + }, { + name: "oidc failed", + s: func() *PingSourceStatus { + s := &PingSourceStatus{} + s.InitializeConditions() + s.MarkOIDCIdentityCreatedFailed("Unable to create the OIDC identity", "") + s.MarkSink(exampleAddr) + return s + }(), + condQuery: PingSourceConditionReady, + want: &apis.Condition{ + Type: PingSourceConditionReady, + Status: corev1.ConditionFalse, + Reason: "Unable to create the OIDC identity", + }, }} for _, test := range tests { diff --git a/pkg/reconciler/pingsource/controller.go b/pkg/reconciler/pingsource/controller.go index ff52bad2b0b..724908e6a67 100644 --- a/pkg/reconciler/pingsource/controller.go +++ b/pkg/reconciler/pingsource/controller.go @@ -19,6 +19,10 @@ package pingsource import ( "context" + sourcesv1 "knative.dev/eventing/pkg/apis/sources/v1" + + serviceaccountinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/serviceaccount" + "go.uber.org/zap" appsv1 "k8s.io/api/apps/v1" @@ -60,18 +64,26 @@ func NewController( logger.Fatalw("Error converting leader election configuration to JSON", zap.Error(err)) } - featureStore := feature.NewStore(logging.FromContext(ctx).Named("feature-config-store")) + var globalResync func(obj interface{}) + + featureStore := feature.NewStore(logging.FromContext(ctx).Named("feature-config-store"), func(name string, value interface{}) { + if globalResync != nil { + globalResync(nil) + } + }) featureStore.WatchConfigs(cmw) // Configure the reconciler deploymentInformer := deploymentinformer.Get(ctx) pingSourceInformer := pingsourceinformer.Get(ctx) + serviceaccountInformer := serviceaccountinformer.Get(ctx) r := &Reconciler{ - kubeClientSet: kubeclient.Get(ctx), - leConfig: leConfig, - configAcc: reconcilersource.WatchConfigurations(ctx, component, cmw), + kubeClientSet: kubeclient.Get(ctx), + leConfig: leConfig, + configAcc: reconcilersource.WatchConfigurations(ctx, component, cmw), + serviceAccountLister: serviceaccountInformer.Lister(), } impl := pingsourcereconciler.NewImpl(ctx, r, func(impl *controller.Impl) controller.Options { @@ -80,6 +92,10 @@ func NewController( } }) + globalResync = func(interface{}) { + impl.GlobalResync(pingSourceInformer.Informer()) + } + r.sinkResolver = resolver.NewURIResolver(ctx, cmw, impl.Tracker) pingSourceInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) @@ -97,5 +113,10 @@ func NewController( )), }) + serviceaccountInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ + FilterFunc: controller.FilterController(&sourcesv1.PingSource{}), + Handler: controller.HandleAll(impl.EnqueueControllerOf), + }) + return impl } diff --git a/pkg/reconciler/pingsource/pingsource.go b/pkg/reconciler/pingsource/pingsource.go index ea1afae131e..1bad1df76f5 100644 --- a/pkg/reconciler/pingsource/pingsource.go +++ b/pkg/reconciler/pingsource/pingsource.go @@ -21,6 +21,10 @@ import ( "encoding/json" "fmt" + v1 "k8s.io/client-go/listers/core/v1" + "knative.dev/eventing/pkg/apis/feature" + "knative.dev/eventing/pkg/auth" + "go.uber.org/zap" appsv1 "k8s.io/api/apps/v1" @@ -74,6 +78,8 @@ type Reconciler struct { // Leader election configuration for the mt receive adapter leConfig string + + serviceAccountLister v1.ServiceAccountLister } // Check that our Reconciler implements ReconcileKind @@ -99,6 +105,24 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, source *sourcesv1.PingSo } } + // OIDC authentication + featureFlags := feature.FromContext(ctx) + if featureFlags.IsOIDCAuthentication() { + saName := auth.GetOIDCServiceAccountNameForResource(sourcesv1.SchemeGroupVersion.WithKind("PingSource"), source.ObjectMeta) + source.Status.Auth = &duckv1.AuthStatus{ + ServiceAccountName: &saName, + } + + if err := auth.EnsureOIDCServiceAccountExistsForResource(ctx, r.serviceAccountLister, r.kubeClientSet, sourcesv1.SchemeGroupVersion.WithKind("PingSource"), source.ObjectMeta); err != nil { + source.Status.MarkOIDCIdentityCreatedFailed("Unable to resolve service account for OIDC authentication", "%v", err) + return err + } + source.Status.MarkOIDCIdentityCreatedSucceeded() + } else { + source.Status.Auth = nil + source.Status.MarkOIDCIdentityCreatedSucceededWithReason(fmt.Sprintf("%s feature disabled", feature.OIDCAuthentication), "") + } + sinkAddr, err := r.sinkResolver.AddressableFromDestinationV1(ctx, *dest, source) if err != nil { source.Status.MarkNoSink("NotFound", "") diff --git a/pkg/reconciler/pingsource/pingsource_test.go b/pkg/reconciler/pingsource/pingsource_test.go index d2c099983f6..4e84a050926 100644 --- a/pkg/reconciler/pingsource/pingsource_test.go +++ b/pkg/reconciler/pingsource/pingsource_test.go @@ -18,9 +18,13 @@ package pingsource import ( "context" + "fmt" "os" "testing" + "knative.dev/eventing/pkg/apis/feature" + "knative.dev/eventing/pkg/auth" + cloudevents "github.com/cloudevents/sdk-go/v2" "k8s.io/utils/pointer" @@ -141,6 +145,7 @@ func TestAllCases(t *testing.T) { rtv1.WithInitPingSourceConditions, rtv1.WithPingSourceStatusObservedGeneration(generation), rtv1.WithPingSourceSinkNotFound, + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }}, WantEvents: []string{ @@ -184,6 +189,7 @@ func TestAllCases(t *testing.T) { rtv1.WithInitPingSourceConditions, rtv1.WithPingSourceStatusObservedGeneration(generation), rtv1.WithPingSourceSinkNotFound, + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }}, WantEvents: []string{ @@ -239,6 +245,7 @@ func TestAllCases(t *testing.T) { rtv1.WithInitPingSourceConditions, rtv1.WithPingSourceSink(sinkAddressable), rtv1.WithPingSourceStatusObservedGeneration(generation), + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }}, }, { @@ -281,6 +288,7 @@ func TestAllCases(t *testing.T) { rtv1.WithPingSourceSink(sinkAddressable), rtv1.WithPingSourceCloudEventAttributes, rtv1.WithPingSourceStatusObservedGeneration(generation), + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }}, WantEvents: []string{ @@ -337,6 +345,7 @@ func TestAllCases(t *testing.T) { }), rtv1.WithPingSourceCloudEventAttributes, rtv1.WithPingSourceStatusObservedGeneration(generation), + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }}, WantEvents: []string{ @@ -392,6 +401,7 @@ func TestAllCases(t *testing.T) { rtv1.WithPingSourceSink(sinkAddressable), rtv1.WithPingSourceCloudEventAttributes, rtv1.WithPingSourceStatusObservedGeneration(generation), + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }}, WantUpdates: []clientgotesting.UpdateActionImpl{{ @@ -437,6 +447,7 @@ func TestAllCases(t *testing.T) { rtv1.WithPingSourceSink(sinkAddressable), rtv1.WithPingSourceCloudEventAttributes, rtv1.WithPingSourceStatusObservedGeneration(generation), + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }}, WantEvents: []string{ @@ -485,6 +496,7 @@ func TestAllCases(t *testing.T) { rtv1.WithPingSourceSink(sinkAddressable), rtv1.WithPingSourceCloudEventAttributes, rtv1.WithPingSourceStatusObservedGeneration(generation), + rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), ), }}, WantEvents: []string{ @@ -494,15 +506,127 @@ func TestAllCases(t *testing.T) { patchFinalizers(sourceName, testNS), }, }, + { + Name: "OIDC: creates OIDC service account", + Ctx: feature.ToContext(context.Background(), feature.Flags{ + feature.OIDCAuthentication: feature.Enabled, + }), + Objects: []runtime.Object{ + rtv1.NewPingSource(sourceName, testNS, + rtv1.WithPingSourceSpec(sourcesv1.PingSourceSpec{ + Schedule: testSchedule, + ContentType: testContentType, + Data: testData, + SourceSpec: duckv1.SourceSpec{ + Sink: sinkDest, + }, + }), + rtv1.WithPingSource(sourceUID), + rtv1.WithPingSourceObjectMetaGeneration(generation), + ), + rtv1.NewChannel(sinkName, testNS, + rtv1.WithInitChannelConditions, + rtv1.WithChannelAddress(sinkAddressable), + ), + makeAvailableMTAdapter(), + }, + Key: testNS + "/" + sourceName, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: rtv1.NewPingSource(sourceName, testNS, + rtv1.WithPingSourceSpec(sourcesv1.PingSourceSpec{ + Schedule: testSchedule, + ContentType: testContentType, + Data: testData, + SourceSpec: duckv1.SourceSpec{ + Sink: sinkDest, + }, + }), + rtv1.WithPingSource(sourceUID), + rtv1.WithPingSourceObjectMetaGeneration(generation), + // Status Update: + rtv1.WithInitPingSourceConditions, + rtv1.WithPingSourceDeployed, + rtv1.WithPingSourceSink(sinkAddressable), + rtv1.WithPingSourceCloudEventAttributes, + rtv1.WithPingSourceStatusObservedGeneration(generation), + rtv1.WithPingSourceOIDCIdentityCreatedSucceeded(), + rtv1.WithPingSourceOIDCServiceAccountName(makePingSourceOIDCServiceAccount().Name), + ), + }}, + WantCreates: []runtime.Object{ + makePingSourceOIDCServiceAccount(), + }, + WantEvents: []string{ + Eventf(corev1.EventTypeNormal, "FinalizerUpdate", "Updated %q finalizers", sourceName), + }, + WantPatches: []clientgotesting.PatchActionImpl{ + patchFinalizers(sourceName, testNS), + }, + }, + { + Name: "OIDC: PingSource not ready on invalid OIDC service account", + Ctx: feature.ToContext(context.Background(), feature.Flags{ + feature.OIDCAuthentication: feature.Enabled, + }), + Objects: []runtime.Object{ + makePingSourceOIDCServiceAccountWithoutOwnerRef(), + rtv1.NewPingSource(sourceName, testNS, + rtv1.WithPingSourceSpec(sourcesv1.PingSourceSpec{ + Schedule: testSchedule, + ContentType: testContentType, + Data: testData, + SourceSpec: duckv1.SourceSpec{ + Sink: sinkDest, + }, + }), + rtv1.WithPingSource(sourceUID), + rtv1.WithPingSourceObjectMetaGeneration(generation), + ), + rtv1.NewChannel(sinkName, testNS, + rtv1.WithInitChannelConditions, + rtv1.WithChannelAddress(sinkAddressable), + ), + makeAvailableMTAdapter(), + }, + Key: testNS + "/" + sourceName, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: rtv1.NewPingSource(sourceName, testNS, + rtv1.WithPingSourceSpec(sourcesv1.PingSourceSpec{ + Schedule: testSchedule, + ContentType: testContentType, + Data: testData, + SourceSpec: duckv1.SourceSpec{ + Sink: sinkDest, + }, + }), + rtv1.WithPingSource(sourceUID), + rtv1.WithPingSourceObjectMetaGeneration(generation), + // Status Update: + rtv1.WithInitPingSourceConditions, + rtv1.WithPingSourceStatusObservedGeneration(generation), + rtv1.WithPingSourceOIDCIdentityCreatedFailed("Unable to resolve service account for OIDC authentication", fmt.Sprintf("service account %s not owned by PingSource %s", makePingSourceOIDCServiceAccountWithoutOwnerRef().Name, sourceName)), + rtv1.WithPingSourceOIDCServiceAccountName(makePingSourceOIDCServiceAccount().Name), + ), + }}, + WantErr: true, + WantEvents: []string{ + Eventf(corev1.EventTypeNormal, "FinalizerUpdate", "Updated %q finalizers", sourceName), + Eventf(corev1.EventTypeWarning, "InternalError", fmt.Sprintf("service account %s not owned by PingSource %s", makePingSourceOIDCServiceAccountWithoutOwnerRef().Name, sourceName)), + }, + WantPatches: []clientgotesting.PatchActionImpl{ + patchFinalizers(sourceName, testNS), + }, + }, } logger := logtesting.TestLogger(t) table.Test(t, rtv1.MakeFactory(func(ctx context.Context, listers *rtv1.Listers, cmw configmap.Watcher) controller.Reconciler { ctx = addressable.WithDuck(ctx) r := &Reconciler{ - configAcc: &reconcilersource.EmptyVarsGenerator{}, - kubeClientSet: fakekubeclient.Get(ctx), - tracker: tracker.New(func(types.NamespacedName) {}, 0), + configAcc: &reconcilersource.EmptyVarsGenerator{}, + kubeClientSet: fakekubeclient.Get(ctx), + tracker: tracker.New(func(types.NamespacedName) {}, 0), + serviceAccountLister: listers.GetServiceAccountLister(), } r.sinkResolver = resolver.NewURIResolverFromTracker(ctx, tracker.New(func(types.NamespacedName) {}, 0)) @@ -561,3 +685,22 @@ func patchFinalizers(name, namespace string) clientgotesting.PatchActionImpl { action.Patch = []byte(patch) return action } + +func makePingSourceOIDCServiceAccount() *corev1.ServiceAccount { + return auth.GetOIDCServiceAccountForResource(sourcesv1.SchemeGroupVersion.WithKind("PingSource"), metav1.ObjectMeta{ + Name: sourceName, + Namespace: testNS, + UID: sourceUID, + }) +} + +func makePingSourceOIDCServiceAccountWithoutOwnerRef() *corev1.ServiceAccount { + sa := auth.GetOIDCServiceAccountForResource(sourcesv1.SchemeGroupVersion.WithKind("PingSource"), metav1.ObjectMeta{ + Name: sourceName, + Namespace: testNS, + UID: sourceUID, + }) + sa.OwnerReferences = nil + + return sa +} diff --git a/pkg/reconciler/testing/v1/pingsource.go b/pkg/reconciler/testing/v1/pingsource.go index 86ae5b329f4..e930ed46027 100644 --- a/pkg/reconciler/testing/v1/pingsource.go +++ b/pkg/reconciler/testing/v1/pingsource.go @@ -18,8 +18,11 @@ package testing import ( "context" + "fmt" "time" + "knative.dev/eventing/pkg/apis/feature" + "knative.dev/eventing/pkg/reconciler/testing" duckv1 "knative.dev/pkg/apis/duck/v1" @@ -107,3 +110,31 @@ func WithPingSourceDeleted(c *v1.PingSource) { t := metav1.NewTime(time.Unix(1e9, 0)) c.SetDeletionTimestamp(&t) } + +func WithPingSourceOIDCIdentityCreatedSucceeded() PingSourceOption { + return func(c *v1.PingSource) { + c.Status.MarkOIDCIdentityCreatedSucceeded() + } +} + +func WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled() PingSourceOption { + return func(c *v1.PingSource) { + c.Status.MarkOIDCIdentityCreatedSucceededWithReason(fmt.Sprintf("%s feature disabled", feature.OIDCAuthentication), "") + } +} + +func WithPingSourceOIDCIdentityCreatedFailed(reason, message string) PingSourceOption { + return func(c *v1.PingSource) { + c.Status.MarkOIDCIdentityCreatedFailed(reason, message) + } +} + +func WithPingSourceOIDCServiceAccountName(name string) PingSourceOption { + return func(c *v1.PingSource) { + if c.Status.Auth == nil { + c.Status.Auth = &duckv1.AuthStatus{} + } + + c.Status.Auth.ServiceAccountName = &name + } +}