diff --git a/cns/NetworkContainerContract.go b/cns/NetworkContainerContract.go index a724eaf5bc..b5b9d2626f 100644 --- a/cns/NetworkContainerContract.go +++ b/cns/NetworkContainerContract.go @@ -463,6 +463,12 @@ type PublishNetworkContainerRequest struct { CreateNetworkContainerRequestBody []byte } +// NetworkContainerParameters parameters available in network container operations +type NetworkContainerParameters struct { + AuthToken string + AssociatedInterfaceID string +} + // PublishNetworkContainerResponse specifies the response to publish network container request. type PublishNetworkContainerResponse struct { Response Response diff --git a/cns/nmagent/client.go b/cns/nmagent/client.go index e372dc8948..ce0d8c5db7 100644 --- a/cns/nmagent/client.go +++ b/cns/nmagent/client.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "net/http" + "net/url" "time" "github.com/Azure/azure-container-networking/cns/logger" @@ -20,11 +21,16 @@ const ( GetNmAgentSupportedApiURLFmt = "http://%s/machine/plugins/?comp=nmagent&type=GetSupportedApis" GetNetworkContainerVersionURLFmt = "http://%s/machine/plugins/?comp=nmagent&type=NetworkManagement/interfaces/%s/networkContainers/%s/version/authenticationToken/%s/api-version/1" GetNcVersionListWithOutTokenURLFmt = "http://%s/machine/plugins/?comp=nmagent&type=NetworkManagement/interfaces/api-version/%s" + JoinNetworkURLFmt = "NetworkManagement/joinedVirtualNetworks/%s/api-version/1" + PutNetworkValueFmt = "NetworkManagement/interfaces/%s/networkContainers/%s/authenticationToken/%s/api-version/1" + DeleteNetworkContainerURLFmt = "NetworkManagement/interfaces/%s/networkContainers/%s/authenticationToken/%s/api-version/1/method/DELETE" ) // WireServerIP - wire server ip var ( WireserverIP = "168.63.129.16" + WireServerPath = "machine/plugins" + WireServerScheme = "http" getNcVersionListWithOutTokenURLVersion = "2" ) @@ -65,13 +71,30 @@ func NewClient(url string) (*Client, error) { } // JoinNetwork joins the given network -func JoinNetwork(networkID, joinNetworkURL string) (*http.Response, error) { +func JoinNetwork(networkID string) (*http.Response, error) { logger.Printf("[NMAgentClient] JoinNetwork: %s", networkID) // Empty body is required as wireserver cannot handle a post without the body. var body bytes.Buffer json.NewEncoder(&body).Encode("") - response, err := common.GetHttpClient().Post(joinNetworkURL, "application/json", &body) + + joinNetworkTypeValue := fmt.Sprintf( + JoinNetworkURLFmt, + networkID) + + joinNetworkURL := url.URL{ + Host: WireserverIP, + Path: WireServerPath, + Scheme: WireServerScheme, + } + + queryString := joinNetworkURL.Query() + queryString.Set("type", joinNetworkTypeValue) + queryString.Set("comp", "nmagent") + + joinNetworkURL.RawQuery = queryString.Encode() + + response, err := common.PostCtx(context.TODO(), common.GetHttpClient(), joinNetworkURL.String(), "application/json", &body) if err == nil && response.StatusCode == http.StatusOK { defer response.Body.Close() @@ -84,11 +107,29 @@ func JoinNetwork(networkID, joinNetworkURL string) (*http.Response, error) { } // PublishNetworkContainer publishes given network container -func PublishNetworkContainer(networkContainerID, createNetworkContainerURL string, requestBodyData []byte) (*http.Response, error) { +func PublishNetworkContainer(networkContainerID, associatedInterfaceID, accessToken string, requestBodyData []byte) (*http.Response, error) { logger.Printf("[NMAgentClient] PublishNetworkContainer NC: %s", networkContainerID) + createNcTypeValue := fmt.Sprintf( + PutNetworkValueFmt, + associatedInterfaceID, + networkContainerID, + accessToken) + + createURL := url.URL{ + Host: WireserverIP, + Path: WireServerPath, + Scheme: WireServerScheme, + } + + queryString := createURL.Query() + queryString.Set("type", createNcTypeValue) + queryString.Set("comp", "nmagent") + + createURL.RawQuery = queryString.Encode() + requestBody := bytes.NewBuffer(requestBodyData) - response, err := common.GetHttpClient().Post(createNetworkContainerURL, "application/json", requestBody) + response, err := common.PostCtx(context.TODO(), common.GetHttpClient(), createURL.String(), "application/json", requestBody) logger.Printf("[NMAgentClient][Response] Publish NC: %s. Response: %+v. Error: %v", networkContainerID, response, err) @@ -97,13 +138,31 @@ func PublishNetworkContainer(networkContainerID, createNetworkContainerURL strin } // UnpublishNetworkContainer unpublishes given network container -func UnpublishNetworkContainer(networkContainerID, deleteNetworkContainerURL string) (*http.Response, error) { +func UnpublishNetworkContainer(networkContainerID, associatedInterfaceID, accessToken string) (*http.Response, error) { logger.Printf("[NMAgentClient] UnpublishNetworkContainer NC: %s", networkContainerID) + deleteNCTypeValue := fmt.Sprintf( + DeleteNetworkContainerURLFmt, + associatedInterfaceID, + networkContainerID, + accessToken) + + deleteURL := url.URL{ + Host: WireserverIP, + Path: WireServerPath, + Scheme: WireServerScheme, + } + + queryString := deleteURL.Query() + queryString.Set("type", deleteNCTypeValue) + queryString.Set("comp", "nmagent") + + deleteURL.RawQuery = queryString.Encode() + // Empty body is required as wireserver cannot handle a post without the body. var body bytes.Buffer json.NewEncoder(&body).Encode("") - response, err := common.GetHttpClient().Post(deleteNetworkContainerURL, "application/json", &body) + response, err := common.PostCtx(context.TODO(), common.GetHttpClient(), deleteURL.String(), "application/json", &body) logger.Printf("[NMAgentClient][Response] Unpublish NC: %s. Response: %+v. Error: %v", networkContainerID, response, err) diff --git a/cns/restserver/api.go b/cns/restserver/api.go index ecbc3efa7e..b44c98e36b 100644 --- a/cns/restserver/api.go +++ b/cns/restserver/api.go @@ -5,11 +5,13 @@ package restserver import ( "context" + "errors" "fmt" "io" "net" "net/http" "net/url" + "regexp" "runtime" "strings" @@ -23,6 +25,19 @@ import ( "github.com/Azure/azure-container-networking/platform" ) +var ( + ncRegex = regexp.MustCompile(`NetworkManagement/interfaces/(.{0,36})/networkContainers/(.{0,36})/authenticationToken/(.{0,36})/api-version/1(/method/DELETE)?`) + ErrInvalidNcURLFormat = errors.New("Invalid network container url format") +) + +// ncURLExpectedMatches defines the size of matches expected from exercising the ncRegex +// 1) the original url (nuance related to golangs regex package) +// 2) the associated interface parameter +// 3) the ncid parameter +// 4) the authentication token parameter +// 5) the optional delete path +const ncURLExpectedMatches = 5 + // This file contains implementation of all HTTP APIs which are exposed to external clients. // TODO: break it even further per module (network, nc, etc) like it is done for ipam @@ -1072,22 +1087,30 @@ func (service *HTTPRestService) getNumberOfCPUCores(w http.ResponseWriter, r *ht logger.Response(service.Name, numOfCPUCoresResp, resp.ReturnCode, err) } -func getInterfaceIdFromCreateNetworkContainerURL( - createNetworkContainerURL string) string { - return strings.Split(strings.Split(createNetworkContainerURL, "interfaces/")[1], "/")[0] -} +func getAuthTokenAndInterfaceIDFromNcURL(networkContainerURL string) (*cns.NetworkContainerParameters, error) { + ncURL, err := url.Parse(networkContainerURL) + if err != nil { + return nil, fmt.Errorf("failed to parse network container url, %w", err) + } -func getAuthTokenFromCreateNetworkContainerURL( - createNetworkContainerURL string) string { - return strings.Split(strings.Split(createNetworkContainerURL, "authenticationToken/")[1], "/")[0] -} + queryParams := ncURL.Query() -func extractHostFromJoinNetworkURL(urlToParse string) string { - parsedURL, err := url.Parse(urlToParse) - if err != nil { - return "" + // current format of create network url has a path after a query parameter "type" + // doing this parsing due to this structure + typeQueryParamVal := queryParams.Get("type") + if typeQueryParamVal == "" { + return nil, fmt.Errorf("no type query param, %w", ErrInvalidNcURLFormat) } - return parsedURL.Host + + // .{0,128} gets from zero to 128 characters of any kind + // ()? is optional + matches := ncRegex.FindStringSubmatch(typeQueryParamVal) + + if len(matches) != ncURLExpectedMatches { + return nil, fmt.Errorf("unexpected number of matches in url, %w", ErrInvalidNcURLFormat) + } + + return &cns.NetworkContainerParameters{AssociatedInterfaceID: matches[1], AuthToken: matches[3]}, nil } // Publish Network Container by calling nmagent @@ -1109,6 +1132,8 @@ func (service *HTTPRestService) publishNetworkContainer(w http.ResponseWriter, r err = service.Listener.Decode(w, r, &req) + creteNcURLCopy := req.CreateNetworkContainerURL + // reqCopy creates a copy of incoming request. It doesn't copy the authentication token info // to avoid logging it. reqCopy := cns.PublishNetworkContainerRequest{ @@ -1119,14 +1144,38 @@ func (service *HTTPRestService) publishNetworkContainer(w http.ResponseWriter, r } logger.Request(service.Name, &reqCopy, err) + + // TODO - refactor this method for better error handling if err != nil { return } + var ncParameters *cns.NetworkContainerParameters + ncParameters, err = getAuthTokenAndInterfaceIDFromNcURL(creteNcURLCopy) + if err != nil { + logger.Errorf("[Azure-CNS] nc parameters validation failed with %+v", err) + w.WriteHeader(http.StatusBadRequest) + + badRequestResponse := &cns.PublishNetworkContainerResponse{ + Response: cns.Response{ + ReturnCode: http.StatusBadRequest, + Message: fmt.Sprintf("Request contains a unexpected create url format in request body: %v", reqCopy.CreateNetworkContainerURL), + }, + PublishErrorStr: fmt.Sprintf("Bad request: Request contains a unexpected create url format in request body: %v", reqCopy.CreateNetworkContainerURL), + PublishStatusCode: http.StatusBadRequest, + } + err = service.Listener.Encode(w, &badRequestResponse) + logger.Response(service.Name, badRequestResponse, badRequestResponse.Response.ReturnCode, err) + return + } + switch r.Method { case "POST": // Join the network - publishResponse, publishError, err = service.joinNetwork(req.NetworkID, req.JoinNetworkURL) + // Please refactor this + // do not reuse the below variable between network join and publish + // nolint:bodyclose // existing code needs refactoring + publishResponse, publishError, err = service.joinNetwork(req.NetworkID) if err == nil { isNetworkJoined = true } else { @@ -1136,9 +1185,12 @@ func (service *HTTPRestService) publishNetworkContainer(w http.ResponseWriter, r if isNetworkJoined { // Publish Network Container + + // nolint:bodyclose // existing code needs refactoring publishResponse, publishError = nmagent.PublishNetworkContainer( req.NetworkContainerID, - req.CreateNetworkContainerURL, + ncParameters.AssociatedInterfaceID, + ncParameters.AuthToken, req.CreateNetworkContainerRequestBody) if publishError != nil || publishResponse.StatusCode != http.StatusOK { returnMessage = fmt.Sprintf("Failed to publish Network Container: %s", req.NetworkContainerID) @@ -1149,20 +1201,11 @@ func (service *HTTPRestService) publishNetworkContainer(w http.ResponseWriter, r } // Store ncGetVersionURL needed for calling NMAgent to check if vfp programming is completed for the NC - primaryInterfaceIdentifier := getInterfaceIdFromCreateNetworkContainerURL(req.CreateNetworkContainerURL) - authToken := getAuthTokenFromCreateNetworkContainerURL(req.CreateNetworkContainerURL) - - // we attempt to extract the wireserver IP to use from the request, otherwise default to the well-known IP. - hostIP := extractHostFromJoinNetworkURL(req.JoinNetworkURL) - if hostIP == "" { - hostIP = nmagent.WireserverIP - } - ncGetVersionURL := fmt.Sprintf(nmagent.GetNetworkContainerVersionURLFmt, - hostIP, - primaryInterfaceIdentifier, + nmagent.WireserverIP, + ncParameters.AssociatedInterfaceID, req.NetworkContainerID, - authToken) + ncParameters.AuthToken) ncVersionURLs.Store(cns.SwiftPrefix+req.NetworkContainerID, ncGetVersionURL) default: @@ -1219,6 +1262,8 @@ func (service *HTTPRestService) unpublishNetworkContainer(w http.ResponseWriter, err = service.Listener.Decode(w, r, &req) + deleteNcURLCopy := req.DeleteNetworkContainerURL + // reqCopy creates a copy of incoming request. It doesn't copy the authentication token info // to avoid logging it. reqCopy := cns.UnpublishNetworkContainerRequest{ @@ -1233,12 +1278,32 @@ func (service *HTTPRestService) unpublishNetworkContainer(w http.ResponseWriter, return } + var ncParameters *cns.NetworkContainerParameters + ncParameters, err = getAuthTokenAndInterfaceIDFromNcURL(deleteNcURLCopy) + if err != nil { + logger.Errorf("[Azure-CNS] nc parameters validation failed with %+v", err) + w.WriteHeader(http.StatusBadRequest) + + badRequestResponse := &cns.UnpublishNetworkContainerResponse{ + Response: cns.Response{ + ReturnCode: http.StatusBadRequest, + Message: fmt.Sprintf("Request contains a unexpected delete url format in request body: %v", reqCopy.DeleteNetworkContainerURL), + }, + UnpublishErrorStr: fmt.Sprintf("Bad request: Request contains a unexpected delete url format in request body: %v", reqCopy.DeleteNetworkContainerURL), + UnpublishStatusCode: http.StatusBadRequest, + } + err = service.Listener.Encode(w, &badRequestResponse) + logger.Response(service.Name, badRequestResponse, badRequestResponse.Response.ReturnCode, err) + return + } + switch r.Method { case "POST": // Join Network if not joined already isNetworkJoined = service.isNetworkJoined(req.NetworkID) if !isNetworkJoined { - unpublishResponse, unpublishError, err = service.joinNetwork(req.NetworkID, req.JoinNetworkURL) + // nolint:bodyclose // existing code needs refactoring + unpublishResponse, unpublishError, err = service.joinNetwork(req.NetworkID) if err == nil { isNetworkJoined = true } else { @@ -1251,7 +1316,8 @@ func (service *HTTPRestService) unpublishNetworkContainer(w http.ResponseWriter, // Unpublish Network Container unpublishResponse, unpublishError = nmagent.UnpublishNetworkContainer( req.NetworkContainerID, - req.DeleteNetworkContainerURL) + ncParameters.AssociatedInterfaceID, + ncParameters.AuthToken) if unpublishError != nil || unpublishResponse.StatusCode != http.StatusOK { returnMessage = fmt.Sprintf("Failed to unpublish Network Container: %s", req.NetworkContainerID) returnCode = types.NetworkContainerUnpublishFailed diff --git a/cns/restserver/api_test.go b/cns/restserver/api_test.go index 34fc663f89..844cd0d115 100644 --- a/cns/restserver/api_test.go +++ b/cns/restserver/api_test.go @@ -416,7 +416,7 @@ func TestGetNetworkContainerVersionStatus(t *testing.T) { podNamespace: "testpodnamespace", } - createNC(t, params) + createNC(t, params, false) if err := getNetworkContainerByContext(t, params); err != nil { t.Errorf("TestGetNetworkContainerByOrchestratorContext failed Err:%+v", err) @@ -447,7 +447,7 @@ func TestGetNetworkContainerVersionStatus(t *testing.T) { podNamespace: "testpodnamespace", } - createNC(t, params) + createNC(t, params, false) if err := getNetworkContainerByContextExpectedError(t, params); err != nil { t.Errorf("TestGetNetworkContainerVersionStatus failed") @@ -471,7 +471,7 @@ func TestGetNetworkContainerVersionStatus(t *testing.T) { podNamespace: "testpodnamespace", } - createNC(t, params) + createNC(t, params, true) if err := getNetworkContainerByContext(t, params); err != nil { t.Errorf("TestGetNetworkContainerVersionStatus failed") @@ -494,7 +494,7 @@ func TestGetNetworkContainerVersionStatus(t *testing.T) { podNamespace: "testpodnamespace", } - createNC(t, params) + createNC(t, params, false) if err := getNetworkContainerByContextExpectedError(t, params); err != nil { t.Errorf("TestGetNetworkContainerVersionStatus failed") @@ -507,33 +507,67 @@ func TestGetNetworkContainerVersionStatus(t *testing.T) { } } +//nolint:gocritic // param is just for testing func createNC( t *testing.T, - params createOrUpdateNetworkContainerParams) { + params createOrUpdateNetworkContainerParams, + expectError bool) { if err := createOrUpdateNetworkContainerWithParams(t, params); err != nil { t.Errorf("createOrUpdateNetworkContainerWithParams failed Err:%+v", err) t.Fatal(err) } - publishNCViaCNS(t, params.vnetID, params.ncID) + createNetworkContainerURL := "http://" + nmagentEndpoint + + "/machine/plugins/?comp=nmagent&type=NetworkManagement/interfaces/dummyIntf/networkContainers/dummyNCURL/authenticationToken/dummyT/api-version/1" + + err := publishNCViaCNS(t, params.vnetID, params.ncID, createNetworkContainerURL, expectError) + if err != nil { + t.Fatal(err) + } } func TestPublishNCViaCNS(t *testing.T) { fmt.Println("Test: publishNetworkContainer") - publishNCViaCNS(t, "vnet1", "ethWebApp") + + createNetworkContainerURL := "http://" + nmagentEndpoint + + "/machine/plugins/?comp=nmagent&type=NetworkManagement/interfaces/dummyIntf/networkContainers/dummyNCURL/authenticationToken/dummyT/api-version/1" + err := publishNCViaCNS(t, "vnet1", "ethWebApp", createNetworkContainerURL, false) + if err != nil { + t.Fatal(fmt.Errorf("publish container failed %w ", err)) + } + + createNetworkContainerURL = "http://" + nmagentEndpoint + + "/machine/plugins/?comp=nmagent&type=NetworkManagement/interfaces/dummyIntf/networkContainers/dummyNCURL/authenticationTok/" + + "8636c99d-7861-401f-b0d3-7e5b7dc8183c" + + "/api-version/1" + + err = publishNCViaCNS(t, "vnet1", "ethWebApp", createNetworkContainerURL, true) + if err == nil { + t.Fatal("Expected a bad request error due to create network url being incorrect") + } + + createNetworkContainerURL = "http://" + nmagentEndpoint + + "/machine/plugins/?comp=nmagent&NetworkManagement/interfaces/dummyIntf/networkContainers/dummyNCURL/authenticationToken/" + + "8636c99d-7861-401f-b0d3-7e5b7dc8183c8636c99d-7861-401f-b0d3-7e5b7dc8183c" + + "/api-version/1" + + err = publishNCViaCNS(t, "vnet1", "ethWebApp", createNetworkContainerURL, true) + if err == nil { + t.Fatal("Expected a bad request error due to create network url having more characters than permitted in auth token") + } } func publishNCViaCNS(t *testing.T, - networkID string, - networkContainerID string) { + networkID, + networkContainerID, + createNetworkContainerURL string, + expectError bool) error { var ( body bytes.Buffer resp cns.PublishNetworkContainerResponse ) joinNetworkURL := "http://" + nmagentEndpoint + "/dummyVnetURL" - createNetworkContainerURL := "http://" + nmagentEndpoint + - "/interfaces/dummyIntf/networkContainers/dummyNCURL/authenticationToken/dummyT/api-version" publishNCRequest := &cns.PublishNetworkContainerRequest{ NetworkID: networkID, @@ -546,7 +580,7 @@ func publishNCViaCNS(t *testing.T, json.NewEncoder(&body).Encode(publishNCRequest) req, err := http.NewRequest(http.MethodPost, cns.PublishNetworkContainer, &body) if err != nil { - t.Fatal(err) + return fmt.Errorf("Failed to create publish request %w", err) } w := httptest.NewRecorder() @@ -554,51 +588,59 @@ func publishNCViaCNS(t *testing.T, err = decodeResponse(w, &resp) if err != nil || resp.Response.ReturnCode != 0 { - t.Errorf("PublishNetworkContainer failed with response %+v Err:%+v", resp, err) - t.Fatal(err) + if !expectError { + t.Errorf("PublishNetworkContainer failed with response %+v Err:%+v", resp, err) + } + return err } fmt.Printf("PublishNetworkContainer succeded with response %+v, raw:%+v\n", resp, w.Body) + return nil } -func TestExtractHost(t *testing.T) { - joinURL := "http://127.0.0.1:9001/machine/plugins/?comp=nmagent&type=NetworkManagement/joinedVirtualNetworks/c9b8e695-2de1-11eb-bf54-000d3af666c8/api-version/1" - - host := extractHostFromJoinNetworkURL(joinURL) - expected := "127.0.0.1:9001" - if host != expected { - t.Fatalf("expected host %q, got %q", expected, host) +func TestUnpublishNCViaCNS(t *testing.T) { + deleteNetworkContainerURL := "http://" + nmagentEndpoint + + "/machine/plugins/?comp=nmagent&type=NetworkManagement/interfaces/dummyIntf/networkContainers/dummyNCURL/authenticationToken/dummyT/api-version/1/method/DELETE" + err := publishNCViaCNS(t, "vnet1", "ethWebApp", deleteNetworkContainerURL, false) + if err != nil { + t.Fatal(err) } - joinURL = "http://168.63.129.16/machine/plugins/?comp=nmagent&type=NetworkManagement/joinedVirtualNetworks/4941a21f-1a8d-4d0f-8256-cc6e73a8cd22/api-version/1" + deleteNetworkContainerURL = "http://" + nmagentEndpoint + + "/machine/plugins/?comp=nmagent&type=NetworkManagement/interfaces/dummyIntf/networkContainers/dummyNCURL/authenticationToke/" + + "8636c99d-7861-401f-b0d3-7e5b7dc8183c" + + "/api-version/1/method/DELETE" - host = extractHostFromJoinNetworkURL(joinURL) - expected = "168.63.129.16" - if host != expected { - t.Fatalf("expected host %q, got %q", expected, host) + err = publishNCViaCNS(t, "vnet1", "ethWebApp", deleteNetworkContainerURL, true) + if err == nil { + t.Fatal("Expected a bad request error due to delete network url being incorrect") } - joinURL = "http://168.63.129.16/joinedVirtualNetworks/4941a21f-1a8d-4d0f-8256-cc6e73a8cd22/api-version/1" + deleteNetworkContainerURL = "http://" + nmagentEndpoint + + "/machine/plugins/?comp=nmagent&NetworkManagement/interfaces/dummyIntf/networkContainers/dummyNCURL/authenticationToken/" + + "8636c99d-7861-401f-b0d3-7e5b7dc8183c8636c99d-7861-401f-b0d3-7e5b7dc8183c" + + "/api-version/1/method/DELETE" - host = extractHostFromJoinNetworkURL(joinURL) - expected = "168.63.129.16" - if host != expected { - t.Fatalf("expected host %q, got %q", expected, host) + err = testUnpublishNCViaCNS(t, "vnet1", "ethWebApp", deleteNetworkContainerURL, true) + if err == nil { + t.Fatal("Expected a bad request error due to create network url having more characters than permitted in auth token") } } -func TestUnpublishNCViaCNS(t *testing.T) { - fmt.Println("Test: unpublishNetworkContainer") - +func testUnpublishNCViaCNS(t *testing.T, + networkID, + networkContainerID, + deleteNetworkContainerURL string, + expectError bool, +) error { var ( body bytes.Buffer resp cns.UnpublishNetworkContainerResponse ) - networkID := "vnet1" - networkContainerID := "ethWebApp" + fmt.Println("Test: unpublishNetworkContainer") + joinNetworkURL := "http://" + nmagentEndpoint + "/dummyVnetURL" - deleteNetworkContainerURL := "http://" + nmagentEndpoint + "/networkContainers/dummyNCURL" unpublishNCRequest := &cns.UnpublishNetworkContainerRequest{ NetworkID: networkID, @@ -610,7 +652,7 @@ func TestUnpublishNCViaCNS(t *testing.T) { json.NewEncoder(&body).Encode(unpublishNCRequest) req, err := http.NewRequest(http.MethodPost, cns.UnpublishNetworkContainer, &body) if err != nil { - t.Fatal(err) + return fmt.Errorf("Failed to create unpublish request %w", err) } w := httptest.NewRecorder() @@ -618,11 +660,15 @@ func TestUnpublishNCViaCNS(t *testing.T) { err = decodeResponse(w, &resp) if err != nil || resp.Response.ReturnCode != 0 { - t.Errorf("UnpublishNetworkContainer failed with response %+v Err:%+v", resp, err) - t.Fatal(err) + if !expectError { + t.Errorf("UnpublishNetworkContainer failed with response %+v Err:%+v", resp, err) + } + return err } fmt.Printf("UnpublishNetworkContainer succeded with response %+v, raw:%+v\n", resp, w.Body) + + return nil } func TestNmAgentSupportedApisHandler(t *testing.T) { diff --git a/cns/restserver/util.go b/cns/restserver/util.go index fe815fccda..f64999fcfe 100644 --- a/cns/restserver/util.go +++ b/cns/restserver/util.go @@ -130,14 +130,13 @@ func (service *HTTPRestService) saveNetworkContainerGoalState( createNetworkContainerRequest := req createNetworkContainerRequest.AuthorizationToken = "" - service.state.ContainerStatus[req.NetworkContainerid] = - containerstatus{ - ID: req.NetworkContainerid, - VMVersion: req.Version, - CreateNetworkContainerRequest: createNetworkContainerRequest, - HostVersion: hostVersion, - VfpUpdateComplete: vfpUpdateComplete, - } + service.state.ContainerStatus[req.NetworkContainerid] = containerstatus{ + ID: req.NetworkContainerid, + VMVersion: req.Version, + CreateNetworkContainerRequest: createNetworkContainerRequest, + HostVersion: hostVersion, + VfpUpdateComplete: vfpUpdateComplete, + } switch req.NetworkContainerType { case cns.AzureContainerInstance: @@ -316,7 +315,6 @@ func validateIPSubnet(ipSubnet cns.IPSubnet) error { func (service *HTTPRestService) removeToBeDeletedIPStateUntransacted( ipID string, skipValidation bool, ) (types.ResponseCode, string) { - // this is set if caller has already done the validation if !skipValidation { ipConfigStatus, exists := service.PodIPConfigState[ipID] @@ -373,8 +371,7 @@ func (service *HTTPRestService) getNetworkContainerResponse( if exists { // If the goal state is available with CNS, check if the NC is pending VFP programming - waitingForUpdate, getNetworkContainerResponse.Response.ReturnCode, getNetworkContainerResponse.Response.Message = - service.isNCWaitingForUpdate(service.state.ContainerStatus[containerID].CreateNetworkContainerRequest.Version, containerID) + waitingForUpdate, getNetworkContainerResponse.Response.ReturnCode, getNetworkContainerResponse.Response.Message = service.isNCWaitingForUpdate(service.state.ContainerStatus[containerID].CreateNetworkContainerRequest.Version, containerID) //nolint:lll // bad code // If the return code is not success, return the error to the caller if getNetworkContainerResponse.Response.ReturnCode == types.NetworkContainerVfpProgramPending { logger.Errorf("[Azure-CNS] isNCWaitingForUpdate failed for NC: %s with error: %s", @@ -623,12 +620,9 @@ func (service *HTTPRestService) setNetworkStateJoined(networkID string) { // Join Network by calling nmagent func (service *HTTPRestService) joinNetwork( - networkID string, - joinNetworkURL string) (*http.Response, error, error) { + networkID string) (*http.Response, error, error) { var err error - joinResponse, joinErr := nmagent.JoinNetwork( - networkID, - joinNetworkURL) + joinResponse, joinErr := nmagent.JoinNetwork(networkID) if joinErr == nil && joinResponse.StatusCode == http.StatusOK { // Network joined successfully diff --git a/common/utils.go b/common/utils.go index 423a9e6100..40c7169579 100644 --- a/common/utils.go +++ b/common/utils.go @@ -4,6 +4,7 @@ package common import ( + "context" "encoding/binary" "encoding/json" "encoding/xml" @@ -287,3 +288,21 @@ func GetExecutableDirectory() (string, error) { return dir, err } + +func PostCtx(ctx context.Context, cl *http.Client, url, contentType string, body io.Reader) (*http.Response, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body) + if err != nil { + return nil, fmt.Errorf("could not create POST request: %w", err) + } + + req.Header.Set("Content-Type", contentType) + var resp *http.Response + resp, err = cl.Do(req) + if err != nil { + // returning response as well + // cause some methods seem to depend on that for error handling + return resp, fmt.Errorf("POST request received response %w", err) + } + + return resp, nil +}