Skip to content

Commit 9a3a101

Browse files
committed
Add HomeAZ info to NodeInfo CRD upon creation
1 parent b823b09 commit 9a3a101

File tree

2 files changed

+200
-8
lines changed

2 files changed

+200
-8
lines changed

cns/service/main.go

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,7 +1419,7 @@ func InitializeCRDState(ctx context.Context, z *zap.Logger, httpRestService cns.
14191419
if _, ok := node.Labels[configuration.LabelNodeSwiftV2]; ok {
14201420
cnsconfig.EnableSwiftV2 = true
14211421
cnsconfig.WatchPods = true
1422-
if nodeInfoErr := createOrUpdateNodeInfoCRD(ctx, kubeConfig, node); nodeInfoErr != nil {
1422+
if nodeInfoErr := createOrUpdateNodeInfoCRD(ctx, z, kubeConfig, node); nodeInfoErr != nil {
14231423
return errors.Wrap(nodeInfoErr, "error creating or updating nodeinfo crd")
14241424
}
14251425
}
@@ -1694,29 +1694,68 @@ func getPodInfoByIPProvider(
16941694

16951695
// createOrUpdateNodeInfoCRD polls imds to learn the VM Unique ID and then creates or updates the NodeInfo CRD
16961696
// with that vm unique ID
1697-
func createOrUpdateNodeInfoCRD(ctx context.Context, restConfig *rest.Config, node *corev1.Node) error {
1697+
func createOrUpdateNodeInfoCRD(ctx context.Context, z *zap.Logger, 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, z, 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, z *zap.Logger, 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+
homeAzResponse, err := nmaCli.GetHomeAz(ctx)
1748+
if err != nil {
1749+
z.Warn("failed to get HomeAZ from nmagent", zap.Error(err))
1750+
} else if homeAzResponse.HomeAz > 0 {
1751+
nodeInfoSpec.HomeAZ = fmt.Sprintf("AZ%02d", homeAzResponse.HomeAz)
1752+
}
1753+
17131754
nodeInfo := &mtv1alpha1.NodeInfo{
17141755
ObjectMeta: metav1.ObjectMeta{
17151756
Name: node.Name,
17161757
},
1717-
Spec: mtv1alpha1.NodeInfoSpec{
1718-
VMUniqueID: vmUniqueID,
1719-
},
1758+
Spec: nodeInfoSpec,
17201759
}
17211760

17221761
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,14 @@ 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+
"go.uber.org/zap"
19+
corev1 "k8s.io/api/core/v1"
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1421
)
1522

1623
// MockHTTPClient is a mock implementation of HTTPClient
@@ -69,3 +76,149 @@ func TestSendRegisterNodeRequest_StatusAccepted(t *testing.T) {
6976

7077
assert.Error(t, sendRegisterNodeRequest(ctx, mockClient, httpServiceFake, nodeRegisterReq, url))
7178
}
79+
80+
// mockIMDSClient is a mock implementation of the VMUniqueIDGetter interface
81+
type mockIMDSClient struct {
82+
vmUniqueID string
83+
err error
84+
}
85+
86+
func (m *mockIMDSClient) GetVMUniqueID(_ context.Context) (string, error) {
87+
return m.vmUniqueID, m.err
88+
}
89+
90+
// mockNMAgentClient is a mock implementation of the HomeAzGetter interface
91+
type mockNMAgentClient struct {
92+
homeAzResponse nmagent.AzResponse
93+
err error
94+
}
95+
96+
func (m *mockNMAgentClient) GetHomeAz(_ context.Context) (nmagent.AzResponse, error) {
97+
return m.homeAzResponse, m.err
98+
}
99+
100+
// mockNodeInfoClient is a mock implementation of the NodeInfoClient interface
101+
type mockNodeInfoClient struct {
102+
createdNodeInfo *mtv1alpha1.NodeInfo
103+
err error
104+
}
105+
106+
func (m *mockNodeInfoClient) CreateOrUpdate(_ context.Context, nodeInfo *mtv1alpha1.NodeInfo, _ string) error {
107+
m.createdNodeInfo = nodeInfo
108+
return m.err
109+
}
110+
111+
func TestBuildNodeInfoSpec_WithHomeAZ(t *testing.T) {
112+
tests := []struct {
113+
name string
114+
vmUniqueID string
115+
vmUniqueIDErr error
116+
homeAzResponse nmagent.AzResponse
117+
homeAzErr error
118+
expectedSpec mtv1alpha1.NodeInfoSpec
119+
expectedNodeErr bool
120+
}{
121+
{
122+
name: "success with HomeAZ zone 1",
123+
vmUniqueID: "test-vm-unique-id",
124+
vmUniqueIDErr: nil,
125+
homeAzResponse: nmagent.AzResponse{HomeAz: 1},
126+
homeAzErr: nil,
127+
expectedSpec: mtv1alpha1.NodeInfoSpec{
128+
VMUniqueID: "test-vm-unique-id",
129+
HomeAZ: "AZ01",
130+
},
131+
expectedNodeErr: false,
132+
},
133+
{
134+
name: "success with HomeAZ zone 2",
135+
vmUniqueID: "another-vm-id",
136+
vmUniqueIDErr: nil,
137+
homeAzResponse: nmagent.AzResponse{HomeAz: 2},
138+
homeAzErr: nil,
139+
expectedSpec: mtv1alpha1.NodeInfoSpec{
140+
VMUniqueID: "another-vm-id",
141+
HomeAZ: "AZ02",
142+
},
143+
expectedNodeErr: false,
144+
},
145+
{
146+
name: "success with HomeAZ zone 10",
147+
vmUniqueID: "vm-id-zone10",
148+
vmUniqueIDErr: nil,
149+
homeAzResponse: nmagent.AzResponse{HomeAz: 10},
150+
homeAzErr: nil,
151+
expectedSpec: mtv1alpha1.NodeInfoSpec{
152+
VMUniqueID: "vm-id-zone10",
153+
HomeAZ: "AZ10",
154+
},
155+
expectedNodeErr: false,
156+
},
157+
{
158+
name: "HomeAZ not available - should succeed without HomeAZ",
159+
vmUniqueID: "test-vm-id",
160+
vmUniqueIDErr: nil,
161+
homeAzResponse: nmagent.AzResponse{},
162+
homeAzErr: errors.New("nmagent HomeAZ not available"),
163+
expectedSpec: mtv1alpha1.NodeInfoSpec{
164+
VMUniqueID: "test-vm-id",
165+
HomeAZ: "", // HomeAZ should be empty when not available
166+
},
167+
expectedNodeErr: false,
168+
},
169+
{
170+
name: "IMDS error - should fail",
171+
vmUniqueID: "",
172+
vmUniqueIDErr: errors.New("imds error"),
173+
homeAzResponse: nmagent.AzResponse{HomeAz: 1},
174+
homeAzErr: nil,
175+
expectedSpec: mtv1alpha1.NodeInfoSpec{},
176+
expectedNodeErr: true,
177+
},
178+
{
179+
name: "HomeAZ zone 0 - should be treated as not available",
180+
vmUniqueID: "test-vm-id",
181+
vmUniqueIDErr: nil,
182+
homeAzResponse: nmagent.AzResponse{HomeAz: 0},
183+
homeAzErr: nil,
184+
expectedSpec: mtv1alpha1.NodeInfoSpec{
185+
VMUniqueID: "test-vm-id",
186+
HomeAZ: "",
187+
},
188+
expectedNodeErr: false,
189+
},
190+
}
191+
192+
for _, tt := range tests {
193+
t.Run(tt.name, func(t *testing.T) {
194+
z := zap.NewNop()
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(), z, 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)