in cns/restserver/api.go [1005:1122]
func (service *HTTPRestService) unpublishNetworkContainer(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "UnpublishNetworkContainer expects a POST", http.StatusBadRequest)
return
}
var req cns.UnpublishNetworkContainerRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, fmt.Sprintf("could not decode request body: %v", err), http.StatusBadRequest)
return
}
logger.Request(service.Name, req, nil)
ncParams, err := extractNCParamsFromURL(req.DeleteNetworkContainerURL)
if err != nil {
resp := cns.UnpublishNetworkContainerResponse{
Response: cns.Response{
ReturnCode: http.StatusBadRequest,
Message: fmt.Sprintf("unexpected delete nc url format. url %s: %v ", req.DeleteNetworkContainerURL, err),
},
}
respondJSON(w, http.StatusBadRequest, resp)
logger.Response(service.Name, resp, resp.Response.ReturnCode, err)
return
}
ctx := r.Context()
var unpublishBody nmagent.DeleteContainerRequest
var azrNC bool
err = json.Unmarshal(req.DeleteNetworkContainerRequestBody, &unpublishBody)
if err != nil {
// If the body contains only `""\n`, it is non-AZR NC
// In this case, we should not return an error
// However, if the body is not `""\n`, it is invalid and therefore, we must return an error
// []byte{34, 34, 10} here represents []byte(`""`+"\n")
if !bytes.Equal(req.DeleteNetworkContainerRequestBody, []byte{34, 34, 10}) {
http.Error(w, fmt.Sprintf("could not unmarshal delete network container body: %v", err), http.StatusBadRequest)
return
}
} else {
// If unmarshalling was successful, it is an AZR NC
azrNC = true
}
/* For AZR scenarios, if NMAgent is restarted, it loses state and does not know what VNETs to subscribe to.
As it no longer has VNET state, delete nc calls would fail. We need to add join VNET call for all AZR
nc unpublish calls just like publish nc calls.
*/
if azrNC || !service.isNetworkJoined(req.NetworkID) {
joinResp, err := service.wsproxy.JoinNetwork(ctx, req.NetworkID) //nolint:govet // ok to shadow
if err != nil {
resp := cns.UnpublishNetworkContainerResponse{
Response: cns.Response{
ReturnCode: types.NetworkJoinFailed,
Message: fmt.Sprintf("failed to join network %s: %v", req.NetworkID, err),
},
UnpublishErrorStr: err.Error(),
}
respondJSON(w, http.StatusOK, resp) // legacy behavior
logger.Response(service.Name, resp, resp.Response.ReturnCode, err)
return
}
joinBytes, _ := io.ReadAll(joinResp.Body)
_ = joinResp.Body.Close()
if joinResp.StatusCode != http.StatusOK {
resp := cns.UnpublishNetworkContainerResponse{
Response: cns.Response{
ReturnCode: types.NetworkJoinFailed,
Message: fmt.Sprintf("failed to join network %s. did not get 200 from wireserver", req.NetworkID),
},
UnpublishStatusCode: joinResp.StatusCode,
UnpublishResponseBody: joinBytes,
}
respondJSON(w, http.StatusOK, resp) // legacy behavior
logger.Response(service.Name, resp, resp.Response.ReturnCode, nil)
return
}
service.setNetworkStateJoined(req.NetworkID)
logger.Printf("[Azure-CNS] joined vnet %s during nc %s unpublish. AZREnabled: %t, wireserver response: %v", req.NetworkID, req.NetworkContainerID, unpublishBody.AZREnabled, string(joinBytes))
}
publishResp, err := service.wsproxy.UnpublishNC(ctx, ncParams, req.DeleteNetworkContainerRequestBody)
if err != nil {
resp := cns.UnpublishNetworkContainerResponse{
Response: cns.Response{
ReturnCode: types.NetworkContainerUnpublishFailed,
Message: fmt.Sprintf("failed to publish nc %s: %v", req.NetworkContainerID, err),
},
UnpublishErrorStr: err.Error(),
}
respondJSON(w, http.StatusOK, resp) // legacy behavior
logger.Response(service.Name, resp, resp.Response.ReturnCode, err)
return
}
publishBytes, _ := io.ReadAll(publishResp.Body)
_ = publishResp.Body.Close()
resp := cns.UnpublishNetworkContainerResponse{
UnpublishStatusCode: publishResp.StatusCode,
UnpublishResponseBody: publishBytes,
}
if publishResp.StatusCode != http.StatusOK {
resp.Response = cns.Response{
ReturnCode: types.NetworkContainerUnpublishFailed,
Message: fmt.Sprintf("failed to unpublish nc %s. did not get 200 from wireserver", req.NetworkContainerID),
}
}
respondJSON(w, http.StatusOK, resp)
logger.Response(service.Name, resp, resp.Response.ReturnCode, nil)
}