Skip to content

Commit 507a1a2

Browse files
committed
Add HomeAZ info to NodeInfo CRD upon creation
1 parent b823b09 commit 507a1a2

File tree

2 files changed

+201
-6
lines changed

2 files changed

+201
-6
lines changed

cns/service/main.go

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,27 +1696,69 @@ func getPodInfoByIPProvider(
16961696
// with that vm unique ID
16971697
func createOrUpdateNodeInfoCRD(ctx context.Context, restConfig *rest.Config, node *corev1.Node) error {
16981698
imdsCli := imds.NewClient()
1699-
vmUniqueID, err := imdsCli.GetVMUniqueID(ctx)
1699+
1700+
nmaConfig, err := nmagent.NewConfig("")
17001701
if err != nil {
1701-
return errors.Wrap(err, "error getting vm unique ID from imds")
1702+
return errors.Wrap(err, "failed to create nmagent config")
1703+
}
1704+
nmaCli, err := nmagent.NewClient(nmaConfig)
1705+
if err != nil {
1706+
return errors.Wrap(err, "failed to create nmagent client")
17021707
}
17031708

17041709
directcli, err := client.New(restConfig, client.Options{Scheme: multitenancy.Scheme})
17051710
if err != nil {
17061711
return errors.Wrap(err, "failed to create ctrl client")
17071712
}
17081713

1709-
nodeInfoCli := multitenancy.NodeInfoClient{
1714+
nodeInfoCli := &multitenancy.NodeInfoClient{
17101715
Cli: directcli,
17111716
}
17121717

1718+
return buildAndCreateNodeInfo(ctx, imdsCli, nmaCli, nodeInfoCli, node)
1719+
}
1720+
1721+
// VMUniqueIDGetter is an interface for getting VM unique ID from IMDS
1722+
type VMUniqueIDGetter interface {
1723+
GetVMUniqueID(ctx context.Context) (string, error)
1724+
}
1725+
1726+
// HomeAzGetter is an interface for getting HomeAZ from NMAgent
1727+
type HomeAzGetter interface {
1728+
GetHomeAz(ctx context.Context) (nmagent.AzResponse, error)
1729+
}
1730+
1731+
// NodeInfoCreator is an interface for creating/updating NodeInfo CRD
1732+
type NodeInfoCreator interface {
1733+
CreateOrUpdate(ctx context.Context, nodeInfo *mtv1alpha1.NodeInfo, fieldOwner string) error
1734+
}
1735+
1736+
// buildAndCreateNodeInfo builds the NodeInfo spec with VMUniqueID and HomeAZ and creates/updates the CRD
1737+
func buildAndCreateNodeInfo(ctx context.Context, imdsCli VMUniqueIDGetter, nmaCli HomeAzGetter, nodeInfoCli NodeInfoCreator, node *corev1.Node) error {
1738+
vmUniqueID, err := imdsCli.GetVMUniqueID(ctx)
1739+
if err != nil {
1740+
return errors.Wrap(err, "error getting vm unique ID from imds")
1741+
}
1742+
1743+
nodeInfoSpec := mtv1alpha1.NodeInfoSpec{
1744+
VMUniqueID: vmUniqueID,
1745+
}
1746+
1747+
// Fetch HomeAZ from NMAgent - this is optional, so we don't fail if it's not available
1748+
homeAzResponse, err := nmaCli.GetHomeAz(ctx)
1749+
if err != nil {
1750+
// Log the error but don't fail - HomeAZ is optional
1751+
logger.Printf("[Azure CNS] Warning: failed to get HomeAZ from nmagent: %v", err)
1752+
} else if homeAzResponse.HomeAz > 0 {
1753+
// Format HomeAZ as "AZXX" where XX is the zero-padded zone number
1754+
nodeInfoSpec.HomeAZ = fmt.Sprintf("AZ%02d", homeAzResponse.HomeAz)
1755+
}
1756+
17131757
nodeInfo := &mtv1alpha1.NodeInfo{
17141758
ObjectMeta: metav1.ObjectMeta{
17151759
Name: node.Name,
17161760
},
1717-
Spec: mtv1alpha1.NodeInfoSpec{
1718-
VMUniqueID: vmUniqueID,
1719-
},
1761+
Spec: nodeInfoSpec,
17201762
}
17211763

17221764
if err := controllerutil.SetOwnerReference(node, nodeInfo, multitenancy.Scheme); err != nil {

cns/service/main_test.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ import (
1010
"github.com/Azure/azure-container-networking/cns"
1111
"github.com/Azure/azure-container-networking/cns/fakes"
1212
"github.com/Azure/azure-container-networking/cns/logger"
13+
mtv1alpha1 "github.com/Azure/azure-container-networking/crd/multitenancy/api/v1alpha1"
14+
"github.com/Azure/azure-container-networking/nmagent"
15+
"github.com/pkg/errors"
1316
"github.com/stretchr/testify/assert"
17+
"github.com/stretchr/testify/require"
18+
corev1 "k8s.io/api/core/v1"
19+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1420
)
1521

1622
// MockHTTPClient is a mock implementation of HTTPClient
@@ -69,3 +75,150 @@ func TestSendRegisterNodeRequest_StatusAccepted(t *testing.T) {
6975

7076
assert.Error(t, sendRegisterNodeRequest(ctx, mockClient, httpServiceFake, nodeRegisterReq, url))
7177
}
78+
79+
// mockIMDSClient is a mock implementation of the VMUniqueIDGetter interface
80+
type mockIMDSClient struct {
81+
vmUniqueID string
82+
err error
83+
}
84+
85+
func (m *mockIMDSClient) GetVMUniqueID(_ context.Context) (string, error) {
86+
return m.vmUniqueID, m.err
87+
}
88+
89+
// mockNMAgentClient is a mock implementation of the HomeAzGetter interface
90+
type mockNMAgentClient struct {
91+
homeAzResponse nmagent.AzResponse
92+
err error
93+
}
94+
95+
func (m *mockNMAgentClient) GetHomeAz(_ context.Context) (nmagent.AzResponse, error) {
96+
return m.homeAzResponse, m.err
97+
}
98+
99+
// mockNodeInfoClient is a mock implementation of the NodeInfoClient interface
100+
type mockNodeInfoClient struct {
101+
createdNodeInfo *mtv1alpha1.NodeInfo
102+
err error
103+
}
104+
105+
func (m *mockNodeInfoClient) CreateOrUpdate(_ context.Context, nodeInfo *mtv1alpha1.NodeInfo, _ string) error {
106+
m.createdNodeInfo = nodeInfo
107+
return m.err
108+
}
109+
110+
func TestBuildNodeInfoSpec_WithHomeAZ(t *testing.T) {
111+
tests := []struct {
112+
name string
113+
vmUniqueID string
114+
vmUniqueIDErr error
115+
homeAzResponse nmagent.AzResponse
116+
homeAzErr error
117+
expectedSpec mtv1alpha1.NodeInfoSpec
118+
expectedNodeErr bool
119+
}{
120+
{
121+
name: "success with HomeAZ zone 1",
122+
vmUniqueID: "test-vm-unique-id",
123+
vmUniqueIDErr: nil,
124+
homeAzResponse: nmagent.AzResponse{HomeAz: 1},
125+
homeAzErr: nil,
126+
expectedSpec: mtv1alpha1.NodeInfoSpec{
127+
VMUniqueID: "test-vm-unique-id",
128+
HomeAZ: "AZ01",
129+
},
130+
expectedNodeErr: false,
131+
},
132+
{
133+
name: "success with HomeAZ zone 2",
134+
vmUniqueID: "another-vm-id",
135+
vmUniqueIDErr: nil,
136+
homeAzResponse: nmagent.AzResponse{HomeAz: 2},
137+
homeAzErr: nil,
138+
expectedSpec: mtv1alpha1.NodeInfoSpec{
139+
VMUniqueID: "another-vm-id",
140+
HomeAZ: "AZ02",
141+
},
142+
expectedNodeErr: false,
143+
},
144+
{
145+
name: "success with HomeAZ zone 10",
146+
vmUniqueID: "vm-id-zone10",
147+
vmUniqueIDErr: nil,
148+
homeAzResponse: nmagent.AzResponse{HomeAz: 10},
149+
homeAzErr: nil,
150+
expectedSpec: mtv1alpha1.NodeInfoSpec{
151+
VMUniqueID: "vm-id-zone10",
152+
HomeAZ: "AZ10",
153+
},
154+
expectedNodeErr: false,
155+
},
156+
{
157+
name: "HomeAZ not available - should succeed without HomeAZ",
158+
vmUniqueID: "test-vm-id",
159+
vmUniqueIDErr: nil,
160+
homeAzResponse: nmagent.AzResponse{},
161+
homeAzErr: errors.New("nmagent HomeAZ not available"),
162+
expectedSpec: mtv1alpha1.NodeInfoSpec{
163+
VMUniqueID: "test-vm-id",
164+
HomeAZ: "", // HomeAZ should be empty when not available
165+
},
166+
expectedNodeErr: false,
167+
},
168+
{
169+
name: "IMDS error - should fail",
170+
vmUniqueID: "",
171+
vmUniqueIDErr: errors.New("imds error"),
172+
homeAzResponse: nmagent.AzResponse{HomeAz: 1},
173+
homeAzErr: nil,
174+
expectedSpec: mtv1alpha1.NodeInfoSpec{},
175+
expectedNodeErr: true,
176+
},
177+
{
178+
name: "HomeAZ zone 0 - should be treated as not available",
179+
vmUniqueID: "test-vm-id",
180+
vmUniqueIDErr: nil,
181+
homeAzResponse: nmagent.AzResponse{HomeAz: 0},
182+
homeAzErr: nil,
183+
expectedSpec: mtv1alpha1.NodeInfoSpec{
184+
VMUniqueID: "test-vm-id",
185+
HomeAZ: "", // Zone 0 means not in an availability zone
186+
},
187+
expectedNodeErr: false,
188+
},
189+
}
190+
191+
for _, tt := range tests {
192+
t.Run(tt.name, func(t *testing.T) {
193+
// Initialize logger to avoid nil pointer dereference
194+
logger.InitLogger("testlogs", 0, 0, "./")
195+
196+
imdsCli := &mockIMDSClient{
197+
vmUniqueID: tt.vmUniqueID,
198+
err: tt.vmUniqueIDErr,
199+
}
200+
nmaCli := &mockNMAgentClient{
201+
homeAzResponse: tt.homeAzResponse,
202+
err: tt.homeAzErr,
203+
}
204+
nodeInfoCli := &mockNodeInfoClient{}
205+
node := &corev1.Node{
206+
ObjectMeta: metav1.ObjectMeta{
207+
Name: "test-node",
208+
UID: "test-uid",
209+
},
210+
}
211+
212+
err := buildAndCreateNodeInfo(context.Background(), imdsCli, nmaCli, nodeInfoCli, node)
213+
214+
if tt.expectedNodeErr {
215+
require.Error(t, err)
216+
return
217+
}
218+
require.NoError(t, err)
219+
require.NotNil(t, nodeInfoCli.createdNodeInfo)
220+
assert.Equal(t, tt.expectedSpec.VMUniqueID, nodeInfoCli.createdNodeInfo.Spec.VMUniqueID)
221+
assert.Equal(t, tt.expectedSpec.HomeAZ, nodeInfoCli.createdNodeInfo.Spec.HomeAZ)
222+
})
223+
}
224+
}

0 commit comments

Comments
 (0)