Skip to content

Commit 1d3b30b

Browse files
authored
Merge pull request #825 from kmurudi/CNS_Debug_HTTPRestService_Contexts
[CNS] Debug - Expose In-memory data from HTTPRestService
2 parents cf7d8a5 + 9aa3781 commit 1d3b30b

File tree

9 files changed

+265
-16
lines changed

9 files changed

+265
-16
lines changed

cns/NetworkContainerContract.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const (
2424
ReleaseIPConfig = "/network/releaseipconfig"
2525
GetIPAddresses = "/debug/getipaddresses"
2626
GetPodIPOrchestratorContext = "/debug/getpodcontext"
27+
GetHTTPRestData = "/debug/getrestdata"
2728
)
2829

2930
// NetworkContainer Prefixes

cns/api.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,15 @@ type NodeConfiguration struct {
175175
type IPAMPoolMonitor interface {
176176
Start(ctx context.Context, poolMonitorRefreshMilliseconds int) error
177177
Update(scalar nnc.Scaler, spec nnc.NodeNetworkConfigSpec) error
178+
GetStateSnapshot() IpamPoolMonitorStateSnapshot
179+
}
180+
181+
//struct to expose state values for IPAMPoolMonitor struct
182+
type IpamPoolMonitorStateSnapshot struct {
183+
MinimumFreeIps int64
184+
MaximumFreeIps int64
185+
UpdatingIpsNotInUseCount int
186+
CachedNNC nnc.NodeNetworkConfig
178187
}
179188

180189
// Response describes generic response from CNS.

cns/cnsclient/cli.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/Azure/azure-container-networking/cns"
10+
"github.com/Azure/azure-container-networking/cns/restserver"
1011
)
1112

