From 8e6b2ccfb15e7ad314c74702e02c7bbd3ad4c17c Mon Sep 17 00:00:00 2001 From: vidyasagar-m Date: Tue, 22 Apr 2025 06:53:49 +0530 Subject: [PATCH 1/3] improve unit test coverage --- cmd/k8s-bigip-ctlr/main.go | 1 + pkg/agent/agentAS3_test.go | 10 +- pkg/agent/as3/as3Manager_test.go | 18 +- pkg/agent/as3/postManager_test.go | 30 ++-- pkg/controller/apiHandler.go | 97 ++++++----- pkg/controller/apiHandler_test.go | 218 ++++++++++++++++++++++++ pkg/controller/as3Handler.go | 131 ++++++++------- pkg/controller/as3Handler_test.go | 222 ++++++++++++++++++++++++- pkg/controller/as3Parser.go | 84 ++++------ pkg/controller/backend.go | 3 +- pkg/controller/backend_test.go | 63 +++++++ pkg/controller/controller.go | 4 +- pkg/controller/controller_suit_test.go | 40 ++++- pkg/controller/controller_test.go | 73 +++++++- pkg/controller/gtmBackend.go | 2 +- pkg/controller/gtmBackend_test.go | 55 ++++++ pkg/controller/nativeResourceWorker.go | 3 +- pkg/controller/postManager_test.go | 16 +- pkg/controller/requestHandler.go | 22 +-- pkg/controller/types.go | 7 +- pkg/controller/worker_test.go | 4 +- 21 files changed, 874 insertions(+), 229 deletions(-) create mode 100644 pkg/controller/apiHandler_test.go create mode 100644 pkg/controller/backend_test.go diff --git a/cmd/k8s-bigip-ctlr/main.go b/cmd/k8s-bigip-ctlr/main.go index 012848d15..d433720d2 100644 --- a/cmd/k8s-bigip-ctlr/main.go +++ b/cmd/k8s-bigip-ctlr/main.go @@ -1012,6 +1012,7 @@ func initController( agentParams, nil, td, + nil, ) return ctlr } diff --git a/pkg/agent/agentAS3_test.go b/pkg/agent/agentAS3_test.go index 9e455780f..15a2aca69 100644 --- a/pkg/agent/agentAS3_test.go +++ b/pkg/agent/agentAS3_test.go @@ -19,7 +19,7 @@ type ( RespIndex int } - responceCtx struct { + responseCtx struct { tenant string status float64 body string @@ -35,13 +35,13 @@ func newMockPostManager() *mockPostManager { return mockPM } -func (mockPM *mockPostManager) SetResponses(responces []responceCtx, method string) { +func (mockPM *mockPostManager) SetResponses(responses []responseCtx, method string) { var body string responseMap := make(mockhc.ResponseConfigMap) responseMap[method] = &mockhc.ResponseConfig{} - for _, resp := range responces { + for _, resp := range responses { if resp.body == "" { if resp.status == http.StatusOK { body = fmt.Sprintf(`{"results":[{"code":%f,"message":"none", "tenant": "%s"}]}`, @@ -81,7 +81,7 @@ var _ = Describe("Agent AS3 Tests", func() { Expect(ok).To(BeTrue()) // Test Get BIGIP Reg key tnt := "test" - mockPM.SetResponses([]responceCtx{{ + mockPM.SetResponses([]responseCtx{{ tenant: tnt, status: http.StatusOK, body: `{"registrationKey": "sfiifhanji"}`, @@ -90,7 +90,7 @@ var _ = Describe("Agent AS3 Tests", func() { // test clean function Expect(ag.Clean("test")).To(BeNil()) // set invalid status - mockPM.SetResponses([]responceCtx{{ + mockPM.SetResponses([]responseCtx{{ tenant: tnt, status: http.StatusServiceUnavailable, body: `{"registrationKey": "sfiifhanji"}`, diff --git a/pkg/agent/as3/as3Manager_test.go b/pkg/agent/as3/as3Manager_test.go index bfb808635..40c503286 100644 --- a/pkg/agent/as3/as3Manager_test.go +++ b/pkg/agent/as3/as3Manager_test.go @@ -322,7 +322,7 @@ var _ = Describe("AS3Manager Tests", func() { It("Check BigIP App services available", func() { mockPM := newMockPostManger() mockMgr.PostManager = mockPM.PostManager - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: "test", status: http.StatusOK, @@ -330,7 +330,7 @@ var _ = Describe("AS3Manager Tests", func() { }, }, http.MethodGet) Expect(mockMgr.IsBigIPAppServicesAvailable()).To(BeNil()) - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: "test", status: http.StatusOK, @@ -341,7 +341,7 @@ var _ = Describe("AS3Manager Tests", func() { Expect(mockMgr.as3Version).To(Equal(defaultAS3Version)) Expect(mockMgr.as3Release).To(Equal(defaultAS3Version + "-" + defaultAS3Build)) Expect(mockMgr.as3SchemaVersion).To(Equal(fmt.Sprintf("%.2f.0", as3Version))) - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: "test", status: http.StatusOK, @@ -374,7 +374,7 @@ var _ = Describe("AS3Manager Tests", func() { mockPM.AS3PostDelay = 2 mockMgr.PostManager = mockPM.PostManager go mockMgr.ConfigDeployer() - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "test", status: http.StatusOK, body: "", @@ -409,7 +409,7 @@ var _ = Describe("AS3Manager Tests", func() { tnt := "test" mockPM := newMockPostManger() mockMgr.PostManager = mockPM.PostManager - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: tnt, status: http.StatusOK, body: "", @@ -435,7 +435,7 @@ var _ = Describe("AS3Manager Tests", func() { mockMgr.as3ActiveConfig.tenantMap["Tenant2"] = true mockPM := newMockPostManger() mockMgr.PostManager = mockPM.PostManager - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "Tenant1", status: http.StatusOK, body: ""}, @@ -483,7 +483,7 @@ var _ = Describe("AS3Manager Tests", func() { It("Clean CIS managed Partition", func() { mockPM := newMockPostManger() mockMgr.PostManager = mockPM.PostManager - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "Tenant1", status: http.StatusOK, body: ""}, @@ -495,7 +495,7 @@ var _ = Describe("AS3Manager Tests", func() { It("Handle Post Failures", func() { mockPM := newMockPostManger() mockMgr.PostManager = mockPM.PostManager - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "Tenant1", status: http.StatusOK, body: ""}, @@ -507,7 +507,7 @@ var _ = Describe("AS3Manager Tests", func() { mockMgr.FilterTenants = true // setting failed tenant context mockMgr.failedContext.failedTenants["Tenant1"] = as3Declaration("") - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "Tenant1", status: http.StatusOK, body: ""}, diff --git a/pkg/agent/as3/postManager_test.go b/pkg/agent/as3/postManager_test.go index 5f71429c8..00833031a 100644 --- a/pkg/agent/as3/postManager_test.go +++ b/pkg/agent/as3/postManager_test.go @@ -17,7 +17,7 @@ type ( RespIndex int } - responceCtx struct { + responseCtx struct { tenant string status float64 body string @@ -33,13 +33,13 @@ func newMockPostManger() *mockPostManager { return mockPM } -func (mockPM *mockPostManager) setResponses(responces []responceCtx, method string) { +func (mockPM *mockPostManager) setResponses(responses []responseCtx, method string) { var body string responseMap := make(mockhc.ResponseConfigMap) responseMap[method] = &mockhc.ResponseConfig{} - for _, resp := range responces { + for _, resp := range responses { if resp.body == "" { if resp.status == http.StatusOK { body = fmt.Sprintf(`{"results":[{"code":%f,"message":"none", "tenant": "%s"}]}`, @@ -86,7 +86,7 @@ var _ = Describe("PostManager Tests", func() { It("Handle HTTP Status OK, Accepted & Created", func() { tnt := "test" - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: tnt, status: http.StatusOK, @@ -109,7 +109,7 @@ var _ = Describe("PostManager Tests", func() { It("Handle Expected HTTP Response Errors", func() { tnt := "test" - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: tnt, status: http.StatusServiceUnavailable, @@ -128,7 +128,7 @@ var _ = Describe("PostManager Tests", func() { It("Handle Unexpected HTTP Response Errors", func() { tnt := "test" - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: tnt, status: http.StatusRequestTimeout, @@ -152,7 +152,7 @@ var _ = Describe("PostManager Tests", func() { It("Handle HTTP Response Errors: StatusServiceUnavailable", func() { tnt := "test" - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: tnt, status: http.StatusServiceUnavailable, @@ -165,7 +165,7 @@ var _ = Describe("PostManager Tests", func() { }) It("Handle HTTP Response Errors: StatusNotFound", func() { tnt := "test" - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: tnt, status: http.StatusNotFound, @@ -178,7 +178,7 @@ var _ = Describe("PostManager Tests", func() { }) It("Handle HTTP Response Errors: StatusUnprocessableEntity", func() { tnt := "test" - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: tnt, status: http.StatusUnprocessableEntity, @@ -199,7 +199,7 @@ var _ = Describe("PostManager Tests", func() { }) It("Get BIG-IP AS3 Version", func() { - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: "test", status: http.StatusOK, @@ -211,7 +211,7 @@ var _ = Describe("PostManager Tests", func() { }) It("Validation1: Get BIG-IP AS3 Version", func() { - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: "test", status: http.StatusNotFound, @@ -223,7 +223,7 @@ var _ = Describe("PostManager Tests", func() { }) It("Validation2: Get BIG-IP AS3 Version", func() { - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: "test", status: http.StatusNotFound, @@ -235,7 +235,7 @@ var _ = Describe("PostManager Tests", func() { }) It("Validation3: Get BIG-IP AS3 Version", func() { - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: "test", status: http.StatusNotFound, @@ -250,7 +250,7 @@ var _ = Describe("PostManager Tests", func() { Describe("Get BIGIP Registration key", func() { It("Get Registration key successfully", func() { tnt := "test" - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: tnt, status: http.StatusOK, body: `{"registrationKey": "sfiifhanji"}`, @@ -261,7 +261,7 @@ var _ = Describe("PostManager Tests", func() { }) It("Handle Failures while Getting Registration key", func() { tnt := "test" - mockPM.setResponses([]responceCtx{ + mockPM.setResponses([]responseCtx{ { tenant: tnt, status: http.StatusNotFound, diff --git a/pkg/controller/apiHandler.go b/pkg/controller/apiHandler.go index 27620136f..b38747efb 100644 --- a/pkg/controller/apiHandler.go +++ b/pkg/controller/apiHandler.go @@ -15,10 +15,18 @@ type PostManagerInterface interface { setupBIGIPRESTClient() } -func NewGTMAPIHandler(params AgentParams, respChan chan *agentPostConfig) *GTMAPIHandler { - gtm := >MAPIHandler{ - BaseAPIHandler: NewBaseAPIHandler(params, GTMBigIP, respChan), - Partition: DEFAULT_GTM_PARTITION, +func NewGTMAPIHandler(params AgentParams, baseAPIHandler *BaseAPIHandler, respChan chan *agentPostConfig) *GTMAPIHandler { + var gtm *GTMAPIHandler + if baseAPIHandler == nil { + gtm = >MAPIHandler{ + BaseAPIHandler: NewBaseAPIHandler(params, GTMBigIP, respChan), + Partition: DEFAULT_GTM_PARTITION, + } + } else { + gtm = >MAPIHandler{ + BaseAPIHandler: baseAPIHandler, + Partition: DEFAULT_GTM_PARTITION, + } } switch params.ApiType { case AS3: @@ -47,9 +55,16 @@ func NewBaseAPIHandler(params AgentParams, kind string, respChan chan *agentPost } } -func NewLTMAPIHandler(params AgentParams, kind string, respChan chan *agentPostConfig) *LTMAPIHandler { - ltm := <MAPIHandler{ - BaseAPIHandler: NewBaseAPIHandler(params, kind, respChan), +func NewLTMAPIHandler(params AgentParams, kind string, baseAPIHandler *BaseAPIHandler, respChan chan *agentPostConfig) *LTMAPIHandler { + var ltm *LTMAPIHandler + if baseAPIHandler == nil { + ltm = <MAPIHandler{ + BaseAPIHandler: NewBaseAPIHandler(params, kind, respChan), + } + } else { + ltm = <MAPIHandler{ + BaseAPIHandler: baseAPIHandler, + } } // Initialize appropriate API handler based on type switch params.ApiType { @@ -142,43 +157,43 @@ func (api *BaseAPIHandler) IsBigIPAppServicesAvailable() error { return api.APIHandler.getVersionsFromBigIPResponse(httpResp, responseMap) } -func (api *BaseAPIHandler) GetDeclarationFromBigIP() (map[string]interface{}, error) { - url := api.APIHandler.getAPIURL([]string{}) +// func (api *BaseAPIHandler) GetDeclarationFromBigIP() (map[string]interface{}, error) { +// url := api.APIHandler.getAPIURL([]string{}) - req, err := api.createHTTPRequest(url, api.postManagerPrefix) - if err != nil { - return nil, fmt.Errorf("Internal Error") - } - httpResp, responseMap := api.httpReq(req) - if httpResp == nil || responseMap == nil { - return nil, fmt.Errorf("Internal Error") - } +// req, err := api.createHTTPRequest(url, api.postManagerPrefix) +// if err != nil { +// return nil, fmt.Errorf("Internal Error") +// } +// httpResp, responseMap := api.httpReq(req) +// if httpResp == nil || responseMap == nil { +// return nil, fmt.Errorf("Internal Error") +// } - return api.APIHandler.getDeclarationFromBigIPResponse(httpResp, responseMap) +// return api.APIHandler.getDeclarationFromBigIPResponse(httpResp, responseMap) -} +// } -func (gtmApi *GTMAPIHandler) GetDeclarationFromBigIP(postManagerPrefix string) (map[string]interface{}, error) { - url := gtmApi.APIHandler.getAPIURL([]string{}) +// func (gtmApi *GTMAPIHandler) GetDeclarationFromBigIP(postManagerPrefix string) (map[string]interface{}, error) { +// url := gtmApi.APIHandler.getAPIURL([]string{}) - // Create HTTP request - req, err := http.NewRequest("GET", url, nil) - if err != nil { - log.Errorf("[%s]%v Creating new HTTP request error: %v ", gtmApi.apiType, postManagerPrefix, err) - return nil, err - } +// // Create HTTP request +// req, err := http.NewRequest("GET", url, nil) +// if err != nil { +// log.Errorf("[%s]%v Creating new HTTP request error: %v ", gtmApi.apiType, postManagerPrefix, err) +// return nil, err +// } - // Set basic auth credentials - req.SetBasicAuth(gtmApi.BIGIPUsername, gtmApi.BIGIPPassword) +// // Set basic auth credentials +// req.SetBasicAuth(gtmApi.BIGIPUsername, gtmApi.BIGIPPassword) - // Make HTTP request - httpResp, responseMap := gtmApi.httpReq(req) - if httpResp == nil || responseMap == nil { - return nil, fmt.Errorf("Internal Error") - } +// // Make HTTP request +// httpResp, responseMap := gtmApi.httpReq(req) +// if httpResp == nil || responseMap == nil { +// return nil, fmt.Errorf("Internal Error") +// } - return gtmApi.APIHandler.getDeclarationFromBigIPResponse(httpResp, responseMap) -} +// return gtmApi.APIHandler.getDeclarationFromBigIPResponse(httpResp, responseMap) +// } func (api *BaseAPIHandler) createHTTPRequest(url string, postManagerPrefix string) (*http.Request, error) { // Create HTTP request @@ -194,11 +209,11 @@ func (api *BaseAPIHandler) createHTTPRequest(url string, postManagerPrefix strin return req, nil } -func (api *BaseAPIHandler) publishConfig(cfg *agentPostConfig) { - log.Debugf("[%s]%v PostManager Accepted the configuration", api.apiType, api.postManagerPrefix) - // postConfig updates the tenantResponseMap with response codes - api.postConfig(cfg) -} +// func (api *BaseAPIHandler) publishConfig(cfg *agentPostConfig) { +// log.Debugf("[%s]%v PostManager Accepted the configuration", api.apiType, api.postManagerPrefix) +// // postConfig updates the tenantResponseMap with response codes +// api.postConfig(cfg) +// } func (api *BaseAPIHandler) PopulateAPIVersion() { version, build, schemaVersion, err := api.GetBigIPAPIVersion() diff --git a/pkg/controller/apiHandler_test.go b/pkg/controller/apiHandler_test.go new file mode 100644 index 000000000..3c1c04267 --- /dev/null +++ b/pkg/controller/apiHandler_test.go @@ -0,0 +1,218 @@ +package controller + +import ( + "io" + "net/http" + "strings" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("API Handler tests", func() { + Describe("Validate base api handler", func() { + It("Verify base api handler", func() { + baseAPIHandler := NewBaseAPIHandler( + AgentParams{ + ApiType: AS3, + PrimaryParams: PostParams{BIGIPURL: "http://127.0.0.1:8080", + BIGIPPassword: "password", + BIGIPUsername: "username"}, + Partition: "default", + }, + PrimaryBigIP, + make(chan *agentPostConfig, 1), + ) + Expect(baseAPIHandler).NotTo(BeNil(), "Base api handler must not be nil") + Expect(baseAPIHandler.PostManager).NotTo(BeNil(), "Post manager must not be nil") + Expect(baseAPIHandler.PostManager.BIGIPURL).To(Equal("http://127.0.0.1:8080"), "BIGIPURL didn't matched with the agent params BIGIPURL") + }) + }) + Describe("Post config tests with HTTP response handlers", func() { + var mockBaseAPIHandler *BaseAPIHandler + var postConfig *agentPostConfig + var tenantResponseMap map[string]tenantResponse + + BeforeEach(func() { + mockBaseAPIHandler = newMockBaseAPIHandler() + tenantDeclMap := make(map[string]as3Tenant) + tenantResponseMap = make(map[string]tenantResponse) + tenantResponseMap["test"] = tenantResponse{} + tenantResponseMap["test1"] = tenantResponse{} + tenantDeclMap["test"] = as3Tenant{ + "class": "Tenant", + "defaultRouteDomain": 0, + as3SharedApplication: "shared", + "label": "cis2.x", + } + postConfig = &agentPostConfig{ + reqMeta: requestMeta{ + id: 1, + }, + as3APIURL: "https://127.0.0.1/mgmt/shared/appsvcs/declare", + data: `{"class": "AS3", "declaration": {"class": "ADC", "test": {"class": "Tenant", "testApp": {"class": "Application", "webcert":{"class": "Certificate", "certificate": "abc", "privateKey": "abc", "chainCA": "abc"}}}}}`, + incomingTenantDeclMap: tenantDeclMap, + tenantResponseMap: tenantResponseMap, + } + }) + It("Validate post config for OK response", func() { + client, _ := getMockHttpClient([]responseCtx{ + { + tenant: "test", + status: http.StatusOK, + body: io.NopCloser(strings.NewReader("{\"results\": [{\"code\": 200, \"message\": \"success\", \"tenant\": \"test\"}], \"declaration\": {\"class\": \"ADC\", \"test\": {\"class\": \"Tenant\", \"testApp\": {\"class\": \"Application\", \"webcert\":{\"class\": \"Certificate\", \"certificate\": \"abc\", \"privateKey\": \"abc\", \"chainCA\": \"abc\"}}}}}")), + }, + }, http.MethodPost) + mockBaseAPIHandler.httpClient = client + mockBaseAPIHandler.postConfig(postConfig) + Expect(postConfig).NotTo(BeNil(), "post config must not be nil") + Expect(postConfig.tenantResponseMap["test"].agentResponseCode).To(Equal(http.StatusOK), "Post configuration must have status code 200") + }) + It("Validate post config for CREATED or ACCEPTED response", func() { + client, _ := getMockHttpClient([]responseCtx{ + { + tenant: "test", + status: http.StatusCreated, + body: io.NopCloser(strings.NewReader("{\"id\": \"1234\"}")), + }, + }, http.MethodPost) + mockBaseAPIHandler.httpClient = client + mockBaseAPIHandler.postConfig(postConfig) + Expect(postConfig).NotTo(BeNil(), "Post configuration must not be nil") + Expect(postConfig.acceptedTaskId).To(Equal("1234"), "Post configuration must have accepted task id") + // Expect(postConfig.tenantResponseMap).To(Equal(http.StatusCreated), "Post configuration must have status code 201") + }) + It("Validate post config for MULTISTATUS", func() { + + client, _ := getMockHttpClient([]responseCtx{ + { + tenant: "test", + status: http.StatusMultiStatus, + body: io.NopCloser(strings.NewReader("{\"results\": [{\"code\": 200, \"message\": \"success\", \"tenant\": \"test\"}, {\"code\": 422, \"message\": \"resulted in failure\", \"tenant\": \"test1\"}], \"declaration\": {\"class\": \"ADC\", \"test\": {\"class\": \"Tenant\", \"testApp\": {\"class\": \"Application\", \"webcert\":{\"class\": \"Certificate\", \"certificate\": \"abc\", \"privateKey\": \"abc\", \"chainCA\": \"abc\"}}}, \"test1\": { \"class\": \"Tenant\", \"testApp\": {\"class\": \"Application\", \"webcert\":{\"class\": \"Certificate\", \"certificate\": \"abc\", \"privateKey\": \"abc\", \"chainCA\": \"abc\"}}}}}")), + }, + }, http.MethodPost) + mockBaseAPIHandler.httpClient = client + mockBaseAPIHandler.postConfig(postConfig) + Expect(postConfig).NotTo(BeNil(), "Post configuration must not be nil") + Expect(postConfig.tenantResponseMap["test"].agentResponseCode).To(Equal(http.StatusOK), "Post configuration must have status code 200") + Expect(postConfig.tenantResponseMap["test1"].agentResponseCode).To(Equal(422), "Post configuration must have status code 422") + }) + It("Validate post config for UNAUTHORIZED response", func() { + client, _ := getMockHttpClient([]responseCtx{ + { + tenant: "test", + status: http.StatusUnauthorized, + body: io.NopCloser(strings.NewReader("{\"code\": 401, \"message\": \"Unauthorized service\"}")), + }, + }, http.MethodPost) + mockBaseAPIHandler.httpClient = client + mockBaseAPIHandler.postConfig(postConfig) + Expect(postConfig).NotTo(BeNil(), "Post configuration must not be nil") + Expect(postConfig.tenantResponseMap["test"].agentResponseCode).To(Equal(http.StatusUnauthorized), "Post configuration must have status code 401") + }) + It("Validate post config for NOT FOUND response", func() { + client, _ := getMockHttpClient([]responseCtx{ + { + tenant: "test", + status: http.StatusNotFound, + body: io.NopCloser(strings.NewReader("{\"error\": {\"code\": 404, \"message\": \"RPM is not installed on BIGIP\"}}")), + }, + }, http.MethodPost) + mockBaseAPIHandler.httpClient = client + mockBaseAPIHandler.postConfig(postConfig) + Expect(postConfig).NotTo(BeNil(), "Post configuration must not be nil") + Expect(postConfig.tenantResponseMap["test"].agentResponseCode).To(Equal(http.StatusNotFound), "Post configuration must have status code 401") + }) + It("Validate post config for SERVICE UNAVAILABLE response", func() { + client, _ := getMockHttpClient([]responseCtx{ + { + tenant: "test", + status: http.StatusServiceUnavailable, + body: io.NopCloser(strings.NewReader("{\"error\": {\"code\": 503, \"message\": \"BIGIP is busy\"}}")), + }, + }, http.MethodPost) + mockBaseAPIHandler.httpClient = client + mockBaseAPIHandler.postConfig(postConfig) + Expect(postConfig).NotTo(BeNil(), "Post configuration must not be nil") + Expect(postConfig.tenantResponseMap["test"].agentResponseCode).To(Equal(http.StatusServiceUnavailable), "Post configuration must have status code 503") + }) + It("Validate post config for unknown response error cases", func() { + client, _ := getMockHttpClient([]responseCtx{ + { + tenant: "test", + status: http.StatusUnprocessableEntity, + body: io.NopCloser(strings.NewReader("{\"results\": [{\"code\": 422, \"message\": \"Failure on post\", \"tenant\": \"test\"}]}")), + }, + { + tenant: "test", + status: http.StatusForbidden, + body: io.NopCloser(strings.NewReader("{\"error\": {\"code\": 403, \"message\": \"Forbidden request\"}}")), + }, + { + tenant: "test", + status: http.StatusBadRequest, + body: io.NopCloser(strings.NewReader("{\"code\": 400}")), + }, + { + tenant: "test", + status: http.StatusBadRequest, + body: io.NopCloser(strings.NewReader("{\"code\": 400")), + }, + }, http.MethodPost) + mockBaseAPIHandler.httpClient = client + mockBaseAPIHandler.postConfig(postConfig) + Expect(postConfig).NotTo(BeNil(), "Post configuration must not be nil") + Expect(postConfig.tenantResponseMap["test"].agentResponseCode).To(Equal(http.StatusUnprocessableEntity), "Post configuration must have status code 503") + mockBaseAPIHandler.postConfig(postConfig) + Expect(postConfig).NotTo(BeNil(), "Post configuration must not be nil") + Expect(postConfig.tenantResponseMap["test"].agentResponseCode).To(Equal(http.StatusForbidden), "Post configuration must have status code 503") + mockBaseAPIHandler.postConfig(postConfig) + Expect(postConfig).NotTo(BeNil(), "Post configuration must not be nil") + Expect(postConfig.tenantResponseMap["test"].agentResponseCode).To(Equal(http.StatusBadRequest), "Post configuration must have status code 503") + mockBaseAPIHandler.postConfig(postConfig) + Expect(postConfig).NotTo(BeNil(), "Post configuration must not be nil") + Expect(postConfig.tenantResponseMap["test"].agentResponseCode).To(Equal(http.StatusBadRequest), "Post configuration must have status code 503") + Expect(postConfig.tenantResponseMap["test"].message).To(Equal("Big-IP Responded with error code: 400 -- verify the logs for detailed error"), "Post configuration must have status code 400") + }) + }) + Describe("BIGIP utils functions", func() { + var mockBaseAPIHandler *BaseAPIHandler + + BeforeEach(func() { + mockBaseAPIHandler = newMockBaseAPIHandler() + }) + + It("Get BIGIP registration key", func() { + mockBaseAPIHandler.httpClient, _ = getMockHttpClient([]responseCtx{ + { + status: http.StatusOK, + body: io.NopCloser(strings.NewReader("{\"registrationKey\": \"abcd-efgh-ijkl-mnopqr\"}")), + }, + { + status: http.StatusNotFound, + body: io.NopCloser(strings.NewReader("{\"code\": 404}")), + }, + { + status: http.StatusUnauthorized, + body: io.NopCloser(strings.NewReader("{\"code\": 401, \"message\": \"Unauthorized service\"}")), + }, + { + status: http.StatusUnprocessableEntity, + body: io.NopCloser(strings.NewReader("{\"code\": 422, \"message\": \"Error while fetching key\"}")), + }, + }, http.MethodGet) + response, err := mockBaseAPIHandler.GetBigipRegKey() + Expect(response).To(Equal("abcd-efgh-ijkl-mnopqr"), "BIGIP reg key did not match") + Expect(err).To(BeNil(), "BIGIP reg key error must be nil") + response, err = mockBaseAPIHandler.GetBigipRegKey() + Expect(response).To(BeEmpty(), "BIGIP reg key expected to be empty") + Expect(err.Error()).To(Equal("RPM is not installed on BIGIP, Error response from BIGIP with status code 404")) + response, err = mockBaseAPIHandler.GetBigipRegKey() + Expect(response).To(BeEmpty(), "BIGIP reg key expected to be empty") + Expect(err.Error()).To(Equal("Internal Error")) + response, err = mockBaseAPIHandler.GetBigipRegKey() + Expect(response).To(BeEmpty(), "BIGIP reg key expected to be empty") + Expect(err.Error()).To(Equal("error response from BIGIP with status code 422")) + }) + }) +}) diff --git a/pkg/controller/as3Handler.go b/pkg/controller/as3Handler.go index 47930cb9b..d53e61a85 100644 --- a/pkg/controller/as3Handler.go +++ b/pkg/controller/as3Handler.go @@ -20,7 +20,7 @@ type ApiTypeHandlerInterface interface { UpdateApiVersion(version string, build string, schemaVersion string) getVersionURL() string getVersionsFromResponse(httpResp *http.Response, responseMap map[string]interface{}) (string, string, string, error) - removeDeletedTenantsForBigIP(Config map[string]interface{}, rsConfig *ResourceConfigRequest, cisLabel, partition string) + // removeDeletedTenantsForBigIP(Config map[string]interface{}, rsConfig *ResourceConfigRequest, cisLabel, partition string) handleResponseStatusOK(responseMap map[string]interface{}, cfg *agentPostConfig) bool handleMultiStatus(responseMap map[string]interface{}, cfg *agentPostConfig) bool handleResponseAccepted(responseMap map[string]interface{}, cfg *agentPostConfig) bool @@ -31,7 +31,7 @@ type ApiTypeHandlerInterface interface { handleNilResponseMap(code int, cfg *agentPostConfig) bool getRegKeyFromResponse(httpResp *http.Response, responseMap map[string]interface{}) (string, error) getVersionsFromBigIPResponse(httpResp *http.Response, responseMap map[string]interface{}) error - getDeclarationFromBigIPResponse(httpResp *http.Response, responseMap map[string]interface{}) (map[string]interface{}, error) + // getDeclarationFromBigIPResponse(httpResp *http.Response, responseMap map[string]interface{}) (map[string]interface{}, error) updateTenantConfigStatus(id string, httpResp *http.Response, responseMap map[string]interface{}, cfg *agentPostConfig) pollTenantStatus(cfg *agentPostConfig) verifyTenantConfigStatus(id string, agentCfg *agentPostConfig) @@ -108,8 +108,13 @@ func (am *AS3Handler) logRequest(cfg string) { } func (am *AS3Handler) logResponse(responseMap map[string]interface{}) { + // Avoid modifying the original response + responseMapCopy := make(map[string]interface{}) + for key, value := range responseMap { + responseMapCopy[key] = value + } // removing the certificates/privateKey from response log - if declaration, ok := (responseMap["declaration"]).([]interface{}); ok { + if declaration, ok := (responseMapCopy["declaration"]).(map[string]interface{}); ok { for _, value := range declaration { if tenantMap, ok := value.(map[string]interface{}); ok { for _, value2 := range tenantMap { @@ -132,7 +137,7 @@ func (am *AS3Handler) logResponse(responseMap map[string]interface{}) { log.Errorf("[AS3]%v error while reading declaration from AS3 response: %v\n", am.postManagerPrefix, err) return } - responseMap["declaration"] = as3Declaration(decl) + responseMapCopy["declaration"] = as3Declaration(decl) } log.Debugf("[AS3]%v Raw response from Big-IP: %v ", am.postManagerPrefix, responseMap) } @@ -179,37 +184,37 @@ func (am *AS3Handler) getVersionsFromResponse(httpResp *http.Response, responseM } } -func (am *AS3Handler) getDeclarationFromBigIPResponse(httpResp *http.Response, responseMap map[string]interface{}) (map[string]interface{}, error) { - // Check response status code - switch httpResp.StatusCode { - case http.StatusOK: - return responseMap, nil - case http.StatusNotFound: - if code, ok := responseMap["code"].(float64); ok { - if int(code) == http.StatusNotFound { - return nil, fmt.Errorf("%s RPM is not installed on BIGIP,"+ - " Error response from BIGIP with status code %v", am.apiType, httpResp.StatusCode) - } - } else { - am.logResponse(responseMap) - } - case http.StatusUnauthorized: - if code, ok := responseMap["code"].(float64); ok { - if int(code) == http.StatusUnauthorized { - if _, ok := responseMap["message"].(string); ok { - return nil, fmt.Errorf("authentication failed,"+ - " Error response from BIGIP with status code %v Message: %v", httpResp.StatusCode, responseMap["message"]) - } else { - return nil, fmt.Errorf("authentication failed,"+ - " Error response from BIGIP with status code %v", httpResp.StatusCode) - } - } - } else { - am.logResponse(responseMap) - } - } - return nil, fmt.Errorf("error response from BIGIP with status code %v", httpResp.StatusCode) -} +//func (am *AS3Handler) getDeclarationFromBigIPResponse(httpResp *http.Response, responseMap map[string]interface{}) (map[string]interface{}, error) { +// // Check response status code +// switch httpResp.StatusCode { +// case http.StatusOK: +// return responseMap, nil +// case http.StatusNotFound: +// if code, ok := responseMap["code"].(float64); ok { +// if int(code) == http.StatusNotFound { +// return nil, fmt.Errorf("%s RPM is not installed on BIGIP,"+ +// " Error response from BIGIP with status code %v", am.apiType, httpResp.StatusCode) +// } +// } else { +// am.logResponse(responseMap) +// } +// case http.StatusUnauthorized: +// if code, ok := responseMap["code"].(float64); ok { +// if int(code) == http.StatusUnauthorized { +// if _, ok := responseMap["message"].(string); ok { +// return nil, fmt.Errorf("authentication failed,"+ +// " Error response from BIGIP with status code %v Message: %v", httpResp.StatusCode, responseMap["message"]) +// } else { +// return nil, fmt.Errorf("authentication failed,"+ +// " Error response from BIGIP with status code %v", httpResp.StatusCode) +// } +// } +// } else { +// am.logResponse(responseMap) +// } +// } +// return nil, fmt.Errorf("error response from BIGIP with status code %v", httpResp.StatusCode) +//} func (am *AS3Handler) getVersionsFromBigIPResponse(httpResp *http.Response, responseMap map[string]interface{}) error { switch httpResp.StatusCode { @@ -222,8 +227,8 @@ func (am *AS3Handler) getVersionsFromBigIPResponse(httpResp *http.Response, resp case http.StatusNotFound: return fmt.Errorf("AS3 RPM is not installed on BIGIP") - case http.StatusUnauthorized: - return fmt.Errorf("authentication failed for AS3 version check") + //case http.StatusUnauthorized: + // return fmt.Errorf("authentication failed for AS3 version check") default: return fmt.Errorf("error response from BIGIP with status code %v", httpResp.StatusCode) @@ -298,18 +303,18 @@ func (am *AS3Handler) getRegKeyFromResponse(httpResp *http.Response, responseMap } return "", fmt.Errorf("error response from BIGIP with status code %v", httpResp.StatusCode) - case http.StatusUnauthorized: - if code, ok := responseMap["code"].(float64); ok { - if int(code) == http.StatusUnauthorized { - if msg, ok := responseMap["message"].(string); ok { - return "", fmt.Errorf("authentication failed,"+ - " Error response from BIGIP with status code %v Message: %v", httpResp.StatusCode, msg) - } - return "", fmt.Errorf("authentication failed,"+ - " Error response from BIGIP with status code %v", httpResp.StatusCode) - } - } - return "", fmt.Errorf("error response from BIGIP with status code %v", httpResp.StatusCode) + //case http.StatusUnauthorized: + // if code, ok := responseMap["code"].(float64); ok { + // if int(code) == http.StatusUnauthorized { + // if msg, ok := responseMap["message"].(string); ok { + // return "", fmt.Errorf("authentication failed,"+ + // " Error response from BIGIP with status code %v Message: %v", httpResp.StatusCode, msg) + // } + // return "", fmt.Errorf("authentication failed,"+ + // " Error response from BIGIP with status code %v", httpResp.StatusCode) + // } + // } + // return "", fmt.Errorf("error response from BIGIP with status code %v", httpResp.StatusCode) default: return "", fmt.Errorf("error response from BIGIP with status code %v", httpResp.StatusCode) @@ -509,19 +514,19 @@ func (am *AS3Handler) handleResponseOthers(responseMap map[string]interface{}, c return unknownResponse } -func (am *AS3Handler) removeDeletedTenantsForBigIP(as3Config map[string]interface{}, rsConfig *ResourceConfigRequest, cisLabel, partition string) { - for k, v := range as3Config { - if decl, ok := v.(map[string]interface{}); ok { - if label, found := decl["label"]; found && label == cisLabel && k != partition+"_gtm" { - if _, ok := rsConfig.ltmConfig[k]; !ok { - // adding an empty tenant to delete the tenant from BIGIP - priority := 1 - rsConfig.ltmConfig[k] = &PartitionConfig{Priority: &priority} - } - } - } - } -} +//func (am *AS3Handler) removeDeletedTenantsForBigIP(as3Config map[string]interface{}, rsConfig *ResourceConfigRequest, cisLabel, partition string) { +// for k, v := range as3Config { +// if decl, ok := v.(map[string]interface{}); ok { +// if label, found := decl["label"]; found && label == cisLabel && k != partition+"_gtm" { +// if _, ok := rsConfig.ltmConfig[k]; !ok { +// // adding an empty tenant to delete the tenant from BIGIP +// priority := 1 +// rsConfig.ltmConfig[k] = &PartitionConfig{Priority: &priority} +// } +// } +// } +// } +//} // Creates AS3 adc only for tenants with updated configuration func (am *AS3Handler) createAPIConfig(rsConfig ResourceConfigRequest, ccclGTMAgent bool, userAgent string, gtmOnSeparateServer bool) agentPostConfig { @@ -783,7 +788,7 @@ func (am *AS3Handler) pollTenantStatus(cfg *agentPostConfig) { for cfg.acceptedTaskId != "" { log.Debugf("[%s]%v waiting for %v before checking taskId %v", am.apiType, am.postManagerPrefix, cfg.timeout, cfg.acceptedTaskId) <-time.After(cfg.timeout) - cfg.tenantResponseMap = make(map[string]tenantResponse) + //cfg.tenantResponseMap = make(map[string]tenantResponse) am.verifyTenantConfigStatus(cfg.acceptedTaskId, cfg) } } diff --git a/pkg/controller/as3Handler_test.go b/pkg/controller/as3Handler_test.go index 155564187..6e282aed0 100644 --- a/pkg/controller/as3Handler_test.go +++ b/pkg/controller/as3Handler_test.go @@ -2,14 +2,17 @@ package controller import ( "encoding/json" + "io" + "net/http" + "strings" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/util/intstr" - "strings" "sync" ) -var _ = Describe("Backend Tests", func() { +var _ = Describe("AS3Handler Tests", func() { Describe("Prepare AS3 Declaration", func() { var mem1, mem2, mem3, mem4 PoolMember @@ -73,6 +76,7 @@ var _ = Describe("Backend Tests", func() { }, }, } + rsCfg.Virtual.IRules = []string{"none", "/common/irule1", "/common/irule_2", "/common/http_redirect_irule"} rsCfg.IRulesMap = IRulesMap{ NameRef{"custom_iRule", DEFAULT_PARTITION}: &IRule{ Name: "custom_iRule", @@ -85,6 +89,21 @@ var _ = Describe("Backend Tests", func() { Code: "tcl code blocks", }, } + rsCfg.IntDgMap = InternalDataGroupMap{ + NameRef{"static-internal-dg", "test"}: DataGroupNamespaceMap{ + "intDg1": &InternalDataGroup{ + Name: "static-internal-dg", + Partition: "test", + Type: "string", + Records: []InternalDataGroupRecord{ + { + Name: "apiTye", + Data: AS3, + }, + }, + }, + }, + } rsCfg.Policies = Policies{ Policy{ Name: "policy1", @@ -132,6 +151,80 @@ var _ = Describe("Backend Tests", func() { Request: true, }, }, + Actions: []*action{ + { + Forward: true, + Request: true, + Redirect: true, + HTTPURI: true, + HTTPHost: true, + Pool: "default_svc_2", + Log: true, + Location: PrimaryBigIP, + Message: "log action", + Replace: true, + Value: "urihost", + WAF: true, + Policy: "/common/policy3", + Enabled: true, + Drop: true, + PersistMethod: SourceAddress, + }, + { + PersistMethod: DestinationAddress, + }, + { + PersistMethod: CookieHash, + }, + { + PersistMethod: CookieInsert, + }, + { + PersistMethod: CookieRewrite, + }, + { + PersistMethod: CookiePassive, + }, + { + PersistMethod: Universal, + }, + { + PersistMethod: Carp, + }, + { + PersistMethod: Hash, + }, + { + PersistMethod: Disable, + }, + { + PersistMethod: "Disable", + }, + }, + }, + }, + }, + Policy{ + Name: "policy3", + Strategy: "first-match", + Rules: Rules{ + &Rule{ + Conditions: []*condition{ + { + Path: true, + Name: "condition3", + Values: []string{"/common/test"}, + HTTPURI: true, + Equals: true, + Index: 3, + }, + { + Tcp: true, + Address: true, + Values: []string{"10.10.10.10"}, + Request: true, + }, + }, Actions: []*action{ { Forward: true, @@ -403,6 +496,17 @@ var _ = Describe("Backend Tests", func() { rsCfg.Virtual.AllowVLANs = []string{"flannel_vxlan"} rsCfg.Virtual.Destination = "172.13.14.6:1600" rsCfg.customProfiles = make(map[SecretKey]CustomProfile) + rsCfg.Virtual.ProfileL4 = "/Common/profileL4" + rsCfg.Virtual.ProfileDOS = "/Common/profileDOS" + rsCfg.Virtual.ProfileBotDefense = "/Common/profileBotDefense" + rsCfg.Virtual.TCP.Client = "/Common/tcpClient" + rsCfg.Virtual.TCP.Server = "/Common/tcpServer" + rsCfg.Virtual.TranslateServerAddress = true + rsCfg.Virtual.TranslateServerPort = true + rsCfg.Virtual.Source = "ts" + rsCfg.Virtual.PoolName = "/Common/customPool" + rsCfg.Virtual.Destination = "/test/172.13.14.15:8080" + rsCfg.Pools = Pools{ Pool{ Name: "pool1", @@ -429,6 +533,62 @@ var _ = Describe("Backend Tests", func() { Expect(strings.Contains(decl, "connectionLimit")).To(BeTrue()) Expect(strings.Contains(decl, "profileFTP")).To(BeTrue()) + }) + It("Ingresslink Declaration", func() { + rsCfg := &ResourceConfig{} + rsCfg.MetaData.Active = true + rsCfg.MetaData.ResourceType = IngressLink + rsCfg.Virtual.Name = "crd_il_172.13.14.16" + rsCfg.Virtual.IpProtocol = "http" + rsCfg.Virtual.ProfileL4 = "/Common/profileL4" + rsCfg.Virtual.ProfileDOS = "/Common/profileDOS" + rsCfg.Virtual.ProfileBotDefense = "/Common/profileBotDefense" + rsCfg.Virtual.TCP.Client = "/Common/tcpClient" + rsCfg.Virtual.TCP.Server = "/Common/tcpServer" + rsCfg.Virtual.Profiles = ProfileRefs{ + ProfileRef{ + Name: "serverssl", + Partition: "Common", + Context: "udp", + }, + ProfileRef{ + Name: "clientssl", + Partition: "Common", + Context: "udp", + BigIPProfile: true, + }, + } + rsCfg.Virtual.TranslateServerAddress = true + rsCfg.Virtual.TranslateServerPort = true + rsCfg.Virtual.Source = "inglink" + rsCfg.Virtual.PoolName = "/Common/customPool" + rsCfg.Virtual.Destination = "/test/172.13.14.5:8080" + rsCfg.Pools = Pools{ + Pool{ + Name: "pool1", + Members: []PoolMember{mem1, mem2}, + MinimumMonitors: intstr.IntOrString{Type: 0, IntVal: 1}, + }, + } + + config := ResourceConfigRequest{ + ltmConfig: make(LTMConfig), + shareNodes: true, + gtmConfig: GTMConfig{}, + defaultRouteDomain: 1, + } + + zero := 0 + config.ltmConfig["default"] = &PartitionConfig{ResourceMap: make(ResourceMap), Priority: &zero} + config.ltmConfig["default"].ResourceMap["crd_il_172.13.14.16"] = rsCfg + + agentPostCfg := as3Handler.createAPIConfig(config, false, "", false) + decl := agentPostCfg.data + Expect(decl).ToNot(Equal(""), "Failed to Create AS3 Declaration") + Expect(strings.Contains(decl, "adminState")).To(BeTrue()) + Expect(strings.Contains(decl, "connectionLimit")).To(BeTrue()) + Expect(strings.Contains(decl, "profileL4")).To(BeTrue()) + }) It("Delete partition", func() { config := ResourceConfigRequest{ @@ -636,4 +796,62 @@ var _ = Describe("Backend Tests", func() { }) }) + Describe("Poll tenant status", func() { + var agent *Agent + var config *agentPostConfig + var mockBaseAPIHandler *BaseAPIHandler + BeforeEach(func() { + agent = &Agent{} + mockBaseAPIHandler = newMockBaseAPIHandler() + tenantDeclMap := make(map[string]as3Tenant) + tenantResponseMap := make(map[string]tenantResponse) + tenantResponseMap["test"] = tenantResponse{} + tenantDeclMap["test"] = as3Tenant{ + "class": "Tenant", + "defaultRouteDomain": 0, + as3SharedApplication: "shared", + "label": "cis2.x", + } + config = &agentPostConfig{ + acceptedTaskId: "123", + reqMeta: requestMeta{ + id: 1, + }, + as3APIURL: "https://127.0.0.1/mgmt/shared/appsvcs/declare", + data: `{"class": "AS3", "declaration": {"class": "ADC", "test": {"class": "Tenant", "testApp": {"class": "Application", "webcert":{"class": "Certificate", "certificate": "abc", "privateKey": "abc", "chainCA": "abc"}}}}}`, + incomingTenantDeclMap: tenantDeclMap, + tenantResponseMap: tenantResponseMap, + } + }) + It("Verify tenant status", func() { + mockBaseAPIHandler.httpClient, _ = getMockHttpClient([]responseCtx{{ + tenant: "test", + status: http.StatusOK, + body: io.NopCloser(strings.NewReader("{\"results\": [{\"code\": 200, \"message\": \"success\", \"tenant\": \"test\"}], \"declaration\": {\"class\": \"ADC\", \"test\": {\"class\": \"Tenant\", \"testApp\": {\"class\": \"Application\", \"webcert\":{\"class\": \"Certificate\", \"certificate\": \"abc\", \"privateKey\": \"abc\", \"chainCA\": \"abc\"}}}}}")), + }, { + tenant: "test", + status: http.StatusUnprocessableEntity, + body: io.NopCloser(strings.NewReader("{\"id\": \"123\"}")), + }}, http.MethodGet) + agent.APIHandler = &APIHandler{ + LTM: <MAPIHandler{ + BaseAPIHandler: mockBaseAPIHandler, + }, + GTM: >MAPIHandler{ + BaseAPIHandler: mockBaseAPIHandler, + }, + } + + // verify status ok + agent.LTM.APIHandler.pollTenantStatus(config) + Expect(config.acceptedTaskId).To(BeEmpty(), "Accepted task id should be empty") + Expect(config.tenantResponseMap["test"].agentResponseCode).To(Equal(http.StatusOK), "Response code should be 200") + + config.acceptedTaskId = "123" + agent.LTM.APIHandler.pollTenantStatus(config) + Expect(config.acceptedTaskId).To(BeEmpty(), "Accepted task id should be empty") + Expect(config.tenantResponseMap["test"].agentResponseCode).To(Equal(http.StatusUnprocessableEntity), "Response code should be 422") + }) + }) + }) diff --git a/pkg/controller/as3Parser.go b/pkg/controller/as3Parser.go index 7c4c39318..2c44b49fb 100644 --- a/pkg/controller/as3Parser.go +++ b/pkg/controller/as3Parser.go @@ -53,11 +53,11 @@ func (ap *AS3Parser) getDeletedTenantDeclaration(tenant, cisLabel string, defaul func (ap *AS3Parser) processIRulesForAS3(rsCfg *ResourceConfig, sharedApp as3Application) { // Skip processing IRules for "None" value - for _, v := range rsCfg.Virtual.IRules { - if v == "none" { - continue - } - } + //for _, v := range rsCfg.Virtual.IRules { + // if v == "none" { + // continue + // } + //} // Create irule declaration for _, v := range rsCfg.IRulesMap { iRule := &as3IRules{} @@ -69,11 +69,11 @@ func (ap *AS3Parser) processIRulesForAS3(rsCfg *ResourceConfig, sharedApp as3App func (ap *AS3Parser) processDataGroupForAS3(rsCfg *ResourceConfig, sharedApp as3Application) { // Skip processing DataGroup for "None" iRule value - for _, v := range rsCfg.Virtual.IRules { - if v == "none" { - continue - } - } + //for _, v := range rsCfg.Virtual.IRules { + // if v == "none" { + // continue + // } + //} for _, idg := range rsCfg.IntDgMap { for _, dg := range idg { dataGroupRecord, found := sharedApp[dg.Name] @@ -618,9 +618,6 @@ func (ap *AS3Parser) createRuleAction(rl *Rule, rulesData *as3Rule) { if v.Forward { action.Type = "forward" } - if v.Log { - action.Type = "log" - } if v.Request { action.Event = "request" } @@ -637,6 +634,7 @@ func (ap *AS3Parser) createRuleAction(rl *Rule, rulesData *as3Rule) { action.Location = v.Location } if v.Log { + action.Type = "log" action.Write = &as3LogMessage{ Message: v.Message, } @@ -672,7 +670,7 @@ func (ap *AS3Parser) createRuleAction(rl *Rule, rulesData *as3Rule) { BigIP: v.Policy, } } - if v.Enabled != nil { + if v.Enabled { action.Enabled = v.Enabled } // Add drop action if specified @@ -681,24 +679,20 @@ func (ap *AS3Parser) createRuleAction(rl *Rule, rulesData *as3Rule) { } if v.PersistMethod != "" { + action.Event = "request" + action.Type = "persist" switch v.PersistMethod { case SourceAddress: - action.Event = "request" - action.Type = "persist" action.SourceAddress = &PersistMetaData{ Netmask: v.Netmask, Timeout: v.Timeout, } case DestinationAddress: - action.Event = "request" - action.Type = "persist" action.DestinationAddress = &PersistMetaData{ Netmask: v.Netmask, Timeout: v.Timeout, } case CookieHash: - action.Event = "request" - action.Type = "persist" action.CookieHash = &PersistMetaData{ Timeout: v.Timeout, Offset: v.Offset, @@ -706,49 +700,35 @@ func (ap *AS3Parser) createRuleAction(rl *Rule, rulesData *as3Rule) { Name: v.Name, } case CookieInsert: - action.Event = "request" - action.Type = "persist" action.CookieInsert = &PersistMetaData{ Name: v.Name, Expiry: v.Expiry, } case CookieRewrite: - action.Event = "request" - action.Type = "persist" action.CookieRewrite = &PersistMetaData{ Name: v.Name, Expiry: v.Expiry, } case CookiePassive: - action.Event = "request" - action.Type = "persist" action.CookiePassive = &PersistMetaData{ Name: v.Name, } case Universal: - action.Event = "request" - action.Type = "persist" action.Universal = &PersistMetaData{ Key: v.Key, Timeout: v.Timeout, } case Carp: - action.Event = "request" - action.Type = "persist" action.Carp = &PersistMetaData{ Key: v.Key, Timeout: v.Timeout, } case Hash: - action.Event = "request" - action.Type = "persist" action.Hash = &PersistMetaData{ Key: v.Key, Timeout: v.Timeout, } case Disable: - action.Event = "request" - action.Type = "persist" action.Disable = &PersistMetaData{} default: log.Warning("provide a persist method value from sourceAddress, destinationAddress, cookieInsert, cookieRewrite, cookiePassive, cookieHash, universal, hash, and carp") @@ -759,24 +739,24 @@ func (ap *AS3Parser) createRuleAction(rl *Rule, rulesData *as3Rule) { } } -func (ap *AS3Parser) DeepEqualJSON(decl1, decl2 as3Declaration) bool { - if decl1 == "" && decl2 == "" { - return true - } - var o1, o2 interface{} - - err := json.Unmarshal([]byte(decl1), &o1) - if err != nil { - return false - } - - err = json.Unmarshal([]byte(decl2), &o2) - if err != nil { - return false - } - - return reflect.DeepEqual(o1, o2) -} +//func (ap *AS3Parser) DeepEqualJSON(decl1, decl2 as3Declaration) bool { +// if decl1 == "" && decl2 == "" { +// return true +// } +// var o1, o2 interface{} +// +// err := json.Unmarshal([]byte(decl1), &o1) +// if err != nil { +// return false +// } +// +// err = json.Unmarshal([]byte(decl2), &o2) +// if err != nil { +// return false +// } +// +// return reflect.DeepEqual(o1, o2) +//} func (ap *AS3Parser) processProfilesForAS3(rsCfg *ResourceConfig, sharedApp as3Application) { if svc, ok := sharedApp[rsCfg.Virtual.Name].(*as3Service); ok { diff --git a/pkg/controller/backend.go b/pkg/controller/backend.go index bbc15ab7b..e91dfd482 100644 --- a/pkg/controller/backend.go +++ b/pkg/controller/backend.go @@ -109,7 +109,8 @@ func (agent *Agent) agentWorker() { // this post manager lock prevents that tenant cache map is not read by other components // while this post manager is processing this request agent.LTM.PostManager.declUpdate.Lock() - agent.LTM.publishConfig(agentConfig) + + agent.LTM.postConfig(agentConfig) /* If there are any tenants with 201 response code, poll for its status continuously and block incoming requests diff --git a/pkg/controller/backend_test.go b/pkg/controller/backend_test.go new file mode 100644 index 000000000..1b445a266 --- /dev/null +++ b/pkg/controller/backend_test.go @@ -0,0 +1,63 @@ +package controller + +import ( + "io" + "net/http" + "strings" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Backend Tests", func() { + Describe("agent worker tests with different status codes", func() { + var agent *Agent + var postConfig *agentPostConfig + var mockBaseAPIHandler *BaseAPIHandler + BeforeEach(func() { + mockBaseAPIHandler = newMockBaseAPIHandler() + agent = &Agent{} + tenantDeclMap := make(map[string]as3Tenant) + tenantDeclMap["test"] = as3Tenant{ + "class": "Tenant", + "defaultRouteDomain": 0, + as3SharedApplication: "shared", + "label": "cis2.x", + } + postConfig = &agentPostConfig{ + reqMeta: requestMeta{ + id: 1, + }, + as3APIURL: "https://127.0.0.1/mgmt/shared/appsvcs/declare", + data: `{"class": "AS3", "declaration": {"class": "ADC", "test": {"class": "Tenant", "testApp": {"class": "Application", "webcert":{"class": "Certificate", "certificate": "abc", "privateKey": "abc", "chainCA": "abc"}}}}}`, + incomingTenantDeclMap: tenantDeclMap, + tenantResponseMap: make(map[string]tenantResponse), + } + }) + It("Agent worker test", func() { + client, _ := getMockHttpClient([]responseCtx{{ + tenant: "test", + status: http.StatusOK, + body: io.NopCloser(strings.NewReader("{\"results\": [{\"code\": 200, \"message\": \"success\", \"tenant\": \"test\"}], \"declaration\": {\"class\": \"ADC\", \"test\": {\"class\": \"Tenant\", \"testApp\": {\"class\": \"Application\", \"webcert\":{\"class\": \"Certificate\", \"certificate\": \"abc\", \"privateKey\": \"abc\", \"chainCA\": \"abc\"}}}}}")), + }}, http.MethodPost) + mockBaseAPIHandler.httpClient = client + agent.APIHandler = &APIHandler{ + LTM: <MAPIHandler{ + BaseAPIHandler: mockBaseAPIHandler, + }, + GTM: >MAPIHandler{ + BaseAPIHandler: mockBaseAPIHandler, + }, + } + go agent.agentWorker() + agent.LTM.PostManager.postChan <- postConfig + response := <-agent.LTM.PostManager.respChan + + Expect(response).NotTo(BeNil(), "response should not be nil") + Expect(response.tenantResponseMap["test"].agentResponseCode).To(Equal(http.StatusOK), "response code should be 200") + + close(agent.LTM.PostManager.postChan) + close(agent.LTM.PostManager.respChan) + }) + }) +}) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 206031cae..450bd5da1 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -149,7 +149,7 @@ const ( ) // NewController creates a new Controller Instance. -func NewController(params Params, startController bool, agentParams AgentParams, handler *RequestHandler, td *teem.TeemsData) *Controller { +func NewController(params Params, startController bool, agentParams AgentParams, handler *RequestHandler, td *teem.TeemsData, baseAPIHandler *BaseAPIHandler) *Controller { ctlr := &Controller{ resources: NewResourceStore(), @@ -177,7 +177,7 @@ func NewController(params Params, startController bool, agentParams AgentParams, TeemData: td, } if handler == nil { - ctlr.RequestHandler = ctlr.NewRequestHandler(agentParams) + ctlr.RequestHandler = ctlr.NewRequestHandler(agentParams, baseAPIHandler) } else { ctlr.RequestHandler = handler } diff --git a/pkg/controller/controller_suit_test.go b/pkg/controller/controller_suit_test.go index 7a9c1e979..bd1c2d069 100644 --- a/pkg/controller/controller_suit_test.go +++ b/pkg/controller/controller_suit_test.go @@ -2,17 +2,19 @@ package controller import ( "fmt" + "io" + "net/http" + "strings" + "testing" + cisapiv1 "github.com/F5Networks/k8s-bigip-ctlr/v2/config/apis/cis/v1" + "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/test" "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/writer" mockhc "github.com/f5devcentral/mockhttpclient" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" routeapi "github.com/openshift/api/route/v1" - "io" v1 "k8s.io/api/core/v1" - "net/http" - "strings" - "testing" ) func TestController(t *testing.T) { @@ -32,7 +34,7 @@ type ( RespIndex int } - responceCtx struct { + responseCtx struct { tenant string status float64 body io.ReadCloser @@ -65,11 +67,11 @@ func newMockPostManger() *mockPostManager { return mockPM } -func getMockHttpClient(responces []responceCtx, method string) (*http.Client, error) { +func getMockHttpClient(responses []responseCtx, method string) (*http.Client, error) { responseMap := make(mockhc.ResponseConfigMap) responseMap[method] = &mockhc.ResponseConfig{} - for _, resp := range responces { + for _, resp := range responses { var bodyContent string if resp.body == nil { if resp.status == http.StatusOK { @@ -94,8 +96,8 @@ func getMockHttpClient(responces []responceCtx, method string) (*http.Client, er return mockhc.NewMockHTTPClient(responseMap) } -func (mockPM *mockPostManager) setResponses(responces []responceCtx, method string) { - client, _ := getMockHttpClient(responces, method) +func (mockPM *mockPostManager) setResponses(responses []responseCtx, method string) { + client, _ := getMockHttpClient(responses, method) mockPM.httpClient = client } @@ -127,6 +129,26 @@ func newMockRequestHandler(writer writer.Writer) *RequestHandler { } } +func newMockBaseAPIHandler() *BaseAPIHandler { + pm := &PostManager{ + postChan: make(chan *agentPostConfig, 1), + respChan: make(chan *agentPostConfig, 1), + PostParams: PostParams{ + BIGIPURL: "https://127.0.0.1", + BIGIPPassword: "password", + BIGIPUsername: "username", + LogRequest: true, + LogResponse: true, + }, + TokenManagerInterface: test.NewMockTokenManager("test-token"), + } + return &BaseAPIHandler{ + apiType: AS3, + PostManager: pm, + APIHandler: NewAS3Handler(pm, "test"), + } +} + func (m *mockController) addEDNS(edns *cisapiv1.ExternalDNS) { appInf, _ := m.getNamespacedCommonInformer(m.multiClusterHandler.LocalClusterName, edns.ObjectMeta.Namespace) appInf.ednsInformer.GetStore().Add(edns) diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go index 41d8c58ba..97e957ddf 100644 --- a/pkg/controller/controller_test.go +++ b/pkg/controller/controller_test.go @@ -5,17 +5,22 @@ import ( "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/test" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "io" v1 "k8s.io/api/core/v1" k8sfake "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/rest" + "net/http" + "strings" ) var _ = Describe("OtherSDNType", func() { var mockCtlr *mockController var selectors map[string]string + var mockBaseAPIHandler *BaseAPIHandler var pod *v1.Pod BeforeEach(func() { mockCtlr = newMockController() + mockBaseAPIHandler = newMockBaseAPIHandler() mockCtlr.multiClusterHandler = NewClusterHandler("") go mockCtlr.multiClusterHandler.ResourceEventWatcher() // Handles the resource status updates @@ -66,7 +71,10 @@ var _ = Describe("OtherSDNType", func() { BIGIPUsername: "username"}, Partition: "default", }, - mockRequestHandler, mockCtlr.TeemData) + mockRequestHandler, + mockCtlr.TeemData, + nil, + ) Expect(ctlrOpenShift.processedHostPath).NotTo(BeNil(), "processedHostPath object should not be nil") Expect(ctlrOpenShift.shareNodes).To(BeFalse(), "shareNodes should not be enable") Expect(ctlrOpenShift.vxlanMgr).To(BeNil(), "vxlanMgr should be created") @@ -81,10 +89,66 @@ var _ = Describe("OtherSDNType", func() { AgentParams{ PrimaryParams: PostParams{BIGIPURL: "http://127.0.0.1:8080"}, }, - mockRequestHandler, mockCtlr.TeemData) + mockRequestHandler, + mockCtlr.TeemData, + nil, + ) Expect(ctlrK8s.processedHostPath).To(BeNil(), "processedHostPath object should be nil") Expect(ctlrK8s.shareNodes).To(BeTrue(), "shareNodes should be enable") }) + It("Create a new controller object without request handler", func() { + client, _ := getMockHttpClient([]responseCtx{ + { + status: http.StatusOK, + body: io.NopCloser(strings.NewReader("{\"version\": \"17.1\", \"release\": \"3.52\", \"schemaCurrent\": \"5\"}")), + }, + { + status: http.StatusOK, + body: io.NopCloser(strings.NewReader("{\"version\": \"17.1\"}")), + }, + { + status: http.StatusOK, + body: io.NopCloser(strings.NewReader("{\"version\": \"17.1\", \"release\": \"3.52\", \"schemaCurrent\": \"5\"}")), + }, + { + status: http.StatusOK, + body: io.NopCloser(strings.NewReader("{\"version\": \"17.1\"}")), + }, + }, http.MethodGet) + mockBaseAPIHandler.httpClient = client + ctlrOpenShift := NewController(Params{ + Mode: OpenShiftMode, + PoolMemberType: Cluster, + Config: &rest.Config{}, + NamespaceLabel: "ctlr=cis", + VXLANMode: "multi-point", + VXLANName: "vxlan0", + CustomResourceLabel: DefaultCustomResourceLabel, + }, false, + AgentParams{ + ApiType: AS3, + EnableIPV6: true, + PrimaryParams: PostParams{ + BIGIPURL: "http://127.0.0.1:8080", + BIGIPPassword: "password", + BIGIPUsername: "username", + }, + GTMParams: PostParams{ + BIGIPURL: "http://127.0.0.1:8080", + BIGIPPassword: "password", + BIGIPUsername: "admin", + }, + CCCLGTMAgent: false, + Partition: "default", + }, + nil, + nil, + mockBaseAPIHandler, + ) + Expect(ctlrOpenShift.processedHostPath).NotTo(BeNil(), "processedHostPath object should not be nil") + Expect(ctlrOpenShift.shareNodes).To(BeFalse(), "shareNodes should not be enable") + Expect(ctlrOpenShift.vxlanMgr).To(BeNil(), "vxlanMgr should be created") + }) It("Validate the IPAM configuration", func() { mockWriter := &test.MockWriter{FailStyle: test.Success} mockRequestHandler := newMockRequestHandler(mockWriter) @@ -95,7 +159,10 @@ var _ = Describe("OtherSDNType", func() { AgentParams{ PrimaryParams: PostParams{BIGIPURL: "http://127.0.0.1:8080"}, }, - mockRequestHandler, mockCtlr.TeemData) + mockRequestHandler, + mockCtlr.TeemData, + nil, + ) ctlr.multiClusterHandler = NewClusterHandler("") ctlr.multiClusterHandler.ClusterConfigs[""] = &ClusterConfig{InformerStore: initInformerStore(), namespaces: make(map[string]struct{})} diff --git a/pkg/controller/gtmBackend.go b/pkg/controller/gtmBackend.go index 617e3f88f..fab8dead9 100644 --- a/pkg/controller/gtmBackend.go +++ b/pkg/controller/gtmBackend.go @@ -23,7 +23,7 @@ func (agent *Agent) gtmWorker() { // this post manager lock prevents that tenant cache map is not read by other components // while this post manager is processing this request agent.GTM.PostManager.declUpdate.Lock() - agent.GTM.publishConfig(agentConfig) + agent.GTM.postConfig(agentConfig) /* If there are any tenants with 201 response code, poll for its status continuously and block incoming requests diff --git a/pkg/controller/gtmBackend_test.go b/pkg/controller/gtmBackend_test.go index b694b87ae..8e6efcbc9 100644 --- a/pkg/controller/gtmBackend_test.go +++ b/pkg/controller/gtmBackend_test.go @@ -1,12 +1,67 @@ package controller import ( + "io" + "net/http" + "strings" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Backend Tests", func() { + Describe("GTM Worker test", func() { + var agent *Agent + var postConfig *agentPostConfig + var mockBaseAPIHandler *BaseAPIHandler + BeforeEach(func() { + mockBaseAPIHandler = newMockBaseAPIHandler() + agent = &Agent{} + tenantDeclMap := make(map[string]as3Tenant) + tenantDeclMap["test_gtm"] = as3Tenant{ + "class": "Tenant", + "defaultRouteDomain": 0, + as3SharedApplication: "shared", + "label": "cis2.x", + } + postConfig = &agentPostConfig{ + reqMeta: requestMeta{ + id: 1, + }, + as3APIURL: "https://127.0.0.1/mgmt/shared/appsvcs/declare", + data: `{"class": "AS3", "declaration": {"class": "ADC", "test": {"class": "Tenant", "testApp": {"class": "Application", "webcert":{"class": "Certificate", "certificate": "abc", "privateKey": "abc", "chainCA": "abc"}}}}}`, + incomingTenantDeclMap: tenantDeclMap, + tenantResponseMap: make(map[string]tenantResponse), + } + }) + It("GTM worker", func() { + client, _ := getMockHttpClient([]responseCtx{{ + tenant: "test_gtm", + status: http.StatusOK, + body: io.NopCloser(strings.NewReader("{\"results\": [{\"code\": 200, \"message\": \"success\", \"tenant\": \"test_gtm\"}], \"declaration\": {\"class\": \"ADC\", \"test\": {\"class\": \"Tenant\", \"testApp\": {\"class\": \"Application\", \"webcert\":{\"class\": \"Certificate\", \"certificate\": \"abc\", \"privateKey\": \"abc\", \"chainCA\": \"abc\"}}}}}")), + }}, http.MethodPost) + mockBaseAPIHandler.httpClient = client + agent.APIHandler = &APIHandler{ + LTM: <MAPIHandler{ + BaseAPIHandler: mockBaseAPIHandler, + }, + GTM: >MAPIHandler{ + BaseAPIHandler: mockBaseAPIHandler, + }, + } + go agent.gtmWorker() + agent.GTM.PostManager.postChan <- postConfig + response := <-agent.GTM.PostManager.respChan + + Expect(response).NotTo(BeNil(), "response should not be nil") + Expect(response.tenantResponseMap["test_gtm"].agentResponseCode).To(Equal(http.StatusOK), "response code should be 200") + + close(agent.LTM.PostManager.postChan) + close(agent.LTM.PostManager.respChan) + }) + }) + Describe("Prepare AS3 GTM PostManager", func() { BeforeEach(func() { DEFAULT_GTM_PARTITION = "test_gtm" diff --git a/pkg/controller/nativeResourceWorker.go b/pkg/controller/nativeResourceWorker.go index 984ec8b19..c29d80725 100644 --- a/pkg/controller/nativeResourceWorker.go +++ b/pkg/controller/nativeResourceWorker.go @@ -240,10 +240,9 @@ func (ctlr *Controller) processRoutes(routeGroup string, triggerDelete bool) err // addDefaultWAFDisableRule adds WAF disable action for rules without WAF and a default WAF disable rule func (ctlr *Controller) addDefaultWAFDisableRule(rsCfg *ResourceConfig, wafDisableRuleName string) { - enabled := false wafDisableAction := &action{ WAF: true, - Enabled: &enabled, + Enabled: false, } wafDropAction := &action{ Drop: true, diff --git a/pkg/controller/postManager_test.go b/pkg/controller/postManager_test.go index b447f78c7..74cd38591 100644 --- a/pkg/controller/postManager_test.go +++ b/pkg/controller/postManager_test.go @@ -186,7 +186,7 @@ var _ = Describe("PostManager", func() { Describe("postConfig", func() { Context("when the request is successful", func() { BeforeEach(func() { - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "test", status: http.StatusOK, body: io.NopCloser(strings.NewReader("{\"status\": \"success\"}")), @@ -216,7 +216,7 @@ var _ = Describe("PostManager", func() { Context("when the HTTP POST fails", func() { BeforeEach(func() { - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "test", status: http.StatusNotFound, body: io.NopCloser(strings.NewReader("")), @@ -233,7 +233,7 @@ var _ = Describe("PostManager", func() { Describe("httpPOST", func() { Context("when the response body cannot be read", func() { BeforeEach(func() { - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "test", status: http.StatusOK, body: io.NopCloser(&errorReader{}), @@ -250,7 +250,7 @@ var _ = Describe("PostManager", func() { Context("when the response body is not valid JSON", func() { BeforeEach(func() { - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "test", status: http.StatusOK, body: io.NopCloser(strings.NewReader("not json")), @@ -267,7 +267,7 @@ var _ = Describe("PostManager", func() { Context("when the response is Unauthorized", func() { BeforeEach(func() { - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "test", status: http.StatusUnauthorized, body: io.NopCloser(strings.NewReader("Unauthorized")), @@ -295,7 +295,7 @@ var _ = Describe("PostManager with TokenManager", func() { mockPM.BIGIPUsername = "testuser" mockPM.BIGIPPassword = "testpass" mockPM.TokenManagerInterface = test.NewMockTokenManager("test-token") - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "test", status: http.StatusOK, body: io.NopCloser(strings.NewReader("{\"status\": \"success\"}")), @@ -348,7 +348,7 @@ var _ = Describe("PostManager with TokenManager", func() { // Force token to be fetched token := mockPM.GetToken() Expect(token).To(Equal("test-token")) - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "test", status: http.StatusOK, body: io.NopCloser(strings.NewReader("{\"version\": \"3.36.0\"}")), @@ -368,7 +368,7 @@ var _ = Describe("PostManager with TokenManager", func() { Context("token refresh on 401", func() { It("should attempt to refresh token when receiving 401 response", func() { - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: "test", status: http.StatusUnauthorized, body: io.NopCloser(strings.NewReader("{\"version\": \"3.36.0\"}")), diff --git a/pkg/controller/requestHandler.go b/pkg/controller/requestHandler.go index 3bc88c445..fba477ed8 100644 --- a/pkg/controller/requestHandler.go +++ b/pkg/controller/requestHandler.go @@ -10,7 +10,7 @@ import ( var OsExit = os.Exit -func (ctlr *Controller) NewRequestHandler(agentParams AgentParams) *RequestHandler { +func (ctlr *Controller) NewRequestHandler(agentParams AgentParams, baseAPIHandler *BaseAPIHandler) *RequestHandler { var refreshTokenInterval time.Duration if agentParams.RefreshTokenInterval != 0 { refreshTokenInterval = time.Duration(agentParams.RefreshTokenInterval) * time.Hour @@ -25,7 +25,7 @@ func (ctlr *Controller) NewRequestHandler(agentParams AgentParams) *RequestHandl } gtmOnSeparateBigIPServer := isGTMOnSeparateServer(agentParams) if (agentParams.PrimaryParams != PostParams{}) { - reqHandler.PrimaryBigIPWorker = reqHandler.NewAgentWorker(PrimaryBigIP, gtmOnSeparateBigIPServer) + reqHandler.PrimaryBigIPWorker = reqHandler.NewAgentWorker(PrimaryBigIP, gtmOnSeparateBigIPServer, baseAPIHandler) reqHandler.CcclHandler(reqHandler.PrimaryBigIPWorker) // start the token manager go reqHandler.PrimaryBigIPWorker.getPostManager().TokenManagerInterface.Start(make(chan struct{}), refreshTokenInterval) @@ -33,7 +33,7 @@ func (ctlr *Controller) NewRequestHandler(agentParams AgentParams) *RequestHandl go reqHandler.PrimaryBigIPWorker.agentWorker() } if (agentParams.SecondaryParams != PostParams{}) { - reqHandler.SecondaryBigIPWorker = reqHandler.NewAgentWorker(SecondaryBigIP, gtmOnSeparateBigIPServer) + reqHandler.SecondaryBigIPWorker = reqHandler.NewAgentWorker(SecondaryBigIP, gtmOnSeparateBigIPServer, baseAPIHandler) reqHandler.CcclHandler(reqHandler.SecondaryBigIPWorker) // start the token manager go reqHandler.SecondaryBigIPWorker.getPostManager().TokenManagerInterface.Start(make(chan struct{}), refreshTokenInterval) @@ -42,7 +42,7 @@ func (ctlr *Controller) NewRequestHandler(agentParams AgentParams) *RequestHandl } // Run the GTM Agent only in case of separate server and not in cccl mode if gtmOnSeparateBigIPServer && !agentParams.CCCLGTMAgent { - reqHandler.GTMBigIPWorker = reqHandler.NewAgentWorker(GTMBigIP, gtmOnSeparateBigIPServer) + reqHandler.GTMBigIPWorker = reqHandler.NewAgentWorker(GTMBigIP, gtmOnSeparateBigIPServer, baseAPIHandler) // start the token manager go reqHandler.GTMBigIPWorker.getPostManager().TokenManagerInterface.Start(make(chan struct{}), refreshTokenInterval) // start the worker @@ -190,12 +190,12 @@ func (reqHandler *RequestHandler) CcclHandler(agent *Agent) { } } -func (reqHandler *RequestHandler) NewAgentWorker(kind string, gtmOnSeparateBigIpServer bool) *Agent { +func (reqHandler *RequestHandler) NewAgentWorker(kind string, gtmOnSeparateBigIpServer bool, baseAPIHandler *BaseAPIHandler) *Agent { var err error var agent *Agent switch kind { case GTMBigIP: - agent = reqHandler.NewAgent(GTMBigIP) + agent = reqHandler.NewAgent(GTMBigIP, baseAPIHandler) err = agent.GTM.IsBigIPAppServicesAvailable() if err != nil { log.Errorf("%v", err) @@ -203,7 +203,7 @@ func (reqHandler *RequestHandler) NewAgentWorker(kind string, gtmOnSeparateBigIp OsExit(1) } case SecondaryBigIP: - agent = reqHandler.NewAgent(SecondaryBigIP) + agent = reqHandler.NewAgent(SecondaryBigIP, baseAPIHandler) err = agent.LTM.IsBigIPAppServicesAvailable() if err != nil { log.Errorf("%v", err) @@ -211,7 +211,7 @@ func (reqHandler *RequestHandler) NewAgentWorker(kind string, gtmOnSeparateBigIp OsExit(1) } case PrimaryBigIP: - agent = reqHandler.NewAgent(PrimaryBigIP) + agent = reqHandler.NewAgent(PrimaryBigIP, baseAPIHandler) err = agent.LTM.IsBigIPAppServicesAvailable() if err != nil { log.Errorf("%v", err) @@ -228,7 +228,7 @@ func (reqHandler *RequestHandler) NewAgentWorker(kind string, gtmOnSeparateBigIp return agent } -func (reqHandler *RequestHandler) NewAgent(kind string) *Agent { +func (reqHandler *RequestHandler) NewAgent(kind string, baseAPIHandler *BaseAPIHandler) *Agent { agent := &Agent{ APIHandler: &APIHandler{}, ccclGTMAgent: reqHandler.agentParams.CCCLGTMAgent, @@ -238,11 +238,11 @@ func (reqHandler *RequestHandler) NewAgent(kind string) *Agent { switch kind { case GTMBigIP: DEFAULT_GTM_PARTITION = reqHandler.agentParams.Partition + "_gtm" - agent.APIHandler.GTM = NewGTMAPIHandler(reqHandler.agentParams, reqHandler.respChan) + agent.APIHandler.GTM = NewGTMAPIHandler(reqHandler.agentParams, baseAPIHandler,reqHandler.respChan) default: DEFAULT_PARTITION = reqHandler.agentParams.Partition DEFAULT_GTM_PARTITION = reqHandler.agentParams.Partition + "_gtm" - agent.APIHandler.LTM = NewLTMAPIHandler(reqHandler.agentParams, kind, reqHandler.respChan) + agent.APIHandler.LTM = NewLTMAPIHandler(reqHandler.agentParams, kind, baseAPIHandler, reqHandler.respChan) } return agent } diff --git a/pkg/controller/types.go b/pkg/controller/types.go index d033486fd..6fe7ddc5d 100644 --- a/pkg/controller/types.go +++ b/pkg/controller/types.go @@ -18,11 +18,12 @@ package controller import ( "container/list" - "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/tokenmanager" "net/http" "sync" "time" + "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/tokenmanager" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/informers" @@ -649,7 +650,7 @@ type ( WAF bool `json:"waf,omitempty"` Policy string `json:"policy,omitempty"` Drop bool `json:"drop,omitempty"` - Enabled *bool `json:"enabled,omitempty"` + Enabled bool `json:"enabled,omitempty"` Log bool `json:"log,omitempty"` Message string `json:"message,omitempty"` PersistMethod string `json:"method,omitempty"` @@ -1064,7 +1065,7 @@ type ( Event string `json:"event,omitempty"` Select *as3ActionForwardSelect `json:"select,omitempty"` Policy *as3ResourcePointer `json:"policy,omitempty"` - Enabled *bool `json:"enabled,omitempty"` + Enabled bool `json:"enabled,omitempty"` Location string `json:"location,omitempty"` Replace *as3ActionReplaceMap `json:"replace,omitempty"` Write *as3LogMessage `json:"write,omitempty"` diff --git a/pkg/controller/worker_test.go b/pkg/controller/worker_test.go index 029ddf3cb..e29b63d7f 100644 --- a/pkg/controller/worker_test.go +++ b/pkg/controller/worker_test.go @@ -1594,7 +1594,7 @@ var _ = Describe("Worker Tests", func() { // mockPM.PostDelay = mockPM.setupBIGIPRESTClient() tnt := "test" - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: tnt, status: http.StatusOK, body: io.NopCloser(strings.NewReader("")), @@ -3007,7 +3007,7 @@ var _ = Describe("Worker Tests", func() { mockPM.BIGIPPassword = "pswd" mockPM.setupBIGIPRESTClient() tnt := "test" - mockPM.setResponses([]responceCtx{{ + mockPM.setResponses([]responseCtx{{ tenant: tnt, status: http.StatusOK, body: io.NopCloser(strings.NewReader("")), From 794e74e50709d304f36a44f9861623eff373965f Mon Sep 17 00:00:00 2001 From: vidyasagar-m Date: Tue, 22 Apr 2025 07:01:22 +0530 Subject: [PATCH 2/3] fix format --- pkg/controller/requestHandler.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/controller/requestHandler.go b/pkg/controller/requestHandler.go index fba477ed8..1a3704c60 100644 --- a/pkg/controller/requestHandler.go +++ b/pkg/controller/requestHandler.go @@ -1,11 +1,12 @@ package controller import ( - log "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/vlogger" - "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/writer" "os" "strings" "time" + + log "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/vlogger" + "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/writer" ) var OsExit = os.Exit @@ -238,7 +239,7 @@ func (reqHandler *RequestHandler) NewAgent(kind string, baseAPIHandler *BaseAPIH switch kind { case GTMBigIP: DEFAULT_GTM_PARTITION = reqHandler.agentParams.Partition + "_gtm" - agent.APIHandler.GTM = NewGTMAPIHandler(reqHandler.agentParams, baseAPIHandler,reqHandler.respChan) + agent.APIHandler.GTM = NewGTMAPIHandler(reqHandler.agentParams, baseAPIHandler, reqHandler.respChan) default: DEFAULT_PARTITION = reqHandler.agentParams.Partition DEFAULT_GTM_PARTITION = reqHandler.agentParams.Partition + "_gtm" From 7713075dc3822437f21371def8f8a70d896352ff Mon Sep 17 00:00:00 2001 From: vidyasagar-m Date: Thu, 24 Apr 2025 22:30:49 +0530 Subject: [PATCH 3/3] fix format --- pkg/controller/backend.go | 5 +++-- pkg/controller/gtmBackend.go | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/controller/backend.go b/pkg/controller/backend.go index e91dfd482..e5eeaa475 100644 --- a/pkg/controller/backend.go +++ b/pkg/controller/backend.go @@ -17,11 +17,12 @@ package controller import ( - rsc "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/resource" - log "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/vlogger" "strconv" "strings" "time" + + rsc "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/resource" + log "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/vlogger" ) var DEFAULT_PARTITION string diff --git a/pkg/controller/gtmBackend.go b/pkg/controller/gtmBackend.go index fab8dead9..ba8ab68f3 100644 --- a/pkg/controller/gtmBackend.go +++ b/pkg/controller/gtmBackend.go @@ -1,8 +1,9 @@ package controller import ( - log "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/vlogger" "time" + + log "github.com/F5Networks/k8s-bigip-ctlr/v2/pkg/vlogger" ) // whenever it gets unblocked, retries failed declarations and polls for accepted tenant statuses