From 01af5fdbe963d2a89dc34cf5fc49c6bbf66717c0 Mon Sep 17 00:00:00 2001 From: Bojan Zelic Date: Tue, 24 Dec 2024 10:12:45 -0700 Subject: [PATCH 1/5] General: Vault authentication via cross-namespace service accounts Signed-off-by: Bojan Zelic --- CHANGELOG.md | 1 + Makefile | 2 +- .../v1alpha1/triggerauthentication_types.go | 3 + ...keda.sh_clustertriggerauthentications.yaml | 2 + .../bases/keda.sh_triggerauthentications.yaml | 2 + pkg/mock/mock_client/mock_interfaces.go | 426 +++++++++++------- .../resolver/hashicorpvault_handler.go | 83 +++- .../resolver/hashicorpvault_handler_test.go | 114 ++++- pkg/scaling/resolver/scale_resolvers.go | 2 +- 9 files changed, 451 insertions(+), 184 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44ea6920faf..baceb35318f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ To learn more about active deprecations, we recommend checking [GitHub Discussio - **General**: Enable OpenSSF Scorecard to enhance security practices across the project ([#5913](https://github.com/kedacore/keda/issues/5913)) - **General**: Introduce new NSQ scaler ([#3281](https://github.com/kedacore/keda/issues/3281)) - **General**: Operator flag to control patching of webhook resources certificates ([#6184](https://github.com/kedacore/keda/issues/6184)) +- **General**: Vault authentication via cross-namespace service accounts ([#6153](https://github.com/kedacore/keda/issues/6153)) #### Experimental diff --git a/Makefile b/Makefile index b1d6e561d2f..884d77e49fc 100644 --- a/Makefile +++ b/Makefile @@ -182,7 +182,7 @@ pkg/mock/mock_scale/mock_interfaces.go: vendor/k8s.io/client-go/scale/interfaces $(MOCKGEN) k8s.io/client-go/scale ScalesGetter,ScaleInterface > $@ pkg/mock/mock_client/mock_interfaces.go: vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go mkdir -p pkg/mock/mock_client - $(MOCKGEN) sigs.k8s.io/controller-runtime/pkg/client Patch,Reader,Writer,StatusClient,StatusWriter,Client,WithWatch,FieldIndexer > $@ + $(MOCKGEN) sigs.k8s.io/controller-runtime/pkg/client Patch,Reader,Writer,StatusClient,StatusWriter,Client,WithWatch,FieldIndexer,SubResourceClient > $@ pkg/scalers/liiklus/mocks/mock_liiklus.go: $(MOCKGEN) -destination=$@ github.com/kedacore/keda/v2/pkg/scalers/liiklus LiiklusServiceClient diff --git a/apis/keda/v1alpha1/triggerauthentication_types.go b/apis/keda/v1alpha1/triggerauthentication_types.go index 0b0d9ffa315..05c801a23b7 100644 --- a/apis/keda/v1alpha1/triggerauthentication_types.go +++ b/apis/keda/v1alpha1/triggerauthentication_types.go @@ -236,6 +236,9 @@ type Credential struct { // +optional ServiceAccount string `json:"serviceAccount,omitempty"` + + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` } // VaultAuthentication contains the list of Hashicorp Vault authentication methods diff --git a/config/crd/bases/keda.sh_clustertriggerauthentications.yaml b/config/crd/bases/keda.sh_clustertriggerauthentications.yaml index d8a74647ffc..f7b2e9cd4cb 100644 --- a/config/crd/bases/keda.sh_clustertriggerauthentications.yaml +++ b/config/crd/bases/keda.sh_clustertriggerauthentications.yaml @@ -437,6 +437,8 @@ spec: properties: serviceAccount: type: string + serviceAccountName: + type: string token: type: string type: object diff --git a/config/crd/bases/keda.sh_triggerauthentications.yaml b/config/crd/bases/keda.sh_triggerauthentications.yaml index 9c38fa2ada4..c5a824bd146 100644 --- a/config/crd/bases/keda.sh_triggerauthentications.yaml +++ b/config/crd/bases/keda.sh_triggerauthentications.yaml @@ -436,6 +436,8 @@ spec: properties: serviceAccount: type: string + serviceAccountName: + type: string token: type: string type: object diff --git a/pkg/mock/mock_client/mock_interfaces.go b/pkg/mock/mock_client/mock_interfaces.go index f8a0a715dfd..bbc2dc38f54 100644 --- a/pkg/mock/mock_client/mock_interfaces.go +++ b/pkg/mock/mock_client/mock_interfaces.go @@ -1,9 +1,9 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: sigs.k8s.io/controller-runtime/pkg/client (interfaces: Patch,Reader,Writer,StatusClient,StatusWriter,Client,WithWatch,FieldIndexer) +// Source: sigs.k8s.io/controller-runtime/pkg/client (interfaces: Patch,Reader,Writer,StatusClient,StatusWriter,Client,WithWatch,FieldIndexer,SubResourceClient) // // Generated by this command: // -// mockgen sigs.k8s.io/controller-runtime/pkg/client Patch,Reader,Writer,StatusClient,StatusWriter,Client,WithWatch,FieldIndexer +// mockgen sigs.k8s.io/controller-runtime/pkg/client Patch,Reader,Writer,StatusClient,StatusWriter,Client,WithWatch,FieldIndexer,SubResourceClient // // Package mock_client is a generated GoMock package. @@ -26,6 +26,7 @@ import ( type MockPatch struct { ctrl *gomock.Controller recorder *MockPatchMockRecorder + isgomock struct{} } // MockPatchMockRecorder is the mock recorder for MockPatch. @@ -46,18 +47,18 @@ func (m *MockPatch) EXPECT() *MockPatchMockRecorder { } // Data mocks base method. -func (m *MockPatch) Data(arg0 client.Object) ([]byte, error) { +func (m *MockPatch) Data(obj client.Object) ([]byte, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Data", arg0) + ret := m.ctrl.Call(m, "Data", obj) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // Data indicates an expected call of Data. -func (mr *MockPatchMockRecorder) Data(arg0 any) *gomock.Call { +func (mr *MockPatchMockRecorder) Data(obj any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Data", reflect.TypeOf((*MockPatch)(nil).Data), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Data", reflect.TypeOf((*MockPatch)(nil).Data), obj) } // Type mocks base method. @@ -78,6 +79,7 @@ func (mr *MockPatchMockRecorder) Type() *gomock.Call { type MockReader struct { ctrl *gomock.Controller recorder *MockReaderMockRecorder + isgomock struct{} } // MockReaderMockRecorder is the mock recorder for MockReader. @@ -98,10 +100,10 @@ func (m *MockReader) EXPECT() *MockReaderMockRecorder { } // Get mocks base method. -func (m *MockReader) Get(arg0 context.Context, arg1 types.NamespacedName, arg2 client.Object, arg3 ...client.GetOption) error { +func (m *MockReader) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1, arg2} - for _, a := range arg3 { + varargs := []any{ctx, key, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Get", varargs...) @@ -110,17 +112,17 @@ func (m *MockReader) Get(arg0 context.Context, arg1 types.NamespacedName, arg2 c } // Get indicates an expected call of Get. -func (mr *MockReaderMockRecorder) Get(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call { +func (mr *MockReaderMockRecorder) Get(ctx, key, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1, arg2}, arg3...) + varargs := append([]any{ctx, key, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockReader)(nil).Get), varargs...) } // List mocks base method. -func (m *MockReader) List(arg0 context.Context, arg1 client.ObjectList, arg2 ...client.ListOption) error { +func (m *MockReader) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, list} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "List", varargs...) @@ -129,9 +131,9 @@ func (m *MockReader) List(arg0 context.Context, arg1 client.ObjectList, arg2 ... } // List indicates an expected call of List. -func (mr *MockReaderMockRecorder) List(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockReaderMockRecorder) List(ctx, list any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, list}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockReader)(nil).List), varargs...) } @@ -139,6 +141,7 @@ func (mr *MockReaderMockRecorder) List(arg0, arg1 any, arg2 ...any) *gomock.Call type MockWriter struct { ctrl *gomock.Controller recorder *MockWriterMockRecorder + isgomock struct{} } // MockWriterMockRecorder is the mock recorder for MockWriter. @@ -159,10 +162,10 @@ func (m *MockWriter) EXPECT() *MockWriterMockRecorder { } // Create mocks base method. -func (m *MockWriter) Create(arg0 context.Context, arg1 client.Object, arg2 ...client.CreateOption) error { +func (m *MockWriter) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Create", varargs...) @@ -171,17 +174,17 @@ func (m *MockWriter) Create(arg0 context.Context, arg1 client.Object, arg2 ...cl } // Create indicates an expected call of Create. -func (mr *MockWriterMockRecorder) Create(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockWriterMockRecorder) Create(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockWriter)(nil).Create), varargs...) } // Delete mocks base method. -func (m *MockWriter) Delete(arg0 context.Context, arg1 client.Object, arg2 ...client.DeleteOption) error { +func (m *MockWriter) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Delete", varargs...) @@ -190,17 +193,17 @@ func (m *MockWriter) Delete(arg0 context.Context, arg1 client.Object, arg2 ...cl } // Delete indicates an expected call of Delete. -func (mr *MockWriterMockRecorder) Delete(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockWriterMockRecorder) Delete(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockWriter)(nil).Delete), varargs...) } // DeleteAllOf mocks base method. -func (m *MockWriter) DeleteAllOf(arg0 context.Context, arg1 client.Object, arg2 ...client.DeleteAllOfOption) error { +func (m *MockWriter) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DeleteAllOf", varargs...) @@ -209,17 +212,17 @@ func (m *MockWriter) DeleteAllOf(arg0 context.Context, arg1 client.Object, arg2 } // DeleteAllOf indicates an expected call of DeleteAllOf. -func (mr *MockWriterMockRecorder) DeleteAllOf(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockWriterMockRecorder) DeleteAllOf(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllOf", reflect.TypeOf((*MockWriter)(nil).DeleteAllOf), varargs...) } // Patch mocks base method. -func (m *MockWriter) Patch(arg0 context.Context, arg1 client.Object, arg2 client.Patch, arg3 ...client.PatchOption) error { +func (m *MockWriter) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1, arg2} - for _, a := range arg3 { + varargs := []any{ctx, obj, patch} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Patch", varargs...) @@ -228,17 +231,17 @@ func (m *MockWriter) Patch(arg0 context.Context, arg1 client.Object, arg2 client } // Patch indicates an expected call of Patch. -func (mr *MockWriterMockRecorder) Patch(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call { +func (mr *MockWriterMockRecorder) Patch(ctx, obj, patch any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1, arg2}, arg3...) + varargs := append([]any{ctx, obj, patch}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockWriter)(nil).Patch), varargs...) } // Update mocks base method. -func (m *MockWriter) Update(arg0 context.Context, arg1 client.Object, arg2 ...client.UpdateOption) error { +func (m *MockWriter) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Update", varargs...) @@ -247,9 +250,9 @@ func (m *MockWriter) Update(arg0 context.Context, arg1 client.Object, arg2 ...cl } // Update indicates an expected call of Update. -func (mr *MockWriterMockRecorder) Update(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockWriterMockRecorder) Update(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockWriter)(nil).Update), varargs...) } @@ -257,6 +260,7 @@ func (mr *MockWriterMockRecorder) Update(arg0, arg1 any, arg2 ...any) *gomock.Ca type MockStatusClient struct { ctrl *gomock.Controller recorder *MockStatusClientMockRecorder + isgomock struct{} } // MockStatusClientMockRecorder is the mock recorder for MockStatusClient. @@ -294,6 +298,7 @@ func (mr *MockStatusClientMockRecorder) Status() *gomock.Call { type MockStatusWriter struct { ctrl *gomock.Controller recorder *MockStatusWriterMockRecorder + isgomock struct{} } // MockStatusWriterMockRecorder is the mock recorder for MockStatusWriter. @@ -314,10 +319,10 @@ func (m *MockStatusWriter) EXPECT() *MockStatusWriterMockRecorder { } // Create mocks base method. -func (m *MockStatusWriter) Create(arg0 context.Context, arg1, arg2 client.Object, arg3 ...client.SubResourceCreateOption) error { +func (m *MockStatusWriter) Create(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceCreateOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1, arg2} - for _, a := range arg3 { + varargs := []any{ctx, obj, subResource} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Create", varargs...) @@ -326,17 +331,17 @@ func (m *MockStatusWriter) Create(arg0 context.Context, arg1, arg2 client.Object } // Create indicates an expected call of Create. -func (mr *MockStatusWriterMockRecorder) Create(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call { +func (mr *MockStatusWriterMockRecorder) Create(ctx, obj, subResource any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1, arg2}, arg3...) + varargs := append([]any{ctx, obj, subResource}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockStatusWriter)(nil).Create), varargs...) } // Patch mocks base method. -func (m *MockStatusWriter) Patch(arg0 context.Context, arg1 client.Object, arg2 client.Patch, arg3 ...client.SubResourcePatchOption) error { +func (m *MockStatusWriter) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1, arg2} - for _, a := range arg3 { + varargs := []any{ctx, obj, patch} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Patch", varargs...) @@ -345,17 +350,17 @@ func (m *MockStatusWriter) Patch(arg0 context.Context, arg1 client.Object, arg2 } // Patch indicates an expected call of Patch. -func (mr *MockStatusWriterMockRecorder) Patch(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call { +func (mr *MockStatusWriterMockRecorder) Patch(ctx, obj, patch any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1, arg2}, arg3...) + varargs := append([]any{ctx, obj, patch}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockStatusWriter)(nil).Patch), varargs...) } // Update mocks base method. -func (m *MockStatusWriter) Update(arg0 context.Context, arg1 client.Object, arg2 ...client.SubResourceUpdateOption) error { +func (m *MockStatusWriter) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Update", varargs...) @@ -364,9 +369,9 @@ func (m *MockStatusWriter) Update(arg0 context.Context, arg1 client.Object, arg2 } // Update indicates an expected call of Update. -func (mr *MockStatusWriterMockRecorder) Update(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockStatusWriterMockRecorder) Update(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockStatusWriter)(nil).Update), varargs...) } @@ -374,6 +379,7 @@ func (mr *MockStatusWriterMockRecorder) Update(arg0, arg1 any, arg2 ...any) *gom type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder + isgomock struct{} } // MockClientMockRecorder is the mock recorder for MockClient. @@ -394,10 +400,10 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder { } // Create mocks base method. -func (m *MockClient) Create(arg0 context.Context, arg1 client.Object, arg2 ...client.CreateOption) error { +func (m *MockClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Create", varargs...) @@ -406,17 +412,17 @@ func (m *MockClient) Create(arg0 context.Context, arg1 client.Object, arg2 ...cl } // Create indicates an expected call of Create. -func (mr *MockClientMockRecorder) Create(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockClientMockRecorder) Create(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockClient)(nil).Create), varargs...) } // Delete mocks base method. -func (m *MockClient) Delete(arg0 context.Context, arg1 client.Object, arg2 ...client.DeleteOption) error { +func (m *MockClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Delete", varargs...) @@ -425,17 +431,17 @@ func (m *MockClient) Delete(arg0 context.Context, arg1 client.Object, arg2 ...cl } // Delete indicates an expected call of Delete. -func (mr *MockClientMockRecorder) Delete(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockClientMockRecorder) Delete(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockClient)(nil).Delete), varargs...) } // DeleteAllOf mocks base method. -func (m *MockClient) DeleteAllOf(arg0 context.Context, arg1 client.Object, arg2 ...client.DeleteAllOfOption) error { +func (m *MockClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DeleteAllOf", varargs...) @@ -444,17 +450,17 @@ func (m *MockClient) DeleteAllOf(arg0 context.Context, arg1 client.Object, arg2 } // DeleteAllOf indicates an expected call of DeleteAllOf. -func (mr *MockClientMockRecorder) DeleteAllOf(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockClientMockRecorder) DeleteAllOf(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllOf", reflect.TypeOf((*MockClient)(nil).DeleteAllOf), varargs...) } // Get mocks base method. -func (m *MockClient) Get(arg0 context.Context, arg1 types.NamespacedName, arg2 client.Object, arg3 ...client.GetOption) error { +func (m *MockClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1, arg2} - for _, a := range arg3 { + varargs := []any{ctx, key, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Get", varargs...) @@ -463,47 +469,47 @@ func (m *MockClient) Get(arg0 context.Context, arg1 types.NamespacedName, arg2 c } // Get indicates an expected call of Get. -func (mr *MockClientMockRecorder) Get(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call { +func (mr *MockClientMockRecorder) Get(ctx, key, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1, arg2}, arg3...) + varargs := append([]any{ctx, key, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClient)(nil).Get), varargs...) } // GroupVersionKindFor mocks base method. -func (m *MockClient) GroupVersionKindFor(arg0 runtime.Object) (schema.GroupVersionKind, error) { +func (m *MockClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GroupVersionKindFor", arg0) + ret := m.ctrl.Call(m, "GroupVersionKindFor", obj) ret0, _ := ret[0].(schema.GroupVersionKind) ret1, _ := ret[1].(error) return ret0, ret1 } // GroupVersionKindFor indicates an expected call of GroupVersionKindFor. -func (mr *MockClientMockRecorder) GroupVersionKindFor(arg0 any) *gomock.Call { +func (mr *MockClientMockRecorder) GroupVersionKindFor(obj any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupVersionKindFor", reflect.TypeOf((*MockClient)(nil).GroupVersionKindFor), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupVersionKindFor", reflect.TypeOf((*MockClient)(nil).GroupVersionKindFor), obj) } // IsObjectNamespaced mocks base method. -func (m *MockClient) IsObjectNamespaced(arg0 runtime.Object) (bool, error) { +func (m *MockClient) IsObjectNamespaced(obj runtime.Object) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsObjectNamespaced", arg0) + ret := m.ctrl.Call(m, "IsObjectNamespaced", obj) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsObjectNamespaced indicates an expected call of IsObjectNamespaced. -func (mr *MockClientMockRecorder) IsObjectNamespaced(arg0 any) *gomock.Call { +func (mr *MockClientMockRecorder) IsObjectNamespaced(obj any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsObjectNamespaced", reflect.TypeOf((*MockClient)(nil).IsObjectNamespaced), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsObjectNamespaced", reflect.TypeOf((*MockClient)(nil).IsObjectNamespaced), obj) } // List mocks base method. -func (m *MockClient) List(arg0 context.Context, arg1 client.ObjectList, arg2 ...client.ListOption) error { +func (m *MockClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, list} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "List", varargs...) @@ -512,17 +518,17 @@ func (m *MockClient) List(arg0 context.Context, arg1 client.ObjectList, arg2 ... } // List indicates an expected call of List. -func (mr *MockClientMockRecorder) List(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockClientMockRecorder) List(ctx, list any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, list}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockClient)(nil).List), varargs...) } // Patch mocks base method. -func (m *MockClient) Patch(arg0 context.Context, arg1 client.Object, arg2 client.Patch, arg3 ...client.PatchOption) error { +func (m *MockClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1, arg2} - for _, a := range arg3 { + varargs := []any{ctx, obj, patch} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Patch", varargs...) @@ -531,9 +537,9 @@ func (m *MockClient) Patch(arg0 context.Context, arg1 client.Object, arg2 client } // Patch indicates an expected call of Patch. -func (mr *MockClientMockRecorder) Patch(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call { +func (mr *MockClientMockRecorder) Patch(ctx, obj, patch any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1, arg2}, arg3...) + varargs := append([]any{ctx, obj, patch}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockClient)(nil).Patch), varargs...) } @@ -580,24 +586,24 @@ func (mr *MockClientMockRecorder) Status() *gomock.Call { } // SubResource mocks base method. -func (m *MockClient) SubResource(arg0 string) client.SubResourceClient { +func (m *MockClient) SubResource(subResource string) client.SubResourceClient { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubResource", arg0) + ret := m.ctrl.Call(m, "SubResource", subResource) ret0, _ := ret[0].(client.SubResourceClient) return ret0 } // SubResource indicates an expected call of SubResource. -func (mr *MockClientMockRecorder) SubResource(arg0 any) *gomock.Call { +func (mr *MockClientMockRecorder) SubResource(subResource any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubResource", reflect.TypeOf((*MockClient)(nil).SubResource), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubResource", reflect.TypeOf((*MockClient)(nil).SubResource), subResource) } // Update mocks base method. -func (m *MockClient) Update(arg0 context.Context, arg1 client.Object, arg2 ...client.UpdateOption) error { +func (m *MockClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Update", varargs...) @@ -606,9 +612,9 @@ func (m *MockClient) Update(arg0 context.Context, arg1 client.Object, arg2 ...cl } // Update indicates an expected call of Update. -func (mr *MockClientMockRecorder) Update(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockClientMockRecorder) Update(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockClient)(nil).Update), varargs...) } @@ -616,6 +622,7 @@ func (mr *MockClientMockRecorder) Update(arg0, arg1 any, arg2 ...any) *gomock.Ca type MockWithWatch struct { ctrl *gomock.Controller recorder *MockWithWatchMockRecorder + isgomock struct{} } // MockWithWatchMockRecorder is the mock recorder for MockWithWatch. @@ -636,10 +643,10 @@ func (m *MockWithWatch) EXPECT() *MockWithWatchMockRecorder { } // Create mocks base method. -func (m *MockWithWatch) Create(arg0 context.Context, arg1 client.Object, arg2 ...client.CreateOption) error { +func (m *MockWithWatch) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Create", varargs...) @@ -648,17 +655,17 @@ func (m *MockWithWatch) Create(arg0 context.Context, arg1 client.Object, arg2 .. } // Create indicates an expected call of Create. -func (mr *MockWithWatchMockRecorder) Create(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockWithWatchMockRecorder) Create(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockWithWatch)(nil).Create), varargs...) } // Delete mocks base method. -func (m *MockWithWatch) Delete(arg0 context.Context, arg1 client.Object, arg2 ...client.DeleteOption) error { +func (m *MockWithWatch) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Delete", varargs...) @@ -667,17 +674,17 @@ func (m *MockWithWatch) Delete(arg0 context.Context, arg1 client.Object, arg2 .. } // Delete indicates an expected call of Delete. -func (mr *MockWithWatchMockRecorder) Delete(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockWithWatchMockRecorder) Delete(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockWithWatch)(nil).Delete), varargs...) } // DeleteAllOf mocks base method. -func (m *MockWithWatch) DeleteAllOf(arg0 context.Context, arg1 client.Object, arg2 ...client.DeleteAllOfOption) error { +func (m *MockWithWatch) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DeleteAllOf", varargs...) @@ -686,17 +693,17 @@ func (m *MockWithWatch) DeleteAllOf(arg0 context.Context, arg1 client.Object, ar } // DeleteAllOf indicates an expected call of DeleteAllOf. -func (mr *MockWithWatchMockRecorder) DeleteAllOf(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockWithWatchMockRecorder) DeleteAllOf(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllOf", reflect.TypeOf((*MockWithWatch)(nil).DeleteAllOf), varargs...) } // Get mocks base method. -func (m *MockWithWatch) Get(arg0 context.Context, arg1 types.NamespacedName, arg2 client.Object, arg3 ...client.GetOption) error { +func (m *MockWithWatch) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1, arg2} - for _, a := range arg3 { + varargs := []any{ctx, key, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Get", varargs...) @@ -705,47 +712,47 @@ func (m *MockWithWatch) Get(arg0 context.Context, arg1 types.NamespacedName, arg } // Get indicates an expected call of Get. -func (mr *MockWithWatchMockRecorder) Get(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call { +func (mr *MockWithWatchMockRecorder) Get(ctx, key, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1, arg2}, arg3...) + varargs := append([]any{ctx, key, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockWithWatch)(nil).Get), varargs...) } // GroupVersionKindFor mocks base method. -func (m *MockWithWatch) GroupVersionKindFor(arg0 runtime.Object) (schema.GroupVersionKind, error) { +func (m *MockWithWatch) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GroupVersionKindFor", arg0) + ret := m.ctrl.Call(m, "GroupVersionKindFor", obj) ret0, _ := ret[0].(schema.GroupVersionKind) ret1, _ := ret[1].(error) return ret0, ret1 } // GroupVersionKindFor indicates an expected call of GroupVersionKindFor. -func (mr *MockWithWatchMockRecorder) GroupVersionKindFor(arg0 any) *gomock.Call { +func (mr *MockWithWatchMockRecorder) GroupVersionKindFor(obj any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupVersionKindFor", reflect.TypeOf((*MockWithWatch)(nil).GroupVersionKindFor), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupVersionKindFor", reflect.TypeOf((*MockWithWatch)(nil).GroupVersionKindFor), obj) } // IsObjectNamespaced mocks base method. -func (m *MockWithWatch) IsObjectNamespaced(arg0 runtime.Object) (bool, error) { +func (m *MockWithWatch) IsObjectNamespaced(obj runtime.Object) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsObjectNamespaced", arg0) + ret := m.ctrl.Call(m, "IsObjectNamespaced", obj) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsObjectNamespaced indicates an expected call of IsObjectNamespaced. -func (mr *MockWithWatchMockRecorder) IsObjectNamespaced(arg0 any) *gomock.Call { +func (mr *MockWithWatchMockRecorder) IsObjectNamespaced(obj any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsObjectNamespaced", reflect.TypeOf((*MockWithWatch)(nil).IsObjectNamespaced), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsObjectNamespaced", reflect.TypeOf((*MockWithWatch)(nil).IsObjectNamespaced), obj) } // List mocks base method. -func (m *MockWithWatch) List(arg0 context.Context, arg1 client.ObjectList, arg2 ...client.ListOption) error { +func (m *MockWithWatch) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, list} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "List", varargs...) @@ -754,17 +761,17 @@ func (m *MockWithWatch) List(arg0 context.Context, arg1 client.ObjectList, arg2 } // List indicates an expected call of List. -func (mr *MockWithWatchMockRecorder) List(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockWithWatchMockRecorder) List(ctx, list any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, list}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockWithWatch)(nil).List), varargs...) } // Patch mocks base method. -func (m *MockWithWatch) Patch(arg0 context.Context, arg1 client.Object, arg2 client.Patch, arg3 ...client.PatchOption) error { +func (m *MockWithWatch) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1, arg2} - for _, a := range arg3 { + varargs := []any{ctx, obj, patch} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Patch", varargs...) @@ -773,9 +780,9 @@ func (m *MockWithWatch) Patch(arg0 context.Context, arg1 client.Object, arg2 cli } // Patch indicates an expected call of Patch. -func (mr *MockWithWatchMockRecorder) Patch(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call { +func (mr *MockWithWatchMockRecorder) Patch(ctx, obj, patch any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1, arg2}, arg3...) + varargs := append([]any{ctx, obj, patch}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockWithWatch)(nil).Patch), varargs...) } @@ -822,24 +829,24 @@ func (mr *MockWithWatchMockRecorder) Status() *gomock.Call { } // SubResource mocks base method. -func (m *MockWithWatch) SubResource(arg0 string) client.SubResourceClient { +func (m *MockWithWatch) SubResource(subResource string) client.SubResourceClient { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubResource", arg0) + ret := m.ctrl.Call(m, "SubResource", subResource) ret0, _ := ret[0].(client.SubResourceClient) return ret0 } // SubResource indicates an expected call of SubResource. -func (mr *MockWithWatchMockRecorder) SubResource(arg0 any) *gomock.Call { +func (mr *MockWithWatchMockRecorder) SubResource(subResource any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubResource", reflect.TypeOf((*MockWithWatch)(nil).SubResource), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubResource", reflect.TypeOf((*MockWithWatch)(nil).SubResource), subResource) } // Update mocks base method. -func (m *MockWithWatch) Update(arg0 context.Context, arg1 client.Object, arg2 ...client.UpdateOption) error { +func (m *MockWithWatch) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Update", varargs...) @@ -848,17 +855,17 @@ func (m *MockWithWatch) Update(arg0 context.Context, arg1 client.Object, arg2 .. } // Update indicates an expected call of Update. -func (mr *MockWithWatchMockRecorder) Update(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockWithWatchMockRecorder) Update(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockWithWatch)(nil).Update), varargs...) } // Watch mocks base method. -func (m *MockWithWatch) Watch(arg0 context.Context, arg1 client.ObjectList, arg2 ...client.ListOption) (watch.Interface, error) { +func (m *MockWithWatch) Watch(ctx context.Context, obj client.ObjectList, opts ...client.ListOption) (watch.Interface, error) { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, obj} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Watch", varargs...) @@ -868,9 +875,9 @@ func (m *MockWithWatch) Watch(arg0 context.Context, arg1 client.ObjectList, arg2 } // Watch indicates an expected call of Watch. -func (mr *MockWithWatchMockRecorder) Watch(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockWithWatchMockRecorder) Watch(ctx, obj any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockWithWatch)(nil).Watch), varargs...) } @@ -878,6 +885,7 @@ func (mr *MockWithWatchMockRecorder) Watch(arg0, arg1 any, arg2 ...any) *gomock. type MockFieldIndexer struct { ctrl *gomock.Controller recorder *MockFieldIndexerMockRecorder + isgomock struct{} } // MockFieldIndexerMockRecorder is the mock recorder for MockFieldIndexer. @@ -898,15 +906,115 @@ func (m *MockFieldIndexer) EXPECT() *MockFieldIndexerMockRecorder { } // IndexField mocks base method. -func (m *MockFieldIndexer) IndexField(arg0 context.Context, arg1 client.Object, arg2 string, arg3 client.IndexerFunc) error { +func (m *MockFieldIndexer) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IndexField", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "IndexField", ctx, obj, field, extractValue) ret0, _ := ret[0].(error) return ret0 } // IndexField indicates an expected call of IndexField. -func (mr *MockFieldIndexerMockRecorder) IndexField(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockFieldIndexerMockRecorder) IndexField(ctx, obj, field, extractValue any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexField", reflect.TypeOf((*MockFieldIndexer)(nil).IndexField), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexField", reflect.TypeOf((*MockFieldIndexer)(nil).IndexField), ctx, obj, field, extractValue) +} + +// MockSubResourceClient is a mock of SubResourceClient interface. +type MockSubResourceClient struct { + ctrl *gomock.Controller + recorder *MockSubResourceClientMockRecorder + isgomock struct{} +} + +// MockSubResourceClientMockRecorder is the mock recorder for MockSubResourceClient. +type MockSubResourceClientMockRecorder struct { + mock *MockSubResourceClient +} + +// NewMockSubResourceClient creates a new mock instance. +func NewMockSubResourceClient(ctrl *gomock.Controller) *MockSubResourceClient { + mock := &MockSubResourceClient{ctrl: ctrl} + mock.recorder = &MockSubResourceClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSubResourceClient) EXPECT() *MockSubResourceClientMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockSubResourceClient) Create(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceCreateOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj, subResource} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Create", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockSubResourceClientMockRecorder) Create(ctx, obj, subResource any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj, subResource}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockSubResourceClient)(nil).Create), varargs...) +} + +// Get mocks base method. +func (m *MockSubResourceClient) Get(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceGetOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj, subResource} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Get", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Get indicates an expected call of Get. +func (mr *MockSubResourceClientMockRecorder) Get(ctx, obj, subResource any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj, subResource}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockSubResourceClient)(nil).Get), varargs...) +} + +// Patch mocks base method. +func (m *MockSubResourceClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj, patch} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Patch", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Patch indicates an expected call of Patch. +func (mr *MockSubResourceClientMockRecorder) Patch(ctx, obj, patch any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj, patch}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockSubResourceClient)(nil).Patch), varargs...) +} + +// Update mocks base method. +func (m *MockSubResourceClient) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Update", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockSubResourceClientMockRecorder) Update(ctx, obj any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockSubResourceClient)(nil).Update), varargs...) } diff --git a/pkg/scaling/resolver/hashicorpvault_handler.go b/pkg/scaling/resolver/hashicorpvault_handler.go index e80b7302cda..7dedb33dcbe 100644 --- a/pkg/scaling/resolver/hashicorpvault_handler.go +++ b/pkg/scaling/resolver/hashicorpvault_handler.go @@ -17,29 +17,41 @@ limitations under the License. package resolver import ( + "context" "encoding/json" "errors" "fmt" "os" "strings" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "github.com/go-logr/logr" vaultapi "github.com/hashicorp/vault/api" + "k8s.io/apimachinery/pkg/types" kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" + authenticationv1 "k8s.io/api/authentication/v1" + + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" ) // HashicorpVaultHandler is specification of Hashi Corp Vault type HashicorpVaultHandler struct { - vault *kedav1alpha1.HashiCorpVault - client *vaultapi.Client - stopCh chan struct{} + vault *kedav1alpha1.HashiCorpVault + client *vaultapi.Client + k8sClient client.Client + namespace string + stopCh chan struct{} } // NewHashicorpVaultHandler creates a HashicorpVaultHandler object -func NewHashicorpVaultHandler(v *kedav1alpha1.HashiCorpVault) *HashicorpVaultHandler { +func NewHashicorpVaultHandler(v *kedav1alpha1.HashiCorpVault, client client.Client, namespace string) *HashicorpVaultHandler { return &HashicorpVaultHandler{ - vault: v, + vault: v, + k8sClient: client, + namespace: namespace, } } @@ -88,6 +100,8 @@ func (vh *HashicorpVaultHandler) Initialize(logger logr.Logger) error { // token Extract a vault token from the Authentication method func (vh *HashicorpVaultHandler) token(client *vaultapi.Client) (string, error) { var token string + var jwt []byte + var err error switch vh.vault.Authentication { case kedav1alpha1.VaultAuthenticationToken: @@ -116,14 +130,59 @@ func (vh *HashicorpVaultHandler) token(client *vaultapi.Client) (string, error) vh.vault.Credential = &defaultCred } - if len(vh.vault.Credential.ServiceAccount) == 0 { - return token, errors.New("k8s SA file not in config") + if vh.vault.Credential.ServiceAccountName == "" && len(vh.vault.Credential.ServiceAccount) == 0 { + return token, errors.New("k8s SA file not in config or serviceAccountName not supplied") } - // Get the JWT from POD - jwt, err := os.ReadFile(vh.vault.Credential.ServiceAccount) - if err != nil { - return token, err + if vh.vault.Credential.ServiceAccountName != "" { + // generate token from namespace + saName := types.NamespacedName{Name: vh.vault.Credential.ServiceAccountName, Namespace: vh.namespace} + sa := &corev1.ServiceAccount{} + secret := &corev1.Secret{} + + if err = vh.k8sClient.Get(context.Background(), saName, sa); err != nil { + if apierrors.IsNotFound(err) { + return token, errors.New(fmt.Sprintf("Failed to retreive service account name: %s namespace: %s", saName.Name, saName.Namespace)) + } + } + + if len(sa.Secrets) > 0 { + //using legacy service account secrets + secretName := types.NamespacedName{Name: sa.Secrets[0].Name, Namespace: vh.namespace} + + if err = vh.k8sClient.Get(context.Background(), secretName, secret); err != nil { + if apierrors.IsNotFound(err) { + return token, errors.New(fmt.Sprintf("Failed to retreive secret for service account name: %s namespace: %s", secretName.Name, secretName.Namespace)) + } + } + + jwt = secret.Data["token"] + } + + if len(jwt) == 0 { + tokenTTL := int64(600) // min allowed duration is 10 mins + // this token is only used once for the initial authentication + // renewals happen independently on the vault token + tokenRequest := &authenticationv1.TokenRequest{ + Spec: authenticationv1.TokenRequestSpec{ + Audiences: []string{"https://kubernetes.default.svc"}, + ExpirationSeconds: &tokenTTL, + }, + } + + if err := vh.k8sClient.SubResource("token").Create(context.TODO(), sa, tokenRequest); err != nil { + return token, errors.New(fmt.Sprintf("Failed to create token for service account name: %s namespace: %s", saName.Name, saName.Namespace)) + } + + jwt = []byte(tokenRequest.Status.Token) + } + + } else if len(vh.vault.Credential.ServiceAccount) != 0 { + // Get the JWT from POD + jwt, err = os.ReadFile(vh.vault.Credential.ServiceAccount) + if err != nil { + return token, err + } } data := map[string]interface{}{"jwt": string(jwt), "role": vh.vault.Role} @@ -131,8 +190,8 @@ func (vh *HashicorpVaultHandler) token(client *vaultapi.Client) (string, error) if err != nil { return token, err } - token = secret.Auth.ClientToken + default: return token, fmt.Errorf("vault auth method %s is not supported", vh.vault.Authentication) } diff --git a/pkg/scaling/resolver/hashicorpvault_handler_test.go b/pkg/scaling/resolver/hashicorpvault_handler_test.go index 5051faeb332..0e672df2459 100644 --- a/pkg/scaling/resolver/hashicorpvault_handler_test.go +++ b/pkg/scaling/resolver/hashicorpvault_handler_test.go @@ -17,6 +17,7 @@ limitations under the License. package resolver import ( + "context" "encoding/base64" "encoding/json" "fmt" @@ -27,9 +28,15 @@ import ( vaultapi "github.com/hashicorp/vault/api" "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" logf "sigs.k8s.io/controller-runtime/pkg/log" kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" + "github.com/kedacore/keda/v2/pkg/mock/mock_client" + authenticationv1 "k8s.io/api/authentication/v1" + "sigs.k8s.io/controller-runtime/pkg/client" ) const ( @@ -71,6 +78,11 @@ var ( "test": kedaSecretValue, "array": []string{kedaSecretValue}, } + kubernetesAuthDataKeda = map[string]interface{}{ + "auth": map[string]interface{}{ + "client_token": vaultTestToken, + }, + } ) type pkiRequestTestData struct { @@ -93,7 +105,10 @@ var pkiRequestTestDataset = []pkiRequestTestData{ } func TestGetPkiRequest(t *testing.T) { - vault := NewHashicorpVaultHandler(nil) + ctrl := gomock.NewController(t) + client := mock_client.NewMockClient(ctrl) + + vault := NewHashicorpVaultHandler(nil, client, "default") for _, testData := range pkiRequestTestDataset { var secret kedav1alpha1.VaultSecret @@ -119,6 +134,7 @@ func TestGetPkiRequest(t *testing.T) { func mockVault(t *testing.T, useRootToken bool) *httptest.Server { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var data map[string]interface{} + var auth *vaultapi.SecretAuth switch r.URL.Path { case "/v1/auth/token/lookup-self": data = vaultTokenSelf @@ -148,7 +164,10 @@ func mockVault(t *testing.T, useRootToken bool) *httptest.Server { "private_key_type": "rsa", "serial_number": "4c:79:c6:2c:23:65:77:73:c2:79:49:8c:c8:fe:ad:e3:78:68:0f:86", } - + case "/v1/auth/kubernetes/login": + auth = &vaultapi.SecretAuth{ + ClientToken: vaultTestToken, + } default: t.Logf("Got request at path %s", r.URL.Path) w.WriteHeader(404) @@ -161,7 +180,7 @@ func mockVault(t *testing.T, useRootToken bool) *httptest.Server { Data: data, Renewable: false, Warnings: nil, - Auth: nil, + Auth: auth, WrapInfo: nil, } var out, _ = json.Marshal(secret) @@ -173,6 +192,8 @@ func mockVault(t *testing.T, useRootToken bool) *httptest.Server { func TestHashicorpVaultHandler_getSecretValue_specify_secret_type(t *testing.T) { server := mockVault(t, false) defer server.Close() + ctrl := gomock.NewController(t) + client := mock_client.NewMockClient(ctrl) vault := kedav1alpha1.HashiCorpVault{ Address: server.URL, @@ -181,7 +202,7 @@ func TestHashicorpVaultHandler_getSecretValue_specify_secret_type(t *testing.T) Token: vaultTestToken, }, } - vaultHandler := NewHashicorpVaultHandler(&vault) + vaultHandler := NewHashicorpVaultHandler(&vault, client, "default") err := vaultHandler.Initialize(logf.Log.WithName("test")) defer vaultHandler.Stop() assert.Nil(t, err) @@ -313,6 +334,8 @@ var resolveRequestTestDataSet = []resolveRequestTestData{ func TestHashicorpVaultHandler_ResolveSecret(t *testing.T) { server := mockVault(t, false) defer server.Close() + ctrl := gomock.NewController(t) + client := mock_client.NewMockClient(ctrl) vault := kedav1alpha1.HashiCorpVault{ Address: server.URL, @@ -321,7 +344,7 @@ func TestHashicorpVaultHandler_ResolveSecret(t *testing.T) { Token: vaultTestToken, }, } - vaultHandler := NewHashicorpVaultHandler(&vault) + vaultHandler := NewHashicorpVaultHandler(&vault, client, "default") err := vaultHandler.Initialize(logf.Log.WithName("test")) defer vaultHandler.Stop() assert.Nil(t, err) @@ -357,7 +380,10 @@ func TestHashicorpVaultHandler_ResolveSecret_UsingRootToken(t *testing.T) { Token: vaultTestToken, }, } - vaultHandler := NewHashicorpVaultHandler(&vault) + ctrl := gomock.NewController(t) + client := mock_client.NewMockClient(ctrl) + + vaultHandler := NewHashicorpVaultHandler(&vault, client, "default") err := vaultHandler.Initialize(logf.Log.WithName("test")) defer vaultHandler.Stop() assert.Nil(t, err) @@ -386,6 +412,8 @@ func TestHashicorpVaultHandler_DefaultKubernetesVaultRole(t *testing.T) { defaultServiceAccountPath := "/var/run/secrets/kubernetes.io/serviceaccount/token" server := mockVault(t, false) defer server.Close() + ctrl := gomock.NewController(t) + client := mock_client.NewMockClient(ctrl) vault := kedav1alpha1.HashiCorpVault{ Address: server.URL, @@ -394,7 +422,7 @@ func TestHashicorpVaultHandler_DefaultKubernetesVaultRole(t *testing.T) { Role: "my-role", } - vaultHandler := NewHashicorpVaultHandler(&vault) + vaultHandler := NewHashicorpVaultHandler(&vault, client, "default") err := vaultHandler.Initialize(logf.Log.WithName("test")) defer vaultHandler.Stop() assert.Errorf(t, err, "open %s : no such file or directory", defaultServiceAccountPath) @@ -404,6 +432,8 @@ func TestHashicorpVaultHandler_DefaultKubernetesVaultRole(t *testing.T) { func TestHashicorpVaultHandler_ResolveSecrets_SameCertAndKey(t *testing.T) { server := mockVault(t, false) defer server.Close() + ctrl := gomock.NewController(t) + client := mock_client.NewMockClient(ctrl) vault := kedav1alpha1.HashiCorpVault{ Address: server.URL, @@ -412,7 +442,7 @@ func TestHashicorpVaultHandler_ResolveSecrets_SameCertAndKey(t *testing.T) { Token: vaultTestToken, }, } - vaultHandler := NewHashicorpVaultHandler(&vault) + vaultHandler := NewHashicorpVaultHandler(&vault, client, "default") err := vaultHandler.Initialize(logf.Log.WithName("test")) defer vaultHandler.Stop() assert.Nil(t, err) @@ -473,6 +503,9 @@ func TestHashicorpVaultHandler_fetchSecret(t *testing.T) { server := mockVault(t, false) defer server.Close() + ctrl := gomock.NewController(t) + client := mock_client.NewMockClient(ctrl) + vault := kedav1alpha1.HashiCorpVault{ Address: server.URL, Authentication: kedav1alpha1.VaultAuthenticationToken, @@ -480,7 +513,8 @@ func TestHashicorpVaultHandler_fetchSecret(t *testing.T) { Token: vaultTestToken, }, } - vaultHandler := NewHashicorpVaultHandler(&vault) + + vaultHandler := NewHashicorpVaultHandler(&vault, client, "default") err := vaultHandler.Initialize(logf.Log.WithName("test")) defer vaultHandler.Stop() assert.Nil(t, err) @@ -526,6 +560,9 @@ func TestHashicorpVaultHandler_Initialize(t *testing.T) { server := mockVault(t, false) defer server.Close() + ctrl := gomock.NewController(t) + client := mock_client.NewMockClient(ctrl) + for _, testData := range initialiseTestDataSet { func() { vault := kedav1alpha1.HashiCorpVault{ @@ -536,7 +573,7 @@ func TestHashicorpVaultHandler_Initialize(t *testing.T) { }, Namespace: testData.namespace, } - vaultHandler := NewHashicorpVaultHandler(&vault) + vaultHandler := NewHashicorpVaultHandler(&vault, client, testData.namespace) err := vaultHandler.Initialize(logf.Log.WithName("test")) defer vaultHandler.Stop() assert.Nil(t, err) @@ -601,6 +638,9 @@ func TestHashicorpVaultHandler_Token_VaultTokenAuth(t *testing.T) { server := mockVault(t, false) defer server.Close() + ctrl := gomock.NewController(t) + client := mock_client.NewMockClient(ctrl) + for _, testData := range tokenTestDataSet { func() { vault := kedav1alpha1.HashiCorpVault{ @@ -610,7 +650,7 @@ func TestHashicorpVaultHandler_Token_VaultTokenAuth(t *testing.T) { Role: testData.role, Mount: testData.mount, } - vaultHandler := NewHashicorpVaultHandler(&vault) + vaultHandler := NewHashicorpVaultHandler(&vault, client, "default") defer vaultHandler.Stop() config := vaultapi.DefaultConfig() @@ -627,3 +667,55 @@ func TestHashicorpVaultHandler_Token_VaultTokenAuth(t *testing.T) { }() } } + +func TestHashicorpVaultHandler_Token_ServiceAccountAuth(t *testing.T) { + server := mockVault(t, false) + defer server.Close() + + ctrl := gomock.NewController(t) + mockK8sClient := mock_client.NewMockClient(ctrl) + mockSubresourceClient := mock_client.NewMockSubResourceClient(ctrl) + + defer ctrl.Finish() + + // Mock getting the service account + mockK8sClient.EXPECT(). + Get(gomock.Any(), types.NamespacedName{Name: "test-sa", Namespace: "default"}, gomock.AssignableToTypeOf(&corev1.ServiceAccount{})). + DoAndReturn(func(_ context.Context, _ types.NamespacedName, sa *corev1.ServiceAccount, _ ...client.GetOption) error { + sa.Name = "test-sa" + sa.Namespace = "default" + return nil + }) + + mockSubresourceClient.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.AssignableToTypeOf(&authenticationv1.TokenRequest{})). + DoAndReturn(func(ctx context.Context, obj client.Object, subResource *authenticationv1.TokenRequest, opts ...client.SubResourceCreateOption) error { + subResource.Status.Token = "test" + return nil + }) + // Mock creating the token request + mockK8sClient.EXPECT().SubResource("token").AnyTimes().Return( + mockSubresourceClient, + ) + + vault := kedav1alpha1.HashiCorpVault{ + Address: server.URL, + Authentication: kedav1alpha1.VaultAuthenticationKubernetes, + Mount: "kubernetes", + Role: "keda-role", + Credential: &kedav1alpha1.Credential{ + ServiceAccountName: "test-sa", + }, + } + + vaultHandler := NewHashicorpVaultHandler(&vault, mockK8sClient, "default") + defer vaultHandler.Stop() + + config := vaultapi.DefaultConfig() + config.Address = server.URL + client, err := vaultapi.NewClient(config) + assert.NoError(t, err) + + token, err := vaultHandler.token(client) + assert.NoError(t, err) + assert.NotEmpty(t, token) +} diff --git a/pkg/scaling/resolver/scale_resolvers.go b/pkg/scaling/resolver/scale_resolvers.go index 6ca40672fc6..a97722f15af 100644 --- a/pkg/scaling/resolver/scale_resolvers.go +++ b/pkg/scaling/resolver/scale_resolvers.go @@ -265,7 +265,7 @@ func resolveAuthRef(ctx context.Context, client client.Client, logger logr.Logge } } if triggerAuthSpec.HashiCorpVault != nil && len(triggerAuthSpec.HashiCorpVault.Secrets) > 0 { - vault := NewHashicorpVaultHandler(triggerAuthSpec.HashiCorpVault) + vault := NewHashicorpVaultHandler(triggerAuthSpec.HashiCorpVault, client, namespace) err := vault.Initialize(logger) defer vault.Stop() if err != nil { From 097fec2e1f951115753940532b91e3ab2bf90805 Mon Sep 17 00:00:00 2001 From: Bojan Zelic Date: Tue, 24 Dec 2024 10:15:21 -0700 Subject: [PATCH 2/5] General: Vault authentication via cross-namespace service accounts Signed-off-by: Bojan Zelic --- pkg/mock/mock_client/mock_interfaces.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/mock/mock_client/mock_interfaces.go b/pkg/mock/mock_client/mock_interfaces.go index e33354a3184..effee047854 100644 --- a/pkg/mock/mock_client/mock_interfaces.go +++ b/pkg/mock/mock_client/mock_interfaces.go @@ -700,9 +700,6 @@ func (mr *MockWithWatchMockRecorder) DeleteAllOf(ctx, obj any, opts ...any) *gom } // Get mocks base method. -<<<<<<< HEAD -func (m *MockWithWatch) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { -======= func (m *MockWithWatch) Get(ctx context.Context, key types.NamespacedName, obj client.Object, opts ...client.GetOption) error { for _, a := range opts { } @@ -917,7 +914,6 @@ func (m *MockFieldIndexer) IndexField(ctx context.Context, obj client.Object, fi func (mr *MockFieldIndexerMockRecorder) IndexField(ctx, obj, field, extractValue any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexField", reflect.TypeOf((*MockFieldIndexer)(nil).IndexField), ctx, obj, field, extractValue) -<<<<<<< HEAD } // MockSubResourceClient is a mock of SubResourceClient interface. @@ -1018,6 +1014,4 @@ func (mr *MockSubResourceClientMockRecorder) Update(ctx, obj any, opts ...any) * mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, obj}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockSubResourceClient)(nil).Update), varargs...) -======= ->>>>>>> 5db5a3a42a9a233eb76cffd77bd5666b4ef87a08 } From a141b41e85ca996dbd26216dcacde36da308cb43 Mon Sep 17 00:00:00 2001 From: Bojan Zelic Date: Tue, 24 Dec 2024 10:25:01 -0700 Subject: [PATCH 3/5] General: Vault authentication via cross-namespace service accounts Signed-off-by: Bojan Zelic --- pkg/scaling/resolver/hashicorpvault_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/scaling/resolver/hashicorpvault_handler.go b/pkg/scaling/resolver/hashicorpvault_handler.go index 7dedb33dcbe..f513909d35f 100644 --- a/pkg/scaling/resolver/hashicorpvault_handler.go +++ b/pkg/scaling/resolver/hashicorpvault_handler.go @@ -170,7 +170,7 @@ func (vh *HashicorpVaultHandler) token(client *vaultapi.Client) (string, error) }, } - if err := vh.k8sClient.SubResource("token").Create(context.TODO(), sa, tokenRequest); err != nil { + if err := vh.k8sClient.SubResource("token").Create(context.Background(), sa, tokenRequest); err != nil { return token, errors.New(fmt.Sprintf("Failed to create token for service account name: %s namespace: %s", saName.Name, saName.Namespace)) } From 95e7c4748034275cbc83deca5542067b5c494755 Mon Sep 17 00:00:00 2001 From: Bojan Zelic Date: Tue, 24 Dec 2024 10:40:29 -0700 Subject: [PATCH 4/5] General: Vault authentication via cross-namespace service accounts Signed-off-by: Bojan Zelic --- pkg/mock/mock_client/mock_interfaces.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/mock/mock_client/mock_interfaces.go b/pkg/mock/mock_client/mock_interfaces.go index effee047854..e41cb327bba 100644 --- a/pkg/mock/mock_client/mock_interfaces.go +++ b/pkg/mock/mock_client/mock_interfaces.go @@ -701,7 +701,10 @@ func (mr *MockWithWatchMockRecorder) DeleteAllOf(ctx, obj any, opts ...any) *gom // Get mocks base method. func (m *MockWithWatch) Get(ctx context.Context, key types.NamespacedName, obj client.Object, opts ...client.GetOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, key, obj} for _, a := range opts { + varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Get", varargs...) ret0, _ := ret[0].(error) From bfa2613f420e5ea984a7e47158dcbe3d4898527c Mon Sep 17 00:00:00 2001 From: Bojan Zelic Date: Tue, 31 Dec 2024 14:53:01 -0700 Subject: [PATCH 5/5] add e2e test Signed-off-by: Bojan Zelic --- config/e2e/kustomization.yaml | 8 ++- ...patch_operator.yml => patch_operator.yaml} | 0 config/e2e/patch_rbac.yaml | 9 +++ .../resolver/hashicorpvault_handler.go | 24 +++----- .../resolver/hashicorpvault_handler_test.go | 9 +-- .../hashicorp_vault/hashicorp_vault_test.go | 59 +++++++++++++++++-- 6 files changed, 79 insertions(+), 30 deletions(-) rename config/e2e/{patch_operator.yml => patch_operator.yaml} (100%) create mode 100644 config/e2e/patch_rbac.yaml diff --git a/config/e2e/kustomization.yaml b/config/e2e/kustomization.yaml index 8ed4cd78173..12a7cef8398 100644 --- a/config/e2e/kustomization.yaml +++ b/config/e2e/kustomization.yaml @@ -9,9 +9,15 @@ resources: apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization patches: -- path: patch_operator.yml +- path: patch_operator.yaml target: group: apps kind: Deployment name: keda-operator version: v1 +- path: patch_rbac.yaml + target: + group: rbac.authorization.k8s.io + kind: ClusterRole + name: keda-operator + version: v1 diff --git a/config/e2e/patch_operator.yml b/config/e2e/patch_operator.yaml similarity index 100% rename from config/e2e/patch_operator.yml rename to config/e2e/patch_operator.yaml diff --git a/config/e2e/patch_rbac.yaml b/config/e2e/patch_rbac.yaml new file mode 100644 index 00000000000..3ea11919d92 --- /dev/null +++ b/config/e2e/patch_rbac.yaml @@ -0,0 +1,9 @@ + - op: add + path: /rules/- + value: + apiGroups: + - "" + resources: + - serviceaccounts/token + verbs: + - create diff --git a/pkg/scaling/resolver/hashicorpvault_handler.go b/pkg/scaling/resolver/hashicorpvault_handler.go index f513909d35f..f109c75ca9a 100644 --- a/pkg/scaling/resolver/hashicorpvault_handler.go +++ b/pkg/scaling/resolver/hashicorpvault_handler.go @@ -19,22 +19,19 @@ package resolver import ( "context" "encoding/json" - "errors" "fmt" "os" "strings" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "github.com/go-logr/logr" vaultapi "github.com/hashicorp/vault/api" - "k8s.io/apimachinery/pkg/types" - - kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" + "github.com/pkg/errors" authenticationv1 "k8s.io/api/authentication/v1" - corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + + kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" ) // HashicorpVaultHandler is specification of Hashi Corp Vault @@ -141,19 +138,15 @@ func (vh *HashicorpVaultHandler) token(client *vaultapi.Client) (string, error) secret := &corev1.Secret{} if err = vh.k8sClient.Get(context.Background(), saName, sa); err != nil { - if apierrors.IsNotFound(err) { - return token, errors.New(fmt.Sprintf("Failed to retreive service account name: %s namespace: %s", saName.Name, saName.Namespace)) - } + return token, errors.Wrap(err, fmt.Sprintf("Failed to retrieve service account name: %s namespace: %s", saName.Name, saName.Namespace)) } if len(sa.Secrets) > 0 { - //using legacy service account secrets + // using legacy service account secrets secretName := types.NamespacedName{Name: sa.Secrets[0].Name, Namespace: vh.namespace} if err = vh.k8sClient.Get(context.Background(), secretName, secret); err != nil { - if apierrors.IsNotFound(err) { - return token, errors.New(fmt.Sprintf("Failed to retreive secret for service account name: %s namespace: %s", secretName.Name, secretName.Namespace)) - } + return token, errors.Wrap(err, fmt.Sprintf("Failed to retrieve secret for service account name: %s namespace: %s", secretName.Name, secretName.Namespace)) } jwt = secret.Data["token"] @@ -171,12 +164,11 @@ func (vh *HashicorpVaultHandler) token(client *vaultapi.Client) (string, error) } if err := vh.k8sClient.SubResource("token").Create(context.Background(), sa, tokenRequest); err != nil { - return token, errors.New(fmt.Sprintf("Failed to create token for service account name: %s namespace: %s", saName.Name, saName.Namespace)) + return token, errors.Wrap(err, fmt.Sprintf("Failed to create token for service account name: %s namespace: %s", saName.Name, saName.Namespace)) } jwt = []byte(tokenRequest.Status.Token) } - } else if len(vh.vault.Credential.ServiceAccount) != 0 { // Get the JWT from POD jwt, err = os.ReadFile(vh.vault.Credential.ServiceAccount) diff --git a/pkg/scaling/resolver/hashicorpvault_handler_test.go b/pkg/scaling/resolver/hashicorpvault_handler_test.go index 0e672df2459..e5c7c930c2f 100644 --- a/pkg/scaling/resolver/hashicorpvault_handler_test.go +++ b/pkg/scaling/resolver/hashicorpvault_handler_test.go @@ -29,14 +29,14 @@ import ( vaultapi "github.com/hashicorp/vault/api" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" + authenticationv1 "k8s.io/api/authentication/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" "github.com/kedacore/keda/v2/pkg/mock/mock_client" - authenticationv1 "k8s.io/api/authentication/v1" - "sigs.k8s.io/controller-runtime/pkg/client" ) const ( @@ -78,11 +78,6 @@ var ( "test": kedaSecretValue, "array": []string{kedaSecretValue}, } - kubernetesAuthDataKeda = map[string]interface{}{ - "auth": map[string]interface{}{ - "client_token": vaultTestToken, - }, - } ) type pkiRequestTestData struct { diff --git a/tests/secret-providers/hashicorp_vault/hashicorp_vault_test.go b/tests/secret-providers/hashicorp_vault/hashicorp_vault_test.go index cd135e50d4a..a81fe987eb3 100644 --- a/tests/secret-providers/hashicorp_vault/hashicorp_vault_test.go +++ b/tests/secret-providers/hashicorp_vault/hashicorp_vault_test.go @@ -70,6 +70,8 @@ type templateData struct { MonitoredAppName string PrometheusServerName string VaultPkiCommonName string + VaultRole string + VaultServiceAccountName string } const ( @@ -128,9 +130,12 @@ metadata: spec: hashiCorpVault: address: http://vault.{{.VaultNamespace}}:8200 - authentication: token + authentication: {{.HashiCorpAuthentication}} + role: {{.VaultRole}} + mount: kubernetes credential: token: {{.HashiCorpToken}} + serviceAccountName: {{.VaultServiceAccountName}} secrets: - parameter: connection key: connectionString @@ -413,6 +418,13 @@ spec: pkiPolicyTemplate = `path "pki*" { capabilities = [ "create", "read", "update", "delete", "list", "sudo" ] }` + + secretReadPolicyTemplate = `path "secret/data/keda" { + capabilities = ["read"] +} +path "secret/metadata/keda" { + capabilities = ["read", "list"] +}` ) func TestPkiSecretsEngine(t *testing.T) { @@ -432,7 +444,7 @@ func TestPkiSecretsEngine(t *testing.T) { // Create kubernetes resources kc := GetKubernetesClient(t) useKubernetesAuth := test.authentication == "kubernetes" - hashiCorpToken, promPkiData := setupHashiCorpVault(t, kc, 2, useKubernetesAuth, true) + hashiCorpToken, promPkiData := setupHashiCorpVault(t, kc, 2, useKubernetesAuth, true, false) prometheus.Install(t, kc, prometheusServerName, testNamespace, promPkiData) // Create kubernetes resources for testing @@ -460,16 +472,29 @@ func TestSecretsEngine(t *testing.T) { name string vaultEngineVersion uint vaultSecretPath string + useKubernetesAuth bool + useDelegatesSAAuth bool }{ { name: "vault kv engine v1", vaultEngineVersion: 1, vaultSecretPath: "secret/keda", + useKubernetesAuth: false, + useDelegatesSAAuth: false, + }, + { + name: "vault kv engine v2", + vaultEngineVersion: 2, + vaultSecretPath: "secret/data/keda", + useKubernetesAuth: false, + useDelegatesSAAuth: false, }, { name: "vault kv engine v2", vaultEngineVersion: 2, vaultSecretPath: "secret/data/keda", + useKubernetesAuth: true, + useDelegatesSAAuth: true, }, } @@ -480,7 +505,7 @@ func TestSecretsEngine(t *testing.T) { data, postgreSQLtemplates := getPostgreSQLTemplateData() CreateKubernetesResources(t, kc, testNamespace, data, postgreSQLtemplates) - hashiCorpToken, _ := setupHashiCorpVault(t, kc, test.vaultEngineVersion, false, false) + hashiCorpToken, _ := setupHashiCorpVault(t, kc, test.vaultEngineVersion, test.useKubernetesAuth, false, test.useDelegatesSAAuth) assert.True(t, WaitForStatefulsetReplicaReadyCount(t, kc, postgreSQLStatefulSetName, testNamespace, 1, 60, 3), "replica count should be %d after 3 minutes", 1) @@ -493,8 +518,19 @@ func TestSecretsEngine(t *testing.T) { // Create kubernetes resources for testing data, templates := getTemplateData() - data.HashiCorpToken = RemoveANSI(hashiCorpToken) data.VaultSecretPath = test.vaultSecretPath + data.VaultRole = "keda" + if test.useKubernetesAuth { + data.HashiCorpAuthentication = "kubernetes" + } else { + data.HashiCorpAuthentication = "token" + data.HashiCorpToken = RemoveANSI(hashiCorpToken) + } + + if test.useDelegatesSAAuth { + data.VaultRole = "vault-delegated-sa" + data.VaultServiceAccountName = "default" + } KubectlApplyMultipleWithTemplate(t, data, templates) assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, deploymentName, testNamespace, minReplicaCount, 60, 3), @@ -548,7 +584,7 @@ func setupHashiCorpVaultPki(t *testing.T, podName string, nameSpace string) *pro return &pkiData } -func setupHashiCorpVault(t *testing.T, kc *kubernetes.Clientset, kvVersion uint, useKubernetesAuth, pki bool) (string, *prometheus.VaultPkiData) { +func setupHashiCorpVault(t *testing.T, kc *kubernetes.Clientset, kvVersion uint, useKubernetesAuth, pki, delegatedAuth bool) (string, *prometheus.VaultPkiData) { CreateNamespace(t, kc, vaultNamespace) _, err := ExecuteCommand("helm repo add hashicorp https://helm.releases.hashicorp.com") @@ -572,7 +608,7 @@ func setupHashiCorpVault(t *testing.T, kc *kubernetes.Clientset, kvVersion uint, // Enable Kubernetes auth if useKubernetesAuth { if pki { - remoteFile := "/tmp/policy.hcl" + remoteFile := "/tmp/pki_policy.hcl" KubectlCopyToPod(t, pkiPolicyTemplate, remoteFile, podName, vaultNamespace) assert.NoErrorf(t, err, "cannot create policy file in hashicorp vault - %s", err) _, _, err = ExecCommandOnSpecificPod(t, podName, vaultNamespace, fmt.Sprintf("vault policy write pkiPolicy %s", remoteFile)) @@ -584,7 +620,18 @@ func setupHashiCorpVault(t *testing.T, kc *kubernetes.Clientset, kvVersion uint, assert.NoErrorf(t, err, "cannot set kubernetes host in hashicorp vault - %s", err) _, _, err = ExecCommandOnSpecificPod(t, podName, vaultNamespace, "vault write auth/kubernetes/role/keda bound_service_account_names=keda-operator bound_service_account_namespaces=keda policies=pkiPolicy ttl=1h") assert.NoErrorf(t, err, "cannot cerate keda role in hashicorp vault - %s", err) + if delegatedAuth { + remoteFile := "/tmp/secret_read_policy.hcl" + KubectlCopyToPod(t, secretReadPolicyTemplate, remoteFile, podName, vaultNamespace) + assert.NoErrorf(t, err, "cannot create policy file in hashicorp vault - %s", err) + _, _, err = ExecCommandOnSpecificPod(t, podName, vaultNamespace, fmt.Sprintf("vault policy write secretReadPolicy %s", remoteFile)) + assert.NoErrorf(t, err, "cannot create policy in hashicorp vault - %s", err) + + _, _, err = ExecCommandOnSpecificPod(t, podName, vaultNamespace, fmt.Sprintf("vault write auth/kubernetes/role/vault-delegated-sa bound_service_account_names=default bound_service_account_namespaces=%s policies=secretReadPolicy ttl=1h", testNamespace)) + assert.NoErrorf(t, err, "cannot cerate keda role in hashicorp vault - %s", err) + } } + // Create kv secret if !pki { _, _, err = ExecCommandOnSpecificPod(t, podName, vaultNamespace, fmt.Sprintf("vault kv put secret/keda connectionString=%s", postgreSQLConnectionString))