1213
const (
@@ -16,6 +17,7 @@ const (
1617
getAllArg = "All"
1718
getPendingReleaseArg = "PendingRelease"
1819
getPodCmdArg = "getPodContexts"
20+
getInMemoryData = "getInMemory"
1921

2022
releaseArg = "release"
2123

@@ -30,6 +32,7 @@ var (
3032
availableCmds = []string{
3133
getCmdArg,
3234
getPodCmdArg,
35+
getInMemoryData,
3336
}
3437

3538
getFlags = []string{
@@ -53,6 +56,8 @@ func HandleCNSClientCommands(cmd, arg string) error {
5356
return getCmd(cnsClient, arg)
5457
case strings.EqualFold(getPodCmdArg, cmd):
5558
return getPodCmd(cnsClient)
59+
case strings.EqualFold(getInMemoryData, cmd):
60+
return getInMemory(cnsClient)
5661
default:
5762
return fmt.Errorf("No debug cmd supplied, options are: %v", getCmdArg)
5863
}
@@ -119,3 +124,20 @@ func printPodContext(podContext map[string]string) {
119124
i++
120125
}
121126
}
127+
128+
func getInMemory(client *CNSClient) error {
129+
130+
inmemoryData, err := client.GetHTTPServiceData()
131+
if err != nil {
132+
return err
133+
}
134+
135+
printInMemoryStruct(inmemoryData.HttpRestServiceData)
136+
return nil
137+
}
138+
139+
func printInMemoryStruct(data restserver.HttpRestServiceData) {
140+
fmt.Println("PodIPIDByOrchestratorContext: ", data.PodIPIDByOrchestratorContext)
141+
fmt.Println("PodIPConfigState: ", data.PodIPConfigState)
142+
fmt.Println("IPAMPoolMonitor: ", data.IPAMPoolMonitor)
143+
}

cns/cnsclient/cnsclient.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,3 +410,42 @@ func (cnsClient *CNSClient) GetPodOrchestratorContext() (map[string]string, erro
410410

411411
return resp.PodContext, err
412412
}
413+
414+
//GetHTTPServiceData gets all public in-memory struct details for debugging purpose
415+
func (cnsClient *CNSClient) GetHTTPServiceData() (restserver.GetHTTPServiceDataResponse, error) {
416+
var (
417+
resp restserver.GetHTTPServiceDataResponse
418+
err error
419+
res *http.Response
420+
)
421+
422+
url := cnsClient.connectionURL + cns.GetHTTPRestData
423+
log.Printf("GetHTTPServiceStruct url %v", url)
424+
425+
res, err = http.Get(url)
426+
if err != nil {
427+
log.Errorf("[Azure CNSClient] HTTP Get returned error %v", err.Error())
428+
return resp, err
429+
}
430+
431+
defer res.Body.Close()
432+
433+
if res.StatusCode != http.StatusOK {
434+
errMsg := fmt.Sprintf("[Azure CNSClient] GetHTTPServiceStruct invalid http status code: %v", res.StatusCode)
435+
log.Errorf(errMsg)
436+
return resp, fmt.Errorf(errMsg)
437+
}
438+
439+
err = json.NewDecoder(res.Body).Decode(&resp)
440+
if err != nil {
441+
log.Errorf("[Azure CNSClient] Error received while parsing GetHTTPServiceStruct response resp:%v err:%v", res.Body, err.Error())
442+
return resp, err
443+
}
444+
445+
if resp.Response.ReturnCode != 0 {
446+
log.Errorf("[Azure CNSClient] GetTTPServiceStruct received error response :%v", resp.Response.Message)
447+
return resp, fmt.Errorf(resp.Response.Message)
448+
}
449+
450+
return resp, err
451+
}

cns/cnsclient/cnsclient_test.go

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import (
1212
"strconv"
1313
"testing"
1414

15+
nnc "github.com/Azure/azure-container-networking/nodenetworkconfig/api/v1alpha"
16+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17+
1518
"github.com/Azure/azure-container-networking/cns"
1619
"github.com/Azure/azure-container-networking/cns/common"
1720
"github.com/Azure/azure-container-networking/cns/fakes"
@@ -129,7 +132,38 @@ func TestMain(m *testing.M) {
129132
httpRestService, err := restserver.NewHTTPRestService(&config, fakes.NewFakeImdsClient(), fakes.NewFakeNMAgentClient())
130133
svc = httpRestService.(*restserver.HTTPRestService)
131134
svc.Name = "cns-test-server"
132-
svc.IPAMPoolMonitor = fakes.NewIPAMPoolMonitorFake()
135+
fakeNNC := nnc.NodeNetworkConfig{
136+
TypeMeta: metav1.TypeMeta{},
137+
ObjectMeta: metav1.ObjectMeta{},
138+
Spec: nnc.NodeNetworkConfigSpec{
139+
RequestedIPCount: 16,
140+
IPsNotInUse: []string{"abc"},
141+
},
142+
Status: nnc.NodeNetworkConfigStatus{
143+
Scaler: nnc.Scaler{
144+
BatchSize: 10,
145+
ReleaseThresholdPercent: 50,
146+
RequestThresholdPercent: 40,
147+
},
148+
NetworkContainers: []nnc.NetworkContainer{
149+
{
150+
ID: "nc1",
151+
PrimaryIP: "10.0.0.11",
152+
SubnetName: "sub1",
153+
IPAssignments: []nnc.IPAssignment{
154+
{
155+
Name: "ip1",
156+
IP: "10.0.0.10",
157+
},
158+
},
159+
DefaultGateway: "10.0.0.1",
160+
SubnetAddressSpace: "10.0.0.0/24",
161+
Version: 2,
162+
},
163+
},
164+
},
165+
}
166+
svc.IPAMPoolMonitor = &fakes.IPAMPoolMonitorFake{FakeMinimumIps: 10, FakeMaximumIps: 20, FakeIpsNotInUseCount: 13, FakecachedNNC: fakeNNC}
133167

134168
if err != nil {
135169
logger.Errorf("Failed to create CNS object, err:%v.\n", err)
@@ -254,8 +288,7 @@ func TestCNSClientPodContextApi(t *testing.T) {
254288
podNamespace := "testpodnamespace"
255289
desiredIpAddress := "10.0.0.5"
256290

257-
secondaryIps := make([]string, 0)
258-
secondaryIps = append(secondaryIps, desiredIpAddress)
291+
secondaryIps := []string{desiredIpAddress}
259292
cnsClient, _ := InitCnsClient("")
260293

261294
addTestStateToRestServer(t, secondaryIps)
@@ -282,4 +315,79 @@ func TestCNSClientPodContextApi(t *testing.T) {
282315
}
283316

284317
t.Log(podcontext)
318+
319+
// release requested IP address, expect success
320+
err = cnsClient.ReleaseIPAddress(orchestratorContext)
321+
if err != nil {
322+
t.Fatalf("Expected to not fail when releasing IP reservation found with context: %+v", err)
323+
}
324+
}
325+
326+
func TestCNSClientDebugAPI(t *testing.T) {
327+
podName := "testpodname"
328+
podNamespace := "testpodnamespace"
329+
desiredIpAddress := "10.0.0.5"
330+
331+
secondaryIps := []string{desiredIpAddress}
332+
cnsClient, _ := InitCnsClient("")
333+
334+
addTestStateToRestServer(t, secondaryIps)
335+
336+
podInfo := cns.KubernetesPodInfo{PodName: podName, PodNamespace: podNamespace}
337+
orchestratorContext, err := json.Marshal(podInfo)
338+
if err != nil {
339+
t.Fatal(err)
340+
}
341+
342+
// request IP address
343+
_, err1 := cnsClient.RequestIPAddress(orchestratorContext)
344+
if err1 != nil {
345+
t.Fatalf("get IP from CNS failed with %+v", err1)
346+
}
347+
348+
//test for debug api/cmd to get inmemory data from HTTPRestService
349+
inmemory, err := cnsClient.GetHTTPServiceData()
350+
if err != nil {
351+
t.Errorf("Get in-memory http REST Struct failed %+v", err)
352+
}
353+
354+
if len(inmemory.HttpRestServiceData.PodIPIDByOrchestratorContext) < 1 {
355+
t.Errorf("OrchestratorContext map is expected but not returned")
356+
}
357+
358+
//testing Pod IP Configuration Status values set for test
359+
podConfig := inmemory.HttpRestServiceData.PodIPConfigState
360+
for _, v := range podConfig {
361+
if v.IPAddress != "10.0.0.5" || v.State != "Allocated" || v.NCID != "testNcId1" {
362+
t.Errorf("Not the expected set values for testing IPConfigurationStatus, %+v", podConfig)
363+
}
364+
}
365+
if len(inmemory.HttpRestServiceData.PodIPConfigState) < 1 {
366+
t.Errorf("PodIpConfigState with atleast 1 entry expected but not returned.")
367+
}
368+
369+
testIpamPoolMonitor := inmemory.HttpRestServiceData.IPAMPoolMonitor
370+
if testIpamPoolMonitor.MinimumFreeIps != 10 || testIpamPoolMonitor.MaximumFreeIps != 20 || testIpamPoolMonitor.UpdatingIpsNotInUseCount != 13 {
371+
t.Errorf("IPAMPoolMonitor state is not reflecting the initial set values, %+v", testIpamPoolMonitor)
372+
}
373+
374+
//check for cached NNC Spec struct values
375+
if testIpamPoolMonitor.CachedNNC.Spec.RequestedIPCount != 16 || len(testIpamPoolMonitor.CachedNNC.Spec.IPsNotInUse) != 1 {
376+
t.Errorf("IPAMPoolMonitor cached NNC Spec is not reflecting the initial set values, %+v", testIpamPoolMonitor.CachedNNC.Spec)
377+
}
378+
379+
//check for cached NNC Status struct values
380+
if testIpamPoolMonitor.CachedNNC.Status.Scaler.BatchSize != 10 || testIpamPoolMonitor.CachedNNC.Status.Scaler.ReleaseThresholdPercent != 50 || testIpamPoolMonitor.CachedNNC.Status.Scaler.RequestThresholdPercent != 40 {
381+
t.Errorf("IPAMPoolMonitor cached NNC Status is not reflecting the initial set values, %+v", testIpamPoolMonitor.CachedNNC.Status.Scaler)
382+
}
383+
384+
if len(testIpamPoolMonitor.CachedNNC.Status.NetworkContainers) != 1 {
385+
t.Errorf("Expected only one Network Container in the list, %+v", testIpamPoolMonitor.CachedNNC.Status.NetworkContainers)
386+
}
387+
388+
t.Logf("In-memory Data: ")
389+
t.Logf("PodIPIDByOrchestratorContext: %+v", inmemory.HttpRestServiceData.PodIPIDByOrchestratorContext)
390+
t.Logf("PodIPConfigState: %+v", inmemory.HttpRestServiceData.PodIPConfigState)
391+
t.Logf("IPAMPoolMonitor: %+v", inmemory.HttpRestServiceData.IPAMPoolMonitor)
392+
285393
}

cns/fakes/ipampoolmonitorfake.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,17 @@ package fakes
33
import (
44
"context"
55

6+
"github.com/Azure/azure-container-networking/cns"
7+
68
nnc "github.com/Azure/azure-container-networking/nodenetworkconfig/api/v1alpha"
79
)
810

9-
type IPAMPoolMonitorFake struct{}
11+
type IPAMPoolMonitorFake struct {
12+
FakeMinimumIps int
13+
FakeMaximumIps int
14+
FakeIpsNotInUseCount int
15+
FakecachedNNC nnc.NodeNetworkConfig
16+
}
1017

1118
func NewIPAMPoolMonitorFake() *IPAMPoolMonitorFake {
1219
return &IPAMPoolMonitorFake{}
@@ -23,3 +30,12 @@ func (ipm *IPAMPoolMonitorFake) Update(scalar nnc.Scaler, spec nnc.NodeNetworkCo
2330
func (ipm *IPAMPoolMonitorFake) Reconcile() error {
2431
return nil
2532
}
33+
34+
func (ipm *IPAMPoolMonitorFake) GetStateSnapshot() cns.IpamPoolMonitorStateSnapshot {
35+
return cns.IpamPoolMonitorStateSnapshot{
36+
MinimumFreeIps: int64(ipm.FakeMinimumIps),
37+
MaximumFreeIps: int64(ipm.FakeMaximumIps),
38+
UpdatingIpsNotInUseCount: ipm.FakeIpsNotInUseCount,
39+
CachedNNC: ipm.FakecachedNNC,
40+
}
41+
}

cns/ipampoolmonitor/ipampoolmonitor.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import (
1515
type CNSIPAMPoolMonitor struct {
1616
pendingRelease bool
1717

18-
cachedNNC nnc.NodeNetworkConfig
18+
cachedNNC nnc.NodeNetworkConfig
1919
updatingIpsNotInUseCount int
20-
scalarUnits nnc.Scaler
20+
scalarUnits nnc.Scaler
2121

2222
httpService cns.HTTPService
2323
rc requestcontroller.RequestController
@@ -30,9 +30,9 @@ type CNSIPAMPoolMonitor struct {
3030
func NewCNSIPAMPoolMonitor(httpService cns.HTTPService, rc requestcontroller.RequestController) *CNSIPAMPoolMonitor {
3131
logger.Printf("NewCNSIPAMPoolMonitor: Create IPAM Pool Monitor")
3232
return &CNSIPAMPoolMonitor{
33-
pendingRelease: false,
34-
httpService: httpService,
35-
rc: rc,
33+
pendingRelease: false,
34+
httpService: httpService,
35+
rc: rc,
3636
}
3737
}
3838

@@ -71,7 +71,6 @@ func (pm *CNSIPAMPoolMonitor) Reconcile() error {
7171
pendingReleaseIPCount := len(pm.httpService.GetPendingReleaseIPConfigs())
7272
availableIPConfigCount := len(pm.httpService.GetAvailableIPConfigs()) // TODO: add pending allocation count to real cns
7373
freeIPConfigCount := pm.cachedNNC.Spec.RequestedIPCount - int64(allocatedPodIPCount)
74-
7574
msg := fmt.Sprintf("[ipam-pool-monitor] Pool Size: %v, Goal Size: %v, BatchSize: %v, MinFree: %v, MaxFree:%v, Allocated: %v, Available: %v, Pending Release: %v, Free: %v, Pending Program: %v",
7675
cnsPodIPConfigCount, pm.cachedNNC.Spec.RequestedIPCount, pm.scalarUnits.BatchSize, pm.MinimumFreeIps, pm.MaximumFreeIps, allocatedPodIPCount, availableIPConfigCount, pendingReleaseIPCount, freeIPConfigCount, pendingProgramCount)
7776

@@ -202,7 +201,6 @@ func (pm *CNSIPAMPoolMonitor) cleanPendingRelease() error {
202201

203202
logger.Printf("[ipam-pool-monitor] cleanPendingRelease: UpdateCRDSpec succeeded for spec %+v", tempNNCSpec)
204203

205-
206204
// save the updated state to cachedSpec
207205
pm.cachedNNC.Spec = tempNNCSpec
208206
pm.pendingRelease = false
@@ -249,3 +247,16 @@ func (pm *CNSIPAMPoolMonitor) Update(scalar nnc.Scaler, spec nnc.NodeNetworkConf
249247

250248
return nil
251249
}
250+
251+
//this function sets the values for state in IPAMPoolMonitor Struct
252+
func (pm *CNSIPAMPoolMonitor) GetStateSnapshot() cns.IpamPoolMonitorStateSnapshot {
253+
pm.mu.Lock()
254+
defer pm.mu.Unlock()
255+
256+
return cns.IpamPoolMonitorStateSnapshot{
257+
MinimumFreeIps: pm.MinimumFreeIps,
258+
MaximumFreeIps: pm.MaximumFreeIps,
259+
UpdatingIpsNotInUseCount: pm.updatingIpsNotInUseCount,
260+
CachedNNC: pm.cachedNNC,
261+
}
262+
}

cns/restserver/ipam.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,38 @@ func (service *HTTPRestService) GetPodIPIDByOrchestratorContext() map[string]str
221221
return service.PodIPIDByOrchestratorContext
222222
}
223223

224+
func (service *HTTPRestService) GetHTTPRestDataHandler(w http.ResponseWriter, r *http.Request) {
225+
var (
226+
resp GetHTTPServiceDataResponse
227+
returnMessage string
228+
err error
229+
)
230+
231+
defer func() {
232+
if err != nil {
233+
resp.Response.ReturnCode = UnexpectedError
234+
resp.Response.Message = returnMessage
235+
}
236+
237+
err = service.Listener.Encode(w, &resp)
238+
logger.Response(service.Name, resp, resp.Response.ReturnCode, ReturnCodeToString(resp.Response.ReturnCode), err)
239+
}()
240+
241+
resp.HttpRestServiceData = service.GetHTTPStruct()
242+
return
243+
}
244+
245+
func (service *HTTPRestService) GetHTTPStruct() HttpRestServiceData {
246+
service.RLock()
247+
defer service.RUnlock()
248+
249+
return HttpRestServiceData{
250+
PodIPIDByOrchestratorContext: service.PodIPIDByOrchestratorContext,
251+
PodIPConfigState: service.PodIPConfigState,
252+
IPAMPoolMonitor: service.IPAMPoolMonitor.GetStateSnapshot(),
253+
}
254+
}
255+
224256
// GetPendingProgramIPConfigs returns list of IPs which are in pending program status
225257
func (service *HTTPRestService) GetPendingProgramIPConfigs() []cns.IPConfigurationStatus {
226258
service.RLock()

0 commit comments

Comments
 (0)