Skip to content

Commit

Permalink
feat: Modifying stateless CNI state to account for swift 2.0 changes. (
Browse files Browse the repository at this point in the history
…#2523)

* Modyfying stateless CNI state to account for swift 2.0 changes

* Removing SecondaryNICInfor fro EPInfo.

* removing SecondaryNic from the epInfo.

* Make change to UpdateEndpointState API to support SwiftV2 for Stateless CNI

* updating Makefile to include azure CNI binary.
  • Loading branch information
behzad-mir authored Apr 25, 2024
1 parent 3783c0c commit 008dadc
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 54 deletions.
14 changes: 13 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ AZURE_IPAM_DIR = $(REPO_ROOT)/azure-ipam
CNM_DIR = $(REPO_ROOT)/cnm/plugin
CNI_NET_DIR = $(REPO_ROOT)/cni/network/plugin
CNI_IPAM_DIR = $(REPO_ROOT)/cni/ipam/plugin
STATELESS_CNI_NET_DIR = $(REPO_ROOT)/cni/network/stateless
CNI_IPAMV6_DIR = $(REPO_ROOT)/cni/ipam/pluginv6
CNI_TELEMETRY_DIR = $(REPO_ROOT)/cni/telemetry/service
ACNCLI_DIR = $(REPO_ROOT)/tools/acncli
Expand All @@ -59,10 +60,13 @@ IMAGE_DIR = $(OUTPUT_DIR)/images
CNM_BUILD_DIR = $(BUILD_DIR)/cnm
CNI_BUILD_DIR = $(BUILD_DIR)/cni
ACNCLI_BUILD_DIR = $(BUILD_DIR)/acncli
STATELESS_CNI_BUILD_DIR = $(CNI_BUILD_DIR)/stateless
CNI_MULTITENANCY_BUILD_DIR = $(BUILD_DIR)/cni-multitenancy
CNI_MULTITENANCY_TRANSPARENT_VLAN_BUILD_DIR = $(BUILD_DIR)/cni-multitenancy-transparent-vlan
CNI_SWIFT_BUILD_DIR = $(BUILD_DIR)/cni-swift
CNI_OVERLAY_BUILD_DIR = $(BUILD_DIR)/cni-overlay
STATELESS_CNI_OVERLAY_BUILD_DIR = $(CNI_OVERLAY_BUILD_DIR)/stateless
STATELESS_CNI_SWIFT_BUILD_DIR = $(CNI_SWIFT_BUILD_DIR)/stateless
CNI_BAREMETAL_BUILD_DIR = $(BUILD_DIR)/cni-baremetal
CNI_DUALSTACK_BUILD_DIR = $(BUILD_DIR)/cni-dualstack
CNS_BUILD_DIR = $(BUILD_DIR)/cns
Expand Down Expand Up @@ -130,7 +134,7 @@ endif

# Shorthand target names for convenience.
azure-cnm-plugin: cnm-binary cnm-archive
azure-cni-plugin: azure-vnet-binary azure-vnet-ipam-binary azure-vnet-ipamv6-binary azure-vnet-telemetry-binary cni-archive
azure-cni-plugin: azure-vnet-binary azure-vnet-stateless-binary azure-vnet-ipam-binary azure-vnet-ipamv6-binary azure-vnet-telemetry-binary cni-archive
azure-cns: azure-cns-binary cns-archive
acncli: acncli-binary acncli-archive
azure-npm: azure-npm-binary npm-archive
Expand Down Expand Up @@ -179,6 +183,10 @@ cnm-binary:
azure-vnet-binary:
cd $(CNI_NET_DIR) && CGO_ENABLED=0 go build -v -o $(CNI_BUILD_DIR)/azure-vnet$(EXE_EXT) -ldflags "-X main.version=$(CNI_VERSION)" -gcflags="-dwarflocationlists=true"

# Build the Azure CNI stateless network binary
azure-vnet-stateless-binary:
cd $(STATELESS_CNI_NET_DIR) && CGO_ENABLED=0 go build -v -o $(STATELESS_CNI_BUILD_DIR)/azure-vnet$(EXE_EXT) -ldflags "-X main.version=$(CNI_VERSION)" -gcflags="-dwarflocationlists=true"

# Build the Azure CNI IPAM binary.
azure-vnet-ipam-binary:
cd $(CNI_IPAM_DIR) && CGO_ENABLED=0 go build -v -o $(CNI_BUILD_DIR)/azure-vnet-ipam$(EXE_EXT) -ldflags "-X main.version=$(CNI_VERSION)" -gcflags="-dwarflocationlists=true"
Expand Down Expand Up @@ -674,12 +682,16 @@ endif
cp cni/azure-$(GOOS)-swift.conflist $(CNI_SWIFT_BUILD_DIR)/10-azure.conflist
cp telemetry/azure-vnet-telemetry.config $(CNI_SWIFT_BUILD_DIR)/azure-vnet-telemetry.config
cp $(CNI_BUILD_DIR)/azure-vnet$(EXE_EXT) $(CNI_BUILD_DIR)/azure-vnet-ipam$(EXE_EXT) $(CNI_BUILD_DIR)/azure-vnet-telemetry$(EXE_EXT) $(CNI_SWIFT_BUILD_DIR)
$(MKDIR) $(STATELESS_CNI_SWIFT_BUILD_DIR)
cp $(STATELESS_CNI_BUILD_DIR)/azure-vnet$(EXE_EXT) $(STATELESS_CNI_SWIFT_BUILD_DIR)
cd $(CNI_SWIFT_BUILD_DIR) && $(ARCHIVE_CMD) $(CNI_SWIFT_ARCHIVE_NAME) azure-vnet$(EXE_EXT) azure-vnet-ipam$(EXE_EXT) azure-vnet-telemetry$(EXE_EXT) 10-azure.conflist azure-vnet-telemetry.config

$(MKDIR) $(CNI_OVERLAY_BUILD_DIR)
cp cni/azure-$(GOOS)-swift-overlay.conflist $(CNI_OVERLAY_BUILD_DIR)/10-azure.conflist
cp telemetry/azure-vnet-telemetry.config $(CNI_OVERLAY_BUILD_DIR)/azure-vnet-telemetry.config
cp $(CNI_BUILD_DIR)/azure-vnet$(EXE_EXT) $(CNI_BUILD_DIR)/azure-vnet-ipam$(EXE_EXT) $(CNI_BUILD_DIR)/azure-vnet-telemetry$(EXE_EXT) $(CNI_OVERLAY_BUILD_DIR)
$(MKDIR) $(STATELESS_CNI_OVERLAY_BUILD_DIR)
cp $(STATELESS_CNI_BUILD_DIR)/azure-vnet$(EXE_EXT) $(STATELESS_CNI_OVERLAY_BUILD_DIR)
cd $(CNI_OVERLAY_BUILD_DIR) && $(ARCHIVE_CMD) $(CNI_OVERLAY_ARCHIVE_NAME) azure-vnet$(EXE_EXT) azure-vnet-ipam$(EXE_EXT) azure-vnet-telemetry$(EXE_EXT) 10-azure.conflist azure-vnet-telemetry.config

$(MKDIR) $(CNI_DUALSTACK_BUILD_DIR)
Expand Down
1 change: 1 addition & 0 deletions cns/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,4 +363,5 @@ type GetHomeAzResponse struct {
type EndpointRequest struct {
HnsEndpointID string `json:"hnsEndpointID"`
HostVethName string `json:"hostVethName"`
InterfaceName string `json:"InterfaceName"`
}
8 changes: 2 additions & 6 deletions cns/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1059,15 +1059,11 @@ func (c *Client) GetEndpoint(ctx context.Context, endpointID string) (*restserve

// UpdateEndpoint calls the EndpointHandlerAPI in CNS
// to update the state of a given EndpointID with either HNSEndpointID or HostVethName
func (c *Client) UpdateEndpoint(ctx context.Context, endpointID, hnsID, vethName string) (*cns.Response, error) {
func (c *Client) UpdateEndpoint(ctx context.Context, endpointID string, ipInfo map[string]*restserver.IPInfo) (*cns.Response, error) {
// build the request
updateEndpoint := cns.EndpointRequest{
HnsEndpointID: hnsID,
HostVethName: vethName,
}
var body bytes.Buffer

if err := json.NewEncoder(&body).Encode(updateEndpoint); err != nil {
if err := json.NewEncoder(&body).Encode(ipInfo); err != nil {
return nil, errors.Wrap(err, "failed to encode updateEndpoint")
}

Expand Down
34 changes: 24 additions & 10 deletions cns/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2710,15 +2710,17 @@ func TestUpdateEndpoint(t *testing.T) {
containerID string
hnsID string
vethName string
ifName string
response *RequestCapture
expReq *cns.EndpointRequest
expReq map[string]*restserver.IPInfo
shouldErr bool
}{
{
"empty",
"",
"",
"",
"",
&RequestCapture{
Next: &mockdo{},
},
Expand All @@ -2730,13 +2732,17 @@ func TestUpdateEndpoint(t *testing.T) {
"foo",
"bar",
"",
"eth0",
&RequestCapture{
Next: &mockdo{
httpStatusCodeToReturn: http.StatusOK,
},
},
&cns.EndpointRequest{
HnsEndpointID: "bar",
map[string]*restserver.IPInfo{
"eth0": {
HnsEndpointID: "bar",
NICType: cns.InfraNIC,
},
},
false,
},
Expand All @@ -2745,13 +2751,17 @@ func TestUpdateEndpoint(t *testing.T) {
"foo",
"",
"bar",
"eth0",
&RequestCapture{
Next: &mockdo{
httpStatusCodeToReturn: http.StatusOK,
},
},
&cns.EndpointRequest{
HostVethName: "bar",
map[string]*restserver.IPInfo{
"eth0": {
HostVethName: "bar",
NICType: cns.InfraNIC,
},
},
false,
},
Expand All @@ -2760,13 +2770,17 @@ func TestUpdateEndpoint(t *testing.T) {
"foo",
"",
"bar",
"eth0",
&RequestCapture{
Next: &mockdo{
httpStatusCodeToReturn: http.StatusBadRequest,
},
},
&cns.EndpointRequest{
HostVethName: "bar",
map[string]*restserver.IPInfo{
"eth0": {
HostVethName: "bar",
NICType: cns.InfraNIC,
},
},
true,
},
Expand All @@ -2784,7 +2798,7 @@ func TestUpdateEndpoint(t *testing.T) {
}

// execute the method under test
res, err := client.UpdateEndpoint(context.TODO(), test.containerID, test.hnsID, test.vethName)
res, err := client.UpdateEndpoint(context.TODO(), test.containerID, test.expReq)
if err != nil && !test.shouldErr {
t.Fatal("unexpected error: err: ", err, res.Message)
}
Expand All @@ -2801,7 +2815,7 @@ func TestUpdateEndpoint(t *testing.T) {
// if a request was expected to be sent, decode it and ensure that it
// matches expectations
if test.expReq != nil {
var gotReq cns.EndpointRequest
var gotReq map[string]*restserver.IPInfo
err = json.NewDecoder(test.response.Request.Body).Decode(&gotReq)
if err != nil {
t.Fatal("error decoding the received request: err:", err)
Expand All @@ -2810,7 +2824,7 @@ func TestUpdateEndpoint(t *testing.T) {
// a nil expReq is semantically meaningful (i.e. "no request"), but in
// order for cmp to work properly, the outer types should be identical.
// Thus we have to dereference it explicitly:
expReq := *test.expReq
expReq := test.expReq

// ensure that the received request is what was expected
if !cmp.Equal(gotReq, expReq) {
Expand Down
45 changes: 31 additions & 14 deletions cns/restserver/ipam.go
Original file line number Diff line number Diff line change
Expand Up @@ -1083,7 +1083,7 @@ func (service *HTTPRestService) GetEndpointHelper(endpointID string) (*EndpointI
func (service *HTTPRestService) UpdateEndpointHandler(w http.ResponseWriter, r *http.Request) {
logger.Printf("[updateEndpoint] updateEndpoint for %s", r.URL.Path)

var req cns.EndpointRequest
var req map[string]*IPInfo
err := service.Listener.Decode(w, r, &req)
endpointID := strings.TrimPrefix(r.URL.Path, cns.EndpointPath)
logger.Request(service.Name, &req, err)
Expand All @@ -1098,11 +1098,10 @@ func (service *HTTPRestService) UpdateEndpointHandler(w http.ResponseWriter, r *
logger.Response(service.Name, response, response.ReturnCode, err)
return
}
if req.HostVethName == "" && req.HnsEndpointID == "" {
logger.Warnf("[updateEndpoint] No HnsEndpointID or HostVethName has been provided")
if err = verifyUpdateEndpointStateRequest(req); err != nil {
response := cns.Response{
ReturnCode: types.InvalidRequest,
Message: "[updateEndpoint] No HnsEndpointID or HostVethName has been provided",
Message: err.Error(),
}
w.Header().Set(cnsReturnCode, response.ReturnCode.String())
err = service.Listener.Encode(w, &response)
Expand Down Expand Up @@ -1131,22 +1130,27 @@ func (service *HTTPRestService) UpdateEndpointHandler(w http.ResponseWriter, r *
}

// UpdateEndpointHelper updates the state of the given endpointId with HNSId or VethName
func (service *HTTPRestService) UpdateEndpointHelper(endpointID string, req cns.EndpointRequest) error {
func (service *HTTPRestService) UpdateEndpointHelper(endpointID string, req map[string]*IPInfo) error {
if service.EndpointStateStore == nil {
return ErrStoreEmpty
}
logger.Printf("[updateEndpoint] Updating endpoint state for infra container %s", endpointID)
if endpointInfo, ok := service.EndpointState[endpointID]; ok {
logger.Printf("[updateEndpoint] Found existing endpoint state for infra container %s", endpointID)
if req.HnsEndpointID != "" {
service.EndpointState[endpointID].HnsEndpointID = req.HnsEndpointID
logger.Printf("[updateEndpoint] update the endpoint %s with HNSID %s", endpointID, req.HnsEndpointID)
}
if req.HostVethName != "" {
service.EndpointState[endpointID].HostVethName = req.HostVethName
logger.Printf("[updateEndpoint] update the endpoint %s with vethName %s", endpointID, req.HostVethName)
for ifName, InterfaceInfo := range req {
logger.Printf("[updateEndpoint] Found existing endpoint state for infra container %s", endpointID)
if InterfaceInfo.HnsEndpointID != "" {
service.EndpointState[endpointID].IfnameToIPMap[ifName].HnsEndpointID = InterfaceInfo.HnsEndpointID
logger.Printf("[updateEndpoint] update the endpoint %s with HNSID %s", endpointID, InterfaceInfo.HnsEndpointID)
}
if InterfaceInfo.HostVethName != "" {
service.EndpointState[endpointID].IfnameToIPMap[ifName].HostVethName = InterfaceInfo.HostVethName
logger.Printf("[updateEndpoint] update the endpoint %s with vethName %s", endpointID, InterfaceInfo.HostVethName)
}
if InterfaceInfo.NICType != "" {
service.EndpointState[endpointID].IfnameToIPMap[ifName].NICType = InterfaceInfo.NICType
logger.Printf("[updateEndpoint] update the endpoint %s with NICType %s", endpointID, InterfaceInfo.NICType)
}
}

err := service.EndpointStateStore.Write(EndpointStoreKey, service.EndpointState)
if err != nil {
return fmt.Errorf("[updateEndpoint] failed to write endpoint state to store for pod %s : %w", endpointInfo.PodName, err)
Expand All @@ -1155,3 +1159,16 @@ func (service *HTTPRestService) UpdateEndpointHelper(endpointID string, req cns.
}
return errors.New("[updateEndpoint] endpoint could not be found in the statefile")
}

// verifyUpdateEndpointStateRequest verify the CNI request body for the UpdateENdpointState API
func verifyUpdateEndpointStateRequest(req map[string]*IPInfo) error {
for ifName, InterfaceInfo := range req {
if InterfaceInfo.HostVethName == "" && InterfaceInfo.HnsEndpointID == "" && InterfaceInfo.NICType == "" {
return errors.New("[updateEndpoint] No NicType, HnsEndpointID or HostVethName has been provided")
}
if ifName == "" {
return errors.New("[updateEndpoint] No Interface has been provided")
}
}
return nil
}
9 changes: 5 additions & 4 deletions cns/restserver/restserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,14 @@ type EndpointInfo struct {
PodName string
PodNamespace string
IfnameToIPMap map[string]*IPInfo // key : interface name, value : IPInfo
HnsEndpointID string
HostVethName string
}

type IPInfo struct {
IPv4 []net.IPNet
IPv6 []net.IPNet
IPv4 []net.IPNet
IPv6 []net.IPNet `json:",omitempty"`
HnsEndpointID string `json:",omitempty"`
HostVethName string `json:",omitempty"`
NICType cns.NICType
}

type GetHTTPServiceDataResponse struct {
Expand Down
Loading

0 comments on commit 008dadc

Please sign in to comment.