Skip to content

feat: Modifying stateless CNI state to account for swift 2.0 changes. #2523

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 25, 2024
Merged
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
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
Loading