1. Enables CNS to handle multiple NCs in NNC.
2. Adds new APIs that allows multiple IPs to be requested and released.
3. This change is needed for dualstack overlay
---------

Co-authored-by: Tim Raymond <traymond@microsoft.com>
This commit is contained in:
rjdenney 2023-04-04 12:00:02 -04:00 коммит произвёл GitHub
Родитель 2538e573a8
Коммит 3e06a07ca2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 1913 добавлений и 556 удалений

Просмотреть файл

@ -27,7 +27,9 @@ const (
AttachContainerToNetwork = "/network/attachcontainertonetwork"
DetachContainerFromNetwork = "/network/detachcontainerfromnetwork"
RequestIPConfig = "/network/requestipconfig"
RequestIPConfigs = "/network/requestipconfigs"
ReleaseIPConfig = "/network/releaseipconfig"
ReleaseIPConfigs = "/network/releaseipconfigs"
PathDebugIPAddresses = "/debug/ipaddresses"
PathDebugPodContext = "/debug/podcontext"
PathDebugRestData = "/debug/restdata"
@ -255,9 +257,9 @@ func UnmarshalPodInfo(b []byte) (PodInfo, error) {
return p, nil
}
// NewPodInfoFromIPConfigRequest builds and returns an implementation of
// PodInfo from the provided IPConfigRequest.
func NewPodInfoFromIPConfigRequest(req IPConfigRequest) (PodInfo, error) {
// NewPodInfoFromIPConfigsRequest builds and returns an implementation of
// PodInfo from the provided IPConfigsRequest.
func NewPodInfoFromIPConfigsRequest(req IPConfigsRequest) (PodInfo, error) {
p, err := UnmarshalPodInfo(req.OrchestratorContext)
if err != nil {
return nil, err
@ -411,9 +413,13 @@ type IPConfigRequest struct {
Ifname string // Used by delegated IPAM
}
func (i IPConfigRequest) String() string {
return fmt.Sprintf("[IPConfigRequest: DesiredIPAddress %s, PodInterfaceID %s, InfraContainerID %s, OrchestratorContext %s]",
i.DesiredIPAddress, i.PodInterfaceID, i.InfraContainerID, string(i.OrchestratorContext))
// Same as IPConfigRequest except that DesiredIPAddresses is passed in as a slice
type IPConfigsRequest struct {
DesiredIPAddresses []string `json:"desiredIPAddresses"`
PodInterfaceID string `json:"podInterfaceID"`
InfraContainerID string `json:"infraContainerID"`
OrchestratorContext json.RawMessage `json:"orchestratorContext"`
Ifname string `json:"ifname"` // Used by delegated IPAM
}
// IPConfigResponse is used in CNS IPAM mode as a response to CNI ADD
@ -422,6 +428,12 @@ type IPConfigResponse struct {
Response Response
}
// IPConfigsResponse is used in CNS IPAM mode to return a slice of IP configs as a response to CNI ADD
type IPConfigsResponse struct {
PodIPInfo []PodIpInfo `json:"podIPInfo"`
Response Response `json:"response"`
}
// GetIPAddressesRequest is used in CNS IPAM mode to get the states of IPConfigs
// The IPConfigStateFilter is a slice of IPs to fetch from CNS that match those states
type GetIPAddressesRequest struct {
@ -440,9 +452,9 @@ type GetIPAddressStatusResponse struct {
Response Response
}
// GetPodContextResponse is used in CNS Client debug mode to get mapping of Orchestrator Context to Pod IP UUID
// GetPodContextResponse is used in CNS Client debug mode to get mapping of Orchestrator Context to Pod IP UUIDs
type GetPodContextResponse struct {
PodContext map[string]string
PodContext map[string][]string // Can have multiple Pod IP UUIDs in the case of dualstack
Response Response
}

Просмотреть файл

@ -56,18 +56,18 @@ func TestUnmarshalPodInfo(t *testing.T) {
}
}
func TestNewPodInfoFromIPConfigRequest(t *testing.T) {
func TestNewPodInfoFromIPConfigsRequest(t *testing.T) {
GlobalPodInfoScheme = InterfaceIDPodInfoScheme
defer func() { GlobalPodInfoScheme = KubernetesPodInfoScheme }()
tests := []struct {
name string
req IPConfigRequest
req IPConfigsRequest
want PodInfo
wantErr bool
}{
{
name: "full req",
req: IPConfigRequest{
req: IPConfigsRequest{
PodInterfaceID: "abcdef-eth0",
InfraContainerID: "abcdef",
OrchestratorContext: []byte(`{"PodName":"pod","PodNamespace":"namespace"}`),
@ -84,7 +84,7 @@ func TestNewPodInfoFromIPConfigRequest(t *testing.T) {
},
{
name: "empty interface id",
req: IPConfigRequest{
req: IPConfigsRequest{
InfraContainerID: "abcdef",
OrchestratorContext: []byte(`{"PodName":"pod","PodNamespace":"namespace"}`),
},
@ -94,7 +94,7 @@ func TestNewPodInfoFromIPConfigRequest(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := NewPodInfoFromIPConfigRequest(tt.req)
got, err := NewPodInfoFromIPConfigsRequest(tt.req)
if tt.wantErr {
assert.Error(t, err)
return

Просмотреть файл

@ -30,7 +30,9 @@ var clientPaths = []string{
cns.CreateHostNCApipaEndpointPath,
cns.DeleteHostNCApipaEndpointPath,
cns.RequestIPConfig,
cns.RequestIPConfigs,
cns.ReleaseIPConfig,
cns.ReleaseIPConfigs,
cns.PathDebugIPAddresses,
cns.PathDebugPodContext,
cns.PathDebugRestData,
@ -45,6 +47,8 @@ var clientPaths = []string{
cns.GetHomeAz,
}
var errAPINotFound error = errors.New("api not found")
type do interface {
Do(*http.Request) (*http.Response, error)
}
@ -374,6 +378,102 @@ func (c *Client) ReleaseIPAddress(ctx context.Context, ipconfig cns.IPConfigRequ
return nil
}
// RequestIPs calls the RequestIPConfigs in CNS
func (c *Client) RequestIPs(ctx context.Context, ipconfig cns.IPConfigsRequest) (*cns.IPConfigsResponse, error) {
var err error
defer func() {
if err != nil {
if e := c.ReleaseIPs(ctx, ipconfig); e != nil {
err = errors.Wrap(e, err.Error())
}
}
}()
var body bytes.Buffer
err = json.NewEncoder(&body).Encode(ipconfig)
if err != nil {
return nil, errors.Wrap(err, "failed to encode IPConfigsRequest")
}
u := c.routes[cns.RequestIPConfigs]
req, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), &body)
if err != nil {
return nil, errors.Wrap(err, "failed to build request")
}
req.Header.Set(headerContentType, contentTypeJSON)
res, err := c.client.Do(req)
// if we get a 404 error
if res.StatusCode == http.StatusNotFound {
return nil, fmt.Errorf("cannot find API RequestIPs %w: %v", errAPINotFound, err) //nolint:errorlint // multiple %w not supported in 1.19
}
if err != nil {
return nil, errors.Wrap(err, "http request failed")
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, errors.Errorf("http response %d", res.StatusCode)
}
var response cns.IPConfigsResponse
err = json.NewDecoder(res.Body).Decode(&response)
if err != nil {
return nil, errors.Wrap(err, "failed to decode IPConfigsResponse")
}
if response.Response.ReturnCode != 0 {
return nil, errors.New(response.Response.Message)
}
return &response, nil
}
// ReleaseIPs calls releaseIPs on which releases the IPs on the pod
func (c *Client) ReleaseIPs(ctx context.Context, ipconfig cns.IPConfigsRequest) error {
var body bytes.Buffer
err := json.NewEncoder(&body).Encode(ipconfig)
if err != nil {
return errors.Wrap(err, "failed to encode IPConfigsRequest")
}
u := c.routes[cns.ReleaseIPConfigs]
req, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), &body)
if err != nil {
return errors.Wrap(err, "failed to build request")
}
req.Header.Set(headerContentType, contentTypeJSON)
res, err := c.client.Do(req)
// if we get a 404 error
if res.StatusCode == http.StatusNotFound {
return fmt.Errorf("cannot find API ReleaseIPs %w: %v", errAPINotFound, err) //nolint:errorlint // multiple %w not supported in 1.19
}
if err != nil {
return errors.Wrap(err, "http request failed")
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return errors.Errorf("http response %d", res.StatusCode)
}
var resp cns.Response
err = json.NewDecoder(res.Body).Decode(&resp)
if err != nil {
return errors.Wrap(err, "failed to decode Response")
}
if resp.ReturnCode != 0 {
return errors.New(resp.Message)
}
return nil
}
// GetIPAddressesMatchingStates takes a variadic number of string parameters, to get all IP Addresses matching a number of states
// usage GetIPAddressesWithStates(ctx, types.Available...)
func (c *Client) GetIPAddressesMatchingStates(ctx context.Context, stateFilter ...types.IPState) ([]cns.IPConfigurationStatus, error) {
@ -420,7 +520,7 @@ func (c *Client) GetIPAddressesMatchingStates(ctx context.Context, stateFilter .
}
// GetPodOrchestratorContext calls GetPodIpOrchestratorContext API on CNS
func (c *Client) GetPodOrchestratorContext(ctx context.Context) (map[string]string, error) {
func (c *Client) GetPodOrchestratorContext(ctx context.Context) (map[string][]string, error) {
u := c.routes[cns.PathDebugPodContext]
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil {

Просмотреть файл

@ -294,6 +294,11 @@ func TestCNSClientRequestAndRelease(t *testing.T) {
t.Log(ipaddresses)
addresses := make([]string, len(ipaddresses))
for i := range ipaddresses {
addresses[i] = ipaddresses[i].IPAddress
}
// release requested IP address, expect success
err = cnsClient.ReleaseIPAddress(context.TODO(), cns.IPConfigRequest{DesiredIPAddress: ipaddresses[0].IPAddress, OrchestratorContext: orchestratorContext})
assert.NoError(t, err, "Expected to not fail when releasing IP reservation found with context")
@ -378,7 +383,9 @@ func TestCNSClientDebugAPI(t *testing.T) {
assert.Len(t, testIpamPoolMonitor.CachedNNC.Status.NetworkContainers, 1, "Expected only one Network Container in the list")
t.Logf("In-memory Data: ")
t.Logf("PodIPIDByOrchestratorContext: %+v", inmemory.HTTPRestServiceData.PodIPIDByPodInterfaceKey)
for i := range inmemory.HTTPRestServiceData.PodIPIDByPodInterfaceKey {
t.Logf("PodIPIDByOrchestratorContext: %+v", inmemory.HTTPRestServiceData.PodIPIDByPodInterfaceKey[i])
}
t.Logf("PodIPConfigState: %+v", inmemory.HTTPRestServiceData.PodIPConfigState)
t.Logf("IPAMPoolMonitor: %+v", inmemory.HTTPRestServiceData.IPAMPoolMonitor)
}
@ -1821,6 +1828,310 @@ func TestReleaseIPAddress(t *testing.T) {
}
}
func TestRequestIPs(t *testing.T) {
emptyRoutes, _ := buildRoutes(defaultBaseURL, clientPaths)
tests := []struct {
name string
ctx context.Context
ipconfig cns.IPConfigsRequest
mockdo *mockdo
routes map[string]url.URL
want *cns.IPConfigsResponse
wantErr bool
}{
{
name: "happy case 1 IP",
ctx: context.TODO(),
ipconfig: cns.IPConfigsRequest{
DesiredIPAddresses: []string{
"testipaddress",
},
PodInterfaceID: "testpodinterfaceid",
InfraContainerID: "testcontainerid",
},
mockdo: &mockdo{
errToReturn: nil,
objToReturn: &cns.IPConfigsResponse{},
httpStatusCodeToReturn: http.StatusOK,
},
routes: emptyRoutes,
want: &cns.IPConfigsResponse{},
wantErr: false,
},
{
name: "happy case 2 IPs",
ctx: context.TODO(),
ipconfig: cns.IPConfigsRequest{
DesiredIPAddresses: []string{
"testipaddress1",
"testipaddress2",
},
PodInterfaceID: "testpodinterfaceid",
InfraContainerID: "testcontainerid",
},
mockdo: &mockdo{
errToReturn: nil,
objToReturn: &cns.IPConfigsResponse{},
httpStatusCodeToReturn: http.StatusOK,
},
routes: emptyRoutes,
want: &cns.IPConfigsResponse{},
wantErr: false,
},
{
name: "bad request",
ctx: context.TODO(),
ipconfig: cns.IPConfigsRequest{
DesiredIPAddresses: []string{
"testipaddress",
},
PodInterfaceID: "testpodinterfaceid",
InfraContainerID: "testcontainerid",
},
mockdo: &mockdo{
errToReturn: errBadRequest,
objToReturn: nil,
httpStatusCodeToReturn: http.StatusBadRequest,
},
routes: emptyRoutes,
want: nil,
wantErr: true,
},
{
name: "bad decoding",
ctx: context.TODO(),
ipconfig: cns.IPConfigsRequest{
DesiredIPAddresses: []string{
"testipaddress",
},
PodInterfaceID: "testpodinterfaceid",
InfraContainerID: "testcontainerid",
},
mockdo: &mockdo{
errToReturn: nil,
objToReturn: []cns.IPConfigsResponse{},
httpStatusCodeToReturn: http.StatusOK,
},
routes: emptyRoutes,
want: nil,
wantErr: true,
},
{
name: "http status not ok",
ctx: context.TODO(),
ipconfig: cns.IPConfigsRequest{},
mockdo: &mockdo{
errToReturn: nil,
objToReturn: nil,
httpStatusCodeToReturn: http.StatusInternalServerError,
},
routes: emptyRoutes,
want: nil,
wantErr: true,
},
{
name: "cns return code not zero",
ctx: context.TODO(),
ipconfig: cns.IPConfigsRequest{
DesiredIPAddresses: []string{
"testipaddress",
},
PodInterfaceID: "testpodinterfaceid",
InfraContainerID: "testcontainerid",
},
mockdo: &mockdo{
errToReturn: nil,
objToReturn: &cns.IPConfigResponse{
Response: cns.Response{
ReturnCode: types.UnsupportedNetworkType,
},
},
httpStatusCodeToReturn: http.StatusOK,
},
routes: emptyRoutes,
want: nil,
wantErr: true,
},
{
name: "nil context",
ctx: nil,
ipconfig: cns.IPConfigsRequest{
DesiredIPAddresses: []string{
"testipaddress",
},
PodInterfaceID: "testpodinterfaceid",
InfraContainerID: "testcontainerid",
},
mockdo: &mockdo{},
routes: emptyRoutes,
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
client := &Client{
client: tt.mockdo,
routes: tt.routes,
}
got, err := client.RequestIPs(tt.ctx, tt.ipconfig)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.want, got)
})
}
}
func TestReleaseIPs(t *testing.T) {
emptyRoutes, _ := buildRoutes(defaultBaseURL, clientPaths)
tests := []struct {
name string
ctx context.Context
ipconfig cns.IPConfigsRequest
mockdo *mockdo
routes map[string]url.URL
wantErr bool
}{
{
name: "happy case 1 IP",
ctx: context.TODO(),
ipconfig: cns.IPConfigsRequest{
DesiredIPAddresses: []string{
"testipaddress",
},
PodInterfaceID: "testpodinterfaceid",
InfraContainerID: "testcontainerid",
},
mockdo: &mockdo{
errToReturn: nil,
objToReturn: &cns.Response{},
httpStatusCodeToReturn: http.StatusOK,
},
routes: emptyRoutes,
wantErr: false,
},
{
name: "happy case 2 IPs",
ctx: context.TODO(),
ipconfig: cns.IPConfigsRequest{
DesiredIPAddresses: []string{
"testipaddress",
},
PodInterfaceID: "testpodinterfaceid",
InfraContainerID: "testcontainerid",
},
mockdo: &mockdo{
errToReturn: nil,
objToReturn: &cns.Response{},
httpStatusCodeToReturn: http.StatusOK,
},
routes: emptyRoutes,
wantErr: false,
},
{
name: "bad request",
ctx: context.TODO(),
ipconfig: cns.IPConfigsRequest{
DesiredIPAddresses: []string{
"testipaddress",
},
PodInterfaceID: "testpodinterfaceid",
InfraContainerID: "testcontainerid",
},
mockdo: &mockdo{
errToReturn: errBadRequest,
objToReturn: nil,
httpStatusCodeToReturn: http.StatusBadRequest,
},
routes: emptyRoutes,
wantErr: true,
},
{
name: "bad decoding",
ctx: context.TODO(),
ipconfig: cns.IPConfigsRequest{
DesiredIPAddresses: []string{
"testipaddress",
},
PodInterfaceID: "testpodinterfaceid",
InfraContainerID: "testcontainerid",
},
mockdo: &mockdo{
errToReturn: nil,
objToReturn: []cns.Response{},
httpStatusCodeToReturn: http.StatusOK,
},
routes: emptyRoutes,
wantErr: true,
},
{
name: "http status not ok",
ctx: context.TODO(),
ipconfig: cns.IPConfigsRequest{},
mockdo: &mockdo{
errToReturn: nil,
objToReturn: nil,
httpStatusCodeToReturn: http.StatusInternalServerError,
},
routes: emptyRoutes,
wantErr: true,
},
{
name: "cns return code not zero",
ctx: context.TODO(),
ipconfig: cns.IPConfigsRequest{
DesiredIPAddresses: []string{
"testipaddress",
},
PodInterfaceID: "testpodinterfaceid",
InfraContainerID: "testcontainerid",
},
mockdo: &mockdo{
errToReturn: nil,
objToReturn: &cns.Response{
ReturnCode: types.UnsupportedNetworkType,
},
httpStatusCodeToReturn: http.StatusOK,
},
routes: emptyRoutes,
wantErr: true,
},
{
name: "nil context",
ctx: nil,
ipconfig: cns.IPConfigsRequest{
DesiredIPAddresses: []string{
"testipaddress",
},
PodInterfaceID: "testpodinterfaceid",
InfraContainerID: "testcontainerid",
},
mockdo: &mockdo{},
routes: emptyRoutes,
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
client := &Client{
client: tt.mockdo,
routes: tt.routes,
}
err := client.ReleaseIPs(tt.ctx, tt.ipconfig)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestGetIPAddressesMatchingStates(t *testing.T) {
emptyRoutes, _ := buildRoutes(defaultBaseURL, clientPaths)
tests := []struct {
@ -1946,7 +2257,7 @@ func TestGetPodOrchestratorContext(t *testing.T) {
ctx context.Context
mockdo *mockdo
routes map[string]url.URL
want map[string]string
want map[string][]string
wantErr bool
}{
{
@ -1955,12 +2266,12 @@ func TestGetPodOrchestratorContext(t *testing.T) {
mockdo: &mockdo{
errToReturn: nil,
objToReturn: &cns.GetPodContextResponse{
PodContext: map[string]string{},
PodContext: map[string][]string{},
},
httpStatusCodeToReturn: http.StatusOK,
},
routes: emptyRoutes,
want: map[string]string{},
want: map[string][]string{},
wantErr: false,
},
{

Просмотреть файл

@ -161,10 +161,10 @@ func (ic *IpamClient) ReserveIPAddress(poolID string, reservationID string) (str
return "", err
}
// ReleaseIPAddress release an Ip address for the reservation id.
// ReleaseIPAddress release an IP address for the reservation id.
func (ic *IpamClient) ReleaseIPAddress(poolID string, reservationID string) error {
var body bytes.Buffer
log.Printf("[Azure CNS] ReleaseIpAddress")
log.Printf("[Azure CNS] ReleaseIPAddress")
client, err := getClient(ic.connectionURL)
if err != nil {

Просмотреть файл

@ -273,9 +273,9 @@ func (service *HTTPRestService) ReconcileNCState(ncRequest *cns.CreateNetworkCon
}
// now parse the secondaryIP list, if it exists in PodInfo list, then assign that ip.
for _, secIpConfig := range ncRequest.SecondaryIPConfigs {
if podInfo, exists := podInfoByIP[secIpConfig.IPAddress]; exists {
logger.Printf("SecondaryIP %+v is assigned to Pod. %+v, ncId: %s", secIpConfig, podInfo, ncRequest.NetworkContainerid)
for _, secIPConfig := range ncRequest.SecondaryIPConfigs {
if podInfo, exists := podInfoByIP[secIPConfig.IPAddress]; exists {
logger.Printf("SecondaryIP %+v is assigned to Pod. %+v, ncId: %s", secIPConfig, podInfo, ncRequest.NetworkContainerid)
jsonContext, err := podInfo.OrchestratorContext()
if err != nil {
@ -283,19 +283,19 @@ func (service *HTTPRestService) ReconcileNCState(ncRequest *cns.CreateNetworkCon
return types.UnexpectedError
}
ipconfigRequest := cns.IPConfigRequest{
DesiredIPAddress: secIpConfig.IPAddress,
ipconfigsRequest := cns.IPConfigsRequest{
DesiredIPAddresses: []string{secIPConfig.IPAddress},
OrchestratorContext: jsonContext,
InfraContainerID: podInfo.InfraContainerID(),
PodInterfaceID: podInfo.InterfaceID(),
}
if _, err := requestIPConfigHelper(service, ipconfigRequest); err != nil {
logger.Errorf("AllocateIPConfig failed for SecondaryIP %+v, podInfo %+v, ncId %s, error: %v", secIpConfig, podInfo, ncRequest.NetworkContainerid, err)
if _, err := requestIPConfigsHelper(service, ipconfigsRequest); err != nil {
logger.Errorf("AllocateIPConfig failed for SecondaryIP %+v, podInfo %+v, ncId %s, error: %v", secIPConfig, podInfo, ncRequest.NetworkContainerid, err)
return types.FailedToAllocateIPConfig
}
} else {
logger.Printf("SecondaryIP %+v is not assigned. ncId: %s", secIpConfig, ncRequest.NetworkContainerid)
logger.Printf("SecondaryIP %+v is not assigned. ncId: %s", secIPConfig, ncRequest.NetworkContainerid)
}
}
@ -371,10 +371,10 @@ func (service *HTTPRestService) CreateOrUpdateNetworkContainerInternal(req *cns.
}
// Validate SecondaryIPConfig
for _, secIpconfig := range req.SecondaryIPConfigs {
for _, secIPConfig := range req.SecondaryIPConfigs {
// Validate Ipconfig
if secIpconfig.IPAddress == "" {
logger.Errorf("Failed to add IPConfig to state: %+v, empty IPSubnet.IPAddress", secIpconfig)
if secIPConfig.IPAddress == "" {
logger.Errorf("Failed to add IPConfig to state: %+v, empty IPSubnet.IPAddress", secIPConfig)
return types.InvalidSecondaryIPConfig
}
}

Просмотреть файл

@ -122,6 +122,7 @@ func TestSyncHostNCVersion(t *testing.T) {
// cns.KubernetesCRD has one more logic compared to other orchestrator type, so test both of them
orchestratorTypes := []string{cns.Kubernetes, cns.KubernetesCRD}
for _, orchestratorType := range orchestratorTypes {
orchestratorType := orchestratorType
t.Run(orchestratorType, func(t *testing.T) {
req := createNCReqeustForSyncHostNCVersion(t)
containerStatus := svc.state.ContainerStatus[req.NetworkContainerid]
@ -417,7 +418,7 @@ func validateCreateOrUpdateNCInternal(t *testing.T, secondaryIpCount int, ncVers
createAndValidateNCRequest(t, secondaryIPConfigs, ncId, ncVersion)
// now Validate Update, add more secondaryIpConfig and it should handle the update
// now Validate Update, add more secondaryIPConfig and it should handle the update
fmt.Println("Validate Scaleup")
for i := 0; i < secondaryIpCount; i++ {
ipaddress := "10.0.0." + strconv.Itoa(startingIndex)
@ -429,7 +430,7 @@ func validateCreateOrUpdateNCInternal(t *testing.T, secondaryIpCount int, ncVers
createAndValidateNCRequest(t, secondaryIPConfigs, ncId, ncVersion)
// now Scale down, delete 3 ipaddresses from secondaryIpConfig req
// now Scale down, delete 3 ipaddresses from secondaryIPConfig req
fmt.Println("Validate Scale down")
count := 0
for ipid := range secondaryIPConfigs {
@ -490,11 +491,6 @@ func validateNetworkRequest(t *testing.T, req cns.CreateNetworkContainerRequest)
t.Fatalf("Failed as Primary IPAddress doesnt match, expected:%s, actual %s", req.IPConfiguration.IPSubnet.IPAddress, actualReq.IPConfiguration.IPSubnet.IPAddress)
}
// Validate Secondary ips are added in the PodMap
if len(svc.PodIPConfigState) != len(req.SecondaryIPConfigs) {
t.Fatalf("Failed as Secondary IP count doesnt match in PodIpConfig state, expected:%d, actual %d", len(req.SecondaryIPConfigs), len(svc.PodIPConfigState))
}
var expectedIPStatus types.IPState
// 0 is the default NMAgent version return from fake GetNetworkContainerInfoFromHost
if containerStatus.CreateNetworkContainerRequest.Version > "0" {
@ -504,35 +500,40 @@ func validateNetworkRequest(t *testing.T, req cns.CreateNetworkContainerRequest)
}
t.Logf("NC version in container status is %s, HostVersion is %s", containerStatus.CreateNetworkContainerRequest.Version, containerStatus.HostVersion)
alreadyValidated := make(map[string]string)
ncCount := 0
for ipid, ipStatus := range svc.PodIPConfigState {
if ipaddress, found := alreadyValidated[ipid]; !found {
if secondaryIpConfig, ok := req.SecondaryIPConfigs[ipid]; !ok {
t.Fatalf("PodIpConfigState has stale ipId: %s, config: %+v", ipid, ipStatus)
} else {
if ipStatus.IPAddress != secondaryIpConfig.IPAddress {
t.Fatalf("IPId: %s IPSubnet doesnt match: expected %+v, actual: %+v", ipid, secondaryIpConfig.IPAddress, ipStatus.IPAddress)
}
// Validate IP state
if ipStatus.PodInfo != nil {
if _, exists := svc.PodIPIDByPodInterfaceKey[ipStatus.PodInfo.Key()]; exists {
if ipStatus.GetState() != types.Assigned {
t.Fatalf("IPId: %s State is not Assigned, ipStatus: %+v", ipid, ipStatus)
}
} else {
t.Fatalf("Failed to find podContext for assigned ip: %+v, podinfo :%+v", ipStatus, ipStatus.PodInfo)
ncCount++
// ignore any IPs that were added from a previous NC
if ncCount > (len(svc.state.ContainerStatus) * len(svc.PodIPConfigState)) {
if ipaddress, found := alreadyValidated[ipid]; !found {
if secondaryIPConfig, ok := req.SecondaryIPConfigs[ipid]; !ok {
t.Fatalf("PodIpConfigState has stale ipId: %s, config: %+v", ipid, ipStatus)
} else {
if ipStatus.IPAddress != secondaryIPConfig.IPAddress {
t.Fatalf("IPId: %s IPSubnet doesnt match: expected %+v, actual: %+v", ipid, secondaryIPConfig.IPAddress, ipStatus.IPAddress)
}
} else if ipStatus.GetState() != expectedIPStatus {
// Todo: Validate for pendingRelease as well
t.Fatalf("IPId: %s State is not as expected, ipStatus is : %+v, expected status is %+v", ipid, ipStatus.GetState(), expectedIPStatus)
}
alreadyValidated[ipid] = ipStatus.IPAddress
}
} else {
// if ipaddress is not same, then fail
if ipaddress != ipStatus.IPAddress {
t.Fatalf("Added the same IP guid :%s with different ipaddress, expected:%s, actual %s", ipid, ipStatus.IPAddress, ipaddress)
// Validate IP state
if ipStatus.PodInfo != nil {
if _, exists := svc.PodIPIDByPodInterfaceKey[ipStatus.PodInfo.Key()]; exists {
if ipStatus.GetState() != types.Assigned {
t.Fatalf("IPId: %s State is not Assigned, ipStatus: %+v", ipid, ipStatus)
}
} else {
t.Fatalf("Failed to find podContext for assigned ip: %+v, podinfo :%+v", ipStatus, ipStatus.PodInfo)
}
} else if ipStatus.GetState() != expectedIPStatus {
// Todo: Validate for pendingRelease as well
t.Fatalf("IPId: %s State is not as expected, ipStatus is : %+v, expected status is %+v", ipid, ipStatus.GetState(), expectedIPStatus)
}
alreadyValidated[ipid] = ipStatus.IPAddress
}
} else {
// if ipaddress is not same, then fail
if ipaddress != ipStatus.IPAddress {
t.Fatalf("Added the same IP guid :%s with different ipaddress, expected:%s, actual %s", ipid, ipStatus.IPAddress, ipaddress)
}
}
}
}
@ -582,27 +583,27 @@ func validateNCStateAfterReconcile(t *testing.T, ncRequest *cns.CreateNetworkCon
}
for ipaddress, podInfo := range expectedAssignedPods {
ipId := svc.PodIPIDByPodInterfaceKey[podInfo.Key()]
ipConfigstate := svc.PodIPConfigState[ipId]
for _, ipID := range svc.PodIPIDByPodInterfaceKey[podInfo.Key()] {
ipConfigstate := svc.PodIPConfigState[ipID]
if ipConfigstate.GetState() != types.Assigned {
t.Fatalf("IpAddress %s is not marked as assigned to Pod: %+v, ipState: %+v", ipaddress, podInfo, ipConfigstate)
}
if ipConfigstate.GetState() != types.Assigned {
t.Fatalf("IpAddress %s is not marked as assigned to Pod: %+v, ipState: %+v", ipaddress, podInfo, ipConfigstate)
}
// Validate if IPAddress matches
if ipConfigstate.IPAddress != ipaddress {
t.Fatalf("IpAddress %s is not same, for Pod: %+v, actual ipState: %+v", ipaddress, podInfo, ipConfigstate)
}
// Validate if IPAddress matches
if ipConfigstate.IPAddress != ipaddress {
t.Fatalf("IpAddress %s is not same, for Pod: %+v, actual ipState: %+v", ipaddress, podInfo, ipConfigstate)
}
// Validate pod context
if reflect.DeepEqual(ipConfigstate.PodInfo, podInfo) != true {
t.Fatalf("OrchestrationContext: is not same, expected: %+v, actual %+v", ipConfigstate.PodInfo, podInfo)
}
// Valdate pod context
if reflect.DeepEqual(ipConfigstate.PodInfo, podInfo) != true {
t.Fatalf("OrchestrationContext: is not same, expected: %+v, actual %+v", ipConfigstate.PodInfo, podInfo)
}
// Validate this IP belongs to a valid NCRequest
nc := svc.state.ContainerStatus[ipConfigstate.NCID]
if _, exists := nc.CreateNetworkContainerRequest.SecondaryIPConfigs[ipConfigstate.ID]; !exists {
t.Fatalf("Secondary IP config doest exist in NC, ncid: %s, ipId %s", ipConfigstate.NCID, ipConfigstate.ID)
// Validate this IP belongs to a valid NCRequest
nc := svc.state.ContainerStatus[ipConfigstate.NCID]
if _, exists := nc.CreateNetworkContainerRequest.SecondaryIPConfigs[ipConfigstate.ID]; !exists {
t.Fatalf("Secondary IP config doest exist in NC, ncid: %s, ipId %s", ipConfigstate.NCID, ipConfigstate.ID)
}
}
}

Просмотреть файл

@ -17,47 +17,35 @@ import (
"github.com/pkg/errors"
)
// used to request an IPConfig from the CNS state
func (service *HTTPRestService) requestIPConfigHandler(w http.ResponseWriter, r *http.Request) {
var ipconfigRequest cns.IPConfigRequest
err := service.Listener.Decode(w, r, &ipconfigRequest)
operationName := "requestIPConfigHandler"
logger.Request(service.Name+operationName, ipconfigRequest, err)
if err != nil {
return
}
var (
errStoreEmpty = errors.New("empty endpoint state store")
errParsePodIPFailed = errors.New("failed to parse pod's ip")
)
// retrieve ipconfig from nc
podInfo, returnCode, returnMessage := service.validateIPConfigRequest(ipconfigRequest)
// requestIPConfigHandlerHelper validates the request, assigns IPs, and returns a response
func (service *HTTPRestService) requestIPConfigHandlerHelper(ipconfigsRequest cns.IPConfigsRequest) (*cns.IPConfigsResponse, error) {
podInfo, returnCode, returnMessage := service.validateIPConfigsRequest(ipconfigsRequest)
if returnCode != types.Success {
reserveResp := &cns.IPConfigResponse{
return &cns.IPConfigsResponse{
Response: cns.Response{
ReturnCode: returnCode,
Message: returnMessage,
},
}
w.Header().Set(cnsReturnCode, reserveResp.Response.ReturnCode.String())
err = service.Listener.Encode(w, &reserveResp)
logger.ResponseEx(service.Name+operationName, ipconfigRequest, reserveResp, reserveResp.Response.ReturnCode, err)
return
}, errors.New("failed to validate ip config request")
}
// record a pod requesting an IP
service.podsPendingIPAssignment.Push(podInfo.Key())
podIPInfo, err := requestIPConfigHelper(service, ipconfigRequest)
podIPInfo, err := requestIPConfigsHelper(service, ipconfigsRequest)
if err != nil {
reserveResp := &cns.IPConfigResponse{
return &cns.IPConfigsResponse{
Response: cns.Response{
ReturnCode: types.FailedToAllocateIPConfig,
Message: fmt.Sprintf("AllocateIPConfig failed: %v, IP config request is %s", err, ipconfigRequest),
Message: fmt.Sprintf("AllocateIPConfig failed: %v, IP config request is %s", err, ipconfigsRequest),
},
PodIpInfo: podIPInfo,
}
w.Header().Set(cnsReturnCode, reserveResp.Response.ReturnCode.String())
err = service.Listener.Encode(w, &reserveResp)
logger.ResponseEx(service.Name+operationName, ipconfigRequest, reserveResp, reserveResp.Response.ReturnCode, err)
return
PodIPInfo: podIPInfo,
}, err
}
// record a pod assigned an IP
@ -70,145 +58,292 @@ func (service *HTTPRestService) requestIPConfigHandler(w http.ResponseWriter, r
// Check if http rest service managed endpoint state is set
if service.Options[common.OptManageEndpointState] == true {
err = service.updateEndpointState(ipconfigRequest, podInfo, podIPInfo)
err = service.updateEndpointState(ipconfigsRequest, podInfo, podIPInfo)
if err != nil {
reserveResp := &cns.IPConfigResponse{
return &cns.IPConfigsResponse{
Response: cns.Response{
ReturnCode: types.UnexpectedError,
Message: fmt.Sprintf("Update endpoint state failed: %v ", err),
},
PodIpInfo: podIPInfo,
}
w.Header().Set(cnsReturnCode, reserveResp.Response.ReturnCode.String())
err = service.Listener.Encode(w, &reserveResp)
logger.ResponseEx(service.Name+operationName, ipconfigRequest, reserveResp, reserveResp.Response.ReturnCode, err)
return
PodIPInfo: podIPInfo,
}, err
}
}
reserveResp := &cns.IPConfigResponse{
return &cns.IPConfigsResponse{
Response: cns.Response{
ReturnCode: types.Success,
},
PodIpInfo: podIPInfo,
PodIPInfo: podIPInfo,
}, nil
}
// requestIPConfigHandler requests an IPConfig from the CNS state
func (service *HTTPRestService) requestIPConfigHandler(w http.ResponseWriter, r *http.Request) {
var ipconfigRequest cns.IPConfigRequest
err := service.Listener.Decode(w, r, &ipconfigRequest)
operationName := "requestIPConfigHandler"
logger.Request(service.Name+operationName, ipconfigRequest, err)
if err != nil {
return
}
// This method can only return 1 IP. If we have more than one NC then we expect to need to return one IP per NC
if len(service.state.ContainerStatus) > 1 {
// we send a response back saying that this API won't be able to return the amount of IPs needed to fulfill the request
reserveResp := &cns.IPConfigResponse{
Response: cns.Response{
ReturnCode: types.InvalidRequest,
Message: fmt.Sprintf("Called API that can only return 1 IP when expecting %d", len(service.state.ContainerStatus)),
},
}
w.Header().Set(cnsReturnCode, reserveResp.Response.ReturnCode.String())
err = service.Listener.Encode(w, &reserveResp)
logger.ResponseEx(service.Name+operationName, ipconfigRequest, reserveResp, reserveResp.Response.ReturnCode, err)
return
}
var ipconfigsRequest cns.IPConfigsRequest
// doesn't fill in DesiredIPAddresses if it is empty in the original request
if ipconfigRequest.DesiredIPAddress != "" {
ipconfigsRequest = cns.IPConfigsRequest{
DesiredIPAddresses: []string{
ipconfigRequest.DesiredIPAddress,
},
PodInterfaceID: ipconfigRequest.PodInterfaceID,
InfraContainerID: ipconfigRequest.InfraContainerID,
OrchestratorContext: ipconfigRequest.OrchestratorContext,
Ifname: ipconfigRequest.Ifname,
}
} else {
ipconfigsRequest = cns.IPConfigsRequest{
PodInterfaceID: ipconfigRequest.PodInterfaceID,
InfraContainerID: ipconfigRequest.InfraContainerID,
OrchestratorContext: ipconfigRequest.OrchestratorContext,
Ifname: ipconfigRequest.Ifname,
}
}
ipConfigsResp, errResp := service.requestIPConfigHandlerHelper(ipconfigsRequest) //nolint:contextcheck // appease linter
if errResp != nil {
// As this API is expected to return IPConfigResponse, generate it from the IPConfigsResponse returned above
reserveResp := &cns.IPConfigResponse{
Response: ipConfigsResp.Response,
}
w.Header().Set(cnsReturnCode, reserveResp.Response.ReturnCode.String())
err = service.Listener.Encode(w, &reserveResp)
logger.ResponseEx(service.Name+operationName, ipconfigsRequest, reserveResp, reserveResp.Response.ReturnCode, err)
return
}
// As this API is expected to return IPConfigResponse, generate it from the IPConfigsResponse returned above.
reserveResp := &cns.IPConfigResponse{
Response: ipConfigsResp.Response,
PodIpInfo: ipConfigsResp.PodIPInfo[0],
}
w.Header().Set(cnsReturnCode, reserveResp.Response.ReturnCode.String())
err = service.Listener.Encode(w, &reserveResp)
logger.ResponseEx(service.Name+operationName, ipconfigRequest, reserveResp, reserveResp.Response.ReturnCode, err)
logger.ResponseEx(service.Name+operationName, ipconfigsRequest, reserveResp, reserveResp.Response.ReturnCode, err)
}
var (
errStoreEmpty = errors.New("empty endpoint state store")
errParsePodIPFailed = errors.New("failed to parse pod's ip")
)
// requestIPConfigsHandler requests multiple IPConfigs from the CNS state
func (service *HTTPRestService) requestIPConfigsHandler(w http.ResponseWriter, r *http.Request) {
var ipconfigsRequest cns.IPConfigsRequest
err := service.Listener.Decode(w, r, &ipconfigsRequest)
operationName := "requestIPConfigsHandler"
logger.Request(service.Name+operationName, ipconfigsRequest, err)
if err != nil {
return
}
func (service *HTTPRestService) updateEndpointState(ipconfigRequest cns.IPConfigRequest, podInfo cns.PodInfo, podIPInfo cns.PodIpInfo) error {
ipConfigsResp, err := service.requestIPConfigHandlerHelper(ipconfigsRequest) // nolint:contextcheck // appease linter
if err != nil {
w.Header().Set(cnsReturnCode, ipConfigsResp.Response.ReturnCode.String())
err = service.Listener.Encode(w, &ipConfigsResp)
logger.ResponseEx(service.Name+operationName, ipconfigsRequest, ipConfigsResp, ipConfigsResp.Response.ReturnCode, err)
return
}
w.Header().Set(cnsReturnCode, ipConfigsResp.Response.ReturnCode.String())
err = service.Listener.Encode(w, &ipConfigsResp)
logger.ResponseEx(service.Name+operationName, ipconfigsRequest, ipConfigsResp, ipConfigsResp.Response.ReturnCode, err)
}
func (service *HTTPRestService) updateEndpointState(ipconfigsRequest cns.IPConfigsRequest, podInfo cns.PodInfo, podIPInfo []cns.PodIpInfo) error {
if service.EndpointStateStore == nil {
return errStoreEmpty
}
service.Lock()
defer service.Unlock()
logger.Printf("[updateEndpointState] Updating endpoint state for infra container %s", ipconfigRequest.InfraContainerID)
if endpointInfo, ok := service.EndpointState[ipconfigRequest.InfraContainerID]; ok {
logger.Warnf("[updateEndpointState] Found existing endpoint state for infra container %s", ipconfigRequest.InfraContainerID)
ip := net.ParseIP(podIPInfo.PodIPConfig.IPAddress)
if ip == nil {
logger.Errorf("failed to parse pod ip address %s", podIPInfo.PodIPConfig.IPAddress)
return errParsePodIPFailed
}
if ip.To4() == nil { // is an ipv6 address
ipconfig := net.IPNet{IP: ip, Mask: net.CIDRMask(int(podIPInfo.PodIPConfig.PrefixLength), 128)} // nolint
for _, ipconf := range endpointInfo.IfnameToIPMap[ipconfigRequest.Ifname].IPv6 {
if ipconf.IP.Equal(ipconfig.IP) {
logger.Printf("[updateEndpointState] Found existing ipv6 ipconfig for infra container %s", ipconfigRequest.InfraContainerID)
return nil
}
logger.Printf("[updateEndpointState] Updating endpoint state for infra container %s", ipconfigsRequest.InfraContainerID)
for i := range podIPInfo {
if endpointInfo, ok := service.EndpointState[ipconfigsRequest.InfraContainerID]; ok {
logger.Warnf("[updateEndpointState] Found existing endpoint state for infra container %s", ipconfigsRequest.InfraContainerID)
ip := net.ParseIP(podIPInfo[i].PodIPConfig.IPAddress)
if ip == nil {
logger.Errorf("failed to parse pod ip address %s", podIPInfo[i].PodIPConfig.IPAddress)
return errParsePodIPFailed
}
endpointInfo.IfnameToIPMap[ipconfigRequest.Ifname].IPv6 = append(endpointInfo.IfnameToIPMap[ipconfigRequest.Ifname].IPv6, ipconfig)
} else {
ipconfig := net.IPNet{IP: ip, Mask: net.CIDRMask(int(podIPInfo.PodIPConfig.PrefixLength), 32)} // nolint
for _, ipconf := range endpointInfo.IfnameToIPMap[ipconfigRequest.Ifname].IPv4 {
if ipconf.IP.Equal(ipconfig.IP) {
logger.Printf("[updateEndpointState] Found existing ipv4 ipconfig for infra container %s", ipconfigRequest.InfraContainerID)
return nil
if ip.To4() == nil { // is an ipv6 address
ipconfig := net.IPNet{IP: ip, Mask: net.CIDRMask(int(podIPInfo[i].PodIPConfig.PrefixLength), 128)} // nolint
for _, ipconf := range endpointInfo.IfnameToIPMap[ipconfigsRequest.Ifname].IPv6 {
if ipconf.IP.Equal(ipconfig.IP) {
logger.Printf("[updateEndpointState] Found existing ipv6 ipconfig for infra container %s", ipconfigsRequest.InfraContainerID)
return nil
}
}
endpointInfo.IfnameToIPMap[ipconfigsRequest.Ifname].IPv6 = append(endpointInfo.IfnameToIPMap[ipconfigsRequest.Ifname].IPv6, ipconfig)
} else {
ipconfig := net.IPNet{IP: ip, Mask: net.CIDRMask(int(podIPInfo[i].PodIPConfig.PrefixLength), 32)} // nolint
for _, ipconf := range endpointInfo.IfnameToIPMap[ipconfigsRequest.Ifname].IPv4 {
if ipconf.IP.Equal(ipconfig.IP) {
logger.Printf("[updateEndpointState] Found existing ipv4 ipconfig for infra container %s", ipconfigsRequest.InfraContainerID)
return nil
}
}
endpointInfo.IfnameToIPMap[ipconfigsRequest.Ifname].IPv4 = append(endpointInfo.IfnameToIPMap[ipconfigsRequest.Ifname].IPv4, ipconfig)
}
endpointInfo.IfnameToIPMap[ipconfigRequest.Ifname].IPv4 = append(endpointInfo.IfnameToIPMap[ipconfigRequest.Ifname].IPv4, ipconfig)
}
service.EndpointState[ipconfigRequest.InfraContainerID] = endpointInfo
} else {
endpointInfo := &EndpointInfo{PodName: podInfo.Name(), PodNamespace: podInfo.Namespace(), IfnameToIPMap: make(map[string]*IPInfo)}
ip := net.ParseIP(podIPInfo.PodIPConfig.IPAddress)
if ip == nil {
logger.Errorf("failed to parse pod ip address %s", podIPInfo.PodIPConfig.IPAddress)
return errParsePodIPFailed
}
ipInfo := &IPInfo{}
if ip.To4() == nil { // is an ipv6 address
ipconfig := net.IPNet{IP: ip, Mask: net.CIDRMask(int(podIPInfo.PodIPConfig.PrefixLength), 128)} // nolint
ipInfo.IPv6 = append(ipInfo.IPv6, ipconfig)
service.EndpointState[ipconfigsRequest.InfraContainerID] = endpointInfo
} else {
ipconfig := net.IPNet{IP: ip, Mask: net.CIDRMask(int(podIPInfo.PodIPConfig.PrefixLength), 32)} // nolint
ipInfo.IPv4 = append(ipInfo.IPv4, ipconfig)
endpointInfo := &EndpointInfo{PodName: podInfo.Name(), PodNamespace: podInfo.Namespace(), IfnameToIPMap: make(map[string]*IPInfo)}
ip := net.ParseIP(podIPInfo[i].PodIPConfig.IPAddress)
if ip == nil {
logger.Errorf("failed to parse pod ip address %s", podIPInfo[i].PodIPConfig.IPAddress)
return errParsePodIPFailed
}
ipInfo := &IPInfo{}
if ip.To4() == nil { // is an ipv6 address
ipconfig := net.IPNet{IP: ip, Mask: net.CIDRMask(int(podIPInfo[i].PodIPConfig.PrefixLength), 128)} // nolint
ipInfo.IPv6 = append(ipInfo.IPv6, ipconfig)
} else {
ipconfig := net.IPNet{IP: ip, Mask: net.CIDRMask(int(podIPInfo[i].PodIPConfig.PrefixLength), 32)} // nolint
ipInfo.IPv4 = append(ipInfo.IPv4, ipconfig)
}
endpointInfo.IfnameToIPMap[ipconfigsRequest.Ifname] = ipInfo
service.EndpointState[ipconfigsRequest.InfraContainerID] = endpointInfo
}
endpointInfo.IfnameToIPMap[ipconfigRequest.Ifname] = ipInfo
service.EndpointState[ipconfigRequest.InfraContainerID] = endpointInfo
}
err := service.EndpointStateStore.Write(EndpointStoreKey, service.EndpointState)
if err != nil {
return fmt.Errorf("failed to write endpoint state to store: %w", err)
err := service.EndpointStateStore.Write(EndpointStoreKey, service.EndpointState)
if err != nil {
return fmt.Errorf("failed to write endpoint state to store: %w", err)
}
}
return nil
}
// releaseIPConfigHandlerHelper validates the request and removes the endpoint associated with the pod
func (service *HTTPRestService) releaseIPConfigHandlerHelper(ipconfigsRequest cns.IPConfigsRequest) (*cns.Response, error) {
podInfo, returnCode, returnMessage := service.validateIPConfigsRequest(ipconfigsRequest)
if returnCode != types.Success {
return &cns.Response{
ReturnCode: returnCode,
Message: returnMessage,
}, fmt.Errorf("failed to validate ip config request") //nolint:goerr113 // return error
}
// Check if http rest service managed endpoint state is set
if service.Options[common.OptManageEndpointState] == true {
if err := service.removeEndpointState(podInfo); err != nil {
resp := &cns.Response{
ReturnCode: types.UnexpectedError,
Message: err.Error(),
}
return resp, fmt.Errorf("releaseIPConfigHandlerHelper remove endpoint state failed because %v, release IP config info %s", resp.Message, ipconfigsRequest) //nolint:goerr113 // return error
}
}
if err := service.releaseIPConfigs(podInfo); err != nil {
return &cns.Response{
ReturnCode: types.UnexpectedError,
Message: err.Error(),
}, fmt.Errorf("releaseIPConfigHandlerHelper releaseIPConfigs failed because %v, release IP config info %s", returnMessage, ipconfigsRequest) //nolint:goerr113 // return error
}
return &cns.Response{
ReturnCode: types.Success,
Message: "",
}, nil
}
// releaseIPConfigHandler frees the IP assigned to a pod from CNS
func (service *HTTPRestService) releaseIPConfigHandler(w http.ResponseWriter, r *http.Request) {
var req cns.IPConfigRequest
err := service.Listener.Decode(w, r, &req)
logger.Request(service.Name+"releaseIPConfigHandler", req, err)
var ipconfigRequest cns.IPConfigRequest
err := service.Listener.Decode(w, r, &ipconfigRequest)
logger.Request(service.Name+"releaseIPConfigHandler", ipconfigRequest, err)
if err != nil {
resp := cns.Response{
ReturnCode: types.UnexpectedError,
Message: err.Error(),
}
logger.Errorf("releaseIPConfigHandler decode failed becase %v, release IP config info %s", resp.Message, req)
logger.Errorf("releaseIPConfigHandler decode failed becase %v, release IP config info %s", resp.Message, ipconfigRequest)
w.Header().Set(cnsReturnCode, resp.ReturnCode.String())
err = service.Listener.Encode(w, &resp)
logger.ResponseEx(service.Name, req, resp, resp.ReturnCode, err)
logger.ResponseEx(service.Name, ipconfigRequest, resp, resp.ReturnCode, err)
return
}
podInfo, returnCode, message := service.validateIPConfigRequest(req)
// Check if http rest service managed endpoint state is set
if service.Options[common.OptManageEndpointState] == true {
if err = service.removeEndpointState(podInfo); err != nil {
resp := cns.Response{
ReturnCode: types.UnexpectedError,
Message: err.Error(),
}
logger.Errorf("releaseIPConfigHandler remove endpoint state failed because %v, release IP config info %s", resp.Message, req)
w.Header().Set(cnsReturnCode, resp.ReturnCode.String())
err = service.Listener.Encode(w, &resp)
logger.ResponseEx(service.Name, req, resp, resp.ReturnCode, err)
return
// check to make sure there aren't multiple NCs
if len(service.state.ContainerStatus) > 1 {
reserveResp := &cns.IPConfigResponse{
Response: cns.Response{
ReturnCode: types.InvalidRequest,
Message: fmt.Sprintf("Called API that can only return 1 IP when expecting %d", len(service.state.ContainerStatus)),
},
}
w.Header().Set(cnsReturnCode, reserveResp.Response.ReturnCode.String())
err = service.Listener.Encode(w, &reserveResp)
logger.ResponseEx(service.Name, ipconfigRequest, reserveResp, reserveResp.Response.ReturnCode, err)
return
}
if err = service.releaseIPConfig(podInfo); err != nil {
returnCode = types.UnexpectedError
message = err.Error()
logger.Errorf("releaseIPConfigHandler releaseIPConfig failed because %v, release IP config info %s", message, req)
ipconfigsRequest := cns.IPConfigsRequest{
DesiredIPAddresses: []string{
ipconfigRequest.DesiredIPAddress,
},
PodInterfaceID: ipconfigRequest.PodInterfaceID,
InfraContainerID: ipconfigRequest.InfraContainerID,
OrchestratorContext: ipconfigRequest.OrchestratorContext,
Ifname: ipconfigRequest.Ifname,
}
resp := cns.Response{
ReturnCode: returnCode,
Message: message,
resp, err := service.releaseIPConfigHandlerHelper(ipconfigsRequest)
if err != nil {
w.Header().Set(cnsReturnCode, resp.ReturnCode.String())
err = service.Listener.Encode(w, &resp)
logger.ResponseEx(service.Name, ipconfigRequest, resp, resp.ReturnCode, err)
}
w.Header().Set(cnsReturnCode, resp.ReturnCode.String())
err = service.Listener.Encode(w, &resp)
logger.ResponseEx(service.Name, req, resp, resp.ReturnCode, err)
logger.ResponseEx(service.Name, ipconfigRequest, resp, resp.ReturnCode, err)
}
// releaseIPConfigsHandler frees multiple IPConfigs from the CNS state
func (service *HTTPRestService) releaseIPConfigsHandler(w http.ResponseWriter, r *http.Request) {
var ipconfigsRequest cns.IPConfigsRequest
err := service.Listener.Decode(w, r, &ipconfigsRequest)
logger.Request(service.Name+"releaseIPConfigsHandler", ipconfigsRequest, err)
if err != nil {
resp := cns.Response{
ReturnCode: types.UnexpectedError,
Message: err.Error(),
}
logger.Errorf("releaseIPConfigsHandler decode failed because %v, release IP config info %s", resp.Message, ipconfigsRequest)
w.Header().Set(cnsReturnCode, resp.ReturnCode.String())
err = service.Listener.Encode(w, &resp)
logger.ResponseEx(service.Name, ipconfigsRequest, resp, resp.ReturnCode, err)
return
}
resp, err := service.releaseIPConfigHandlerHelper(ipconfigsRequest)
if err != nil {
w.Header().Set(cnsReturnCode, resp.ReturnCode.String())
err = service.Listener.Encode(w, &resp)
logger.ResponseEx(service.Name, ipconfigsRequest, resp, resp.ReturnCode, err)
}
w.Header().Set(cnsReturnCode, resp.ReturnCode.String())
err = service.Listener.Encode(w, &resp)
logger.ResponseEx(service.Name, ipconfigsRequest, resp, resp.ReturnCode, err)
}
func (service *HTTPRestService) removeEndpointState(podInfo cns.PodInfo) error {
@ -271,6 +406,7 @@ func (service *HTTPRestService) MarkIPAsPendingRelease(totalIpsToRelease int) (m
return pendingReleasedIps, nil
}
// TODO: Add a change so that we should only update the current state if it is different than the new state
func (service *HTTPRestService) updateIPConfigState(ipID string, updatedState types.IPState, podInfo cns.PodInfo) (cns.IPConfigurationStatus, error) {
if ipConfig, found := service.PodIPConfigState[ipID]; found {
logger.Printf("[updateIPConfigState] Changing IpId [%s] state to [%s], podInfo [%+v]. Current config [%+v]", ipID, updatedState, podInfo, ipConfig)
@ -412,7 +548,12 @@ func (service *HTTPRestService) assignIPConfig(ipconfig cns.IPConfigurationStatu
return err
}
service.PodIPIDByPodInterfaceKey[podInfo.Key()] = ipconfig.ID
if service.PodIPIDByPodInterfaceKey[podInfo.Key()] == nil {
logger.Printf("IP config %v initialized", podInfo.Key())
service.PodIPIDByPodInterfaceKey[podInfo.Key()] = make([]string, 0)
}
service.PodIPIDByPodInterfaceKey[podInfo.Key()] = append(service.PodIPIDByPodInterfaceKey[podInfo.Key()], ipconfig.ID)
return nil
}
@ -431,29 +572,49 @@ func (service *HTTPRestService) unassignIPConfig(ipconfig cns.IPConfigurationSta
// Todo - CNI should also pass the IPAddress which needs to be released to validate if that is the right IP allcoated
// in the first place.
func (service *HTTPRestService) releaseIPConfig(podInfo cns.PodInfo) error {
func (service *HTTPRestService) releaseIPConfigs(podInfo cns.PodInfo) error {
service.Lock()
defer service.Unlock()
ipsToBeReleased := make([]cns.IPConfigurationStatus, 0)
ipID := service.PodIPIDByPodInterfaceKey[podInfo.Key()]
if ipID != "" {
if ipconfig, isExist := service.PodIPConfigState[ipID]; isExist {
logger.Printf("[releaseIPConfig] Releasing IP %+v for pod %+v", ipconfig.IPAddress, podInfo)
_, err := service.unassignIPConfig(ipconfig, podInfo)
if err != nil {
return fmt.Errorf("[releaseIPConfig] failed to mark IPConfig [%+v] as Available. err: %v", ipconfig, err)
for i, ipID := range service.PodIPIDByPodInterfaceKey[podInfo.Key()] {
if ipID != "" {
if ipconfig, isExist := service.PodIPConfigState[ipID]; isExist {
ipsToBeReleased = append(ipsToBeReleased, ipconfig)
} else {
//nolint:goerr113 // return error
return fmt.Errorf("[releaseIPConfigs] Failed to get ipconfig %+v and pod info is %+v. Pod to IPID exists, but IPID to IPConfig doesn't exist, CNS State potentially corrupt",
ipconfig.IPAddress, podInfo)
}
logger.Printf("[releaseIPConfig] Released IP %+v for pod %+v", ipconfig.IPAddress, podInfo)
} else {
logger.Errorf("[releaseIPConfig] Failed to get release ipconfig %+v and pod info is %+v. Pod to IPID exists, but IPID to IPConfig doesn't exist, CNS State potentially corrupt",
ipconfig.IPAddress, podInfo)
return fmt.Errorf("[releaseIPConfig] releaseIPConfig failed. IPconfig %+v and pod info is %+v. Pod to IPID exists, but IPID to IPConfig doesn't exist, CNS State potentially corrupt",
ipconfig.IPAddress, podInfo)
logger.Errorf("[releaseIPConfigs] releaseIPConfigs could not find ipID at index %d for pod [%+v]", i, podInfo)
}
} else {
logger.Errorf("[releaseIPConfig] SetIPConfigAsAvailable ignoring request to release, no allocation found for pod [%+v]", podInfo)
return nil
}
failedToReleaseIP := false
for _, ip := range ipsToBeReleased { //nolint:gocritic // ignore copy
logger.Printf("[releaseIPConfigs] Releasing IP %s for pod %+v", ip.IPAddress, podInfo)
if _, err := service.unassignIPConfig(ip, podInfo); err != nil {
logger.Errorf("[releaseIPConfigs] Failed to release IP %s for pod %+v error: %+v", ip.IPAddress, podInfo, err)
failedToReleaseIP = true
break
}
logger.Printf("[releaseIPConfigs] Released IP %s for pod %+v", ip.IPAddress, podInfo)
}
if failedToReleaseIP {
// reassigns all of the released IPs if we aren't able to release all of them
for _, ip := range ipsToBeReleased { //nolint:gocritic // ignore copy
if err := service.assignIPConfig(ip, podInfo); err != nil {
logger.Errorf("[releaseIPConfigs] failed to mark IPConfig [%+v] back to Assigned. err: %v", ip, err)
}
}
//nolint:goerr113 // return error
return fmt.Errorf("[releaseIPConfigs] Failed to release one or more IPs. Not releasing any IPs for pod %+v", podInfo)
}
logger.Printf("[releaseIPConfigs] Successfully released all IPs for pod %+v", podInfo)
return nil
}
@ -478,101 +639,228 @@ func (service *HTTPRestService) MarkExistingIPsAsPendingRelease(pendingIPIDs []s
return nil
}
func (service *HTTPRestService) GetExistingIPConfig(podInfo cns.PodInfo) (cns.PodIpInfo, bool, error) {
var (
podIpInfo cns.PodIpInfo
isExist bool
)
// Returns the current IP configs for a pod if they exist
func (service *HTTPRestService) GetExistingIPConfig(podInfo cns.PodInfo) ([]cns.PodIpInfo, bool, error) {
service.RLock()
defer service.RUnlock()
ipID := service.PodIPIDByPodInterfaceKey[podInfo.Key()]
if ipID != "" {
if ipState, isExist := service.PodIPConfigState[ipID]; isExist {
err := service.populateIPConfigInfoUntransacted(ipState, &podIpInfo)
return podIpInfo, isExist, err
}
numIPConfigs := len(service.PodIPIDByPodInterfaceKey[podInfo.Key()])
podIPInfo := make([]cns.PodIpInfo, numIPConfigs)
ipConfigExists := false
logger.Errorf("Failed to get existing ipconfig. Pod to IPID exists, but IPID to IPConfig doesn't exist, CNS State potentially corrupt")
return podIpInfo, isExist, fmt.Errorf("Failed to get existing ipconfig. Pod to IPID exists, but IPID to IPConfig doesn't exist, CNS State potentially corrupt")
}
return podIpInfo, isExist, nil
}
func (service *HTTPRestService) AssignDesiredIPConfig(podInfo cns.PodInfo, desiredIPAddress string) (cns.PodIpInfo, error) {
var podIpInfo cns.PodIpInfo
service.Lock()
defer service.Unlock()
for _, ipConfig := range service.PodIPConfigState {
if ipConfig.IPAddress == desiredIPAddress {
switch ipConfig.GetState() { //nolint:exhaustive // ignoring PendingRelease case intentionally
case types.Assigned:
// This IP has already been assigned, if it is assigned to same pod, then return the same
// IPconfiguration
if ipConfig.PodInfo.Key() == podInfo.Key() {
logger.Printf("[AssignDesiredIPConfig]: IP Config [%+v] is already assigned to this Pod [%+v]", ipConfig, podInfo)
} else {
return podIpInfo, errors.Errorf("[AssignDesiredIPConfig] Desired IP is already assigned %+v, requested for pod %+v", ipConfig, podInfo)
for i, ipID := range service.PodIPIDByPodInterfaceKey[podInfo.Key()] {
if ipID != "" {
if ipState, isExist := service.PodIPConfigState[ipID]; isExist {
if err := service.populateIPConfigInfoUntransacted(ipState, &podIPInfo[i]); err != nil {
return podIPInfo, isExist, err
}
case types.Available, types.PendingProgramming:
// This race can happen during restart, where CNS state is lost and thus we have lost the NC programmed version
// As part of reconcile, we mark IPs as Assigned which are already assigned to Pods (listed from APIServer)
if err := service.assignIPConfig(ipConfig, podInfo); err != nil {
return podIpInfo, err
}
default:
return podIpInfo, errors.Errorf("[AllocateDesiredIPConfig] Desired IP is not available %+v", ipConfig)
ipConfigExists = true
} else {
errMsg := fmt.Sprintf("Failed to get existing ipconfig for pod %+v. Pod to IPID exists, but IPID to IPConfig doesn't exist, CNS State potentially corrupt", podInfo)
logger.Errorf(errMsg)
return podIPInfo, false, errors.New(errMsg)
}
err := service.populateIPConfigInfoUntransacted(ipConfig, &podIpInfo)
return podIpInfo, err
}
}
return podIpInfo, fmt.Errorf("Requested IP not found in pool")
return podIPInfo, ipConfigExists, nil
}
func (service *HTTPRestService) AssignAnyAvailableIPConfig(podInfo cns.PodInfo) (cns.PodIpInfo, error) {
// Assigns a pod with all IPs desired
func (service *HTTPRestService) AssignDesiredIPConfigs(podInfo cns.PodInfo, desiredIPAddresses []string) ([]cns.PodIpInfo, error) {
numDesiredIPAddresses := len(desiredIPAddresses)
podIPInfo := make([]cns.PodIpInfo, numDesiredIPAddresses)
// creating a map for the loop to check to see if the IP in the pool is one of the desired IPs
desiredIPMap := make(map[string]struct{})
// slice to keep track of IP configs to assign
ipConfigsToAssign := make([]cns.IPConfigurationStatus, 0)
service.Lock()
defer service.Unlock()
for _, desiredIP := range desiredIPAddresses {
desiredIPMap[desiredIP] = struct{}{}
}
numIPConfigsAssigned := 0
for _, ipConfig := range service.PodIPConfigState { //nolint:gocritic // ignore copy
_, found := desiredIPMap[ipConfig.IPAddress]
// keep searching until the all the desired IPs are found
if !found {
continue
}
switch ipConfig.GetState() { //nolint:exhaustive // ignoring PendingRelease case intentionally
case types.Assigned:
// This IP has already been assigned, if it is assigned to same pod add the IP to podIPInfo
if ipConfig.PodInfo.Key() == podInfo.Key() {
logger.Printf("[AssignDesiredIPConfigs]: IP Config [%+v] is already assigned to this Pod [%+v]", ipConfig, podInfo)
if err := service.populateIPConfigInfoUntransacted(ipConfig, &podIPInfo[numIPConfigsAssigned]); err != nil {
//nolint:goerr113 // return error
return []cns.PodIpInfo{}, fmt.Errorf("[AssignDesiredIPConfigs] Failed to assign IP %+v requested for pod %+v since the IP is already assigned to %+v", ipConfig, podInfo, ipConfig.PodInfo)
}
numIPConfigsAssigned++
} else {
//nolint:goerr113 // return error
return []cns.PodIpInfo{}, fmt.Errorf("[AssignDesiredIPConfigs] Desired IP is already assigned %+v, requested for pod %+v", ipConfig, podInfo)
}
case types.Available, types.PendingProgramming:
// This race can happen during restart, where CNS state is lost and thus we have lost the NC programmed version
// As part of reconcile, we mark IPs as Assigned which are already assigned to Pods (listed from APIServer)
ipConfigsToAssign = append(ipConfigsToAssign, ipConfig)
default:
logger.Errorf("[AssignDesiredIPConfigs] Desired IP is not available %+v", ipConfig)
//nolint:goerr113 // return error
return podIPInfo, fmt.Errorf("IP not available")
}
// checks if found all of the desired IPs either as an available IP or already assigned to the pod
if len(ipConfigsToAssign)+numIPConfigsAssigned == numDesiredIPAddresses {
break
}
}
// if we did not find all of the desired IPs return an error
if len(ipConfigsToAssign)+numIPConfigsAssigned != numDesiredIPAddresses {
//nolint:goerr113 // return error
return podIPInfo, fmt.Errorf("not enough desired IPs found in pool")
}
failedToAssignIP := false
// assigns all IPs that were found as available to the pod
for i := range ipConfigsToAssign {
if err := service.assignIPConfig(ipConfigsToAssign[i], podInfo); err != nil {
logger.Errorf(err.Error())
failedToAssignIP = true
break
}
if err := service.populateIPConfigInfoUntransacted(ipConfigsToAssign[i], &podIPInfo[numIPConfigsAssigned]); err != nil {
logger.Errorf(err.Error())
failedToAssignIP = true
break
}
// adds to the newly assigned IP to the counter
numIPConfigsAssigned++
}
// if we were able to get at least one IP but not all of the desired IPs
if failedToAssignIP {
logger.Printf("[AssignDesiredIPConfigs] Failed to retrieve all desired IPs. Releasing all IPs that were found")
for i := range ipConfigsToAssign {
_, err := service.unassignIPConfig(ipConfigsToAssign[i], podInfo)
if err != nil {
logger.Errorf("[AssignDesiredIPConfigs] failed to mark IPConfig [%+v] back to Available. err: %v", ipConfigsToAssign[i], err)
}
}
//nolint:goerr113 // return error
return podIPInfo, fmt.Errorf("not all requested ips %v were found/available in the pool", desiredIPAddresses)
}
logger.Printf("[AssignDesiredIPConfigs] Successfully assigned all desired IPs for pod %+v", podInfo)
return podIPInfo, nil
}
// Assigns an available IP from each NC on the NNC. If there is one NC then we expect to only have one IP return
// In the case of dualstack we would expect to have one IPv6 from one NC and one IPv4 from a second NC
func (service *HTTPRestService) AssignAvailableIPConfigs(podInfo cns.PodInfo) ([]cns.PodIpInfo, error) {
service.Lock()
defer service.Unlock()
// Sets the number of IPs needed equal to the number of NCs so that we can get one IP per NC
numIPsNeeded := len(service.state.ContainerStatus)
// Creates a slice of PodIpInfo with the size as number of NCs to hold the result for assigned IP configs
podIPInfo := make([]cns.PodIpInfo, numIPsNeeded)
// This map is used to store whether or not we have found an available IP from an NC when looping through the pool
ipsToAssign := make(map[string]cns.IPConfigurationStatus)
// Searches for available IPs in the pool
for _, ipState := range service.PodIPConfigState {
if ipState.GetState() == types.Available {
if err := service.assignIPConfig(ipState, podInfo); err != nil {
return cns.PodIpInfo{}, err
}
podIPInfo := cns.PodIpInfo{}
if err := service.populateIPConfigInfoUntransacted(ipState, &podIPInfo); err != nil {
return cns.PodIpInfo{}, err
}
return podIPInfo, nil
// check if an IP from this NC is already set side for assignment.
_, ncAlreadyMarkedForAssignment := ipsToAssign[ipState.NCID]
// Checks if we haven't already found an IP from that NC and checks if the current IP is available
if ncAlreadyMarkedForAssignment || ipState.GetState() != types.Available {
continue
}
ipsToAssign[ipState.NCID] = ipState
// Once one IP per container is found break out of the loop and stop searching
if len(ipsToAssign) == numIPsNeeded {
break
}
}
//nolint:goerr113
return cns.PodIpInfo{}, fmt.Errorf("no IPs available, waiting on Azure CNS to allocate more")
// Checks to make sure we found one IP for each NC
if len(ipsToAssign) != numIPsNeeded {
//nolint:goerr113 // return error
return podIPInfo, fmt.Errorf("not enough IPs available, waiting on Azure CNS to allocate more")
}
failedToAssignIP := false
numIPConfigsAssigned := 0
// assigns all IPs in the map to the pod
for _, ip := range ipsToAssign { //nolint:gocritic // ignore copy
if err := service.assignIPConfig(ip, podInfo); err != nil {
logger.Errorf(err.Error())
failedToAssignIP = true
break
}
if err := service.populateIPConfigInfoUntransacted(ip, &podIPInfo[numIPConfigsAssigned]); err != nil {
logger.Errorf(err.Error())
failedToAssignIP = true
break
}
numIPConfigsAssigned++
}
// if we were able to find at least one IP but not enough
if failedToAssignIP {
logger.Printf("[AssignAvailableIPConfigs] failed to assign enough IPs. Releasing all IPs that were found")
for _, ipState := range ipsToAssign { //nolint:gocritic // ignore copy
_, err := service.unassignIPConfig(ipState, podInfo)
if err != nil {
logger.Errorf("[AssignAvailableIPConfigs] failed to mark IPConfig [%+v] back to Available. err: %v", ipState, err)
}
}
//nolint:goerr113 // return error
return podIPInfo, fmt.Errorf("not enough IPs available, waiting on Azure CNS to allocate more")
}
logger.Printf("[AssignDesiredIPConfigs] Successfully assigned IPs for pod %+v", podInfo)
return podIPInfo, nil
}
// If IPConfig is already assigned to pod, it returns that else it returns one of the available ipconfigs.
func requestIPConfigHelper(service *HTTPRestService, req cns.IPConfigRequest) (cns.PodIpInfo, error) {
// check if ipconfig already assigned tothis pod and return if exists or error
// If IPConfigs are already assigned to the pod, it returns that else it returns the available ipconfigs.
func requestIPConfigsHelper(service *HTTPRestService, req cns.IPConfigsRequest) ([]cns.PodIpInfo, error) {
// check if ipconfigs already assigned to this pod and return if exists or error
// if error, ipstate is nil, if exists, ipstate is not nil and error is nil
podInfo, err := cns.NewPodInfoFromIPConfigRequest(req)
podInfo, err := cns.NewPodInfoFromIPConfigsRequest(req)
if err != nil {
return cns.PodIpInfo{}, errors.Wrapf(err, "failed to parse IPConfigRequest %v", req)
return []cns.PodIpInfo{}, errors.Wrapf(err, "failed to parse IPConfigsRequest %v", req)
}
if podIPInfo, isExist, err := service.GetExistingIPConfig(podInfo); err != nil || isExist {
return podIPInfo, err
}
// return desired IPConfig
if req.DesiredIPAddress != "" {
return service.AssignDesiredIPConfig(podInfo, req.DesiredIPAddress)
// if the desired IP configs are not specified, assign any free IPConfigs
if len(req.DesiredIPAddresses) == 0 {
return service.AssignAvailableIPConfigs(podInfo)
}
// return any free IPConfig
return service.AssignAnyAvailableIPConfig(podInfo)
if err := validateDesiredIPAddresses(req.DesiredIPAddresses); err != nil {
return []cns.PodIpInfo{}, err
}
return service.AssignDesiredIPConfigs(podInfo, req.DesiredIPAddresses)
}
// checks all desired IPs for a request to make sure they are all valid
func validateDesiredIPAddresses(desiredIPs []string) error {
for _, desiredIP := range desiredIPs {
ip := net.ParseIP(desiredIP)
if ip.To4() == nil && ip.To16() == nil {
//nolint:goerr113 // return error
return fmt.Errorf("[validateDesiredIPAddresses] invalid ip %s specified as desired IP", desiredIP)
}
}
return nil
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -60,7 +60,7 @@ type HTTPRestService struct {
wsproxy wireserverProxy
homeAzMonitor *HomeAzMonitor
networkContainer *networkcontainers.NetworkContainers
PodIPIDByPodInterfaceKey map[string]string // PodInterfaceId is key and value is Pod IP (SecondaryIP) uuid.
PodIPIDByPodInterfaceKey map[string][]string // PodInterfaceId is key and value is slice of Pod IP (SecondaryIP) uuids.
PodIPConfigState map[string]cns.IPConfigurationStatus // Secondary IP ID(uuid) is key
IPAMPoolMonitor cns.IPAMPoolMonitor
routingTable *routes.RoutingTable
@ -108,7 +108,7 @@ type GetHTTPServiceDataResponse struct {
// HTTPRestServiceData represents in-memory CNS data in the debug API paths.
type HTTPRestServiceData struct {
PodIPIDByPodInterfaceKey map[string]string // PodInterfaceId is key and value is Pod IP uuid.
PodIPIDByPodInterfaceKey map[string][]string // PodInterfaceId is key and value is slice of Pod IP uuids.
PodIPConfigState map[string]cns.IPConfigurationStatus // secondaryipid(uuid) is key
IPAMPoolMonitor cns.IpamPoolMonitorStateSnapshot
}
@ -184,7 +184,7 @@ func NewHTTPRestService(config *common.ServiceConfig, wscli interfaceGetter, wsp
primaryInterface: primaryInterface,
}
podIPIDByPodInterfaceKey := make(map[string]string)
podIPIDByPodInterfaceKey := make(map[string][]string)
podIPConfigState := make(map[string]cns.IPConfigurationStatus)
if gen == nil {
@ -254,7 +254,9 @@ func (service *HTTPRestService) Init(config *common.ServiceConfig) error {
listener.AddHandler(cns.PublishNetworkContainer, service.publishNetworkContainer)
listener.AddHandler(cns.UnpublishNetworkContainer, service.unpublishNetworkContainer)
listener.AddHandler(cns.RequestIPConfig, newHandlerFuncWithHistogram(service.requestIPConfigHandler, httpRequestLatency))
listener.AddHandler(cns.RequestIPConfigs, newHandlerFuncWithHistogram(service.requestIPConfigsHandler, httpRequestLatency))
listener.AddHandler(cns.ReleaseIPConfig, newHandlerFuncWithHistogram(service.releaseIPConfigHandler, httpRequestLatency))
listener.AddHandler(cns.ReleaseIPConfigs, newHandlerFuncWithHistogram(service.releaseIPConfigsHandler, httpRequestLatency))
listener.AddHandler(cns.NmAgentSupportedApisPath, service.nmAgentSupportedApisHandler)
listener.AddHandler(cns.PathDebugIPAddresses, service.handleDebugIPAddresses)
listener.AddHandler(cns.PathDebugPodContext, service.handleDebugPodContext)

Просмотреть файл

@ -763,21 +763,21 @@ func (service *HTTPRestService) SendNCSnapShotPeriodically(ctx context.Context,
}
}
func (service *HTTPRestService) validateIPConfigRequest(
ipConfigRequest cns.IPConfigRequest,
func (service *HTTPRestService) validateIPConfigsRequest(
ipConfigsRequest cns.IPConfigsRequest,
) (cns.PodInfo, types.ResponseCode, string) {
if service.state.OrchestratorType != cns.KubernetesCRD && service.state.OrchestratorType != cns.Kubernetes {
return nil, types.UnsupportedOrchestratorType, "ReleaseIPConfig API supported only for kubernetes orchestrator"
}
if ipConfigRequest.OrchestratorContext == nil {
if ipConfigsRequest.OrchestratorContext == nil {
return nil,
types.EmptyOrchestratorContext,
fmt.Sprintf("OrchastratorContext is not set in the req: %+v", ipConfigRequest)
fmt.Sprintf("OrchastratorContext is not set in the req: %+v", ipConfigsRequest)
}
// retrieve podinfo from orchestrator context
podInfo, err := cns.NewPodInfoFromIPConfigRequest(ipConfigRequest)
podInfo, err := cns.NewPodInfoFromIPConfigsRequest(ipConfigsRequest)
if err != nil {
return podInfo, types.UnsupportedOrchestratorContext, err.Error()
}