Skip to content

Commit ac92528

Browse files
committed
Change AuthorizationV1Client to interface for mocking
Signed-off-by: Brett Tofel <[email protected]>
1 parent eec380f commit ac92528

File tree

4 files changed

+126
-21
lines changed

4 files changed

+126
-21
lines changed

cmd/operator-controller/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ import (
3737
k8slabels "k8s.io/apimachinery/pkg/labels"
3838
k8stypes "k8s.io/apimachinery/pkg/types"
3939
apimachineryrand "k8s.io/apimachinery/pkg/util/rand"
40+
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
4041
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
4142
_ "k8s.io/client-go/plugin/pkg/client/auth"
43+
"k8s.io/client-go/rest"
4244
"k8s.io/klog/v2"
4345
"k8s.io/klog/v2/textlogger"
4446
"k8s.io/utils/ptr"
@@ -408,6 +410,10 @@ func run() error {
408410
}
409411

410412
acm := applier.NewAuthClientMapper(clientRestConfigMapper, mgr.GetConfig())
413+
acm.NewForConfig = func(cfg *rest.Config) (authorizationv1client.AuthorizationV1Interface, error) {
414+
// *AuthorizationV1Client implements AuthorizationV1Interface
415+
return authorizationv1client.NewForConfig(cfg)
416+
}
411417

412418
helmApplier := &applier.Helm{
413419
ActionClientGetter: acg,

internal/operator-controller/applier/helm.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,23 +58,21 @@ type Preflight interface {
5858

5959
type RestConfigMapper func(context.Context, client.Object, *rest.Config) (*rest.Config, error)
6060

61+
type NewForConfigFunc func(*rest.Config) (authorizationv1client.AuthorizationV1Interface, error)
62+
6163
type AuthClientMapper struct {
62-
rcm RestConfigMapper
63-
baseCfg *rest.Config
64+
rcm RestConfigMapper
65+
baseCfg *rest.Config
66+
NewForConfig NewForConfigFunc
6467
}
6568

66-
func (acm *AuthClientMapper) GetAuthenticationClient(ctx context.Context, ext *ocv1.ClusterExtension) (*authorizationv1client.AuthorizationV1Client, error) {
69+
func (acm *AuthClientMapper) GetAuthenticationClient(ctx context.Context, ext *ocv1.ClusterExtension) (authorizationv1client.AuthorizationV1Interface, error) {
6770
authcfg, err := acm.rcm(ctx, ext, acm.baseCfg)
6871
if err != nil {
6972
return nil, err
7073
}
7174

72-
authclient, err := authorizationv1client.NewForConfig(authcfg)
73-
if err != nil {
74-
return nil, err
75-
}
76-
77-
return authclient, nil
75+
return acm.NewForConfig(authcfg)
7876
}
7977

8078
type Helm struct {
@@ -198,7 +196,7 @@ func (h *Helm) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExte
198196
}
199197

200198
// Check if RBAC allows the installer service account necessary permissions on the objects in the contentFS
201-
func (h *Helm) checkContentPermissions(ctx context.Context, contentFS fs.FS, authcl *authorizationv1client.AuthorizationV1Client, ext *ocv1.ClusterExtension) error {
199+
func (h *Helm) checkContentPermissions(ctx context.Context, contentFS fs.FS, authcl authorizationv1client.AuthorizationV1Interface, ext *ocv1.ClusterExtension) error {
202200
reg, err := convert.ParseFS(ctx, contentFS)
203201
if err != nil {
204202
return err

internal/operator-controller/applier/helm_test.go

Lines changed: 111 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
97178
var (
98179
// required for unmockable call to convert.RegistryV1ToHelmChart
99180
validFS = fstest.MapFS{
@@ -229,16 +310,23 @@ func TestApply_Installation(t *testing.T) {
229310
}
230311

231312
func 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)

internal/operator-controller/authorization/authorization.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const (
2121
SelfSubjectAccessReview string = "SelfSubjectAccessReview"
2222
)
2323

24-
func CheckObjectPermissions(ctx context.Context, authcl *authorizationv1client.AuthorizationV1Client, objects []client.Object, ext *ocv1.ClusterExtension) error {
24+
func CheckObjectPermissions(ctx context.Context, authcl authorizationv1client.AuthorizationV1Interface, objects []client.Object, ext *ocv1.ClusterExtension) error {
2525
ssrr := &authorizationv1.SelfSubjectRulesReview{
2626
Spec: authorizationv1.SelfSubjectRulesReviewSpec{
2727
Namespace: ext.Spec.Namespace,

0 commit comments

Comments
 (0)