diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 79fd5588fed..5b01605a5da 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -34,6 +34,7 @@ import ( "knative.dev/eventing/pkg/apis/sinks" "knative.dev/eventing/pkg/auth" "knative.dev/eventing/pkg/eventingtls" + "knative.dev/eventing/pkg/reconciler/eventpolicy" "knative.dev/eventing/pkg/reconciler/jobsink" "knative.dev/eventing/pkg/reconciler/apiserversource" @@ -93,6 +94,7 @@ func main() { // Eventing eventtype.NewController, + eventpolicy.NewController, // Flows parallel.NewController, diff --git a/pkg/apis/eventing/v1alpha1/eventpolicy_lifecycle.go b/pkg/apis/eventing/v1alpha1/eventpolicy_lifecycle.go index 30c8575eac9..a74431f0d78 100644 --- a/pkg/apis/eventing/v1alpha1/eventpolicy_lifecycle.go +++ b/pkg/apis/eventing/v1alpha1/eventpolicy_lifecycle.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Knative Authors +Copyright 2024 The Knative Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,10 +20,12 @@ import ( "knative.dev/pkg/apis" ) -var eventPolicyCondSet = apis.NewLivingConditionSet() +var eventPolicyCondSet = apis.NewLivingConditionSet(EventPolicyConditionAuthenticationEnabled, EventPolicyConditionSubjectsResolved) const ( - EventPolicyConditionReady = apis.ConditionReady + EventPolicyConditionReady = apis.ConditionReady + EventPolicyConditionAuthenticationEnabled apis.ConditionType = "AuthenticationEnabled" + EventPolicyConditionSubjectsResolved apis.ConditionType = "SubjectsResolved" ) // GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface. @@ -32,21 +34,41 @@ func (*EventPolicy) GetConditionSet() apis.ConditionSet { } // GetCondition returns the condition currently associated with the given type, or nil. -func (et *EventPolicyStatus) GetCondition(t apis.ConditionType) *apis.Condition { - return eventPolicyCondSet.Manage(et).GetCondition(t) +func (ep *EventPolicyStatus) GetCondition(t apis.ConditionType) *apis.Condition { + return eventPolicyCondSet.Manage(ep).GetCondition(t) } // IsReady returns true if the resource is ready overall. -func (et *EventPolicyStatus) IsReady() bool { - return et.GetTopLevelCondition().IsTrue() +func (ep *EventPolicyStatus) IsReady() bool { + return ep.GetTopLevelCondition().IsTrue() } // GetTopLevelCondition returns the top level Condition. -func (et *EventPolicyStatus) GetTopLevelCondition() *apis.Condition { - return eventPolicyCondSet.Manage(et).GetTopLevelCondition() +func (ep *EventPolicyStatus) GetTopLevelCondition() *apis.Condition { + return eventPolicyCondSet.Manage(ep).GetTopLevelCondition() } // InitializeConditions sets relevant unset conditions to Unknown state. -func (et *EventPolicyStatus) InitializeConditions() { - eventPolicyCondSet.Manage(et).InitializeConditions() +func (ep *EventPolicyStatus) InitializeConditions() { + eventPolicyCondSet.Manage(ep).InitializeConditions() +} + +// MarkOIDCAuthenticationEnabled sets EventPolicyConditionAuthenticationEnabled condition to true. +func (ep *EventPolicyStatus) MarkOIDCAuthenticationEnabled() { + eventPolicyCondSet.Manage(ep).MarkTrue(EventPolicyConditionAuthenticationEnabled) +} + +// MarkOIDCAuthenticationDisabled sets EventPolicyConditionAuthenticationEnabled condition to false. +func (ep *EventPolicyStatus) MarkOIDCAuthenticationDisabled(reason, messageFormat string, messageA ...interface{}) { + eventPolicyCondSet.Manage(ep).MarkFalse(EventPolicyConditionAuthenticationEnabled, reason, messageFormat, messageA...) +} + +// MarkSubjectsResolved sets EventPolicyConditionSubjectsResolved condition to true. +func (ep *EventPolicyStatus) MarkSubjectsResolvedSucceeded() { + eventPolicyCondSet.Manage(ep).MarkTrue(EventPolicyConditionSubjectsResolved) +} + +// MarkSubjectsNotResolved sets EventPolicyConditionSubjectsResolved condition to false. +func (ep *EventPolicyStatus) MarkSubjectsResolvedFailed(reason, messageFormat string, messageA ...interface{}) { + eventPolicyCondSet.Manage(ep).MarkFalse(EventPolicyConditionSubjectsResolved, reason, messageFormat, messageA...) } diff --git a/pkg/apis/eventing/v1alpha1/eventpolicy_lifecycle_test.go b/pkg/apis/eventing/v1alpha1/eventpolicy_lifecycle_test.go index 1f18f054a1f..620c2b595df 100644 --- a/pkg/apis/eventing/v1alpha1/eventpolicy_lifecycle_test.go +++ b/pkg/apis/eventing/v1alpha1/eventpolicy_lifecycle_test.go @@ -49,12 +49,12 @@ func TestEventPolicyGetConditionSet(t *testing.T) { func TestEventPolicyGetCondition(t *testing.T) { tests := []struct { name string - ets *EventPolicyStatus + eps *EventPolicyStatus condQuery apis.ConditionType want *apis.Condition }{{ name: "single condition", - ets: &EventPolicyStatus{ + eps: &EventPolicyStatus{ Status: duckv1.Status{ Conditions: []apis.Condition{ eventPolicyConditionReady, @@ -67,7 +67,7 @@ func TestEventPolicyGetCondition(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got := test.ets.GetCondition(test.condQuery) + got := test.eps.GetCondition(test.condQuery) if diff := cmp.Diff(test.want, got); diff != "" { t.Error("unexpected condition (-want, +got) =", diff) } @@ -78,18 +78,27 @@ func TestEventPolicyGetCondition(t *testing.T) { func TestEventPolicyInitializeConditions(t *testing.T) { tests := []struct { name string - ets *EventPolicyStatus + eps *EventPolicyStatus want *EventPolicyStatus }{ { name: "empty", - ets: &EventPolicyStatus{}, + eps: &EventPolicyStatus{}, want: &EventPolicyStatus{ Status: duckv1.Status{ - Conditions: []apis.Condition{{ - Type: EventPolicyConditionReady, - Status: corev1.ConditionUnknown, - }, + Conditions: []apis.Condition{ + { + Type: EventPolicyConditionAuthenticationEnabled, + Status: corev1.ConditionUnknown, + }, + { + Type: EventPolicyConditionReady, + Status: corev1.ConditionUnknown, + }, + { + Type: EventPolicyConditionSubjectsResolved, + Status: corev1.ConditionUnknown, + }, }, }, }, @@ -98,10 +107,100 @@ func TestEventPolicyInitializeConditions(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - test.ets.InitializeConditions() - if diff := cmp.Diff(test.want, test.ets, ignoreAllButTypeAndStatus); diff != "" { + test.eps.InitializeConditions() + if diff := cmp.Diff(test.want, test.eps, ignoreAllButTypeAndStatus); diff != "" { t.Error("unexpected conditions (-want, +got) =", diff) } }) } } + +func TestEventPolicyReadyCondition(t *testing.T) { + tests := []struct { + name string + eps *EventPolicyStatus + markOIDCAuthenticationEnabled bool + markSubjectsResolvedSucceeded bool + wantReady bool + }{ + { + name: "Initially everything is Unknown, Auth&SubjectsResolved marked as true, EP should become Ready", + eps: &EventPolicyStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + {Type: EventPolicyConditionReady, Status: corev1.ConditionUnknown}, + {Type: EventPolicyConditionAuthenticationEnabled, Status: corev1.ConditionUnknown}, + {Type: EventPolicyConditionSubjectsResolved, Status: corev1.ConditionUnknown}, + }, + }, + }, + markOIDCAuthenticationEnabled: true, + markSubjectsResolvedSucceeded: true, + wantReady: true, + }, + { + name: "Initially everything is True, Auth&SubjectsResolved stay true, EP should stay Ready", + eps: &EventPolicyStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + {Type: EventPolicyConditionReady, Status: corev1.ConditionTrue}, + {Type: EventPolicyConditionAuthenticationEnabled, Status: corev1.ConditionTrue}, + {Type: EventPolicyConditionSubjectsResolved, Status: corev1.ConditionTrue}, + }, + }, + }, + markOIDCAuthenticationEnabled: true, + markSubjectsResolvedSucceeded: true, + wantReady: true, + }, + { + name: "Initially everything is True, then AuthenticationEnabled marked as False, EP should become NotReady", + eps: &EventPolicyStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + {Type: EventPolicyConditionReady, Status: corev1.ConditionTrue}, + {Type: EventPolicyConditionAuthenticationEnabled, Status: corev1.ConditionTrue}, + {Type: EventPolicyConditionSubjectsResolved, Status: corev1.ConditionTrue}, + }, + }, + }, + markOIDCAuthenticationEnabled: false, + markSubjectsResolvedSucceeded: true, + wantReady: false, + }, + { + name: "Initially everything is True, then SubjectsResolved marked as False, EP should become NotReady", + eps: &EventPolicyStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{ + {Type: EventPolicyConditionReady, Status: corev1.ConditionTrue}, + {Type: EventPolicyConditionAuthenticationEnabled, Status: corev1.ConditionTrue}, + {Type: EventPolicyConditionSubjectsResolved, Status: corev1.ConditionTrue}, + }, + }, + }, + markOIDCAuthenticationEnabled: true, + markSubjectsResolvedSucceeded: false, + wantReady: false, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.markOIDCAuthenticationEnabled { + test.eps.MarkOIDCAuthenticationEnabled() + } else { + test.eps.MarkOIDCAuthenticationDisabled("OIDCAuthenticationDisabled", "") + } + if test.markSubjectsResolvedSucceeded { + test.eps.MarkSubjectsResolvedSucceeded() + } else { + test.eps.MarkSubjectsResolvedFailed("SubjectsNotResolved", "") + } + ep := EventPolicy{Status: *test.eps} + got := ep.GetConditionSet().Manage(test.eps).IsHappy() + if test.wantReady != got { + t.Errorf("unexpected readiness: want %v, got %v", test.wantReady, got) + } + }) + } +} diff --git a/pkg/reconciler/channel/channel_test.go b/pkg/reconciler/channel/channel_test.go index 783abe359a2..c1796379bc1 100644 --- a/pkg/reconciler/channel/channel_test.go +++ b/pkg/reconciler/channel/channel_test.go @@ -400,7 +400,7 @@ func TestReconcile(t *testing.T) { WithInMemoryChannelDLSUnknown(), WithInMemoryChannelEventPoliciesReady()), NewEventPolicy(unreadyEventPolicyName, testNS, - WithUnreadyEventPolicyCondition, + WithUnreadyEventPolicyCondition("", ""), WithEventPolicyToRef(channelV1GVK, channelName), ), NewEventPolicy(fmt.Sprintf("%s-%s", unreadyEventPolicyName, channelName), testNS, @@ -453,7 +453,7 @@ func TestReconcile(t *testing.T) { WithEventPolicyToRef(channelV1GVK, channelName), ), NewEventPolicy(unreadyEventPolicyName, testNS, - WithUnreadyEventPolicyCondition, + WithUnreadyEventPolicyCondition("", ""), WithEventPolicyToRef(channelV1GVK, channelName), ), NewEventPolicy(fmt.Sprintf("%s-%s", readyEventPolicyName, channelName), testNS, diff --git a/pkg/reconciler/eventpolicy/controller.go b/pkg/reconciler/eventpolicy/controller.go new file mode 100644 index 00000000000..58b66b06022 --- /dev/null +++ b/pkg/reconciler/eventpolicy/controller.go @@ -0,0 +1,47 @@ +/* +Copyright 2024 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package eventpolicy + +import ( + "context" + + eventpolicyinformer "knative.dev/eventing/pkg/client/injection/informers/eventing/v1alpha1/eventpolicy" + eventpolicyreconciler "knative.dev/eventing/pkg/client/injection/reconciler/eventing/v1alpha1/eventpolicy" + "knative.dev/pkg/configmap" + "knative.dev/pkg/controller" + "knative.dev/pkg/resolver" +) + +// NewController initializes the controller and is called by the generated code +// Registers event handlers to enqueue events +func NewController( + ctx context.Context, + cmw configmap.Watcher, +) *controller.Impl { + // Access informers + eventPolicyInformer := eventpolicyinformer.Get(ctx) + + r := &Reconciler{} + impl := eventpolicyreconciler.NewImpl(ctx, r) + + r.authResolver = resolver.NewAuthenticatableResolverFromTracker(ctx, impl.Tracker) + + // Set up event handlers + eventPolicyInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) + + return impl +} diff --git a/pkg/reconciler/eventpolicy/controller_test.go b/pkg/reconciler/eventpolicy/controller_test.go new file mode 100644 index 00000000000..2ddea353dc3 --- /dev/null +++ b/pkg/reconciler/eventpolicy/controller_test.go @@ -0,0 +1,39 @@ +/* +Copyright 2024 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package eventpolicy + +import ( + "testing" + + "knative.dev/pkg/configmap" + + . "knative.dev/pkg/reconciler/testing" + + // Fake injection informers + _ "knative.dev/eventing/pkg/client/injection/informers/eventing/v1alpha1/eventpolicy/fake" + _ "knative.dev/pkg/client/injection/ducks/duck/v1/authstatus/fake" +) + +func TestNew(t *testing.T) { + ctx, _ := SetupFakeContext(t) + + c := NewController(ctx, configmap.NewStaticWatcher()) + + if c == nil { + t.Fatal("Expected NewController to return a non-nil value") + } +} diff --git a/pkg/reconciler/eventpolicy/eventpolicy.go b/pkg/reconciler/eventpolicy/eventpolicy.go new file mode 100644 index 00000000000..959181e1943 --- /dev/null +++ b/pkg/reconciler/eventpolicy/eventpolicy.go @@ -0,0 +1,55 @@ +/* +Copyright 2024 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package eventpolicy + +import ( + "context" + "fmt" + + "knative.dev/eventing/pkg/apis/eventing/v1alpha1" + "knative.dev/eventing/pkg/apis/feature" + "knative.dev/eventing/pkg/auth" + pkgreconciler "knative.dev/pkg/reconciler" + "knative.dev/pkg/resolver" +) + +type Reconciler struct { + authResolver *resolver.AuthenticatableResolver +} + +// ReconcileKind implements Interface.ReconcileKind. +// 1. Verify the Reference exists. +func (r *Reconciler) ReconcileKind(ctx context.Context, ep *v1alpha1.EventPolicy) pkgreconciler.Event { + featureFlags := feature.FromContext(ctx) + if featureFlags.IsOIDCAuthentication() { + ep.Status.MarkOIDCAuthenticationEnabled() + } else { + ep.Status.MarkOIDCAuthenticationDisabled("OIDCAuthenticationDisabled", "") + return nil + } + // We reconcile the status of the EventPolicy + // by looking at all .spec.from[].refs have subjects + // and accordingly set the eventpolicy status + subjects, err := auth.ResolveSubjects(r.authResolver, ep) + if err != nil { + ep.Status.MarkSubjectsResolvedFailed("SubjectsNotResolved", err.Error()) + return fmt.Errorf("failed to resolve .spec.from[].ref: %w", err) + } + ep.Status.MarkSubjectsResolvedSucceeded() + ep.Status.From = subjects + return nil +} diff --git a/pkg/reconciler/eventpolicy/eventpolicy_test.go b/pkg/reconciler/eventpolicy/eventpolicy_test.go new file mode 100644 index 00000000000..dcc7ab0ec44 --- /dev/null +++ b/pkg/reconciler/eventpolicy/eventpolicy_test.go @@ -0,0 +1,239 @@ +/* +Copyright 2024 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package eventpolicy + +import ( + "context" + "fmt" + "testing" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + clientgotesting "k8s.io/client-go/testing" + "knative.dev/eventing/pkg/apis/feature" + sourcesv1 "knative.dev/eventing/pkg/apis/sources/v1" + fakeeventingclient "knative.dev/eventing/pkg/client/injection/client/fake" + "knative.dev/eventing/pkg/client/injection/reconciler/eventing/v1alpha1/eventpolicy" + . "knative.dev/eventing/pkg/reconciler/testing/v1" + duckv1authstatus "knative.dev/pkg/client/injection/ducks/duck/v1/authstatus" + "knative.dev/pkg/configmap" + "knative.dev/pkg/controller" + logtesting "knative.dev/pkg/logging/testing" + . "knative.dev/pkg/reconciler/testing" + "knative.dev/pkg/resolver" + "knative.dev/pkg/tracker" +) + +const ( + testNS = "test-namespace" + eventPolicyName = "test-eventpolicy" + pingSourceName = "test-pingsource" + apiServerSourceName = "test-apiserversource" + serviceAccountname = "test-sa" +) + +var ( + pingSourceWithServiceAccount = NewPingSource(pingSourceName, testNS, WithPingSourceOIDCServiceAccountName(serviceAccountname)) + apiServerSourceWithServiceAccount = NewApiServerSource(apiServerSourceName, testNS, WithApiServerSourceOIDCServiceAccountName((serviceAccountname))) + pingSourceGVK = v1.GroupVersionKind(sourcesv1.SchemeGroupVersion.WithKind("PingSource")) + apiServerSourceGVK = v1.GroupVersionKind(sourcesv1.SchemeGroupVersion.WithKind("APIServerSource")) + SubjectsNotResolvedErrorMessage = fmt.Sprintf("could not resolve subjects from reference: could not resolve auth status: failed to get authenticatable %s/%s: failed to get object %s/%s: pingsources.sources.knative.dev \"%s\" not found", testNS, pingSourceName, testNS, pingSourceName, pingSourceName) + SubjectsNotResolvedEventMessage = fmt.Sprintf("Warning InternalError failed to resolve .spec.from[].ref: could not resolve subjects from reference: could not resolve auth status: failed to get authenticatable %s/%s: failed to get object %s/%s: pingsources.sources.knative.dev \"%s\" not found", testNS, pingSourceName, testNS, pingSourceName, pingSourceName) +) + +func TestReconcile(t *testing.T) { + table := TableTest{ + { + Name: "bad workqueue key", + // Make sure Reconcile handles bad keys. + Key: "too/many/parts", + }, + // test cases for authentication-oidc feature disabled + { + Name: "with oidc disabled, status set to NotReady", + Ctx: feature.ToContext(context.TODO(), feature.Flags{ + feature.OIDCAuthentication: feature.Disabled, + }), + Key: testNS + "/" + eventPolicyName, + Objects: []runtime.Object{ + NewEventPolicy(eventPolicyName, testNS, + WithInitEventPolicyConditions, + WithEventPolicyFrom(pingSourceGVK, pingSourceName, testNS), + ), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{ + { + Object: NewEventPolicy(eventPolicyName, testNS, + WithEventPolicyFrom(pingSourceGVK, pingSourceName, testNS), + WithEventPolicyAuthenticationDisabledCondition, + WithUnreadyEventPolicyCondition("OIDCAuthenticationDisabled", ""), + WithEventPolicySubjectsResolvedUnknown, + ), + }, + }, + WantErr: false, + }, + + // test cases for authentication-oidc feature enabled + { + Name: "subject not found, status set to NotReady", + Ctx: feature.ToContext(context.TODO(), feature.Flags{ + feature.OIDCAuthentication: feature.Enabled, + }), + Key: testNS + "/" + eventPolicyName, + Objects: []runtime.Object{ + NewEventPolicy(eventPolicyName, testNS, + WithInitEventPolicyConditions, + WithEventPolicyFrom(pingSourceGVK, pingSourceName, testNS), + ), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{ + { + Object: NewEventPolicy(eventPolicyName, testNS, + WithEventPolicyFrom(pingSourceGVK, pingSourceName, testNS), + WithEventPolicyAuthenticationEnabledCondition, + WithUnreadyEventPolicyCondition("SubjectsNotResolved", SubjectsNotResolvedErrorMessage), + WithEventPolicySubjectsResolvedFailed("SubjectsNotResolved", SubjectsNotResolvedErrorMessage), + ), + }, + }, + WantEvents: []string{SubjectsNotResolvedEventMessage}, + WantErr: true, + }, + { + Name: "subject found for pingsource, status set to Ready", + Ctx: feature.ToContext(context.TODO(), feature.Flags{ + feature.OIDCAuthentication: feature.Enabled, + }), + Key: testNS + "/" + eventPolicyName, + Objects: []runtime.Object{ + pingSourceWithServiceAccount, + NewEventPolicy(eventPolicyName, testNS, + WithInitEventPolicyConditions, + WithEventPolicyFrom(pingSourceGVK, pingSourceName, testNS)), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{ + { + Object: NewEventPolicy(eventPolicyName, testNS, + WithEventPolicyFrom(pingSourceGVK, pingSourceName, testNS), + WithEventPolicyStatusFromSub([]string{fmt.Sprintf("system:serviceaccount:%s:%s", testNS, serviceAccountname)}), + WithEventPolicyAuthenticationEnabledCondition, + WithReadyEventPolicyCondition, + WithEventPolicySubjectsResolvedSucceeded, + ), + }, + }, + WantErr: false, + }, + { + Name: "subject found for apiserversource, status set to Ready", + Ctx: feature.ToContext(context.TODO(), feature.Flags{ + feature.OIDCAuthentication: feature.Enabled, + }), + Key: testNS + "/" + eventPolicyName, + Objects: []runtime.Object{ + apiServerSourceWithServiceAccount, + NewEventPolicy(eventPolicyName, testNS, + WithInitEventPolicyConditions, WithEventPolicyFrom(apiServerSourceGVK, apiServerSourceName, testNS)), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{ + { + Object: NewEventPolicy(eventPolicyName, testNS, + WithEventPolicyFrom(apiServerSourceGVK, apiServerSourceName, testNS), + WithEventPolicyStatusFromSub([]string{fmt.Sprintf("system:serviceaccount:%s:%s", testNS, serviceAccountname)}), + WithEventPolicyAuthenticationEnabledCondition, + WithReadyEventPolicyCondition, + WithEventPolicySubjectsResolvedSucceeded, + ), + }, + }, + WantErr: false, + }, + { + Name: "Multiple subjects found, status set to Ready", + Ctx: feature.ToContext(context.TODO(), feature.Flags{ + feature.OIDCAuthentication: feature.Enabled, + }), + Key: testNS + "/" + eventPolicyName, + Objects: []runtime.Object{ + apiServerSourceWithServiceAccount, + pingSourceWithServiceAccount, + NewEventPolicy(eventPolicyName, testNS, + WithInitEventPolicyConditions, + WithEventPolicyFrom(pingSourceGVK, pingSourceName, testNS), + WithEventPolicyFrom(apiServerSourceGVK, apiServerSourceName, testNS)), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{ + { + Object: NewEventPolicy(eventPolicyName, testNS, + WithEventPolicyFrom(pingSourceGVK, pingSourceName, testNS), + WithEventPolicyFrom(apiServerSourceGVK, apiServerSourceName, testNS), + WithEventPolicyStatusFromSub([]string{ + fmt.Sprintf("system:serviceaccount:%s:%s", testNS, serviceAccountname), + fmt.Sprintf("system:serviceaccount:%s:%s", testNS, serviceAccountname), + }), + WithEventPolicyAuthenticationEnabledCondition, + WithReadyEventPolicyCondition, + WithEventPolicySubjectsResolvedSucceeded, + ), + }, + }, + WantErr: false, + }, + + // test cases for authentication-oidc feature disabled afterwards + { + Name: "Ready status EventPolicy updated to NotReady", + Ctx: feature.ToContext(context.TODO(), feature.Flags{ + feature.OIDCAuthentication: feature.Disabled, + }), + Key: testNS + "/" + eventPolicyName, + Objects: []runtime.Object{ + pingSourceWithServiceAccount, + NewEventPolicy(eventPolicyName, testNS, + WithReadyEventPolicyCondition, + WithEventPolicyAuthenticationEnabledCondition, + WithEventPolicySubjectsResolvedSucceeded, + WithEventPolicyFrom(pingSourceGVK, pingSourceName, testNS)), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{ + { + Object: NewEventPolicy(eventPolicyName, testNS, + WithEventPolicyFrom(pingSourceGVK, pingSourceName, testNS), + WithEventPolicyAuthenticationDisabledCondition, + WithUnreadyEventPolicyCondition("OIDCAuthenticationDisabled", ""), + WithEventPolicySubjectsResolvedSucceeded, + ), + }, + }, + WantErr: false, + }, + } + logger := logtesting.TestLogger(t) + table.Test(t, MakeFactory(func(ctx context.Context, listers *Listers, cmw configmap.Watcher) controller.Reconciler { + ctx = duckv1authstatus.WithDuck(ctx) + r := &Reconciler{ + authResolver: resolver.NewAuthenticatableResolverFromTracker(ctx, tracker.New(func(types.NamespacedName) {}, 0))} + return eventpolicy.NewReconciler(ctx, logger, + fakeeventingclient.Get(ctx), listers.GetEventPolicyLister(), + controller.GetEventRecorder(ctx), r) + }, + false, + logger, + )) +} diff --git a/pkg/reconciler/inmemorychannel/controller/inmemorychannel_test.go b/pkg/reconciler/inmemorychannel/controller/inmemorychannel_test.go index f9f859444ba..79e5fa0e012 100644 --- a/pkg/reconciler/inmemorychannel/controller/inmemorychannel_test.go +++ b/pkg/reconciler/inmemorychannel/controller/inmemorychannel_test.go @@ -697,7 +697,7 @@ func TestAllCases(t *testing.T) { ), makeChannelService(NewInMemoryChannel(imcName, testNS)), NewEventPolicy(unreadyEventPolicyName, testNS, - WithUnreadyEventPolicyCondition, + WithUnreadyEventPolicyCondition("", ""), WithEventPolicyToRef(imcV1GVK, imcName), ), }, @@ -735,7 +735,7 @@ func TestAllCases(t *testing.T) { WithEventPolicyToRef(imcV1GVK, imcName), ), NewEventPolicy(unreadyEventPolicyName, testNS, - WithUnreadyEventPolicyCondition, + WithUnreadyEventPolicyCondition("", ""), WithEventPolicyToRef(imcV1GVK, imcName), ), }, diff --git a/pkg/reconciler/testing/v1/eventpolicy.go b/pkg/reconciler/testing/v1/eventpolicy.go index 0d2934ac6ce..442fd397483 100644 --- a/pkg/reconciler/testing/v1/eventpolicy.go +++ b/pkg/reconciler/testing/v1/eventpolicy.go @@ -44,25 +44,72 @@ func NewEventPolicy(name, namespace string, o ...EventPolicyOption) *v1alpha1.Ev return ep } -func WithInitEventPolicyConditions(et *v1alpha1.EventPolicy) { - et.Status.InitializeConditions() +func WithInitEventPolicyConditions(ep *v1alpha1.EventPolicy) { + ep.Status.InitializeConditions() } -func WithReadyEventPolicyCondition(ep *v1alpha1.EventPolicy) { - ep.Status.Conditions = []apis.Condition{ - { - Type: v1alpha1.EventPolicyConditionReady, +func WithEventPolicyAuthenticationEnabledCondition(ep *v1alpha1.EventPolicy) { + ep.Status.Conditions = append(ep.Status.Conditions, + apis.Condition{ + Type: v1alpha1.EventPolicyConditionAuthenticationEnabled, Status: corev1.ConditionTrue, - }, + }) +} + +func WithEventPolicyAuthenticationDisabledCondition(ep *v1alpha1.EventPolicy) { + ep.Status.Conditions = append(ep.Status.Conditions, + apis.Condition{ + Type: v1alpha1.EventPolicyConditionAuthenticationEnabled, + Status: corev1.ConditionFalse, + Reason: "OIDCAuthenticationDisabled", + }) +} + +func WithEventPolicySubjectsResolvedSucceeded(ep *v1alpha1.EventPolicy) { + ep.Status.Conditions = append(ep.Status.Conditions, + apis.Condition{ + Type: v1alpha1.EventPolicyConditionSubjectsResolved, + Status: corev1.ConditionTrue, + }) +} + +func WithEventPolicySubjectsResolvedFailed(reason, message string) EventPolicyOption { + return func(ep *v1alpha1.EventPolicy) { + ep.Status.Conditions = append(ep.Status.Conditions, + apis.Condition{ + Type: v1alpha1.EventPolicyConditionSubjectsResolved, + Status: corev1.ConditionFalse, + Reason: reason, + Message: message, + }) } } -func WithUnreadyEventPolicyCondition(ep *v1alpha1.EventPolicy) { - ep.Status.Conditions = []apis.Condition{ - { +func WithEventPolicySubjectsResolvedUnknown(ep *v1alpha1.EventPolicy) { + ep.Status.Conditions = append(ep.Status.Conditions, + apis.Condition{ + Type: v1alpha1.EventPolicyConditionSubjectsResolved, + Status: corev1.ConditionUnknown, + }) +} + +func WithReadyEventPolicyCondition(ep *v1alpha1.EventPolicy) { + ep.Status.Conditions = append(ep.Status.Conditions, + apis.Condition{ Type: v1alpha1.EventPolicyConditionReady, - Status: corev1.ConditionFalse, - }, + Status: corev1.ConditionTrue, + }) +} + +func WithUnreadyEventPolicyCondition(reason, message string) EventPolicyOption { + return func(ep *v1alpha1.EventPolicy) { + ep.Status.Conditions = append(ep.Status.Conditions, + apis.Condition{ + Type: v1alpha1.EventPolicyConditionReady, + Status: corev1.ConditionFalse, + Reason: reason, + Message: message, + }) } } @@ -102,3 +149,9 @@ func WithEventPolicyOwnerReferences(ownerRefs ...metav1.OwnerReference) EventPol ep.ObjectMeta.OwnerReferences = append(ep.ObjectMeta.OwnerReferences, ownerRefs...) } } + +func WithEventPolicyStatusFromSub(subs []string) EventPolicyOption { + return func(ep *v1alpha1.EventPolicy) { + ep.Status.From = append(ep.Status.From, subs...) + } +} diff --git a/vendor/knative.dev/pkg/client/injection/ducks/duck/v1/authstatus/fake/fake.go b/vendor/knative.dev/pkg/client/injection/ducks/duck/v1/authstatus/fake/fake.go new file mode 100644 index 00000000000..47111b089b7 --- /dev/null +++ b/vendor/knative.dev/pkg/client/injection/ducks/duck/v1/authstatus/fake/fake.go @@ -0,0 +1,30 @@ +/* +Copyright 2022 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package fake + +import ( + authstatus "knative.dev/pkg/client/injection/ducks/duck/v1/authstatus" + injection "knative.dev/pkg/injection" +) + +var Get = authstatus.Get + +func init() { + injection.Fake.RegisterDuck(authstatus.WithDuck) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index a0c170ce403..07b2b0163a6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1081,6 +1081,7 @@ knative.dev/pkg/client/injection/apiextensions/reconciler/apiextensions/v1/custo knative.dev/pkg/client/injection/ducks/duck/v1/addressable knative.dev/pkg/client/injection/ducks/duck/v1/addressable/fake knative.dev/pkg/client/injection/ducks/duck/v1/authstatus +knative.dev/pkg/client/injection/ducks/duck/v1/authstatus/fake knative.dev/pkg/client/injection/ducks/duck/v1/conditions knative.dev/pkg/client/injection/ducks/duck/v1/conditions/fake knative.dev/pkg/client/injection/ducks/duck/v1/kresource