@@ -87,8 +87,12 @@ type Harness struct {
8787 VCRRecorderTF * recorder.Recorder
8888 VCRRecorderOauth * recorder.Recorder
8989
90- client client.Client
91- restConfig * rest.Config
90+ // userClient is a client for kube-apiserver, authenticating as a normal user.
91+ // The webhooks will not special case this user.
92+ userClient client.Client
93+
94+ // userRestConfig is the rest.Config that backs userClient
95+ userRestConfig * rest.Config
9296
9397 // gcpAccessToken is set to the oauth2 token to use for GCP, primarily when GCP is mocked.
9498 gcpAccessToken string
@@ -122,9 +126,9 @@ var httpRoundTripperKey httpRoundTripperKeyType
122126// deprecated: Prefer NewHarness, which can construct a manager and mock gcp etc.
123127func NewHarnessWithManager (ctx context.Context , t * testing.T , mgr manager.Manager ) * Harness {
124128 h := & Harness {
125- T : t ,
126- Ctx : ctx ,
127- client : mgr .GetClient (),
129+ T : t ,
130+ Ctx : ctx ,
131+ userClient : mgr .GetClient (),
128132 }
129133 return h
130134}
@@ -197,6 +201,15 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
197201 if h .KubeTarget == "" {
198202 h .KubeTarget = os .Getenv ("E2E_KUBE_TARGET" )
199203 }
204+
205+ // userRestConfig is the restConfig for authenticating as a normal user.
206+ // The webhooks will not special-case this user.
207+ var userRestConfig * rest.Config
208+
209+ // controllerRestConfig is the restConfig for authenticating as the KCC controllers.
210+ // The webhooks special-case this user (e.g. immutable fields)
211+ var controllerRestConfig * rest.Config
212+
200213 if h .KubeTarget == "envtest" {
201214 whCfgs , err := cnrmwebhook .GetCommonWebhookConfigs ()
202215 if err != nil {
@@ -212,7 +225,7 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
212225 testenvironment .ConfigureWebhookInstallOptions (env , whCfgs )
213226
214227 h .Logf ("starting envtest apiserver" )
215- restConfig , err := env .Start ()
228+ adminRestConfig , err := env .Start ()
216229 if err != nil {
217230 h .Fatalf ("error starting test environment: %v" , err )
218231 }
@@ -223,7 +236,16 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
223236 }
224237 })
225238
226- h .restConfig = restConfig
239+ // The admin user is not really a "normal" user, but we do want full permissions
240+ userRestConfig = adminRestConfig
241+
242+ controllerUser , err := env .ControlPlane .AddUser (envtest.User {
243+ Name : "system:serviceaccount:test-namespace:cnrm-controller-manager" ,
244+ }, adminRestConfig )
245+ if err != nil {
246+ t .Fatalf ("creating cnrm-controller-manager user: %v" , err )
247+ }
248+ controllerRestConfig = controllerUser .Config ()
227249
228250 webhookOptions := webhook.Options {
229251 Port : env .WebhookInstallOptions .LocalServingPort ,
@@ -244,7 +266,7 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
244266 url := env .ControlPlane .GetAPIServer ().SecureServing .URL ("https" , "debug/pprof/profile" )
245267 url .RawQuery = "seconds=30"
246268 t .Logf ("profiling with url %v" , url )
247- httpClient , err := rest .HTTPClientFor (restConfig )
269+ httpClient , err := rest .HTTPClientFor (adminRestConfig )
248270 if err != nil {
249271 return fmt .Errorf ("building http client: %w" , err )
250272 }
@@ -297,7 +319,7 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
297319 }
298320 })
299321
300- h . restConfig = & rest.Config {
322+ userRestConfig = & rest.Config {
301323 Host : addr .String (),
302324 ContentConfig : rest.ContentConfig {
303325 ContentType : "application/json" ,
@@ -334,7 +356,8 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
334356 t := test .NewHTTPRecorder (rt , kubeEventSinks ... )
335357 return t
336358 }
337- h .restConfig .Wrap (wrapTransport )
359+ userRestConfig .Wrap (wrapTransport )
360+ controllerRestConfig .Wrap (wrapTransport )
338361 }
339362
340363 // Set up capture of GCP requests
@@ -353,12 +376,14 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
353376 h .Ctx = ctx
354377 }
355378
356- if h .client == nil {
357- client , err := client .New (h .restConfig , client.Options {})
379+ if h .userClient == nil {
380+ h .userRestConfig = userRestConfig
381+
382+ client , err := client .New (userRestConfig , client.Options {})
358383 if err != nil {
359384 h .Fatalf ("error building client: %v" , err )
360385 }
361- h .client = client
386+ h .userClient = client
362387 }
363388
364389 logging .SetupLogger ()
@@ -386,7 +411,7 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
386411
387412 go func () {
388413 defer wg .Done ()
389- if err := h .client .Create (ctx , crd .DeepCopy ()); err != nil {
414+ if err := h .userClient .Create (ctx , crd .DeepCopy ()); err != nil {
390415 errsMutex .Lock ()
391416 defer errsMutex .Unlock ()
392417 errs = append (errs , fmt .Errorf ("error creating crd %v: %w" , crd .GroupVersionKind (), err ))
@@ -399,6 +424,8 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
399424 h .Fatalf ("error creating crds: %v" , errors .Join (errs ... ))
400425 }
401426 }
427+
428+ configureRBAC (h )
402429 }
403430
404431 var mockCloudGRPCClientConnection * grpc.ClientConn
@@ -408,7 +435,7 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
408435 if h .GCPTarget == GCPTargetModeMock {
409436 t .Logf ("creating mock gcp" )
410437
411- mockCloud := mockgcp .NewMockRoundTripperForTest (t , h .client , storage .NewInMemoryStorage ())
438+ mockCloud := mockgcp .NewMockRoundTripperForTest (t , h .userClient , storage .NewInMemoryStorage ())
412439
413440 mockCloudGRPCClientConnection = mockCloud .NewGRPCConnection (ctx )
414441 h .MockGCP = mockCloud
@@ -690,7 +717,7 @@ func NewHarness(ctx context.Context, t *testing.T, opts ...HarnessOption) *Harne
690717
691718 krmtotf .SetUserAgentForTerraformProvider ()
692719
693- mgr , err := kccmanager .New (mgrContext , h . restConfig , kccConfig )
720+ mgr , err := kccmanager .New (mgrContext , controllerRestConfig , kccConfig )
694721 if err != nil {
695722 t .Fatalf ("error creating new manager: %v" , err )
696723 }
@@ -761,12 +788,15 @@ func (h *Harness) getCloudResourceManagerClient(httpClient *http.Client) *cloudr
761788 return s
762789}
763790
791+ // GetClient returns a client for the kube-apiserver, authenticating as a normal user (not the controller)
792+ // The webhooks will not special-case this user.
764793func (h * Harness ) GetClient () client.Client {
765- return h .client
794+ return h .userClient
766795}
767796
768- func (h * Harness ) GetRESTConfig () * rest.Config {
769- return h .restConfig
797+ // GetUserRestConfig returns the rest.Config that is the equivalent of GetClient()
798+ func (h * Harness ) GetUserRESTConfig () * rest.Config {
799+ return h .userRestConfig
770800}
771801
772802func (h * Harness ) GCPAuthorization () oauth2.TokenSource {
@@ -1273,3 +1303,33 @@ func (s *filterSink) WithName(name string) logr.LogSink {
12731303func (s * filterSink ) Error (err error , msg string , args ... any ) {
12741304 s .sink .Error (err , msg , args ... )
12751305}
1306+
1307+ // configureRBAC will set up RBAC for the controller serviceAccount
1308+ func configureRBAC (h * Harness ) {
1309+ roleBinding := & unstructured.Unstructured {}
1310+ roleBinding .SetAPIVersion ("rbac.authorization.k8s.io/v1" )
1311+ roleBinding .SetKind ("ClusterRoleBinding" )
1312+ roleBinding .SetName ("manager-binding" )
1313+ // TODO: should use cnrm-manager-cluster-role, but it's not readily available
1314+ // roleBinding.Object["roleRef"] = map[string]interface{}{
1315+ // "apiGroup": "rbac.authorization.k8s.io",
1316+ // "kind": "ClusterRole",
1317+ // "name": "cnrm-anager-cluster-role",
1318+ // }
1319+
1320+ roleBinding .Object ["roleRef" ] = map [string ]interface {}{
1321+ "apiGroup" : "rbac.authorization.k8s.io" ,
1322+ "kind" : "ClusterRole" ,
1323+ "name" : "cluster-admin" ,
1324+ }
1325+ roleBinding .Object ["subjects" ] = []map [string ]interface {}{
1326+ {
1327+ "kind" : "ServiceAccount" ,
1328+ "name" : "cnrm-controller-manager" ,
1329+ "namespace" : "cnrm-system" ,
1330+ },
1331+ }
1332+ if err := h .GetClient ().Create (h .Ctx , roleBinding ); err != nil {
1333+ h .T .Fatalf ("creating cluster role binding: %v" , err )
1334+ }
1335+ }
0 commit comments