Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions cns/NetworkContainerContract.go
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ type NetworkInterface struct {
// PublishNetworkContainerRequest specifies request to publish network container via NMAgent.
type PublishNetworkContainerRequest struct {
NetworkID string
SubnetName string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just for logging and debugging?

NetworkContainerID string
JoinNetworkURL string
CreateNetworkContainerURL string
Expand All @@ -683,8 +684,8 @@ type PublishNetworkContainerRequest struct {

func (p PublishNetworkContainerRequest) String() string {
// %q as a verb on a byte slice prints safely escaped text instead of individual bytes
return fmt.Sprintf("{NetworkID:%s NetworkContainerID:%s JoinNetworkURL:%s CreateNetworkContainerURL:%s CreateNetworkContainerRequestBody:%q}",
p.NetworkID, p.NetworkContainerID, p.JoinNetworkURL, p.CreateNetworkContainerURL, p.CreateNetworkContainerRequestBody)
return fmt.Sprintf("{NetworkID:%s SubnetName:%s NetworkContainerID:%s JoinNetworkURL:%s CreateNetworkContainerURL:%s CreateNetworkContainerRequestBody:%q}",
p.NetworkID, p.SubnetName, p.NetworkContainerID, p.JoinNetworkURL, p.CreateNetworkContainerURL, p.CreateNetworkContainerRequestBody)
}

// NetworkContainerParameters parameters available in network container operations
Expand All @@ -711,15 +712,16 @@ func (p PublishNetworkContainerResponse) String() string {
// UnpublishNetworkContainerRequest specifies request to unpublish network container via NMAgent.
type UnpublishNetworkContainerRequest struct {
NetworkID string
SubnetName string
NetworkContainerID string
JoinNetworkURL string
DeleteNetworkContainerURL string
DeleteNetworkContainerRequestBody []byte
}

func (u UnpublishNetworkContainerRequest) String() string {
return fmt.Sprintf("{NetworkID:%s NetworkContainerID:%s JoinNetworkURL:%s DeleteNetworkContainerURL:%s DeleteNetworkContainerRequestBody:%q}",
u.NetworkID, u.NetworkContainerID, u.JoinNetworkURL, u.DeleteNetworkContainerURL, u.DeleteNetworkContainerRequestBody)
return fmt.Sprintf("{NetworkID:%s SubnetName:%s NetworkContainerID:%s JoinNetworkURL:%s DeleteNetworkContainerURL:%s DeleteNetworkContainerRequestBody:%q}",
u.NetworkID, u.SubnetName, u.NetworkContainerID, u.JoinNetworkURL, u.DeleteNetworkContainerURL, u.DeleteNetworkContainerRequestBody)
}

// UnpublishNetworkContainerResponse specifies the response to unpublish network container request.
Expand Down
27 changes: 18 additions & 9 deletions cns/fakes/wireserverproxyfake.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import (
)

type WireserverProxyFake struct {
JoinNetworkFunc func(context.Context, string) (*http.Response, error)
PublishNCFunc func(context.Context, cns.NetworkContainerParameters, []byte) (*http.Response, error)
UnpublishNCFunc func(context.Context, cns.NetworkContainerParameters, []byte) (*http.Response, error)
JoinNetworkFunc func(context.Context, string, bool) (*http.Response, error)
JoinSubnetFunc func(context.Context, string, string, cns.NetworkContainerParameters) (*http.Response, error)
PublishNCFunc func(context.Context, cns.NetworkContainerParameters, []byte, bool) (*http.Response, error)
UnpublishNCFunc func(context.Context, cns.NetworkContainerParameters, []byte, bool) (*http.Response, error)
Comment thread
smittal22 marked this conversation as resolved.
}

const defaultResponseBody = `{"httpStatusCode":"200"}`
Expand All @@ -25,25 +26,33 @@ func defaultResponse() *http.Response {
}
}

func (w *WireserverProxyFake) JoinNetwork(ctx context.Context, vnetID string) (*http.Response, error) {
func (w *WireserverProxyFake) JoinNetwork(ctx context.Context, vnetID string, useRNCPublisher bool) (*http.Response, error) {
if w.JoinNetworkFunc != nil {
return w.JoinNetworkFunc(ctx, vnetID)
return w.JoinNetworkFunc(ctx, vnetID, useRNCPublisher)
}

return defaultResponse(), nil
}

func (w *WireserverProxyFake) PublishNC(ctx context.Context, ncParams cns.NetworkContainerParameters, payload []byte) (*http.Response, error) {
func (w *WireserverProxyFake) JoinSubnet(ctx context.Context, vnetID, subnetName string, ncParams cns.NetworkContainerParameters) (*http.Response, error) {
if w.JoinSubnetFunc != nil {
return w.JoinSubnetFunc(ctx, vnetID, subnetName, ncParams)
}

return defaultResponse(), nil
}

func (w *WireserverProxyFake) PublishNC(ctx context.Context, ncParams cns.NetworkContainerParameters, payload []byte, useRNCPublisher bool) (*http.Response, error) {
if w.PublishNCFunc != nil {
return w.PublishNCFunc(ctx, ncParams, payload)
return w.PublishNCFunc(ctx, ncParams, payload, useRNCPublisher)
}

return defaultResponse(), nil
}

func (w *WireserverProxyFake) UnpublishNC(ctx context.Context, ncParams cns.NetworkContainerParameters, payload []byte) (*http.Response, error) {
func (w *WireserverProxyFake) UnpublishNC(ctx context.Context, ncParams cns.NetworkContainerParameters, payload []byte, useRNCPublisher bool) (*http.Response, error) {
if w.UnpublishNCFunc != nil {
return w.UnpublishNCFunc(ctx, ncParams, payload)
return w.UnpublishNCFunc(ctx, ncParams, payload, useRNCPublisher)
}

return defaultResponse(), nil
Expand Down
131 changes: 120 additions & 11 deletions cns/restserver/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"github.com/Azure/azure-container-networking/cns/types"
"github.com/Azure/azure-container-networking/cns/wireserver"
"github.com/Azure/azure-container-networking/common"
"github.com/Azure/azure-container-networking/nmagent"
"github.com/pkg/errors"
)

Expand All @@ -40,6 +39,16 @@ const (
ncURLExpectedMatches = 5
)

type ncPublishBody struct {
UseRNCPublisher bool `json:"useRNCPublisher"`
}

type ncUnpublishBody struct {
UseRNCPublisher bool `json:"useRNCPublisher"`
AzID uint `json:"azID"`
AZREnabled bool `json:"azrEnabled"`
}

// 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

Expand Down Expand Up @@ -948,7 +957,20 @@ func (service *HTTPRestService) publishNetworkContainer(w http.ResponseWriter, r

ctx := r.Context()

joinResp, err := service.wsproxy.JoinNetwork(ctx, req.NetworkID) //nolint:govet // ok to shadow
var publishBody ncPublishBody
var useRNCPublisher bool

err = json.Unmarshal(req.CreateNetworkContainerRequestBody, &publishBody)
if err != nil {
http.Error(w, fmt.Sprintf("could not unmarshal create network container body: %v", err), http.StatusBadRequest)
return
}

if publishBody.UseRNCPublisher {
useRNCPublisher = true
}

joinResp, err := service.wsproxy.JoinNetwork(ctx, req.NetworkID, useRNCPublisher) //nolint:govet // ok to shadow
if err != nil {
resp := cns.PublishNetworkContainerResponse{
Response: cns.Response{
Expand Down Expand Up @@ -982,7 +1004,48 @@ func (service *HTTPRestService) publishNetworkContainer(w http.ResponseWriter, r
service.setNetworkStateJoined(req.NetworkID)
logger.Printf("[Azure-CNS] joined vnet %s during nc %s publish. wireserver response: %v", req.NetworkID, req.NetworkContainerID, string(joinBytes))

publishResp, err := service.wsproxy.PublishNC(ctx, ncParams, req.CreateNetworkContainerRequestBody)
if useRNCPublisher {
joinSubnetResp, errSubnetJoin := service.wsproxy.JoinSubnet(ctx, req.NetworkID, req.SubnetName, ncParams) //nolint:govet // ok to shadow
if errSubnetJoin != nil {
resp := cns.PublishNetworkContainerResponse{
Response: cns.Response{
ReturnCode: types.SubnetJoinFailed,
Message: fmt.Sprintf("failed to join subnet %s in network %s: %v", req.SubnetName, req.NetworkID, errSubnetJoin),
},
PublishErrorStr: errSubnetJoin.Error(),
}
respondJSON(w, http.StatusOK, resp) // legacy behavior
logger.Response(service.Name, resp, resp.Response.ReturnCode, errSubnetJoin) //nolint:staticcheck // match existing logger usage in this handler
return
}

subnetJoinBytes, _ := io.ReadAll(joinSubnetResp.Body)
_ = joinSubnetResp.Body.Close()

if joinSubnetResp.StatusCode != http.StatusOK {
resp := cns.PublishNetworkContainerResponse{
Response: cns.Response{
ReturnCode: types.SubnetJoinFailed,
Message: fmt.Sprintf("failed to join subnet %s in network %s. did not get 200 from wireserver", req.SubnetName, req.NetworkID),
},
PublishStatusCode: joinSubnetResp.StatusCode,
PublishResponseBody: subnetJoinBytes,
}
respondJSON(w, http.StatusOK, resp) // legacy behavior
logger.Response(service.Name, resp, resp.Response.ReturnCode, nil) //nolint:staticcheck // match existing logger usage in this handler
return
}

logger.Printf( //nolint:staticcheck // match existing logger usage in this handler
"[Azure-CNS] joined subnet %s in vnet %s during nc %s publish. wireserver response: %v",
req.SubnetName,
req.NetworkID,
req.NetworkContainerID,
string(subnetJoinBytes),
)
}

publishResp, err := service.wsproxy.PublishNC(ctx, ncParams, req.CreateNetworkContainerRequestBody, useRNCPublisher)
if err != nil {
resp := cns.PublishNetworkContainerResponse{
Response: cns.Response{
Expand Down Expand Up @@ -1044,8 +1107,9 @@ func (service *HTTPRestService) unpublishNetworkContainer(w http.ResponseWriter,

ctx := r.Context()

var unpublishBody nmagent.DeleteContainerRequest
var unpublishBody ncUnpublishBody
var azrNC bool
var useRNCPublisher bool
err = json.Unmarshal(req.DeleteNetworkContainerRequestBody, &unpublishBody)
if err != nil {
// If the body contains only `""\n`, it is non-AZR NC
Expand All @@ -1059,14 +1123,17 @@ func (service *HTTPRestService) unpublishNetworkContainer(w http.ResponseWriter,
} else {
// If unmarshalling was successful, it is an AZR NC
azrNC = true
if unpublishBody.UseRNCPublisher {
useRNCPublisher = 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
joinResp, err := service.wsproxy.JoinNetwork(ctx, req.NetworkID, useRNCPublisher) //nolint:govet // ok to shadow
if err != nil {
resp := cns.UnpublishNetworkContainerResponse{
Response: cns.Response{
Expand Down Expand Up @@ -1101,7 +1168,49 @@ func (service *HTTPRestService) unpublishNetworkContainer(w http.ResponseWriter,
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 useRNCPublisher {
joinSubnetResp, err := service.wsproxy.JoinSubnet(ctx, req.NetworkID, req.SubnetName, ncParams) //nolint:govet // ok to shadow
if err != nil {
resp := cns.UnpublishNetworkContainerResponse{
Response: cns.Response{
ReturnCode: types.SubnetJoinFailed,
Message: fmt.Sprintf("failed to join subnet %s in network %s: %v", req.SubnetName, req.NetworkID, err),
},
UnpublishErrorStr: err.Error(),
}
respondJSON(w, http.StatusOK, resp) // legacy behavior
logger.Response(service.Name, resp, resp.Response.ReturnCode, err) //nolint:staticcheck // match existing logger usage in this handler
return
}

subnetJoinBytes, _ := io.ReadAll(joinSubnetResp.Body)
_ = joinSubnetResp.Body.Close()

if joinSubnetResp.StatusCode != http.StatusOK {
resp := cns.UnpublishNetworkContainerResponse{
Response: cns.Response{
ReturnCode: types.SubnetJoinFailed,
Message: fmt.Sprintf("failed to join subnet %s in network %s. did not get 200 from wireserver", req.SubnetName, req.NetworkID),
},
UnpublishStatusCode: joinSubnetResp.StatusCode,
UnpublishResponseBody: subnetJoinBytes,
}
respondJSON(w, http.StatusOK, resp) // legacy behavior
logger.Response(service.Name, resp, resp.Response.ReturnCode, nil) //nolint:staticcheck // match existing logger usage in this handler
return
}

logger.Printf( //nolint:staticcheck // match existing logger usage in this handler
"[Azure-CNS] joined subnet %s in vnet %s during nc %s unpublish. AZREnabled: %t, wireserver response: %v",
req.SubnetName,
req.NetworkID,
req.NetworkContainerID,
unpublishBody.AZREnabled,
string(subnetJoinBytes),
)
}

unpublishResp, err := service.wsproxy.UnpublishNC(ctx, ncParams, req.DeleteNetworkContainerRequestBody, useRNCPublisher)
if err != nil {
resp := cns.UnpublishNetworkContainerResponse{
Response: cns.Response{
Expand All @@ -1115,15 +1224,15 @@ func (service *HTTPRestService) unpublishNetworkContainer(w http.ResponseWriter,
return
}

publishBytes, _ := io.ReadAll(publishResp.Body)
_ = publishResp.Body.Close()
unpublishBytes, _ := io.ReadAll(unpublishResp.Body)
_ = unpublishResp.Body.Close()

resp := cns.UnpublishNetworkContainerResponse{
UnpublishStatusCode: publishResp.StatusCode,
UnpublishResponseBody: publishBytes,
UnpublishStatusCode: unpublishResp.StatusCode,
UnpublishResponseBody: unpublishBytes,
}

if publishResp.StatusCode != http.StatusOK {
if unpublishResp.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),
Expand Down
Loading
Loading