@@ -13,6 +13,10 @@ import (
1313 "helm.sh/helm/v3/pkg/chart"
1414 "helm.sh/helm/v3/pkg/release"
1515 "helm.sh/helm/v3/pkg/storage/driver"
16+ authorizationv1 "k8s.io/api/authorization/v1"
17+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+ authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
19+ "k8s.io/client-go/rest"
1620 featuregatetesting "k8s.io/component-base/featuregate/testing"
1721 "sigs.k8s.io/controller-runtime/pkg/client"
1822
@@ -94,6 +98,83 @@ func (mag *mockActionGetter) Reconcile(rel *release.Release) error {
9498 return mag .reconcileErr
9599}
96100
101+ // Helper returns an AuthorizationV1Interface where SSRR always passes.
102+ func newPassingSSRRAuthClient () authorizationv1client.AuthorizationV1Interface {
103+ return & mockAuthorizationV1Client {
104+ ssrrInterface : & mockSelfSubjectRulesReviewInterface {
105+ retSSRR : & authorizationv1.SelfSubjectRulesReview {
106+ Status : authorizationv1.SubjectRulesReviewStatus {
107+ ResourceRules : []authorizationv1.ResourceRule {{
108+ Verbs : []string {"*" },
109+ APIGroups : []string {"*" },
110+ Resources : []string {"*" },
111+ }},
112+ },
113+ },
114+ },
115+ }
116+ }
117+
118+ // Helper builds an AuthClientMapper with the passing SSRR
119+ func newPassingAuthClientMapper () applier.AuthClientMapper {
120+ fakeRestConfig := & rest.Config {Host : "fake-server" }
121+ mockRCM := func (ctx context.Context , obj client.Object , cfg * rest.Config ) (* rest.Config , error ) {
122+ return cfg , nil
123+ }
124+ acm := applier .NewAuthClientMapper (mockRCM , fakeRestConfig )
125+ acm .NewForConfig = func (* rest.Config ) (authorizationv1client.AuthorizationV1Interface , error ) {
126+ return newPassingSSRRAuthClient (), nil
127+ }
128+ return acm
129+ }
130+
131+ // Helper builds a Helm applier with passing SSRR
132+ func buildHelmApplier (mockAcg * mockActionGetter , preflights []applier.Preflight ) applier.Helm {
133+ return applier.Helm {
134+ ActionClientGetter : mockAcg ,
135+ AuthClientMapper : newPassingAuthClientMapper (),
136+ Preflights : preflights ,
137+ }
138+ }
139+
140+ type mockAuthorizationV1Client struct {
141+ ssrrInterface authorizationv1client.SelfSubjectRulesReviewInterface
142+ }
143+
144+ func (m * mockAuthorizationV1Client ) SelfSubjectRulesReviews () authorizationv1client.SelfSubjectRulesReviewInterface {
145+ return m .ssrrInterface
146+ }
147+ func (m * mockAuthorizationV1Client ) RESTClient () rest.Interface {
148+ return nil
149+ }
150+
151+ // Mock for SelfSubjectRulesReviewInterface
152+ type mockSelfSubjectRulesReviewInterface struct {
153+ retSSRR * authorizationv1.SelfSubjectRulesReview
154+ retErr error
155+ }
156+
157+ func (m * mockSelfSubjectRulesReviewInterface ) Create (
158+ ctx context.Context ,
159+ ssrr * authorizationv1.SelfSubjectRulesReview ,
160+ opts metav1.CreateOptions ,
161+ ) (* authorizationv1.SelfSubjectRulesReview , error ) {
162+ // Return either a success or an error, depending on what you want in the test.
163+ return m .retSSRR , m .retErr
164+ }
165+
166+ func (m * mockAuthorizationV1Client ) LocalSubjectAccessReviews (namespace string ) authorizationv1client.LocalSubjectAccessReviewInterface {
167+ return nil
168+ }
169+
170+ func (m * mockAuthorizationV1Client ) SelfSubjectAccessReviews () authorizationv1client.SelfSubjectAccessReviewInterface {
171+ return nil
172+ }
173+
174+ func (m * mockAuthorizationV1Client ) SubjectAccessReviews () authorizationv1client.SubjectAccessReviewInterface {
175+ return nil
176+ }
177+
97178var (
98179 // required for unmockable call to convert.RegistryV1ToHelmChart
99180 validFS = fstest.MapFS {
@@ -229,16 +310,23 @@ func TestApply_Installation(t *testing.T) {
229310}
230311
231312func TestApply_InstallationWithPreflightPermissionsEnabled (t * testing.T ) {
313+ // Set feature gate ONCE at parent level
232314 featuregatetesting .SetFeatureGateDuringTest (t , features .OperatorControllerFeatureGate , features .PreflightPermissions , true )
233315
234316 t .Run ("fails during dry-run installation" , func (t * testing.T ) {
235317 mockAcg := & mockActionGetter {
236318 getClientErr : driver .ErrReleaseNotFound ,
237319 dryRunInstallErr : errors .New ("failed attempting to dry-run install chart" ),
238320 }
239- helmApplier := applier.Helm {ActionClientGetter : mockAcg }
240-
241- objs , state , err := helmApplier .Apply (context .TODO (), validFS , testCE , testObjectLabels , testStorageLabels )
321+ helmApplier := buildHelmApplier (mockAcg , nil )
322+
323+ objs , state , err := helmApplier .Apply (
324+ context .TODO (),
325+ validFS ,
326+ testCE ,
327+ testObjectLabels ,
328+ testStorageLabels ,
329+ )
242330 require .Error (t , err )
243331 require .ErrorContains (t , err , "attempting to dry-run install chart" )
244332 require .Nil (t , objs )
@@ -251,7 +339,8 @@ func TestApply_InstallationWithPreflightPermissionsEnabled(t *testing.T) {
251339 installErr : errors .New ("failed installing chart" ),
252340 }
253341 mockPf := & mockPreflight {installErr : errors .New ("failed during install pre-flight check" )}
254- helmApplier := applier.Helm {ActionClientGetter : mockAcg , Preflights : []applier.Preflight {mockPf }}
342+
343+ helmApplier := buildHelmApplier (mockAcg , []applier.Preflight {mockPf })
255344
256345 objs , state , err := helmApplier .Apply (context .TODO (), validFS , testCE , testObjectLabels , testStorageLabels )
257346 require .Error (t , err )
@@ -265,9 +354,15 @@ func TestApply_InstallationWithPreflightPermissionsEnabled(t *testing.T) {
265354 getClientErr : driver .ErrReleaseNotFound ,
266355 installErr : errors .New ("failed installing chart" ),
267356 }
268- helmApplier := applier.Helm {ActionClientGetter : mockAcg }
269-
270- objs , state , err := helmApplier .Apply (context .TODO (), validFS , testCE , testObjectLabels , testStorageLabels )
357+ helmApplier := buildHelmApplier (mockAcg , nil )
358+
359+ objs , state , err := helmApplier .Apply (
360+ context .TODO (),
361+ validFS ,
362+ testCE ,
363+ testObjectLabels ,
364+ testStorageLabels ,
365+ )
271366 require .Error (t , err )
272367 require .ErrorContains (t , err , "installing chart" )
273368 require .Equal (t , applier .StateNeedsInstall , state )
@@ -282,9 +377,15 @@ func TestApply_InstallationWithPreflightPermissionsEnabled(t *testing.T) {
282377 Manifest : validManifest ,
283378 },
284379 }
285- helmApplier := applier.Helm {ActionClientGetter : mockAcg }
286-
287- objs , state , err := helmApplier .Apply (context .TODO (), validFS , testCE , testObjectLabels , testStorageLabels )
380+ helmApplier := buildHelmApplier (mockAcg , nil )
381+
382+ objs , state , err := helmApplier .Apply (
383+ context .TODO (),
384+ validFS ,
385+ testCE ,
386+ testObjectLabels ,
387+ testStorageLabels ,
388+ )
288389 require .NoError (t , err )
289390 require .Equal (t , applier .StateNeedsInstall , state )
290391 require .NotNil (t , objs )
0 commit comments