Adding UpdatingIpsNotInUse and Cached NNC to Debug API

In-memory data API - adding 2 more fields to IPAMPoolMonitor
This commit is contained in:
Kshitija Murudi 2021-03-26 00:47:39 -07:00
Родитель 9947dba9c0
Коммит 9aa3781a0a
6 изменённых файлов: 183 добавлений и 38 удалений

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

@ -180,8 +180,10 @@ type IPAMPoolMonitor interface {
//struct to expose state values for IPAMPoolMonitor struct
type IpamPoolMonitorStateSnapshot struct {
MinimumFreeIps int64
MaximumFreeIps int64
MinimumFreeIps int64
MaximumFreeIps int64
UpdatingIpsNotInUseCount int
CachedNNC nnc.NodeNetworkConfig
}
// Response describes generic response from CNS.

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

@ -16,11 +16,8 @@ const (
getAllocatedArg = "Allocated"
getAllArg = "All"
getPendingReleaseArg = "PendingRelease"
<<<<<<< HEAD
getPodCmdArg = "getPodContexts"
=======
getInMemoryData = "getInMemory"
>>>>>>> da5071c ([CNS] Debug API to expose In-Memory Data HTTPRestService)
releaseArg = "release"
@ -34,11 +31,8 @@ const (
var (
availableCmds = []string{
getCmdArg,
<<<<<<< HEAD
getPodCmdArg,
=======
getInMemoryData,
>>>>>>> da5071c ([CNS] Debug API to expose In-Memory Data HTTPRestService)
}
getFlags = []string{
@ -60,13 +54,10 @@ func HandleCNSClientCommands(cmd, arg string) error {
switch {
case strings.EqualFold(getCmdArg, cmd):
return getCmd(cnsClient, arg)
<<<<<<< HEAD
case strings.EqualFold(getPodCmdArg, cmd):
return getPodCmd(cnsClient)
=======
case strings.EqualFold(getInMemoryData, cmd):
return getInMemory(cnsClient)
>>>>>>> da5071c ([CNS] Debug API to expose In-Memory Data HTTPRestService)
default:
return fmt.Errorf("No debug cmd supplied, options are: %v", getCmdArg)
}
@ -115,20 +106,13 @@ func printIPAddresses(addrSlice []cns.IPConfigurationStatus) {
}
}
<<<<<<< HEAD
func getPodCmd(client *CNSClient) error {
resp, err := client.GetPodOrchestratorContext()
=======
func getInMemory(client *CNSClient) error {
inmemoryData, err := client.GetHTTPServiceData()
>>>>>>> da5071c ([CNS] Debug API to expose In-Memory Data HTTPRestService)
if err != nil {
return err
}
<<<<<<< HEAD
printPodContext(resp)
return nil
}
@ -139,7 +123,15 @@ func printPodContext(podContext map[string]string) {
fmt.Println(i, " ", orchContext, " : ", podID)
i++
}
=======
}
func getInMemory(client *CNSClient) error {
inmemoryData, err := client.GetHTTPServiceData()
if err != nil {
return err
}
printInMemoryStruct(inmemoryData.HttpRestServiceData)
return nil
}
@ -148,5 +140,4 @@ func printInMemoryStruct(data restserver.HttpRestServiceData) {
fmt.Println("PodIPIDByOrchestratorContext: ", data.PodIPIDByOrchestratorContext)
fmt.Println("PodIPConfigState: ", data.PodIPConfigState)
fmt.Println("IPAMPoolMonitor: ", data.IPAMPoolMonitor)
>>>>>>> da5071c ([CNS] Debug API to expose In-Memory Data HTTPRestService)
}

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

@ -410,3 +410,42 @@ func (cnsClient *CNSClient) GetPodOrchestratorContext() (map[string]string, erro
return resp.PodContext, err
}
//GetHTTPServiceData gets all public in-memory struct details for debugging purpose
func (cnsClient *CNSClient) GetHTTPServiceData() (restserver.GetHTTPServiceDataResponse, error) {
var (
resp restserver.GetHTTPServiceDataResponse
err error
res *http.Response
)
url := cnsClient.connectionURL + cns.GetHTTPRestData
log.Printf("GetHTTPServiceStruct url %v", url)
res, err = http.Get(url)
if err != nil {
log.Errorf("[Azure CNSClient] HTTP Get returned error %v", err.Error())
return resp, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
errMsg := fmt.Sprintf("[Azure CNSClient] GetHTTPServiceStruct invalid http status code: %v", res.StatusCode)
log.Errorf(errMsg)
return resp, fmt.Errorf(errMsg)
}
err = json.NewDecoder(res.Body).Decode(&resp)
if err != nil {
log.Errorf("[Azure CNSClient] Error received while parsing GetHTTPServiceStruct response resp:%v err:%v", res.Body, err.Error())
return resp, err
}
if resp.Response.ReturnCode != 0 {
log.Errorf("[Azure CNSClient] GetTTPServiceStruct received error response :%v", resp.Response.Message)
return resp, fmt.Errorf(resp.Response.Message)
}
return resp, err
}

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

@ -12,6 +12,9 @@ import (
"strconv"
"testing"
nnc "github.com/Azure/azure-container-networking/nodenetworkconfig/api/v1alpha"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/Azure/azure-container-networking/cns"
"github.com/Azure/azure-container-networking/cns/common"
"github.com/Azure/azure-container-networking/cns/fakes"
@ -129,7 +132,38 @@ func TestMain(m *testing.M) {
httpRestService, err := restserver.NewHTTPRestService(&config, fakes.NewFakeImdsClient(), fakes.NewFakeNMAgentClient())
svc = httpRestService.(*restserver.HTTPRestService)
svc.Name = "cns-test-server"
svc.IPAMPoolMonitor = &fakes.IPAMPoolMonitorFake{FakeMinimumIps: 10, FakeMaximumIps: 20}
fakeNNC := nnc.NodeNetworkConfig{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Spec: nnc.NodeNetworkConfigSpec{
RequestedIPCount: 16,
IPsNotInUse: []string{"abc"},
},
Status: nnc.NodeNetworkConfigStatus{
Scaler: nnc.Scaler{
BatchSize: 10,
ReleaseThresholdPercent: 50,
RequestThresholdPercent: 40,
},
NetworkContainers: []nnc.NetworkContainer{
{
ID: "nc1",
PrimaryIP: "10.0.0.11",
SubnetName: "sub1",
IPAssignments: []nnc.IPAssignment{
{
Name: "ip1",
IP: "10.0.0.10",
},
},
DefaultGateway: "10.0.0.1",
SubnetAddressSpace: "10.0.0.0/24",
Version: 2,
},
},
},
}
svc.IPAMPoolMonitor = &fakes.IPAMPoolMonitorFake{FakeMinimumIps: 10, FakeMaximumIps: 20, FakeIpsNotInUseCount: 13, FakecachedNNC: fakeNNC}
if err != nil {
logger.Errorf("Failed to create CNS object, err:%v.\n", err)
@ -254,8 +288,7 @@ func TestCNSClientPodContextApi(t *testing.T) {
podNamespace := "testpodnamespace"
desiredIpAddress := "10.0.0.5"
secondaryIps := make([]string, 0)
secondaryIps = append(secondaryIps, desiredIpAddress)
secondaryIps := []string{desiredIpAddress}
cnsClient, _ := InitCnsClient("")
addTestStateToRestServer(t, secondaryIps)
@ -282,4 +315,79 @@ func TestCNSClientPodContextApi(t *testing.T) {
}
t.Log(podcontext)
// 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)
}
}
func TestCNSClientDebugAPI(t *testing.T) {
podName := "testpodname"
podNamespace := "testpodnamespace"
desiredIpAddress := "10.0.0.5"
secondaryIps := []string{desiredIpAddress}
cnsClient, _ := InitCnsClient("")
addTestStateToRestServer(t, secondaryIps)
podInfo := cns.KubernetesPodInfo{PodName: podName, PodNamespace: podNamespace}
orchestratorContext, err := json.Marshal(podInfo)
if err != nil {
t.Fatal(err)
}
// request IP address
_, err1 := cnsClient.RequestIPAddress(orchestratorContext)
if err1 != nil {
t.Fatalf("get IP from CNS failed with %+v", err1)
}
//test for debug api/cmd to get inmemory data from HTTPRestService
inmemory, err := cnsClient.GetHTTPServiceData()
if err != nil {
t.Errorf("Get in-memory http REST Struct failed %+v", err)
}
if len(inmemory.HttpRestServiceData.PodIPIDByOrchestratorContext) < 1 {
t.Errorf("OrchestratorContext map is expected but not returned")
}
//testing Pod IP Configuration Status values set for test
podConfig := inmemory.HttpRestServiceData.PodIPConfigState
for _, v := range podConfig {
if v.IPAddress != "10.0.0.5" || v.State != "Allocated" || v.NCID != "testNcId1" {
t.Errorf("Not the expected set values for testing IPConfigurationStatus, %+v", podConfig)
}
}
if len(inmemory.HttpRestServiceData.PodIPConfigState) < 1 {
t.Errorf("PodIpConfigState with atleast 1 entry expected but not returned.")
}
testIpamPoolMonitor := inmemory.HttpRestServiceData.IPAMPoolMonitor
if testIpamPoolMonitor.MinimumFreeIps != 10 || testIpamPoolMonitor.MaximumFreeIps != 20 || testIpamPoolMonitor.UpdatingIpsNotInUseCount != 13 {
t.Errorf("IPAMPoolMonitor state is not reflecting the initial set values, %+v", testIpamPoolMonitor)
}
//check for cached NNC Spec struct values
if testIpamPoolMonitor.CachedNNC.Spec.RequestedIPCount != 16 || len(testIpamPoolMonitor.CachedNNC.Spec.IPsNotInUse) != 1 {
t.Errorf("IPAMPoolMonitor cached NNC Spec is not reflecting the initial set values, %+v", testIpamPoolMonitor.CachedNNC.Spec)
}
//check for cached NNC Status struct values
if testIpamPoolMonitor.CachedNNC.Status.Scaler.BatchSize != 10 || testIpamPoolMonitor.CachedNNC.Status.Scaler.ReleaseThresholdPercent != 50 || testIpamPoolMonitor.CachedNNC.Status.Scaler.RequestThresholdPercent != 40 {
t.Errorf("IPAMPoolMonitor cached NNC Status is not reflecting the initial set values, %+v", testIpamPoolMonitor.CachedNNC.Status.Scaler)
}
if len(testIpamPoolMonitor.CachedNNC.Status.NetworkContainers) != 1 {
t.Errorf("Expected only one Network Container in the list, %+v", testIpamPoolMonitor.CachedNNC.Status.NetworkContainers)
}
t.Logf("In-memory Data: ")
t.Logf("PodIPIDByOrchestratorContext: %+v", inmemory.HttpRestServiceData.PodIPIDByOrchestratorContext)
t.Logf("PodIPConfigState: %+v", inmemory.HttpRestServiceData.PodIPConfigState)
t.Logf("IPAMPoolMonitor: %+v", inmemory.HttpRestServiceData.IPAMPoolMonitor)
}

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

@ -9,8 +9,10 @@ import (
)
type IPAMPoolMonitorFake struct {
FakeMinimumIps int
FakeMaximumIps int
FakeMinimumIps int
FakeMaximumIps int
FakeIpsNotInUseCount int
FakecachedNNC nnc.NodeNetworkConfig
}
func NewIPAMPoolMonitorFake() *IPAMPoolMonitorFake {
@ -31,7 +33,9 @@ func (ipm *IPAMPoolMonitorFake) Reconcile() error {
func (ipm *IPAMPoolMonitorFake) GetStateSnapshot() cns.IpamPoolMonitorStateSnapshot {
return cns.IpamPoolMonitorStateSnapshot{
MinimumFreeIps: int64(ipm.FakeMinimumIps),
MaximumFreeIps: int64(ipm.FakeMaximumIps),
MinimumFreeIps: int64(ipm.FakeMinimumIps),
MaximumFreeIps: int64(ipm.FakeMaximumIps),
UpdatingIpsNotInUseCount: ipm.FakeIpsNotInUseCount,
CachedNNC: ipm.FakecachedNNC,
}
}

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

@ -15,9 +15,9 @@ import (
type CNSIPAMPoolMonitor struct {
pendingRelease bool
cachedNNC nnc.NodeNetworkConfig
cachedNNC nnc.NodeNetworkConfig
updatingIpsNotInUseCount int
scalarUnits nnc.Scaler
scalarUnits nnc.Scaler
httpService cns.HTTPService
rc requestcontroller.RequestController
@ -30,9 +30,9 @@ type CNSIPAMPoolMonitor struct {
func NewCNSIPAMPoolMonitor(httpService cns.HTTPService, rc requestcontroller.RequestController) *CNSIPAMPoolMonitor {
logger.Printf("NewCNSIPAMPoolMonitor: Create IPAM Pool Monitor")
return &CNSIPAMPoolMonitor{
pendingRelease: false,
httpService: httpService,
rc: rc,
pendingRelease: false,
httpService: httpService,
rc: rc,
}
}
@ -201,7 +201,6 @@ func (pm *CNSIPAMPoolMonitor) cleanPendingRelease() error {
logger.Printf("[ipam-pool-monitor] cleanPendingRelease: UpdateCRDSpec succeeded for spec %+v", tempNNCSpec)
// save the updated state to cachedSpec
pm.cachedNNC.Spec = tempNNCSpec
pm.pendingRelease = false
@ -250,12 +249,14 @@ func (pm *CNSIPAMPoolMonitor) Update(scalar nnc.Scaler, spec nnc.NodeNetworkConf
}
//this function sets the values for state in IPAMPoolMonitor Struct
func (pm *CNSIPAMPoolMonitor) GetStateSnapshot() cns.IpamPoolMonitorStateSnapshot{
defer pm.mu.Unlock()
func (pm *CNSIPAMPoolMonitor) GetStateSnapshot() cns.IpamPoolMonitorStateSnapshot {
pm.mu.Lock()
defer pm.mu.Unlock()
return cns.IpamPoolMonitorStateSnapshot {
MinimumFreeIps: pm.MinimumFreeIps,
MaximumFreeIps: pm.MaximumFreeIps,
return cns.IpamPoolMonitorStateSnapshot{
MinimumFreeIps: pm.MinimumFreeIps,
MaximumFreeIps: pm.MaximumFreeIps,
UpdatingIpsNotInUseCount: pm.updatingIpsNotInUseCount,
CachedNNC: pm.cachedNNC,
}
}