Skip to content
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
11 changes: 9 additions & 2 deletions cns/NetworkContainerContract.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (
RequestIPConfig = "/network/requestipconfig"
ReleaseIPConfig = "/network/releaseipconfig"
GetIPAddresses = "/debug/getipaddresses"
GetPodIPOrchestratorContext = "/debug/getpodcontext"
)

// NetworkContainer Prefixes
Expand Down Expand Up @@ -244,8 +245,14 @@ type GetIPAddressStateResponse struct {

// GetIPAddressStatusResponse is used in CNS IPAM mode as a response to get IP address, state and Pod info
type GetIPAddressStatusResponse struct {
IPConfigurationStatus[] IPConfigurationStatus
Response Response
IPConfigurationStatus []IPConfigurationStatus
Response Response
}

//GetPodContextResponse is used in CNS Client debug mode to get mapping of Orchestrator Context to Pod IP UUID
type GetPodContextResponse struct {
PodContext map[string]string
Response Response
}

// IPAddressState Only used in the GetIPConfig API to return IP's that match a filter
Expand Down
23 changes: 23 additions & 0 deletions cns/cnsclient/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const (
getAllocatedArg = "Allocated"
getAllArg = "All"
getPendingReleaseArg = "PendingRelease"
getPodCmdArg = "getPodContexts"

releaseArg = "release"

Expand All @@ -28,6 +29,7 @@ const (
var (
availableCmds = []string{
getCmdArg,
getPodCmdArg,
}

getFlags = []string{
Expand All @@ -49,6 +51,8 @@ func HandleCNSClientCommands(cmd, arg string) error {
switch {
case strings.EqualFold(getCmdArg, cmd):
return getCmd(cnsClient, arg)
case strings.EqualFold(getPodCmdArg, cmd):
return getPodCmd(cnsClient)
default:
return fmt.Errorf("No debug cmd supplied, options are: %v", getCmdArg)
}
Expand Down Expand Up @@ -96,3 +100,22 @@ func printIPAddresses(addrSlice []cns.IPConfigurationStatus) {
cns.IPConfigurationStatus.String(addr)
}
}

func getPodCmd(client *CNSClient) error {

resp, err := client.GetPodOrchestratorContext()
if err != nil {
return err
}

printPodContext(resp)
return nil
}

func printPodContext(podContext map[string]string) {
var i = 1
for orchContext, podID := range podContext {
fmt.Println(i, " ", orchContext, " : ", podID)
i++
}
}
39 changes: 39 additions & 0 deletions cns/cnsclient/cnsclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,3 +371,42 @@ func (cnsClient *CNSClient) GetIPAddressesMatchingStates(StateFilter ...string)

return resp.IPConfigurationStatus, err
}

//GetPodOrchestratorContext calls GetPodIpOrchestratorContext API on CNS
func (cnsClient *CNSClient) GetPodOrchestratorContext() (map[string]string, error) {
var (
resp cns.GetPodContextResponse
err error
res *http.Response
)

url := cnsClient.connectionURL + cns.GetPodIPOrchestratorContext
log.Printf("GetPodIPOrchestratorContext url %v", url)

res, err = http.Get(url)
if err != nil {
log.Errorf("[Azure CNSClient] GetPodIPOrchestratorContext HTTP Get returned error %v", err.Error())
return resp.PodContext, err
}

defer res.Body.Close()

if res.StatusCode != http.StatusOK {
errMsg := fmt.Sprintf("[Azure CNSClient] GetPodIPOrchestratorContext invalid http status code: %v", res.StatusCode)
log.Errorf(errMsg)
return resp.PodContext, fmt.Errorf(errMsg)
}

err = json.NewDecoder(res.Body).Decode(&resp)
if err != nil {
log.Errorf("[Azure CNSClient] Error received while parsing GetPodContext response resp:%v err:%v", res.Body, err.Error())
return resp.PodContext, err
}

if resp.Response.ReturnCode != 0 {
log.Errorf("[Azure CNSClient] GetPodContext received error response :%v", resp.Response.Message)
return resp.PodContext, fmt.Errorf(resp.Response.Message)
}

return resp.PodContext, err
}
50 changes: 43 additions & 7 deletions cns/cnsclient/cnsclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,24 +226,60 @@ func TestCNSClientRequestAndRelease(t *testing.T) {
if reflect.DeepEqual(desired, resultIPnet) != true {
t.Fatalf("Desired result not matching actual result, expected: %+v, actual: %+v", desired, resultIPnet)
}
//checking for allocated IP address and pod context printing before ReleaseIPAddress is called
ipaddresses, err := cnsClient.GetIPAddressesMatchingStates(cns.Allocated)
if err != nil {
t.Fatalf("Get allocated IP addresses failed %+v", err)
}

if len(ipaddresses) != 1 {
t.Fatalf("Number of available IP addresses expected to be 1, actual %+v", ipaddresses)
}

if ipaddresses[0].IPAddress != desiredIpAddress && ipaddresses[0].State != cns.Allocated {
t.Fatalf("Available IP address does not match expected, address state: %+v", ipaddresses)
}

t.Log(ipaddresses)

// release requested IP address, expect success
err = cnsClient.ReleaseIPAddress(orchestratorContext)
if err != nil {
t.Fatalf("Expected to not fail when releasing IP reservation found with context: %+v", err)
}
}

ipaddresses, err := cnsClient.GetIPAddressesMatchingStates(cns.Available)
func TestCNSClientPodContextApi(t *testing.T) {
podName := "testpodname"
podNamespace := "testpodnamespace"
desiredIpAddress := "10.0.0.5"

secondaryIps := make([]string, 0)
secondaryIps = append(secondaryIps, desiredIpAddress)
cnsClient, _ := InitCnsClient("")

addTestStateToRestServer(t, secondaryIps)

podInfo := cns.KubernetesPodInfo{PodName: podName, PodNamespace: podNamespace}
orchestratorContext, err := json.Marshal(podInfo)
if err != nil {
t.Fatalf("Get allocated IP addresses failed %+v", err)
t.Fatal(err)
}

if len(ipaddresses) != 1 {
t.Fatalf("Number of available IP addresses expected to be 1, actual %+v", ipaddresses)
// request IP address
_, err = cnsClient.RequestIPAddress(orchestratorContext)
if err != nil {
t.Fatalf("get IP from CNS failed with %+v", err)
}

if ipaddresses[0].IPAddress != desiredIpAddress && ipaddresses[0].State != cns.Available {
t.Fatalf("Available IP address does not match expected, address state: %+v", ipaddresses)
//test for pod ip by orch context map
podcontext, err := cnsClient.GetPodOrchestratorContext()
if err != nil {
t.Errorf("Get pod ip by orchestrator context failed: %+v", err)
}
fmt.Println(ipaddresses)
if len(podcontext) < 1 {
t.Errorf("Expected atleast 1 entry in map for podcontext: %+v", podcontext)
}

t.Log(podcontext)
}
77 changes: 54 additions & 23 deletions cns/restserver/ipam.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,33 +100,33 @@ func (service *HTTPRestService) MarkIPAsPendingRelease(totalIpsToRelease int) (m
defer service.Unlock()

for uuid, existingIpConfig := range service.PodIPConfigState {
if existingIpConfig.State == cns.PendingProgramming {
updatedIpConfig, err := service.updateIPConfigState(uuid, cns.PendingRelease, existingIpConfig.OrchestratorContext)
if err != nil {
return nil, err
}
if existingIpConfig.State == cns.PendingProgramming {
updatedIpConfig, err := service.updateIPConfigState(uuid, cns.PendingRelease, existingIpConfig.OrchestratorContext)
if err != nil {
return nil, err
}

pendingReleasedIps[uuid] = updatedIpConfig
pendingReleasedIps[uuid] = updatedIpConfig
if len(pendingReleasedIps) == totalIpsToRelease {
return pendingReleasedIps, nil
}
}
}
}
// if not all expected IPs are set to PendingRelease, then check the Available IPs

// if not all expected IPs are set to PendingRelease, then check the Available IPs
for uuid, existingIpConfig := range service.PodIPConfigState {
if existingIpConfig.State == cns.Available {
updatedIpConfig, err := service.updateIPConfigState(uuid, cns.PendingRelease, existingIpConfig.OrchestratorContext)
if err != nil {
return nil, err
}

pendingReleasedIps[uuid] = updatedIpConfig
if existingIpConfig.State == cns.Available {
updatedIpConfig, err := service.updateIPConfigState(uuid, cns.PendingRelease, existingIpConfig.OrchestratorContext)
if err != nil {
return nil, err
}

pendingReleasedIps[uuid] = updatedIpConfig

if len(pendingReleasedIps) == totalIpsToRelease {
return pendingReleasedIps, nil
}
}
}
}
}

logger.Printf("[MarkIPAsPendingRelease] Set total ips to PendingRelease %d, expected %d", len(pendingReleasedIps), totalIpsToRelease)
Expand All @@ -140,9 +140,9 @@ func (service *HTTPRestService) updateIPConfigState(ipId string, updatedState st
ipConfig.OrchestratorContext = orchestratorContext
service.PodIPConfigState[ipId] = ipConfig
return ipConfig, nil
}
return cns.IPConfigurationStatus{}, fmt.Errorf("[updateIPConfigState] Failed to update state %s for the IPConfig. ID %s not found PodIPConfigState", updatedState, ipId)
}

return cns.IPConfigurationStatus{}, fmt.Errorf("[updateIPConfigState] Failed to update state %s for the IPConfig. ID %s not found PodIPConfigState", updatedState, ipId)
}

// MarkIpsAsAvailableUntransacted will update pending programming IPs to available if NMAgent side's programmed nc version keep up with nc version.
Expand Down Expand Up @@ -181,6 +181,37 @@ func (service *HTTPRestService) GetPodIPConfigState() map[string]cns.IPConfigura
return service.PodIPConfigState
}

func (service *HTTPRestService) getPodIPIDByOrchestratorContexthandler(w http.ResponseWriter, r *http.Request) {
var (
resp cns.GetPodContextResponse
statusCode int
returnMessage string
err error
)

statusCode = UnexpectedError

defer func() {
if err != nil {
resp.Response.ReturnCode = statusCode
resp.Response.Message = returnMessage
}

err = service.Listener.Encode(w, &resp)
logger.Response(service.Name, resp, resp.Response.ReturnCode, ReturnCodeToString(resp.Response.ReturnCode), err)
}()

resp.PodContext = service.GetPodIPIDByOrchestratorContext()

return
}

func (service *HTTPRestService) GetPodIPIDByOrchestratorContext() map[string]string {
service.RLock()
defer service.RUnlock()
return service.PodIPIDByOrchestratorContext
}

// GetPendingProgramIPConfigs returns list of IPs which are in pending program status
func (service *HTTPRestService) GetPendingProgramIPConfigs() []cns.IPConfigurationStatus {
service.RLock()
Expand Down Expand Up @@ -348,7 +379,7 @@ func (service *HTTPRestService) MarkExistingIPsAsPending(pendingIPIDs []string)
return fmt.Errorf("Failed to mark IP [%v] as pending, currently allocated", id)
}

logger.Printf("[MarkExistingIPsAsPending]: Marking IP [%+v] to PendingRelease", ipconfig)
logger.Printf("[MarkExistingIPsAsPending]: Marking IP [%+v] to PendingRelease", ipconfig)
ipconfig.State = cns.PendingRelease
service.PodIPConfigState[id] = ipconfig
} else {
Expand Down
1 change: 1 addition & 0 deletions cns/restserver/restserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ func (service *HTTPRestService) Init(config *common.ServiceConfig) error {
listener.AddHandler(cns.ReleaseIPConfig, service.releaseIPConfigHandler)
listener.AddHandler(cns.NmAgentSupportedApisPath, service.nmAgentSupportedApisHandler)
listener.AddHandler(cns.GetIPAddresses, service.getIPAddressesHandler)
listener.AddHandler(cns.GetPodIPOrchestratorContext, service.getPodIPIDByOrchestratorContexthandler)

// handlers for v0.2
listener.AddHandler(cns.V2Prefix+cns.SetEnvironmentPath, service.setEnvironment)
Expand Down