@@ -5,11 +5,13 @@ package restserver
55
66import (
77 "context"
8+ "errors"
89 "fmt"
910 "io"
1011 "net"
1112 "net/http"
1213 "net/url"
14+ "regexp"
1315 "runtime"
1416 "strings"
1517
@@ -23,6 +25,19 @@ import (
2325 "github.com/Azure/azure-container-networking/platform"
2426)
2527
28+ var (
29+ ncRegex = regexp .MustCompile (`NetworkManagement/interfaces/(.{0,36})/networkContainers/(.{0,36})/authenticationToken/(.{0,36})/api-version/1(/method/DELETE)?` )
30+ ErrInvalidNcURLFormat = errors .New ("Invalid network container url format" )
31+ )
32+
33+ // ncURLExpectedMatches defines the size of matches expected from exercising the ncRegex
34+ // 1) the original url (nuance related to golangs regex package)
35+ // 2) the associated interface parameter
36+ // 3) the ncid parameter
37+ // 4) the authentication token parameter
38+ // 5) the optional delete path
39+ const ncURLExpectedMatches = 5
40+
2641// This file contains implementation of all HTTP APIs which are exposed to external clients.
2742// TODO: break it even further per module (network, nc, etc) like it is done for ipam
2843
@@ -1072,22 +1087,30 @@ func (service *HTTPRestService) getNumberOfCPUCores(w http.ResponseWriter, r *ht
10721087 logger .Response (service .Name , numOfCPUCoresResp , resp .ReturnCode , err )
10731088}
10741089
1075- func getInterfaceIdFromCreateNetworkContainerURL (
1076- createNetworkContainerURL string ) string {
1077- return strings .Split (strings .Split (createNetworkContainerURL , "interfaces/" )[1 ], "/" )[0 ]
1078- }
1090+ func getAuthTokenAndInterfaceIDFromNcURL (networkContainerURL string ) (* cns.NetworkContainerParameters , error ) {
1091+ ncURL , err := url .Parse (networkContainerURL )
1092+ if err != nil {
1093+ return nil , fmt .Errorf ("failed to parse network container url, %w" , err )
1094+ }
10791095
1080- func getAuthTokenFromCreateNetworkContainerURL (
1081- createNetworkContainerURL string ) string {
1082- return strings .Split (strings .Split (createNetworkContainerURL , "authenticationToken/" )[1 ], "/" )[0 ]
1083- }
1096+ queryParams := ncURL .Query ()
10841097
1085- func extractHostFromJoinNetworkURL (urlToParse string ) string {
1086- parsedURL , err := url .Parse (urlToParse )
1087- if err != nil {
1088- return ""
1098+ // current format of create network url has a path after a query parameter "type"
1099+ // doing this parsing due to this structure
1100+ typeQueryParamVal := queryParams .Get ("type" )
1101+ if typeQueryParamVal == "" {
1102+ return nil , fmt .Errorf ("no type query param, %w" , ErrInvalidNcURLFormat )
10891103 }
1090- return parsedURL .Host
1104+
1105+ // .{0,128} gets from zero to 128 characters of any kind
1106+ // ()? is optional
1107+ matches := ncRegex .FindStringSubmatch (typeQueryParamVal )
1108+
1109+ if len (matches ) != ncURLExpectedMatches {
1110+ return nil , fmt .Errorf ("unexpected number of matches in url, %w" , ErrInvalidNcURLFormat )
1111+ }
1112+
1113+ return & cns.NetworkContainerParameters {AssociatedInterfaceID : matches [1 ], AuthToken : matches [3 ]}, nil
10911114}
10921115
10931116// Publish Network Container by calling nmagent
@@ -1109,6 +1132,8 @@ func (service *HTTPRestService) publishNetworkContainer(w http.ResponseWriter, r
11091132
11101133 err = service .Listener .Decode (w , r , & req )
11111134
1135+ creteNcURLCopy := req .CreateNetworkContainerURL
1136+
11121137 // reqCopy creates a copy of incoming request. It doesn't copy the authentication token info
11131138 // to avoid logging it.
11141139 reqCopy := cns.PublishNetworkContainerRequest {
@@ -1119,14 +1144,38 @@ func (service *HTTPRestService) publishNetworkContainer(w http.ResponseWriter, r
11191144 }
11201145
11211146 logger .Request (service .Name , & reqCopy , err )
1147+
1148+ // TODO - refactor this method for better error handling
11221149 if err != nil {
11231150 return
11241151 }
11251152
1153+ var ncParameters * cns.NetworkContainerParameters
1154+ ncParameters , err = getAuthTokenAndInterfaceIDFromNcURL (creteNcURLCopy )
1155+ if err != nil {
1156+ logger .Errorf ("[Azure-CNS] nc parameters validation failed with %+v" , err )
1157+ w .WriteHeader (http .StatusBadRequest )
1158+
1159+ badRequestResponse := & cns.PublishNetworkContainerResponse {
1160+ Response : cns.Response {
1161+ ReturnCode : http .StatusBadRequest ,
1162+ Message : fmt .Sprintf ("Request contains a unexpected create url format in request body: %v" , reqCopy .CreateNetworkContainerURL ),
1163+ },
1164+ PublishErrorStr : fmt .Sprintf ("Bad request: Request contains a unexpected create url format in request body: %v" , reqCopy .CreateNetworkContainerURL ),
1165+ PublishStatusCode : http .StatusBadRequest ,
1166+ }
1167+ err = service .Listener .Encode (w , & badRequestResponse )
1168+ logger .Response (service .Name , badRequestResponse , badRequestResponse .Response .ReturnCode , err )
1169+ return
1170+ }
1171+
11261172 switch r .Method {
11271173 case "POST" :
11281174 // Join the network
1129- publishResponse , publishError , err = service .joinNetwork (req .NetworkID , req .JoinNetworkURL )
1175+ // Please refactor this
1176+ // do not reuse the below variable between network join and publish
1177+ // nolint:bodyclose // existing code needs refactoring
1178+ publishResponse , publishError , err = service .joinNetwork (req .NetworkID )
11301179 if err == nil {
11311180 isNetworkJoined = true
11321181 } else {
@@ -1136,9 +1185,12 @@ func (service *HTTPRestService) publishNetworkContainer(w http.ResponseWriter, r
11361185
11371186 if isNetworkJoined {
11381187 // Publish Network Container
1188+
1189+ // nolint:bodyclose // existing code needs refactoring
11391190 publishResponse , publishError = nmagent .PublishNetworkContainer (
11401191 req .NetworkContainerID ,
1141- req .CreateNetworkContainerURL ,
1192+ ncParameters .AssociatedInterfaceID ,
1193+ ncParameters .AuthToken ,
11421194 req .CreateNetworkContainerRequestBody )
11431195 if publishError != nil || publishResponse .StatusCode != http .StatusOK {
11441196 returnMessage = fmt .Sprintf ("Failed to publish Network Container: %s" , req .NetworkContainerID )
@@ -1149,20 +1201,11 @@ func (service *HTTPRestService) publishNetworkContainer(w http.ResponseWriter, r
11491201 }
11501202
11511203 // Store ncGetVersionURL needed for calling NMAgent to check if vfp programming is completed for the NC
1152- primaryInterfaceIdentifier := getInterfaceIdFromCreateNetworkContainerURL (req .CreateNetworkContainerURL )
1153- authToken := getAuthTokenFromCreateNetworkContainerURL (req .CreateNetworkContainerURL )
1154-
1155- // we attempt to extract the wireserver IP to use from the request, otherwise default to the well-known IP.
1156- hostIP := extractHostFromJoinNetworkURL (req .JoinNetworkURL )
1157- if hostIP == "" {
1158- hostIP = nmagent .WireserverIP
1159- }
1160-
11611204 ncGetVersionURL := fmt .Sprintf (nmagent .GetNetworkContainerVersionURLFmt ,
1162- hostIP ,
1163- primaryInterfaceIdentifier ,
1205+ nmagent . WireserverIP ,
1206+ ncParameters . AssociatedInterfaceID ,
11641207 req .NetworkContainerID ,
1165- authToken )
1208+ ncParameters . AuthToken )
11661209 ncVersionURLs .Store (cns .SwiftPrefix + req .NetworkContainerID , ncGetVersionURL )
11671210
11681211 default :
@@ -1219,6 +1262,8 @@ func (service *HTTPRestService) unpublishNetworkContainer(w http.ResponseWriter,
12191262
12201263 err = service .Listener .Decode (w , r , & req )
12211264
1265+ deleteNcURLCopy := req .DeleteNetworkContainerURL
1266+
12221267 // reqCopy creates a copy of incoming request. It doesn't copy the authentication token info
12231268 // to avoid logging it.
12241269 reqCopy := cns.UnpublishNetworkContainerRequest {
@@ -1233,12 +1278,32 @@ func (service *HTTPRestService) unpublishNetworkContainer(w http.ResponseWriter,
12331278 return
12341279 }
12351280
1281+ var ncParameters * cns.NetworkContainerParameters
1282+ ncParameters , err = getAuthTokenAndInterfaceIDFromNcURL (deleteNcURLCopy )
1283+ if err != nil {
1284+ logger .Errorf ("[Azure-CNS] nc parameters validation failed with %+v" , err )
1285+ w .WriteHeader (http .StatusBadRequest )
1286+
1287+ badRequestResponse := & cns.UnpublishNetworkContainerResponse {
1288+ Response : cns.Response {
1289+ ReturnCode : http .StatusBadRequest ,
1290+ Message : fmt .Sprintf ("Request contains a unexpected delete url format in request body: %v" , reqCopy .DeleteNetworkContainerURL ),
1291+ },
1292+ UnpublishErrorStr : fmt .Sprintf ("Bad request: Request contains a unexpected delete url format in request body: %v" , reqCopy .DeleteNetworkContainerURL ),
1293+ UnpublishStatusCode : http .StatusBadRequest ,
1294+ }
1295+ err = service .Listener .Encode (w , & badRequestResponse )
1296+ logger .Response (service .Name , badRequestResponse , badRequestResponse .Response .ReturnCode , err )
1297+ return
1298+ }
1299+
12361300 switch r .Method {
12371301 case "POST" :
12381302 // Join Network if not joined already
12391303 isNetworkJoined = service .isNetworkJoined (req .NetworkID )
12401304 if ! isNetworkJoined {
1241- unpublishResponse , unpublishError , err = service .joinNetwork (req .NetworkID , req .JoinNetworkURL )
1305+ // nolint:bodyclose // existing code needs refactoring
1306+ unpublishResponse , unpublishError , err = service .joinNetwork (req .NetworkID )
12421307 if err == nil {
12431308 isNetworkJoined = true
12441309 } else {
@@ -1251,7 +1316,8 @@ func (service *HTTPRestService) unpublishNetworkContainer(w http.ResponseWriter,
12511316 // Unpublish Network Container
12521317 unpublishResponse , unpublishError = nmagent .UnpublishNetworkContainer (
12531318 req .NetworkContainerID ,
1254- req .DeleteNetworkContainerURL )
1319+ ncParameters .AssociatedInterfaceID ,
1320+ ncParameters .AuthToken )
12551321 if unpublishError != nil || unpublishResponse .StatusCode != http .StatusOK {
12561322 returnMessage = fmt .Sprintf ("Failed to unpublish Network Container: %s" , req .NetworkContainerID )
12571323 returnCode = types .NetworkContainerUnpublishFailed
0 commit comments