@@ -93,8 +93,12 @@ type Harness struct {
9393 VCRRecorderTF * recorder.Recorder
9494 VCRRecorderOauth * recorder.Recorder
9595
96- client client.Client
97- restConfig * rest.Config
96+ // userClient is a client for kube-apiserver, authenticating as a normal user.
97+ // The webhooks will not special case this user.
98+ userClient client.Client
99+
100+ // userRestConfig is the rest.Config that backs userClient
101+ userRestConfig * rest.Config
98102
99103 // gcpAccessToken is set to the oauth2 token to use for GCP, primarily when GCP is mocked.
100104 gcpAccessToken string
@@ -128,9 +132,9 @@ var httpRoundTripperKey httpRoundTripperKeyType
128132// deprecated: Prefer NewHarness, which can construct a manager and mock gcp etc.
129133func NewHarnessWithManager (ctx context.Context , t * testing.T , mgr manager.Manager ) * Harness {
130134 h := & Harness {
131- T : t ,
132- Ctx : ctx ,
133- client : mgr .GetClient (),
135+ T : t ,
136+ Ctx : ctx ,
137+ userClient : mgr .GetClient (),
134138 }
135139 return h
136140}
@@ -229,6 +233,15 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
229233 if h .KubeTarget == "" {
230234 h .KubeTarget = os .Getenv ("E2E_KUBE_TARGET" )
231235 }
236+
237+ // userRestConfig is the restConfig for authenticating as a normal user.
238+ // The webhooks will not special-case this user.
239+ var userRestConfig * rest.Config
240+
241+ // controllerRestConfig is the restConfig for authenticating as the KCC controllers.
242+ // The webhooks special-case this user (e.g. immutable fields)
243+ var controllerRestConfig * rest.Config
244+
232245 if h .KubeTarget == "envtest" {
233246 whCfgs , err := cnrmwebhook .GetCommonWebhookConfigs ()
234247 if err != nil {
@@ -244,7 +257,7 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
244257 testenvironment .ConfigureWebhookInstallOptions (env , whCfgs )
245258
246259 h .Logf ("starting envtest apiserver" )
247- restConfig , err := env .Start ()
260+ adminRestConfig , err := env .Start ()
248261 if err != nil {
249262 h .Fatalf ("error starting test environment: %v" , err )
250263 }
@@ -255,7 +268,16 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
255268 }
256269 })
257270
258- h .restConfig = restConfig
271+ // The admin user is not really a "normal" user, but we do want full permissions
272+ userRestConfig = adminRestConfig
273+
274+ controllerUser , err := env .ControlPlane .AddUser (envtest.User {
275+ Name : "system:serviceaccount:cnrm-system:cnrm-controller-manager" ,
276+ }, adminRestConfig )
277+ if err != nil {
278+ t .Fatalf ("creating cnrm-controller-manager user: %v" , err )
279+ }
280+ controllerRestConfig = controllerUser .Config ()
259281
260282 webhookOptions := webhook.Options {
261283 Port : env .WebhookInstallOptions .LocalServingPort ,
@@ -276,7 +298,7 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
276298 url := env .ControlPlane .GetAPIServer ().SecureServing .URL ("https" , "debug/pprof/profile" )
277299 url .RawQuery = "seconds=30"
278300 t .Logf ("profiling with url %v" , url )
279- httpClient , err := rest .HTTPClientFor (restConfig )
301+ httpClient , err := rest .HTTPClientFor (adminRestConfig )
280302 if err != nil {
281303 return fmt .Errorf ("building http client: %w" , err )
282304 }
@@ -329,7 +351,7 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
329351 }
330352 })
331353
332- h . restConfig = & rest.Config {
354+ userRestConfig = & rest.Config {
333355 Host : addr .String (),
334356 ContentConfig : rest.ContentConfig {
335357 ContentType : "application/json" ,
@@ -366,7 +388,8 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
366388 t := test .NewHTTPRecorder (rt , kubeEventSinks ... )
367389 return t
368390 }
369- h .restConfig .Wrap (wrapTransport )
391+ userRestConfig .Wrap (wrapTransport )
392+ controllerRestConfig .Wrap (wrapTransport )
370393 }
371394
372395 // Set up capture of GCP requests
@@ -385,12 +408,14 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
385408 h .Ctx = ctx
386409 }
387410
388- if h .client == nil {
389- client , err := client .New (h .restConfig , client.Options {})
411+ if h .userClient == nil {
412+ h .userRestConfig = userRestConfig
413+
414+ client , err := client .New (userRestConfig , client.Options {})
390415 if err != nil {
391416 h .Fatalf ("error building client: %v" , err )
392417 }
393- h .client = client
418+ h .userClient = client
394419 }
395420
396421 logging .SetupLogger ()
@@ -418,7 +443,7 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
418443
419444 go func () {
420445 defer wg .Done ()
421- if err := h .client .Create (ctx , crd .DeepCopy ()); err != nil {
446+ if err := h .userClient .Create (ctx , crd .DeepCopy ()); err != nil {
422447 errsMutex .Lock ()
423448 defer errsMutex .Unlock ()
424449 errs = append (errs , fmt .Errorf ("error creating crd %v: %w" , crd .GroupVersionKind (), err ))
@@ -431,6 +456,8 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
431456 h .Fatalf ("error creating crds: %v" , errors .Join (errs ... ))
432457 }
433458 }
459+
460+ configureRBAC (h )
434461 }
435462
436463 var mockCloudGRPCClientConnection * grpc.ClientConn
@@ -440,7 +467,7 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
440467 if h .GCPTarget == GCPTargetModeMock {
441468 t .Logf ("creating mock gcp" )
442469
443- mockCloud := mockgcp .NewMockRoundTripperForTest (t , h .client , storage .NewInMemoryStorage ())
470+ mockCloud := mockgcp .NewMockRoundTripperForTest (t , h .userClient , storage .NewInMemoryStorage ())
444471
445472 mockCloudGRPCClientConnection = mockCloud .NewGRPCConnection (ctx )
446473 h .MockGCP = mockCloud
@@ -722,7 +749,7 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
722749
723750 krmtotf .SetUserAgentForTerraformProvider ()
724751
725- mgr , err := kccmanager .New (mgrContext , h . restConfig , kccConfig )
752+ mgr , err := kccmanager .New (mgrContext , controllerRestConfig , kccConfig )
726753 if err != nil {
727754 t .Fatalf ("error creating new manager: %v" , err )
728755 }
@@ -793,12 +820,15 @@ func (h *Harness) getCloudResourceManagerClient(httpClient *http.Client) *cloudr
793820 return s
794821}
795822
823+ // GetClient returns a client for the kube-apiserver, authenticating as a normal user (not the controller)
824+ // The webhooks will not special-case this user.
796825func (h * Harness ) GetClient () client.Client {
797- return h .client
826+ return h .userClient
798827}
799828
800- func (h * Harness ) GetRESTConfig () * rest.Config {
801- return h .restConfig
829+ // GetUserRestConfig returns the rest.Config that is the equivalent of GetClient()
830+ func (h * Harness ) GetUserRESTConfig () * rest.Config {
831+ return h .userRestConfig
802832}
803833
804834func (h * Harness ) GCPAuthorization () oauth2.TokenSource {
@@ -1305,3 +1335,33 @@ func (s *filterSink) WithName(name string) logr.LogSink {
13051335func (s * filterSink ) Error (err error , msg string , args ... any ) {
13061336 s .sink .Error (err , msg , args ... )
13071337}
1338+
1339+ // configureRBAC will set up RBAC for the controller serviceAccount
1340+ func configureRBAC (h * Harness ) {
1341+ roleBinding := & unstructured.Unstructured {}
1342+ roleBinding .SetAPIVersion ("rbac.authorization.k8s.io/v1" )
1343+ roleBinding .SetKind ("ClusterRoleBinding" )
1344+ roleBinding .SetName ("manager-binding" )
1345+ // TODO: should use cnrm-manager-cluster-role, but it's not readily available
1346+ // roleBinding.Object["roleRef"] = map[string]interface{}{
1347+ // "apiGroup": "rbac.authorization.k8s.io",
1348+ // "kind": "ClusterRole",
1349+ // "name": "cnrm-anager-cluster-role",
1350+ // }
1351+
1352+ roleBinding .Object ["roleRef" ] = map [string ]interface {}{
1353+ "apiGroup" : "rbac.authorization.k8s.io" ,
1354+ "kind" : "ClusterRole" ,
1355+ "name" : "cluster-admin" ,
1356+ }
1357+ roleBinding .Object ["subjects" ] = []map [string ]interface {}{
1358+ {
1359+ "kind" : "ServiceAccount" ,
1360+ "name" : "cnrm-controller-manager" ,
1361+ "namespace" : "cnrm-system" ,
1362+ },
1363+ }
1364+ if err := h .GetClient ().Create (h .Ctx , roleBinding ); err != nil {
1365+ h .T .Fatalf ("creating cluster role binding: %v" , err )
1366+ }
1367+ }
0 commit comments