@@ -3,24 +3,14 @@ package main
33import (
44 "bytes"
55 "context"
6- "encoding/json"
7- "fmt"
86 "io"
97 "net/http"
10- "net/http/httptest"
11- "strings"
128 "testing"
13- "time"
149
1510 "github.com/Azure/azure-container-networking/cns"
1611 "github.com/Azure/azure-container-networking/cns/fakes"
1712 "github.com/Azure/azure-container-networking/cns/logger"
18- "github.com/Azure/azure-container-networking/crd/multitenancy/api/v1alpha1"
1913 "github.com/stretchr/testify/assert"
20- "github.com/stretchr/testify/require"
21- corev1 "k8s.io/api/core/v1"
22- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23- "k8s.io/client-go/rest"
2414)
2515
2616// MockHTTPClient is a mock implementation of HTTPClient
@@ -79,184 +69,3 @@ func TestSendRegisterNodeRequest_StatusAccepted(t *testing.T) {
7969
8070 assert .Error (t , sendRegisterNodeRequest (ctx , mockClient , httpServiceFake , nodeRegisterReq , url ))
8171}
82-
83- func TestCreateOrUpdateNodeInfoCRD_PopulatesHomeAZ (t * testing.T ) {
84- vmID := "test-vm-unique-id-12345"
85- homeAZ := uint (2 )
86- HomeAZStr := fmt .Sprintf ("AZ0%d" , homeAZ )
87-
88- // Create mock IMDS server
89- mockIMDSServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
90- if strings .Contains (r .URL .Path , "/metadata/instance/compute" ) {
91- w .Header ().Set ("Content-Type" , "application/json" )
92- w .WriteHeader (http .StatusOK )
93- response := map [string ]interface {}{
94- "vmId" : vmID ,
95- "name" : "test-vm" ,
96- "resourceGroupName" : "test-rg" ,
97- }
98- _ = json .NewEncoder (w ).Encode (response )
99- return
100- }
101- w .WriteHeader (http .StatusNotFound )
102- }))
103- defer mockIMDSServer .Close ()
104-
105- // Create mock CNS server
106- mockCNSServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
107- if strings .Contains (r .URL .Path , "/homeaz" ) || strings .Contains (r .URL .Path , "homeaz" ) {
108- w .Header ().Set ("Content-Type" , "application/json" )
109- w .WriteHeader (http .StatusOK )
110- response := map [string ]interface {}{
111- "ReturnCode" : 0 ,
112- "Message" : "" ,
113- "HomeAzResponse" : map [string ]interface {}{
114- "IsSupported" : true ,
115- "HomeAz" : homeAZ ,
116- },
117- }
118- _ = json .NewEncoder (w ).Encode (response )
119- return
120- }
121- w .WriteHeader (http .StatusNotFound )
122- }))
123- defer mockCNSServer .Close ()
124-
125- // Set up HTTP transport to mock IMDS and CNS
126- originalTransport := http .DefaultTransport
127- defer func () { http .DefaultTransport = originalTransport }()
128-
129- http .DefaultTransport = & mockTransport {
130- imdsServer : mockIMDSServer ,
131- cnsServer : mockCNSServer ,
132- original : originalTransport ,
133- }
134-
135- // Create a mock Kubernetes server that captures the NodeInfo being created
136- var capturedNodeInfo * v1alpha1.NodeInfo
137-
138- mockK8sServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
139- // Handle specific API group discovery - multitenancy.acn.azure.com
140- if r .URL .Path == "/apis/multitenancy.acn.azure.com/v1alpha1" && r .Method == "GET" {
141- w .Header ().Set ("Content-Type" , "application/json" )
142- w .WriteHeader (http .StatusOK )
143- _ = json .NewEncoder (w ).Encode (map [string ]interface {}{
144- "kind" : "APIResourceList" ,
145- "groupVersion" : "multitenancy.acn.azure.com/v1alpha1" ,
146- "resources" : []map [string ]interface {}{
147- {
148- "name" : "nodeinfos" ,
149- "singularName" : "nodeinfo" ,
150- "namespaced" : false ,
151- "kind" : "NodeInfo" ,
152- "verbs" : []string {"create" , "delete" , "get" , "list" , "patch" , "update" , "watch" },
153- },
154- },
155- })
156- return
157- }
158-
159- // Handle NodeInfo resource requests
160- if strings .Contains (r .URL .Path , "nodeinfos" ) || strings .Contains (r .URL .Path , "multitenancy" ) {
161- if r .Method == "POST" || r .Method == "PATCH" || r .Method == "PUT" {
162- body , _ := io .ReadAll (r .Body )
163-
164- // Try to parse the NodeInfo from the request
165- var nodeInfo v1alpha1.NodeInfo
166- if err := json .Unmarshal (body , & nodeInfo ); err == nil {
167- capturedNodeInfo = & nodeInfo
168- }
169-
170- w .Header ().Set ("Content-Type" , "application/json" )
171- w .WriteHeader (http .StatusOK )
172- // Return the created NodeInfo
173- _ = json .NewEncoder (w ).Encode (map [string ]interface {}{
174- "apiVersion" : "multitenancy.acn.azure.com/v1alpha1" ,
175- "kind" : "NodeInfo" ,
176- "metadata" : map [string ]interface {}{
177- "name" : "test-node" ,
178- },
179- "spec" : map [string ]interface {}{
180- "vmUniqueID" : vmID ,
181- "homeAZ" : HomeAZStr ,
182- },
183- })
184- return
185- }
186- }
187-
188- // Default success response for any other API calls
189- w .Header ().Set ("Content-Type" , "application/json" )
190- w .WriteHeader (http .StatusOK )
191- _ = json .NewEncoder (w ).Encode (map [string ]interface {}{
192- "kind" : "Status" ,
193- "status" : "Success" ,
194- })
195- }))
196- defer mockK8sServer .Close ()
197-
198- // Test the function with mocked dependencies
199- ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Second )
200- defer cancel ()
201-
202- // Point to our mock Kubernetes server
203- restConfig := & rest.Config {
204- Host : mockK8sServer .URL ,
205- }
206-
207- node := & corev1.Node {
208- ObjectMeta : metav1.ObjectMeta {Name : "test-node" },
209- }
210-
211- // Call the createOrUpdateNodeInfoCRD function
212- err := createOrUpdateNodeInfoCRD (ctx , restConfig , node )
213-
214- // Verify the function succeeded
215- require .NoError (t , err , "Function should succeed with mocked dependencies" )
216-
217- // Verify the captured values
218- assert .NotNil (t , capturedNodeInfo , "NodeInfo should have been captured from K8s API call" )
219- if capturedNodeInfo != nil {
220- assert .Equal (t , vmID , capturedNodeInfo .Spec .VMUniqueID , "VMUniqueID should be from IMDS" )
221- assert .Equal (t , HomeAZStr , capturedNodeInfo .Spec .HomeAZ , "HomeAZ should be formatted from CNS response" )
222- }
223- }
224-
225- // mockTransport redirects HTTP requests to mock servers for testing.
226- // It intercepts requests to IMDS and CNS endpoints and routes them to local test servers.
227- type mockTransport struct {
228- imdsServer * httptest.Server
229- cnsServer * httptest.Server
230- original http.RoundTripper
231- }
232-
233- func (m * mockTransport ) RoundTrip (req * http.Request ) (* http.Response , error ) {
234- // Redirect IMDS calls to mock IMDS server
235- if req .URL .Host == "169.254.169.254" {
236- req .URL .Scheme = "http"
237- req .URL .Host = strings .TrimPrefix (m .imdsServer .URL , "http://" )
238- resp , err := m .original .RoundTrip (req )
239- if err != nil {
240- return nil , fmt .Errorf ("IMDS mock transport failed: %w" , err )
241- }
242- return resp , nil
243- }
244-
245- // Redirect CNS calls to mock CNS server
246- if req .URL .Host == "localhost:10090" || strings .Contains (req .URL .Host , "10090" ) {
247- req .URL .Scheme = "http"
248- req .URL .Host = strings .TrimPrefix (m .cnsServer .URL , "http://" )
249- resp , err := m .original .RoundTrip (req )
250- if err != nil {
251- return nil , fmt .Errorf ("CNS mock transport failed: %w" , err )
252- }
253- return resp , nil
254- }
255-
256- // All other calls go through original transport
257- resp , err := m .original .RoundTrip (req )
258- if err != nil {
259- return nil , fmt .Errorf ("mock transport failed: %w" , err )
260- }
261- return resp , nil
262- }
0 commit comments