Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
db4166e
feat: support for cilium + nodesubnet
santhoshmprabhu Oct 18, 2024
9cc4ec6
fix: make linter happy
santhoshmprabhu Oct 18, 2024
107ebce
fix: make linter happy
santhoshmprabhu Oct 18, 2024
636ab39
Merge branch 'master' into sanprabhu/cilium-node-subnet
santhoshmprabhu Oct 18, 2024
07a8575
fix: make linter happy
santhoshmprabhu Oct 18, 2024
7f10972
Merge branch 'sanprabhu/cilium-node-subnet' of github.com:Azure/azure…
santhoshmprabhu Oct 18, 2024
c59f2dc
test: add test for nodesubnet
santhoshmprabhu Oct 21, 2024
fbd60a5
chore: add missing files
santhoshmprabhu Oct 21, 2024
4ee1a90
nicer comment
santhoshmprabhu Oct 21, 2024
f94cb77
chore: fix comment typo
santhoshmprabhu Oct 21, 2024
a1c4bfe
fix: update cns/restserver/nodesubnet.go
santhoshmprabhu Oct 22, 2024
2f15117
fix: update cns/restserver/restserver.go
santhoshmprabhu Oct 22, 2024
6e8bda0
refactor: address comments
santhoshmprabhu Oct 22, 2024
7879de5
Merge branch 'sanprabhu/cilium-node-subnet' of github.com:Azure/azure…
santhoshmprabhu Oct 22, 2024
0fe2285
Merge branch 'master' into sanprabhu/cilium-node-subnet
santhoshmprabhu Oct 22, 2024
301de9f
fix: address comments
santhoshmprabhu Oct 22, 2024
73424b5
Merge branch 'sanprabhu/cilium-node-subnet' of github.com:Azure/azure…
santhoshmprabhu Oct 23, 2024
c4d9408
chore:comment cleanup
santhoshmprabhu Oct 23, 2024
2b210d6
fix: do not use bash in ip config update
santhoshmprabhu Oct 23, 2024
a15ee32
fix: address comments
santhoshmprabhu Oct 23, 2024
30c7aa2
fix: make linter happy
santhoshmprabhu Oct 23, 2024
a8b4cf1
Merge branch 'master' into sanprabhu/cilium-node-subnet
santhoshmprabhu Oct 23, 2024
356dc69
chore: move pipeline changes out
santhoshmprabhu Oct 23, 2024
56f3d48
Merge branch 'master' into sanprabhu/cilium-node-subnet
santhoshmprabhu Oct 23, 2024
b3ac582
test: more elaborate test including checks on IP pool state
santhoshmprabhu Oct 24, 2024
6385042
fix: use comments suitable for documentation
santhoshmprabhu Oct 24, 2024
d9b4384
chore: address comments
santhoshmprabhu Oct 24, 2024
2a6881f
chore:make linter happy
santhoshmprabhu Oct 24, 2024
f8f724d
fix: address comments
santhoshmprabhu Oct 25, 2024
a78a6b1
chore: typo
santhoshmprabhu Oct 25, 2024
800785e
chore: address comments
santhoshmprabhu Oct 29, 2024
03b2531
Merge branch 'master' into sanprabhu/cilium-node-subnet
santhoshmprabhu Oct 29, 2024
d06db5d
Merge branch 'master' into sanprabhu/cilium-node-subnet
santhoshmprabhu Oct 30, 2024
f764c8a
fix: update comments
santhoshmprabhu Oct 30, 2024
8acfe0c
Merge branch 'sanprabhu/cilium-node-subnet' of github.com:Azure/azure…
santhoshmprabhu Oct 30, 2024
8530f85
Merge branch 'master' into sanprabhu/cilium-node-subnet
santhoshmprabhu Oct 31, 2024
0cae5e4
Merge branch 'master' into sanprabhu/cilium-node-subnet
santhoshmprabhu Nov 1, 2024
50c2a1c
Merge branch 'master' into sanprabhu/cilium-node-subnet
santhoshmprabhu Nov 1, 2024
f8bcb26
Merge remote-tracking branch 'origin/master' into sanprabhu/cilium-no…
santhoshmprabhu Nov 4, 2024
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: 8 additions & 3 deletions cns/fakes/nmagentclientfake.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import (

// NMAgentClientFake can be used to query to VM Host info.
type NMAgentClientFake struct {
SupportedAPIsF func(context.Context) ([]string, error)
GetNCVersionListF func(context.Context) (nmagent.NCVersionList, error)
GetHomeAzF func(context.Context) (nmagent.AzResponse, error)
SupportedAPIsF func(context.Context) ([]string, error)
GetNCVersionListF func(context.Context) (nmagent.NCVersionList, error)
GetHomeAzF func(context.Context) (nmagent.AzResponse, error)
GetInterfaceIPInfoF func(ctx context.Context) (nmagent.Interfaces, error)
}

func (n *NMAgentClientFake) SupportedAPIs(ctx context.Context) ([]string, error) {
Expand All @@ -30,3 +31,7 @@ func (n *NMAgentClientFake) GetNCVersionList(ctx context.Context) (nmagent.NCVer
func (n *NMAgentClientFake) GetHomeAz(ctx context.Context) (nmagent.AzResponse, error) {
return n.GetHomeAzF(ctx)
}

func (n *NMAgentClientFake) GetInterfaceIPInfo(ctx context.Context) (nmagent.Interfaces, error) {
return n.GetInterfaceIPInfoF(ctx)
}
89 changes: 89 additions & 0 deletions cns/restserver/helper_for_nodesubnet_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package restserver

import (
"context"
"net/netip"
"testing"

"github.com/Azure/azure-container-networking/cns"
"github.com/Azure/azure-container-networking/cns/common"
"github.com/Azure/azure-container-networking/cns/fakes"
"github.com/Azure/azure-container-networking/cns/nodesubnet"
acn "github.com/Azure/azure-container-networking/common"
"github.com/Azure/azure-container-networking/nmagent"
"github.com/Azure/azure-container-networking/store"
)

// GetRestServiceObjectForNodeSubnetTest creates a new HTTPRestService object for use in nodesubnet unit tests.
func GetRestServiceObjectForNodeSubnetTest(t *testing.T, generator CNIConflistGenerator) *HTTPRestService {
config := &common.ServiceConfig{
Name: "test",
Version: "1.0",
ChannelMode: "AzureHost",
Store: store.NewMockStore("test"),
}
interfaces := nmagent.Interfaces{
Entries: []nmagent.Interface{
{
MacAddress: nmagent.MACAddress{0x00, 0x0D, 0x3A, 0xF9, 0xDC, 0xA6},
IsPrimary: true,
InterfaceSubnets: []nmagent.InterfaceSubnet{
{
Prefix: "10.0.0.0/24",
IPAddress: []nmagent.NodeIP{
{
Address: nmagent.IPAddress(netip.AddrFrom4([4]byte{10, 0, 0, 4})),
IsPrimary: true,
},
{
Address: nmagent.IPAddress(netip.AddrFrom4([4]byte{10, 0, 0, 52})),
IsPrimary: false,
},
{
Address: nmagent.IPAddress(netip.AddrFrom4([4]byte{10, 0, 0, 63})),
IsPrimary: false,
},
{
Address: nmagent.IPAddress(netip.AddrFrom4([4]byte{10, 0, 0, 45})),
IsPrimary: false,
},
},
},
},
},
},
}

svc, err := cns.NewService(config.Name, config.Version, config.ChannelMode, config.Store)
if err != nil {
return nil
}

svc.SetOption(acn.OptCnsURL, "")
svc.SetOption(acn.OptCnsPort, "")
err = svc.Initialize(config)
if err != nil {
return nil
}

t.Cleanup(func() { svc.Uninitialize() })

return &HTTPRestService{
Service: svc,
cniConflistGenerator: generator,
state: &httpRestServiceState{},
PodIPConfigState: make(map[string]cns.IPConfigurationStatus),
PodIPIDByPodInterfaceKey: make(map[string][]string),
nma: &fakes.NMAgentClientFake{
GetInterfaceIPInfoF: func(_ context.Context) (nmagent.Interfaces, error) {
return interfaces, nil
},
},
wscli: &fakes.WireserverClientFake{},
}
}

// GetNodesubnetIPFetcher gets the nodesubnetIPFetcher from the HTTPRestService.
func (service *HTTPRestService) GetNodesubnetIPFetcher() *nodesubnet.IPFetcher {
return service.nodesubnetIPFetcher
}
64 changes: 64 additions & 0 deletions cns/restserver/nodesubnet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package restserver

import (
"context"
"net/netip"

"github.com/Azure/azure-container-networking/cns"
"github.com/Azure/azure-container-networking/cns/logger"
nodesubnet "github.com/Azure/azure-container-networking/cns/nodesubnet"
"github.com/Azure/azure-container-networking/cns/types"
"github.com/pkg/errors"
)

var _ nodesubnet.IPConsumer = &HTTPRestService{}

// UpdateIPsForNodeSubnet updates the IP pool of HTTPRestService with newly fetched secondary IPs
func (service *HTTPRestService) UpdateIPsForNodeSubnet(secondaryIPs []netip.Addr) error {
secondaryIPStrs := make([]string, len(secondaryIPs))
for i, ip := range secondaryIPs {
secondaryIPStrs[i] = ip.String()
}

networkContainerRequest := nodesubnet.CreateNodeSubnetNCRequest(secondaryIPStrs)

code, msg := service.saveNetworkContainerGoalState(*networkContainerRequest)
if code != types.Success {
return errors.Errorf("failed to save fetched ips. code: %d, message %s", code, msg)
}

logger.Debugf("IP change processed successfully")

// saved NC successfully. UpdateIPsForNodeSubnet is called only when IPs are fetched from NMAgent.
// We now have IPs to serve IPAM requests. Generate conflist to indicate CNS is ready
service.MustGenerateCNIConflistOnce()
return nil
}

// InitializeNodeSubnet prepares CNS for serving NodeSubnet requests.
// It sets the orchestrator type to KubernetesCRD, reconciles the initial
// CNS state from the statefile, then creates an IP fetcher.
func (service *HTTPRestService) InitializeNodeSubnet(ctx context.Context, podInfoByIPProvider cns.PodInfoByIPProvider) error {
// set orchestrator type
orchestrator := cns.SetOrchestratorTypeRequest{
OrchestratorType: cns.KubernetesCRD,
}
service.SetNodeOrchestrator(&orchestrator)

if podInfoByIPProvider == nil {
logger.Printf("PodInfoByIPProvider is nil, this usually means no saved endpoint state. Skipping reconciliation")
} else if _, err := nodesubnet.ReconcileInitialCNSState(ctx, service, podInfoByIPProvider); err != nil {
return errors.Wrap(err, "reconcile initial CNS state")
}
// statefile (if any) is reconciled. Initialize the IP fetcher. Start the IP fetcher only after the service is started,
// because starting the IP fetcher will generate conflist, which should be done only once we are ready to respond to IPAM requests.
service.nodesubnetIPFetcher = nodesubnet.NewIPFetcher(service.nma, service, 0, 0, logger.Log)

return nil
}

// StartNodeSubnet starts the IP fetcher for NodeSubnet. This will cause secondary IPs to be fetched periodically.
// After the first successful fetch, conflist will be generated to indicate CNS is ready.
func (service *HTTPRestService) StartNodeSubnet(ctx context.Context) {
service.nodesubnetIPFetcher.Start(ctx)
}
144 changes: 144 additions & 0 deletions cns/restserver/nodesubnet_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package restserver_test

import (
"context"
"net"
"testing"

"github.com/Azure/azure-container-networking/cns/cnireconciler"
"github.com/Azure/azure-container-networking/cns/logger"
"github.com/Azure/azure-container-networking/cns/restserver"
"github.com/Azure/azure-container-networking/cns/types"
"github.com/Azure/azure-container-networking/store"
)

// getMockStore creates a mock KeyValueStore with some endpoint state
func getMockStore() store.KeyValueStore {
mockStore := store.NewMockStore("")
endpointState := map[string]*restserver.EndpointInfo{
"12e65d89e58cb23c784e97840cf76866bfc9902089bdc8e87e9f64032e312b0b": {
PodName: "coredns-54b69f46b8-ldmwr",
PodNamespace: "kube-system",
IfnameToIPMap: map[string]*restserver.IPInfo{
"eth0": {
IPv4: []net.IPNet{
{
IP: net.IPv4(10, 0, 0, 52),
Mask: net.CIDRMask(24, 32),
},
},
},
},
},
"1fc5176913a3a1a7facfb823dde3b4ded404041134fef4f4a0c8bba140fc0413": {
PodName: "load-test-7f7d49687d-wxc9p",
PodNamespace: "load-test",
IfnameToIPMap: map[string]*restserver.IPInfo{
"eth0": {
IPv4: []net.IPNet{
{
IP: net.IPv4(10, 0, 0, 63),
Mask: net.CIDRMask(24, 32),
},
},
},
},
},
}

err := mockStore.Write(restserver.EndpointStoreKey, endpointState)
if err != nil {
return nil
}
return mockStore
}

// Mock implementation of CNIConflistGenerator
type MockCNIConflistGenerator struct {
GenerateCalled chan struct{}
}

func (m *MockCNIConflistGenerator) Generate() error {
close(m.GenerateCalled)
return nil
}

func (m *MockCNIConflistGenerator) Close() error {
// Implement the Close method logic here if needed
return nil
}

// TestNodeSubnet tests initialization of NodeSubnet with endpoint info, and verfies that
// the conflist is generated after fetching secondary IPs
func TestNodeSubnet(t *testing.T) {
podInfoByIPProvider, err := cnireconciler.NewCNSPodInfoProvider(getMockStore())
if err != nil {
t.Fatalf("NewCNSPodInfoProvider returned an error: %v", err)
}

// create a real HTTPRestService object
mockCNIConflistGenerator := &MockCNIConflistGenerator{
GenerateCalled: make(chan struct{}),
}
service := restserver.GetRestServiceObjectForNodeSubnetTest(t, mockCNIConflistGenerator)
ctx, cancel := testContext(t)
defer cancel()

err = service.InitializeNodeSubnet(ctx, podInfoByIPProvider)
if err != nil {
t.Fatalf("InitializeNodeSubnet returned an error: %v", err)
}

expectedIPs := map[string]types.IPState{
"10.0.0.52": types.Assigned,
"10.0.0.63": types.Assigned,
}

checkIPassignment(t, service, expectedIPs)

service.StartNodeSubnet(ctx)

if service.GetNodesubnetIPFetcher() == nil {
t.Fatal("NodeSubnetIPFetcher is not initialized")
}

select {
case <-ctx.Done():
t.Errorf("test context done - %s", ctx.Err())
return
case <-mockCNIConflistGenerator.GenerateCalled:
break
}

expectedIPs["10.0.0.45"] = types.Available
checkIPassignment(t, service, expectedIPs)
}

// checkIPassignment checks whether the IP assignment state in the HTTPRestService object matches expectation
func checkIPassignment(t *testing.T, service *restserver.HTTPRestService, expectedIPs map[string]types.IPState) {
if len(service.PodIPConfigState) != len(expectedIPs) {
t.Fatalf("expected 2 entries in PodIPConfigState, got %d", len(service.PodIPConfigState))
}

for ip := range service.GetPodIPConfigState() {
config := service.GetPodIPConfigState()[ip]
if assignmentState, exists := expectedIPs[ip]; !exists {
t.Fatalf("unexpected IP %s in PodIPConfigState", ip)
} else if config.GetState() != assignmentState {
t.Fatalf("expected state 'Assigned' for IP %s, got %s", ip, config.GetState())
}
}
}

// testContext creates a context from the provided testing.T that will be
// canceled if the test suite is terminated.
func testContext(t *testing.T) (context.Context, context.CancelFunc) {
if deadline, ok := t.Deadline(); ok {
return context.WithDeadline(context.Background(), deadline)
}
return context.WithCancel(context.Background())
}

func init() {
logger.InitLogger("testlogs", 0, 0, "./")
}
3 changes: 3 additions & 0 deletions cns/restserver/restserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/Azure/azure-container-networking/cns/dockerclient"
"github.com/Azure/azure-container-networking/cns/logger"
"github.com/Azure/azure-container-networking/cns/networkcontainers"
"github.com/Azure/azure-container-networking/cns/nodesubnet"
"github.com/Azure/azure-container-networking/cns/routes"
"github.com/Azure/azure-container-networking/cns/types"
"github.com/Azure/azure-container-networking/cns/types/bounded"
Expand Down Expand Up @@ -40,6 +41,7 @@ type nmagentClient interface {
SupportedAPIs(context.Context) ([]string, error)
GetNCVersionList(context.Context) (nma.NCVersionList, error)
GetHomeAz(context.Context) (nma.AzResponse, error)
GetInterfaceIPInfo(ctx context.Context) (nma.Interfaces, error)
}

type wireserverProxy interface {
Expand Down Expand Up @@ -76,6 +78,7 @@ type HTTPRestService struct {
IPConfigsHandlerMiddleware cns.IPConfigsHandlerMiddleware
PnpIDByMacAddress map[string]string
imdsClient imdsClient
nodesubnetIPFetcher *nodesubnet.IPFetcher
}

type CNIConflistGenerator interface {
Expand Down
Loading
Loading