[RFC-0010] Add multi-tenant workload identity support for ImageUpdateAutomation with Azure GitRepository

Signed-off-by: Dipti Pai <diptipai89@outlook.com>
This commit is contained in:
Dipti Pai 2025-08-15 12:41:20 -07:00
parent cfbdf5c756
commit f9fc32702d
6 changed files with 33 additions and 3 deletions

2
go.mod
View File

@ -198,3 +198,5 @@ require (
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
)
replace github.com/fluxcd/source-controller/api => github.com/dipti-pai/source-controller/api v0.0.0-20250815055530-ae30fe521c22

4
go.sum
View File

@ -102,6 +102,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dipti-pai/source-controller/api v0.0.0-20250815055530-ae30fe521c22 h1:cQx98gGW+xPQHh/rxa16qTbEUKjYwtA4xKX6/Nw/0Cg=
github.com/dipti-pai/source-controller/api v0.0.0-20250815055530-ae30fe521c22/go.mod h1:MGk/yprMK9SjHbzAT89PUsGV8WnUmpbCiqxbW+K/e+w=
github.com/docker/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A=
github.com/docker/cli v28.2.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
@ -150,8 +152,6 @@ github.com/fluxcd/pkg/ssh v0.20.0 h1:Ak0laIYIc/L8lEfqls/LDWRW8wYPESGaravQsCRGLb8
github.com/fluxcd/pkg/ssh v0.20.0/go.mod h1:sRfAAkxx1GwCGjYirKPnTKdNkNrJRo9kqzWLVFXKv7E=
github.com/fluxcd/pkg/version v0.9.0 h1:pQBHMt9TbnnTUzj3EoMhRi5JUkNBqrTBSAaoLG1ovUA=
github.com/fluxcd/pkg/version v0.9.0/go.mod h1:JU6/UwNbGeMm4gqeyUn/dxl+qwLTi2+X10xpfgWdt9I=
github.com/fluxcd/source-controller/api v1.6.1 h1:ZPTA9lNzBYHmwHfFX978qb8xVkdnQZHF1ggo6BoFm4w=
github.com/fluxcd/source-controller/api v1.6.1/go.mod h1:ZJcAi0nemsnBxjVgmJl0WQzNvB0rMETxQMTdoFosmMw=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=

View File

@ -43,6 +43,7 @@ import (
aclapi "github.com/fluxcd/pkg/apis/acl"
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/auth"
"github.com/fluxcd/pkg/cache"
"github.com/fluxcd/pkg/git"
"github.com/fluxcd/pkg/runtime/acl"
@ -372,6 +373,13 @@ func (r *ImageUpdateAutomationReconciler) reconcile(ctx context.Context, sp *pat
result, retErr = ctrl.Result{}, nil
return
}
if errors.Is(err, source.ErrFeatureGateNotEnabled) {
const gate = auth.FeatureGateObjectLevelWorkloadIdentity
const msgFmt = "to use spec.serviceAccountName for provider authentication please enable the %s feature gate in the controller"
conditions.MarkStalled(obj, meta.FeatureGateDisabledReason, msgFmt, gate)
result, retErr = ctrl.Result{}, nil
return
}
e := fmt.Errorf("failed configuring source manager: %w", err)
conditions.MarkFalse(obj, meta.ReadyCondition, imagev1.SourceManagerFailedReason, "%s", e)
result, retErr = ctrl.Result{}, e
@ -383,7 +391,7 @@ func (r *ImageUpdateAutomationReconciler) reconcile(ctx context.Context, sp *pat
}
}()
// Update any stale Ready=False condition from SourceManager failure.
if conditions.HasAnyReason(obj, meta.ReadyCondition, aclapi.AccessDeniedCondition, imagev1.InvalidSourceConfigReason, imagev1.SourceManagerFailedReason) {
if conditions.HasAnyReason(obj, meta.ReadyCondition, aclapi.AccessDeniedCondition, imagev1.InvalidSourceConfigReason, imagev1.SourceManagerFailedReason, meta.FeatureGateDisabledReason) {
conditions.MarkUnknown(obj, meta.ReadyCondition, meta.ProgressingReason, "reconciliation in progress")
}

View File

@ -209,6 +209,14 @@ func getAuthOpts(ctx context.Context, c client.Client, repo *sourcev1.GitReposit
auth.WithServiceAccountNamespace(srcOpts.objNamespace),
}
if repo.Spec.ServiceAccountName != "" {
// Check object-level workload identity feature gate.
if !auth.IsObjectLevelWorkloadIdentityEnabled() {
return nil, ErrFeatureGateNotEnabled
}
opts = append(opts, auth.WithServiceAccountName(repo.Spec.ServiceAccountName))
}
if srcOpts.tokenCache != nil {
involvedObject := cache.InvolvedObject{
Kind: imagev1.ImageUpdateAutomationKind,

View File

@ -155,6 +155,15 @@ func Test_getAuthOpts_providerAuth(t *testing.T) {
},
wantErr: "ManagedIdentityCredential",
},
{
name: "azure provider with service account and feature gate for object-level identity disabled",
url: "https://dev.azure.com/foo/bar/_git/baz",
beforeFunc: func(obj *sourcev1.GitRepository) {
obj.Spec.Provider = sourcev1.GitProviderAzure
obj.Spec.ServiceAccountName = "azure-sa"
},
wantErr: ErrFeatureGateNotEnabled.Error(),
},
{
name: "github provider with no secret ref",
url: "https://github.com/org/repo.git",

View File

@ -48,6 +48,9 @@ import (
// ErrInvalidSourceConfiguration is an error for invalid source configuration.
var ErrInvalidSourceConfiguration = errors.New("invalid source configuration")
// ErrFeatureGateNotEnabled is an error for when a required feature gate is not enabled.
var ErrFeatureGateNotEnabled = errors.New("required feature gate not enabled")
// RemovedTemplateFieldError represents an error when a removed template field is used.
type RemovedTemplateFieldError struct {
Field string