func()

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)
}