diff --git a/pkg/apis/flows/v1/sequence_lifecycle.go b/pkg/apis/flows/v1/sequence_lifecycle.go index bc6559edacc..441566e2376 100644 --- a/pkg/apis/flows/v1/sequence_lifecycle.go +++ b/pkg/apis/flows/v1/sequence_lifecycle.go @@ -28,7 +28,8 @@ import ( duckv1 "knative.dev/pkg/apis/duck/v1" ) -var sCondSet = apis.NewLivingConditionSet(SequenceConditionReady, SequenceConditionChannelsReady, SequenceConditionSubscriptionsReady, SequenceConditionAddressable) +var sCondSet = apis.NewLivingConditionSet(SequenceConditionReady, SequenceConditionChannelsReady, SequenceConditionSubscriptionsReady, SequenceConditionAddressable, + SequenceConditionOIDCIdentityCreated) const ( // SequenceConditionReady has status True when all subconditions below have been set to True. @@ -45,6 +46,10 @@ const ( // SequenceConditionAddressable has status true when this Sequence meets // the Addressable contract and has a non-empty hostname. SequenceConditionAddressable apis.ConditionType = "Addressable" + + // SequenceConditionOIDCIdentityCreated has status True when the OIDCIdentity has been created. + // This condition is only relevant if the OIDC feature is enabled. + SequenceConditionOIDCIdentityCreated apis.ConditionType = "OIDCIdentityCreated" ) // GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface. @@ -190,3 +195,23 @@ func (ss *SequenceStatus) setAddress(address *duckv1.Addressable) { sCondSet.Manage(ss).MarkTrue(SequenceConditionAddressable) } } + +// MarkOIDCIdentityCreatedSucceeded marks the OIDCIdentityCreated condition as true. +func (ss *SequenceStatus) MarkOIDCIdentityCreatedSucceeded() { + sCondSet.Manage(ss).MarkTrue(SequenceConditionOIDCIdentityCreated) +} + +// MarkOIDCIdentityCreatedSucceededWithReason marks the OIDCIdentityCreated condition as true with the given reason. +func (ss *SequenceStatus) MarkOIDCIdentityCreatedSucceededWithReason(reason, messageFormat string, messageA ...interface{}) { + sCondSet.Manage(ss).MarkTrueWithReason(SequenceConditionOIDCIdentityCreated, reason, messageFormat, messageA...) +} + +// MarkOIDCIdentityCreatedFailed marks the OIDCIdentityCreated condition as false with the given reason. +func (ss *SequenceStatus) MarkOIDCIdentityCreatedFailed(reason, messageFormat string, messageA ...interface{}) { + sCondSet.Manage(ss).MarkFalse(SequenceConditionOIDCIdentityCreated, reason, messageFormat, messageA...) +} + +// MarkOIDCIdentityCreatedUnknown marks the OIDCIdentityCreated condition as unknown with the given reason. +func (ss *SequenceStatus) MarkOIDCIdentityCreatedUnknown(reason, messageFormat string, messageA ...interface{}) { + sCondSet.Manage(ss).MarkUnknown(SequenceConditionOIDCIdentityCreated, reason, messageFormat, messageA...) +} diff --git a/pkg/apis/flows/v1/sequence_lifecycle_test.go b/pkg/apis/flows/v1/sequence_lifecycle_test.go index fd12f77368d..92b4d154b85 100644 --- a/pkg/apis/flows/v1/sequence_lifecycle_test.go +++ b/pkg/apis/flows/v1/sequence_lifecycle_test.go @@ -139,6 +139,9 @@ func TestSequenceInitializeConditions(t *testing.T) { }, { Type: SequenceConditionChannelsReady, Status: corev1.ConditionUnknown, + }, { + Type: SequenceConditionOIDCIdentityCreated, + Status: corev1.ConditionUnknown, }, { Type: SequenceConditionReady, Status: corev1.ConditionUnknown, @@ -166,6 +169,9 @@ func TestSequenceInitializeConditions(t *testing.T) { }, { Type: SequenceConditionChannelsReady, Status: corev1.ConditionFalse, + }, { + Type: SequenceConditionOIDCIdentityCreated, + Status: corev1.ConditionUnknown, }, { Type: SequenceConditionReady, Status: corev1.ConditionUnknown, @@ -193,6 +199,9 @@ func TestSequenceInitializeConditions(t *testing.T) { }, { Type: SequenceConditionChannelsReady, Status: corev1.ConditionUnknown, + }, { + Type: SequenceConditionOIDCIdentityCreated, + Status: corev1.ConditionUnknown, }, { Type: SequenceConditionReady, Status: corev1.ConditionUnknown, @@ -311,45 +320,59 @@ func TestSequencePropagateChannelStatuses(t *testing.T) { func TestSequenceReady(t *testing.T) { tests := []struct { - name string - subs []*messagingv1.Subscription - channels []*eventingduckv1.Channelable - want bool + name string + subs []*messagingv1.Subscription + channels []*eventingduckv1.Channelable + oidcSACreated bool + want bool }{{ - name: "empty", - subs: []*messagingv1.Subscription{}, - channels: []*eventingduckv1.Channelable{}, - want: false, + name: "empty", + subs: []*messagingv1.Subscription{}, + channels: []*eventingduckv1.Channelable{}, + oidcSACreated: false, + want: false, }, { - name: "one channelable not ready, one subscription ready", - channels: []*eventingduckv1.Channelable{getChannelable(false)}, - subs: []*messagingv1.Subscription{getSubscription("sub0", true)}, - want: false, + name: "one channelable not ready, one subscription ready", + channels: []*eventingduckv1.Channelable{getChannelable(false)}, + subs: []*messagingv1.Subscription{getSubscription("sub0", true)}, + oidcSACreated: false, + want: false, }, { - name: "one channelable ready, one subscription not ready", - channels: []*eventingduckv1.Channelable{getChannelable(true)}, - subs: []*messagingv1.Subscription{getSubscription("sub0", false)}, - want: false, + name: "one channelable ready, one subscription not ready", + channels: []*eventingduckv1.Channelable{getChannelable(true)}, + subs: []*messagingv1.Subscription{getSubscription("sub0", false)}, + oidcSACreated: false, + want: false, }, { - name: "one channelable ready, one subscription ready", - channels: []*eventingduckv1.Channelable{getChannelable(true)}, - subs: []*messagingv1.Subscription{getSubscription("sub0", true)}, - want: true, + name: "one channelable ready, one subscription ready, oidc SA created", + channels: []*eventingduckv1.Channelable{getChannelable(true)}, + subs: []*messagingv1.Subscription{getSubscription("sub0", true)}, + oidcSACreated: true, + want: true, }, { - name: "one channelable ready, one not, two subscriptions ready", - channels: []*eventingduckv1.Channelable{getChannelable(true), getChannelable(false)}, - subs: []*messagingv1.Subscription{getSubscription("sub0", true), getSubscription("sub1", true)}, - want: false, + name: "one channelable ready, one not, two subscriptions ready", + channels: []*eventingduckv1.Channelable{getChannelable(true), getChannelable(false)}, + subs: []*messagingv1.Subscription{getSubscription("sub0", true), getSubscription("sub1", true)}, + oidcSACreated: false, + want: false, }, { - name: "two channelables ready, one subscription ready, one not", - channels: []*eventingduckv1.Channelable{getChannelable(true), getChannelable(true)}, - subs: []*messagingv1.Subscription{getSubscription("sub0", true), getSubscription("sub1", false)}, - want: false, + name: "two channelables ready, one subscription ready, one not", + channels: []*eventingduckv1.Channelable{getChannelable(true), getChannelable(true)}, + subs: []*messagingv1.Subscription{getSubscription("sub0", true), getSubscription("sub1", false)}, + oidcSACreated: false, + want: false, }, { - name: "two channelables ready, two subscriptions ready", - channels: []*eventingduckv1.Channelable{getChannelable(true), getChannelable(true)}, - subs: []*messagingv1.Subscription{getSubscription("sub0", true), getSubscription("sub1", true)}, - want: true, + name: "two channelables ready, two subscriptions ready, oidc SA not created", + channels: []*eventingduckv1.Channelable{getChannelable(true), getChannelable(true)}, + subs: []*messagingv1.Subscription{getSubscription("sub0", true), getSubscription("sub1", true)}, + oidcSACreated: false, + want: false, + }, { + name: "two channelables ready, two subscriptions ready, oidc SA created", + channels: []*eventingduckv1.Channelable{getChannelable(true), getChannelable(true)}, + subs: []*messagingv1.Subscription{getSubscription("sub0", true), getSubscription("sub1", true)}, + oidcSACreated: true, + want: true, }} for _, test := range tests { @@ -357,6 +380,10 @@ func TestSequenceReady(t *testing.T) { ps := SequenceStatus{} ps.PropagateChannelStatuses(test.channels) ps.PropagateSubscriptionStatuses(test.subs) + if test.oidcSACreated { + ps.MarkOIDCIdentityCreatedSucceeded() + } + got := ps.IsReady() want := test.want if want != got { diff --git a/pkg/reconciler/sequence/controller.go b/pkg/reconciler/sequence/controller.go index e0d7abed614..6d8a8fe71f6 100644 --- a/pkg/reconciler/sequence/controller.go +++ b/pkg/reconciler/sequence/controller.go @@ -20,16 +20,20 @@ import ( "context" "k8s.io/client-go/tools/cache" + "knative.dev/eventing/pkg/apis/feature" v1 "knative.dev/eventing/pkg/apis/flows/v1" "knative.dev/eventing/pkg/duck" "knative.dev/pkg/configmap" "knative.dev/pkg/controller" + "knative.dev/pkg/logging" eventingclient "knative.dev/eventing/pkg/client/injection/client" "knative.dev/eventing/pkg/client/injection/ducks/duck/v1/channelable" "knative.dev/eventing/pkg/client/injection/informers/flows/v1/sequence" "knative.dev/eventing/pkg/client/injection/informers/messaging/v1/subscription" sequencereconciler "knative.dev/eventing/pkg/client/injection/reconciler/flows/v1/sequence" + kubeclient "knative.dev/pkg/client/injection/kube/client" + serviceaccountinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/serviceaccount" "knative.dev/pkg/injection/clients/dynamicclient" ) @@ -42,14 +46,34 @@ func NewController( sequenceInformer := sequence.Get(ctx) subscriptionInformer := subscription.Get(ctx) + serviceaccountInformer := serviceaccountinformer.Get(ctx) + + 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) r := &Reconciler{ - sequenceLister: sequenceInformer.Lister(), - subscriptionLister: subscriptionInformer.Lister(), - dynamicClientSet: dynamicclient.Get(ctx), - eventingClientSet: eventingclient.Get(ctx), + sequenceLister: sequenceInformer.Lister(), + subscriptionLister: subscriptionInformer.Lister(), + dynamicClientSet: dynamicclient.Get(ctx), + eventingClientSet: eventingclient.Get(ctx), + serviceAccountLister: serviceaccountInformer.Lister(), + kubeclient: kubeclient.Get(ctx), + } + + impl := sequencereconciler.NewImpl(ctx, r, func(impl *controller.Impl) controller.Options { + return controller.Options{ + ConfigStore: featureStore, + } + }) + + globalResync = func(_ interface{}) { + impl.GlobalResync(sequenceInformer.Informer()) } - impl := sequencereconciler.NewImpl(ctx, r) r.channelableTracker = duck.NewListableTrackerFromTracker(ctx, channelable.Get, impl.Tracker) sequenceInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) @@ -61,5 +85,11 @@ func NewController( Handler: controller.HandleAll(impl.EnqueueControllerOf), }) + // Reconcile Sequence when the OIDC service account changes + serviceaccountInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ + FilterFunc: controller.FilterController(&v1.Sequence{}), + Handler: controller.HandleAll(impl.EnqueueControllerOf), + }) + return impl } diff --git a/pkg/reconciler/sequence/controller_test.go b/pkg/reconciler/sequence/controller_test.go index 18faa1dd97b..2e93479d5ba 100644 --- a/pkg/reconciler/sequence/controller_test.go +++ b/pkg/reconciler/sequence/controller_test.go @@ -19,19 +19,28 @@ package sequence import ( "testing" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "knative.dev/pkg/configmap" . "knative.dev/pkg/reconciler/testing" // Fake injection informers + "knative.dev/eventing/pkg/apis/feature" _ "knative.dev/eventing/pkg/client/injection/ducks/duck/v1/channelable/fake" _ "knative.dev/eventing/pkg/client/injection/informers/flows/v1/sequence/fake" _ "knative.dev/eventing/pkg/client/injection/informers/messaging/v1/subscription/fake" + _ "knative.dev/pkg/client/injection/kube/informers/core/v1/serviceaccount/fake" ) func TestNew(t *testing.T) { ctx, _ := SetupFakeContext(t) - c := NewController(ctx, configmap.NewStaticWatcher()) + c := NewController(ctx, configmap.NewStaticWatcher( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: feature.FlagsConfigName, + }, + })) if c == nil { t.Fatal("Expected NewController to return a non-nil value") diff --git a/pkg/reconciler/sequence/sequence.go b/pkg/reconciler/sequence/sequence.go index d37dd3b793a..8cc1ad8e8e9 100644 --- a/pkg/reconciler/sequence/sequence.go +++ b/pkg/reconciler/sequence/sequence.go @@ -29,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" "knative.dev/pkg/kmeta" duckapis "knative.dev/pkg/apis/duck" @@ -36,15 +37,19 @@ import ( pkgreconciler "knative.dev/pkg/reconciler" eventingduckv1 "knative.dev/eventing/pkg/apis/duck/v1" + "knative.dev/eventing/pkg/apis/feature" v1 "knative.dev/eventing/pkg/apis/flows/v1" messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1" + "knative.dev/eventing/pkg/auth" clientset "knative.dev/eventing/pkg/client/clientset/versioned" sequencereconciler "knative.dev/eventing/pkg/client/injection/reconciler/flows/v1/sequence" listers "knative.dev/eventing/pkg/client/listers/flows/v1" messaginglisters "knative.dev/eventing/pkg/client/listers/messaging/v1" "knative.dev/eventing/pkg/duck" + corev1listers "k8s.io/client-go/listers/core/v1" "knative.dev/eventing/pkg/reconciler/sequence/resources" + duckv1 "knative.dev/pkg/apis/duck/v1" ) type Reconciler struct { @@ -57,7 +62,9 @@ type Reconciler struct { eventingClientSet clientset.Interface // dynamicClientSet allows us to configure pluggable Build objects - dynamicClientSet dynamic.Interface + dynamicClientSet dynamic.Interface + serviceAccountLister corev1listers.ServiceAccountLister + kubeclient kubernetes.Interface } // Check that our Reconciler implements sequencereconciler.Interface @@ -122,6 +129,23 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, s *v1.Sequence) pkgrecon return err } + featureFlags := feature.FromContext(ctx) + if featureFlags.IsOIDCAuthentication() { + saName := auth.GetOIDCServiceAccountNameForResource(v1.SchemeGroupVersion.WithKind("Sequence"), s.ObjectMeta) + s.Status.Auth = &duckv1.AuthStatus{ + ServiceAccountName: &saName, + } + + if err := auth.EnsureOIDCServiceAccountExistsForResource(ctx, r.serviceAccountLister, r.kubeclient, v1.SchemeGroupVersion.WithKind("Sequence"), s.ObjectMeta); err != nil { + s.Status.MarkOIDCIdentityCreatedFailed("Unable to resolve service account for OIDC authentication", "%v", err) + return err + } + s.Status.MarkOIDCIdentityCreatedSucceeded() + } else { + s.Status.Auth = nil + s.Status.MarkOIDCIdentityCreatedSucceededWithReason(fmt.Sprintf("%s feature disabled", feature.OIDCAuthentication), "") + } + return r.removeUnwantedSubscriptions(ctx, s, subs) } diff --git a/pkg/reconciler/sequence/sequence_test.go b/pkg/reconciler/sequence/sequence_test.go index b149a72ea01..0e4f3aa0ee3 100644 --- a/pkg/reconciler/sequence/sequence_test.go +++ b/pkg/reconciler/sequence/sequence_test.go @@ -28,8 +28,10 @@ import ( "k8s.io/apimachinery/pkg/types" clientgotesting "k8s.io/client-go/testing" + "knative.dev/eventing/pkg/apis/feature" v1 "knative.dev/eventing/pkg/apis/flows/v1" messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1" + "knative.dev/eventing/pkg/auth" fakeeventingclient "knative.dev/eventing/pkg/client/injection/client/fake" "knative.dev/pkg/apis" @@ -50,6 +52,8 @@ import ( . "knative.dev/pkg/reconciler/testing" . "knative.dev/eventing/pkg/reconciler/testing/v1" + + fakekubeclient "knative.dev/pkg/client/injection/kube/client/fake" ) const ( @@ -186,6 +190,7 @@ func TestAllCases(t *testing.T) { WithInitSequenceConditions, WithSequenceChannelTemplateSpec(imc), WithSequenceSteps([]v1.SequenceStep{{Destination: createDestination(0)}}), + WithSequenceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(), WithSequenceChannelsNotReady("ChannelsNotReady", "Channels are not ready yet, or there are none"), WithSequenceAddressableNotReady("emptyAddress", "addressable is nil"), WithSequenceSubscriptionsNotReady("SubscriptionsNotReady", "Subscriptions are not ready yet, or there are none"), @@ -460,7 +465,8 @@ func TestAllCases(t *testing.T) { Message: "Subscription does not have Ready condition", }, }, - })), + }), + WithSequenceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled()), }}, }, { Name: "threestep", @@ -596,7 +602,8 @@ func TestAllCases(t *testing.T) { Message: "Subscription does not have Ready condition", }, }, - })), + }), + WithSequenceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled()), }}, }, { Name: "threestepwithdeliveryontwo", @@ -732,7 +739,8 @@ func TestAllCases(t *testing.T) { Message: "Subscription does not have Ready condition", }, }, - })), + }), + WithSequenceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled()), }}, }, { Name: "threestepwithreply", @@ -871,7 +879,8 @@ func TestAllCases(t *testing.T) { Message: "Subscription does not have Ready condition", }, }, - })), + }), + WithSequenceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled()), }}, }, { Name: "sequenceupdatesubscription", @@ -938,7 +947,8 @@ func TestAllCases(t *testing.T) { Message: "Subscription does not have Ready condition", }, }, - })), + }), + WithSequenceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled()), }}, }, { Name: "sequenceupdate-remove-step", @@ -1060,7 +1070,8 @@ func TestAllCases(t *testing.T) { Message: "Subscription does not have Ready condition", }, }, - })), + }), + WithSequenceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled()), }}, }, { Name: "sequenceupdate-remove-step-subscription-removal-fails", @@ -1189,7 +1200,8 @@ func TestAllCases(t *testing.T) { Message: "Subscription does not have Ready condition", }, }, - })), + }), + WithSequenceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled()), }}, }, { Name: "sequenceupdate-remove-step-channel-removal-fails", @@ -1314,21 +1326,167 @@ func TestAllCases(t *testing.T) { }, })), }}, - }, - } + }, { + Name: "OIDC: creates OIDC service account", + Key: pKey, + Ctx: feature.ToContext(context.Background(), feature.Flags{ + feature.OIDCAuthentication: feature.Enabled, + }), + Objects: []runtime.Object{ + NewSequence(sequenceName, testNS, + WithInitSequenceConditions, + WithSequenceChannelTemplateSpec(imc), + WithSequenceSteps([]v1.SequenceStep{{Destination: createDestination(0)}})), + createChannel(sequenceName, 0), + resources.NewSubscription(0, + NewSequence(sequenceName, testNS, + WithSequenceChannelTemplateSpec(imc), + WithSequenceSteps([]v1.SequenceStep{{Destination: createDestination(0)}})))}, + WantErr: false, + WantCreates: []runtime.Object{ + makeSequenceOIDCServiceAccount(), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: NewSequence(sequenceName, testNS, + WithInitSequenceConditions, + WithSequenceChannelTemplateSpec(imc), + WithSequenceSteps([]v1.SequenceStep{{Destination: createDestination(0)}}), + WithSequenceChannelsNotReady("ChannelsNotReady", "Channels are not ready yet, or there are none"), + WithSequenceAddressableNotReady("emptyAddress", "addressable is nil"), + WithSequenceSubscriptionsNotReady("SubscriptionsNotReady", "Subscriptions are not ready yet, or there are none"), + WithSequenceChannelStatuses([]v1.SequenceChannelStatus{ + { + Channel: corev1.ObjectReference{ + APIVersion: "messaging.knative.dev/v1", + Kind: "InMemoryChannel", + Name: resources.SequenceChannelName(sequenceName, 0), + Namespace: testNS, + }, + ReadyCondition: apis.Condition{ + Type: apis.ConditionReady, + Status: corev1.ConditionUnknown, + Reason: "NoReady", + Message: "Channel does not have Ready condition", + }, + }, + }), + WithSequenceSubscriptionStatuses([]v1.SequenceSubscriptionStatus{ + { + Subscription: corev1.ObjectReference{ + APIVersion: "messaging.knative.dev/v1", + Kind: "Subscription", + Name: resources.SequenceSubscriptionName(sequenceName, 0), + Namespace: testNS, + }, + ReadyCondition: apis.Condition{ + Type: apis.ConditionReady, + Status: corev1.ConditionUnknown, + Reason: "NoReady", + Message: "Subscription does not have Ready condition", + }, + }, + }), + WithSequenceOIDCIdentityCreatedSucceeded(), + WithSequenceOIDCServiceAccountName(makeSequenceOIDCServiceAccount().Name)), + }}, + }, { + Name: "OIDC: Sequence not ready on invalid OIDC service account", + Key: pKey, + Ctx: feature.ToContext(context.Background(), feature.Flags{ + feature.OIDCAuthentication: feature.Enabled, + }), + Objects: []runtime.Object{ + makeSequenceOIDCServiceAccountWithoutOwnerRef(), + NewSequence(sequenceName, testNS, + WithInitSequenceConditions, + WithSequenceChannelTemplateSpec(imc), + WithSequenceSteps([]v1.SequenceStep{{Destination: createDestination(0)}})), + createChannel(sequenceName, 0), + resources.NewSubscription(0, + NewSequence(sequenceName, testNS, + WithSequenceChannelTemplateSpec(imc), + WithSequenceSteps([]v1.SequenceStep{{Destination: createDestination(0)}})))}, + WantErr: true, + WantCreates: []runtime.Object{}, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: NewSequence(sequenceName, testNS, + WithInitSequenceConditions, + WithSequenceChannelTemplateSpec(imc), + WithSequenceSteps([]v1.SequenceStep{{Destination: createDestination(0)}}), + WithSequenceChannelsNotReady("ChannelsNotReady", "Channels are not ready yet, or there are none"), + WithSequenceAddressableNotReady("emptyAddress", "addressable is nil"), + WithSequenceSubscriptionsNotReady("SubscriptionsNotReady", "Subscriptions are not ready yet, or there are none"), + WithSequenceChannelStatuses([]v1.SequenceChannelStatus{ + { + Channel: corev1.ObjectReference{ + APIVersion: "messaging.knative.dev/v1", + Kind: "InMemoryChannel", + Name: resources.SequenceChannelName(sequenceName, 0), + Namespace: testNS, + }, + ReadyCondition: apis.Condition{ + Type: apis.ConditionReady, + Status: corev1.ConditionUnknown, + Reason: "NoReady", + Message: "Channel does not have Ready condition", + }, + }, + }), + WithSequenceSubscriptionStatuses([]v1.SequenceSubscriptionStatus{ + { + Subscription: corev1.ObjectReference{ + APIVersion: "messaging.knative.dev/v1", + Kind: "Subscription", + Name: resources.SequenceSubscriptionName(sequenceName, 0), + Namespace: testNS, + }, + ReadyCondition: apis.Condition{ + Type: apis.ConditionReady, + Status: corev1.ConditionUnknown, + Reason: "NoReady", + Message: "Subscription does not have Ready condition", + }, + }, + }), + WithSequenceOIDCIdentityCreatedFailed("Unable to resolve service account for OIDC authentication", fmt.Sprintf("service account %s not owned by Sequence %s", makeSequenceOIDCServiceAccountWithoutOwnerRef().Name, sequenceName)), + WithSequenceOIDCServiceAccountName(makeSequenceOIDCServiceAccountWithoutOwnerRef().Name)), + }}, + WantEvents: []string{ + Eventf(corev1.EventTypeWarning, "InternalError", fmt.Sprintf("service account %s not owned by Sequence %s", makeSequenceOIDCServiceAccountWithoutOwnerRef().Name, sequenceName)), + }, + }} logger := logtesting.TestLogger(t) table.Test(t, MakeFactory(func(ctx context.Context, listers *Listers, cmw configmap.Watcher) controller.Reconciler { ctx = channelable.WithDuck(ctx) r := &Reconciler{ - sequenceLister: listers.GetSequenceLister(), - channelableTracker: duck.NewListableTrackerFromTracker(ctx, channelable.Get, tracker.New(func(types.NamespacedName) {}, 0)), - subscriptionLister: listers.GetSubscriptionLister(), - eventingClientSet: fakeeventingclient.Get(ctx), - dynamicClientSet: fakedynamicclient.Get(ctx), + sequenceLister: listers.GetSequenceLister(), + channelableTracker: duck.NewListableTrackerFromTracker(ctx, channelable.Get, tracker.New(func(types.NamespacedName) {}, 0)), + subscriptionLister: listers.GetSubscriptionLister(), + eventingClientSet: fakeeventingclient.Get(ctx), + dynamicClientSet: fakedynamicclient.Get(ctx), + kubeclient: fakekubeclient.Get(ctx), + serviceAccountLister: listers.GetServiceAccountLister(), } return sequence.NewReconciler(ctx, logging.FromContext(ctx), fakeeventingclient.Get(ctx), listers.GetSequenceLister(), controller.GetEventRecorder(ctx), r) }, false, logger)) } + +func makeSequenceOIDCServiceAccount() *corev1.ServiceAccount { + return auth.GetOIDCServiceAccountForResource(v1.SchemeGroupVersion.WithKind("Sequence"), metav1.ObjectMeta{ + Name: sequenceName, + Namespace: testNS, + }) +} + +func makeSequenceOIDCServiceAccountWithoutOwnerRef() *corev1.ServiceAccount { + sa := auth.GetOIDCServiceAccountForResource(v1.SchemeGroupVersion.WithKind("Sequence"), metav1.ObjectMeta{ + Name: sequenceName, + Namespace: testNS, + }) + sa.OwnerReferences = nil + + return sa +} diff --git a/pkg/reconciler/testing/v1/sequence.go b/pkg/reconciler/testing/v1/sequence.go index d54e2b5f113..64232984124 100644 --- a/pkg/reconciler/testing/v1/sequence.go +++ b/pkg/reconciler/testing/v1/sequence.go @@ -18,9 +18,11 @@ package testing import ( "context" + "fmt" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/eventing/pkg/apis/feature" flowsv1 "knative.dev/eventing/pkg/apis/flows/v1" messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1" duckv1 "knative.dev/pkg/apis/duck/v1" @@ -113,3 +115,31 @@ func WithSequenceAddressableNotReady(reason, message string) SequenceOption { p.Status.MarkAddressableNotReady(reason, message) } } + +func WithSequenceOIDCIdentityCreatedSucceeded() SequenceOption { + return func(s *flowsv1.Sequence) { + s.Status.MarkOIDCIdentityCreatedSucceeded() + } +} + +func WithSequenceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled() SequenceOption { + return func(s *flowsv1.Sequence) { + s.Status.MarkOIDCIdentityCreatedSucceededWithReason(fmt.Sprintf("%s feature disabled", feature.OIDCAuthentication), "") + } +} + +func WithSequenceOIDCIdentityCreatedFailed(reason, message string) SequenceOption { + return func(s *flowsv1.Sequence) { + s.Status.MarkOIDCIdentityCreatedFailed(reason, message) + } +} + +func WithSequenceOIDCServiceAccountName(name string) SequenceOption { + return func(s *flowsv1.Sequence) { + if s.Status.Auth == nil { + s.Status.Auth = &duckv1.AuthStatus{} + } + + s.Status.Auth.ServiceAccountName = &name + } +}