@@ -70,6 +70,7 @@ import (
7070 kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
7171 intcache "github.com/fluxcd/kustomize-controller/internal/cache"
7272 "github.com/fluxcd/kustomize-controller/internal/decryptor"
73+ "github.com/fluxcd/kustomize-controller/internal/features"
7374 "github.com/fluxcd/kustomize-controller/internal/inventory"
7475 intruntime "github.com/fluxcd/kustomize-controller/internal/runtime"
7576)
@@ -89,26 +90,37 @@ type KustomizationReconciler struct {
8990 kuberecorder.EventRecorder
9091 runtimeCtrl.Metrics
9192
92- artifactFetchRetries int
93- requeueDependency time.Duration
93+ // Kubernetes options
9494
95- Mapper apimeta.RESTMapper
96- APIReader client.Reader
97- ClusterReader engine.ClusterReaderFactory
98- ControllerName string
99- statusManager string
100- NoCrossNamespaceRefs bool
101- NoRemoteBases bool
95+ APIReader client.Reader
96+ ClusterReader engine.ClusterReaderFactory
97+ ConcurrentSSA int
98+ ControllerName string
99+ KubeConfigOpts runtimeClient.KubeConfigOptions
100+ Mapper apimeta.RESTMapper
101+ StatusManager string
102+
103+ // Multi-tenancy and security options
104+
105+ DefaultServiceAccount string
106+ DisallowedFieldManagers []string
107+ NoCrossNamespaceRefs bool
108+ NoRemoteBases bool
109+ SOPSAgeSecret string
110+ TokenCache * cache.TokenCache
111+
112+ // Retry and requeue options
113+
114+ ArtifactFetchRetries int
115+ DependencyRequeueInterval time.Duration
116+
117+ // Feature gates
118+
119+ AdditiveCELDependencyCheck bool
120+ AllowExternalArtifact bool
102121 FailFast bool
103- DefaultServiceAccount string
104- SOPSAgeSecret string
105- KubeConfigOpts runtimeClient.KubeConfigOptions
106- ConcurrentSSA int
107- DisallowedFieldManagers []string
108- StrictSubstitutions bool
109122 GroupChangeLog bool
110- AdditiveCELDependencyCheck bool
111- TokenCache * cache.TokenCache
123+ StrictSubstitutions bool
112124}
113125
114126func (r * KustomizationReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (result ctrl.Result , retErr error ) {
@@ -207,9 +219,9 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
207219
208220 if acl .IsAccessDenied (err ) {
209221 conditions .MarkFalse (obj , meta .ReadyCondition , apiacl .AccessDeniedReason , "%s" , err )
210- log . Error ( err , "Access denied to cross-namespace source" )
222+ conditions . MarkStalled ( obj , apiacl . AccessDeniedReason , "%s" , err )
211223 r .event (obj , "" , "" , eventv1 .EventSeverityError , err .Error (), nil )
212- return ctrl.Result {RequeueAfter : obj . GetRetryInterval ()}, nil
224+ return ctrl.Result {}, reconcile . TerminalError ( err )
213225 }
214226
215227 // Retry with backoff on transient errors.
@@ -218,10 +230,10 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
218230
219231 // Requeue the reconciliation if the source artifact is not found.
220232 if artifactSource .GetArtifact () == nil {
221- msg := fmt .Sprintf ("Source artifact not found, retrying in %s" , r .requeueDependency .String ())
233+ msg := fmt .Sprintf ("Source artifact not found, retrying in %s" , r .DependencyRequeueInterval .String ())
222234 conditions .MarkFalse (obj , meta .ReadyCondition , meta .ArtifactFailedReason , "%s" , msg )
223235 log .Info (msg )
224- return ctrl.Result {RequeueAfter : r .requeueDependency }, nil
236+ return ctrl.Result {RequeueAfter : r .DependencyRequeueInterval }, nil
225237 }
226238 revision := artifactSource .GetArtifact ().Revision
227239 originRevision := getOriginRevision (artifactSource )
@@ -241,10 +253,10 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
241253
242254 // Retry on transient errors.
243255 conditions .MarkFalse (obj , meta .ReadyCondition , meta .DependencyNotReadyReason , "%s" , err )
244- msg := fmt .Sprintf ("Dependencies do not meet ready condition, retrying in %s" , r .requeueDependency .String ())
256+ msg := fmt .Sprintf ("Dependencies do not meet ready condition, retrying in %s" , r .DependencyRequeueInterval .String ())
245257 log .Info (msg )
246258 r .event (obj , revision , originRevision , eventv1 .EventSeverityInfo , msg , nil )
247- return ctrl.Result {RequeueAfter : r .requeueDependency }, nil
259+ return ctrl.Result {RequeueAfter : r .DependencyRequeueInterval }, nil
248260 }
249261 log .Info ("All dependencies are ready, proceeding with reconciliation" )
250262 }
@@ -254,10 +266,10 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
254266
255267 // Requeue at the specified retry interval if the artifact tarball is not found.
256268 if errors .Is (reconcileErr , fetch .ErrFileNotFound ) {
257- msg := fmt .Sprintf ("Source is not ready, artifact not found, retrying in %s" , r .requeueDependency .String ())
269+ msg := fmt .Sprintf ("Source is not ready, artifact not found, retrying in %s" , r .DependencyRequeueInterval .String ())
258270 conditions .MarkFalse (obj , meta .ReadyCondition , meta .ArtifactFailedReason , "%s" , msg )
259271 log .Info (msg )
260- return ctrl.Result {RequeueAfter : r .requeueDependency }, nil
272+ return ctrl.Result {RequeueAfter : r .DependencyRequeueInterval }, nil
261273 }
262274
263275 // Broadcast the reconciliation failure and requeue at the specified retry interval.
@@ -318,7 +330,7 @@ func (r *KustomizationReconciler) reconcile(
318330 // Download artifact and extract files to the tmp dir.
319331 fetcher := fetch .New (
320332 fetch .WithLogger (ctrl .LoggerFrom (ctx )),
321- fetch .WithRetries (r .artifactFetchRetries ),
333+ fetch .WithRetries (r .ArtifactFetchRetries ),
322334 fetch .WithMaxDownloadSize (tar .UnlimitedUntarSize ),
323335 fetch .WithUntar (tar .WithMaxUntarSize (tar .UnlimitedUntarSize )),
324336 fetch .WithHostnameOverwrite (os .Getenv ("SOURCE_CONTROLLER_LOCALHOST" )),
@@ -625,12 +637,20 @@ func (r *KustomizationReconciler) getSource(ctx context.Context,
625637 Name : obj .Spec .SourceRef .Name ,
626638 }
627639
640+ // Check if cross-namespace references are allowed.
628641 if r .NoCrossNamespaceRefs && sourceNamespace != obj .GetNamespace () {
629642 return src , acl .AccessDeniedError (
630643 fmt .Sprintf ("can't access '%s/%s', cross-namespace references have been blocked" ,
631644 obj .Spec .SourceRef .Kind , namespacedName ))
632645 }
633646
647+ // Check if ExternalArtifact kind is allowed.
648+ if obj .Spec .SourceRef .Kind == sourcev1 .ExternalArtifactKind && ! r .AllowExternalArtifact {
649+ return src , acl .AccessDeniedError (
650+ fmt .Sprintf ("can't access '%s/%s', %s feature gate is disabled" ,
651+ obj .Spec .SourceRef .Kind , namespacedName , features .ExternalArtifact ))
652+ }
653+
634654 switch obj .Spec .SourceRef .Kind {
635655 case sourcev1 .OCIRepositoryKind :
636656 var repository sourcev1.OCIRepository
@@ -1204,7 +1224,7 @@ func (r *KustomizationReconciler) patch(ctx context.Context,
12041224 patchOpts = append (patchOpts ,
12051225 patch.WithOwnedConditions {Conditions : ownedConditions },
12061226 patch.WithForceOverwriteConditions {},
1207- patch .WithFieldOwner (r .statusManager ),
1227+ patch .WithFieldOwner (r .StatusManager ),
12081228 )
12091229
12101230 // Patch the object status, conditions and finalizers.
0 commit comments