Skip to content

Commit

Permalink
OIDC - Support auto generation of PingSource identity service account…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
Leo6Leo authored Oct 19, 2023
1 parent 16d75a9 commit a261e06
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 11 deletions.
6 changes: 6 additions & 0 deletions pkg/adapter/mtping/pingsource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ func TestAllCases(t *testing.T) {
rtv1.WithPingSourceDeployed,
rtv1.WithPingSourceSink(sinkAddr),
rtv1.WithPingSourceCloudEventAttributes,
rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(),
),
},
WantErr: false,
Expand All @@ -112,6 +113,7 @@ func TestAllCases(t *testing.T) {
rtv1.WithPingSourceDeployed,
rtv1.WithPingSourceSink(sinkAddr),
rtv1.WithPingSourceCloudEventAttributes,
rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(),
),
},
WantErr: false,
Expand All @@ -137,6 +139,7 @@ func TestAllCases(t *testing.T) {
rtv1.WithPingSourceDeployed,
rtv1.WithPingSourceSink(sinkAddr),
rtv1.WithPingSourceCloudEventAttributes,
rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(),
),
},
WantErr: false,
Expand All @@ -162,6 +165,7 @@ func TestAllCases(t *testing.T) {
rtv1.WithPingSourceDeployed,
rtv1.WithPingSourceSink(sinkAddr),
rtv1.WithPingSourceCloudEventAttributes,
rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(),
),
},
WantErr: false,
Expand All @@ -188,6 +192,7 @@ func TestAllCases(t *testing.T) {
rtv1.WithPingSourceSink(sinkAddr),
rtv1.WithPingSourceCloudEventAttributes,
rtv1.WithPingSourceDeleted,
rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(),
),
},
WantErr: false,
Expand All @@ -210,6 +215,7 @@ func TestAllCases(t *testing.T) {
rtv1.WithPingSourceSink(sinkAddr),
rtv1.WithPingSourceCloudEventAttributes,
rtv1.WithPingSourceDeleted,
rtv1.WithPingSourceOIDCIdentityCreatedSucceededBecauseOIDCFeatureDisabled(),
),
},
WantErr: false,
Expand Down
22 changes: 21 additions & 1 deletion pkg/apis/sources/v1/ping_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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...)
}
65 changes: 62 additions & 3 deletions pkg/apis/sources/v1/ping_lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func TestPingSourceStatusIsReady(t *testing.T) {
s: func() *PingSourceStatus {
s := &PingSourceStatus{}
s.InitializeConditions()
s.MarkOIDCIdentityCreatedSucceeded()
return s
}(),
wantConditionStatus: corev1.ConditionUnknown,
Expand All @@ -81,6 +82,7 @@ func TestPingSourceStatusIsReady(t *testing.T) {
s: func() *PingSourceStatus {
s := &PingSourceStatus{}
s.InitializeConditions()
s.MarkOIDCIdentityCreatedSucceeded()
s.PropagateDeploymentAvailability(availableDeployment)
return s
}(),
Expand All @@ -91,7 +93,7 @@ func TestPingSourceStatusIsReady(t *testing.T) {
s: func() *PingSourceStatus {
s := &PingSourceStatus{}
s.InitializeConditions()

s.MarkOIDCIdentityCreatedSucceeded()
s.MarkSink(exampleAddr)
return s
}(),
Expand All @@ -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 {
Expand All @@ -122,6 +140,7 @@ func TestPingSourceStatusIsReady(t *testing.T) {
if got != test.want {
t.Errorf("unexpected readiness: want %v, got %v", test.want, got)
}

})
}
}
Expand All @@ -145,6 +164,7 @@ func TestPingSourceStatusGetTopLevelCondition(t *testing.T) {
s: func() *PingSourceStatus {
s := &PingSourceStatus{}
s.InitializeConditions()
s.MarkOIDCIdentityCreatedSucceeded()
return s
}(),
want: &apis.Condition{
Expand All @@ -156,6 +176,7 @@ func TestPingSourceStatusGetTopLevelCondition(t *testing.T) {
s: func() *PingSourceStatus {
s := &PingSourceStatus{}
s.InitializeConditions()
s.MarkOIDCIdentityCreatedSucceeded()
s.PropagateDeploymentAvailability(availableDeployment)
return s
}(),
Expand All @@ -168,6 +189,7 @@ func TestPingSourceStatusGetTopLevelCondition(t *testing.T) {
s: func() *PingSourceStatus {
s := &PingSourceStatus{}
s.InitializeConditions()
s.MarkOIDCIdentityCreatedSucceeded()
s.MarkSink(exampleAddr)
return s
}(),
Expand All @@ -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
Expand All @@ -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")
Expand Down Expand Up @@ -223,6 +264,7 @@ func TestPingSourceStatusGetCondition(t *testing.T) {
s: func() *PingSourceStatus {
s := &PingSourceStatus{}
s.InitializeConditions()
s.MarkOIDCIdentityCreatedSucceeded()
return s
}(),
condQuery: PingSourceConditionReady,
Expand All @@ -235,6 +277,7 @@ func TestPingSourceStatusGetCondition(t *testing.T) {
s: func() *PingSourceStatus {
s := &PingSourceStatus{}
s.InitializeConditions()
s.MarkOIDCIdentityCreatedSucceeded()
s.PropagateDeploymentAvailability(availableDeployment)
return s
}(),
Expand All @@ -248,6 +291,7 @@ func TestPingSourceStatusGetCondition(t *testing.T) {
s: func() *PingSourceStatus {
s := &PingSourceStatus{}
s.InitializeConditions()
s.MarkOIDCIdentityCreatedSucceeded()
s.MarkSink(exampleAddr)
return s
}(),
Expand All @@ -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 {
Expand Down
29 changes: 25 additions & 4 deletions pkg/reconciler/pingsource/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand All @@ -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))
Expand All @@ -97,5 +113,10 @@ func NewController(
)),
})

serviceaccountInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{
FilterFunc: controller.FilterController(&sourcesv1.PingSource{}),
Handler: controller.HandleAll(impl.EnqueueControllerOf),
})

return impl
}
24 changes: 24 additions & 0 deletions pkg/reconciler/pingsource/pingsource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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", "")
Expand Down
Loading

0 comments on commit a261e06

Please sign in to comment.