Skip to content

Commit 5b27f4a

Browse files
rbtrtimraymond
andauthored
Fix missing Wireserver IP configuration in Wireserver Client (#2271)
* Add coverage around GetInterfaces in WS client There's a bug with the wireserver client, in that the Wireserver IP from the CNS config isn't respected. This adds coverage so that it becomes safer to make changes here. * Allow Wireserver client to use a configurable Host This allows the Wireserver client to use a configurable host so that this could, theoretically be mocked. As it currently stands, it's an impediment to running CNS locally, since it uses the real Wireserver IP. * Add WireserverIP config into Wireserver Client The Wireserver client now accepts a hostport configuration option so that its requests can be redirected. This plumbs the config for the WireserverIP to it so that it can be redirected during test. * fix: replace nil with http.NoBody Signed-off-by: Evan Baker <[email protected]> --------- Signed-off-by: Evan Baker <[email protected]> Co-authored-by: Tim Raymond <[email protected]>
1 parent 125e75c commit 5b27f4a

File tree

3 files changed

+120
-6
lines changed

3 files changed

+120
-6
lines changed

cns/service/main.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,13 @@ func main() {
677677
HTTPClient: &http.Client{},
678678
}
679679

680-
httpRestService, err := restserver.NewHTTPRestService(&config, &wireserver.Client{HTTPClient: &http.Client{}}, &wsProxy, nmaClient,
680+
wsclient := &wireserver.Client{
681+
HostPort: cnsconfig.WireserverIP,
682+
HTTPClient: &http.Client{},
683+
Logger: logger.Log,
684+
}
685+
686+
httpRestService, err := restserver.NewHTTPRestService(&config, wsclient, &wsProxy, nmaClient,
681687
endpointStateStore, conflistGenerator, homeAzMonitor)
682688
if err != nil {
683689
logger.Errorf("Failed to create CNS object, err:%v.\n", err)

cns/wireserver/client.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import (
66
"encoding/xml"
77
"io"
88
"net/http"
9+
"net/url"
910

10-
"github.com/Azure/azure-container-networking/cns/logger"
1111
"github.com/pkg/errors"
1212
)
1313

14-
const hostQueryURL = "http://168.63.129.16/machine/plugins?comp=nmagent&type=getinterfaceinfov1"
14+
const (
15+
WireserverIP = "168.63.129.16"
16+
)
1517

1618
type GetNetworkContainerOpts struct {
1719
NetworkContainerID string
@@ -25,14 +27,34 @@ type do interface {
2527
}
2628

2729
type Client struct {
30+
HostPort string
31+
2832
HTTPClient do
33+
Logger interface {
34+
Printf(string, ...any)
35+
}
36+
}
37+
38+
func (c *Client) hostport() string {
39+
return c.HostPort
2940
}
3041

3142
// GetInterfaces queries interfaces from the wireserver.
3243
func (c *Client) GetInterfaces(ctx context.Context) (*GetInterfacesResult, error) {
33-
logger.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromHost")
44+
c.Logger.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromHost")
45+
46+
q := &url.Values{}
47+
q.Add("comp", "nmagent")
48+
q.Add("type", "getinterfaceinfov1")
49+
50+
reqURL := &url.URL{
51+
Scheme: "http",
52+
Host: c.hostport(),
53+
Path: "/machine/plugins",
54+
RawQuery: q.Encode(),
55+
}
3456

35-
req, err := http.NewRequestWithContext(ctx, http.MethodGet, hostQueryURL, nil)
57+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL.String(), http.NoBody)
3658
if err != nil {
3759
return nil, errors.Wrap(err, "failed to construct request")
3860
}
@@ -46,7 +68,7 @@ func (c *Client) GetInterfaces(ctx context.Context) (*GetInterfacesResult, error
4668
return nil, errors.Wrap(err, "failed to read response body")
4769
}
4870

49-
logger.Printf("[Azure CNS] Response received from NMAgent for get interface details: %s", string(b))
71+
c.Logger.Printf("[Azure CNS] Response received from NMAgent for get interface details: %s", string(b))
5072

5173
var res GetInterfacesResult
5274
if err := xml.NewDecoder(bytes.NewReader(b)).Decode(&res); err != nil {

cns/wireserver/client_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package wireserver_test
2+
3+
import (
4+
"context"
5+
"encoding/xml"
6+
"net/http"
7+
"net/http/httptest"
8+
"testing"
9+
10+
"github.com/Azure/azure-container-networking/cns/wireserver"
11+
)
12+
13+
var _ http.RoundTripper = &TestTripper{}
14+
15+
// TestTripper is a mock implementation of a round tripper that allows clients
16+
// to substitute their own implementation, so that HTTP requests can be
17+
// asserted against and stub responses can be generated.
18+
type TestTripper struct {
19+
RoundTripF func(*http.Request) (*http.Response, error)
20+
}
21+
22+
func (t *TestTripper) RoundTrip(req *http.Request) (*http.Response, error) {
23+
return t.RoundTripF(req)
24+
}
25+
26+
type NOPLogger struct{}
27+
28+
func (m *NOPLogger) Printf(_ string, _ ...any) {}
29+
30+
func TestGetInterfaces(t *testing.T) {
31+
tests := []struct {
32+
name string
33+
hostport string
34+
expURL string
35+
}{
36+
{
37+
"real ws url",
38+
"168.63.129.16",
39+
"http://168.63.129.16/machine/plugins?comp=nmagent&type=getinterfaceinfov1",
40+
},
41+
{
42+
"local ws url",
43+
"127.0.0.1:9001",
44+
"http://127.0.0.1:9001/machine/plugins?comp=nmagent&type=getinterfaceinfov1",
45+
},
46+
}
47+
48+
for _, test := range tests {
49+
test := test
50+
t.Run(test.name, func(t *testing.T) {
51+
t.Parallel()
52+
// create a wireserver client using a test tripper so that it can be asserted
53+
// that the correct requests are sent.
54+
var gotURL string
55+
client := &wireserver.Client{
56+
HostPort: test.hostport,
57+
Logger: &NOPLogger{},
58+
HTTPClient: &http.Client{
59+
Transport: &TestTripper{
60+
RoundTripF: func(req *http.Request) (*http.Response, error) {
61+
gotURL = req.URL.String()
62+
rr := httptest.NewRecorder()
63+
resp := wireserver.GetInterfacesResult{}
64+
err := xml.NewEncoder(rr).Encode(&resp)
65+
if err != nil {
66+
t.Fatal("unexpected error encoding mock wireserver response: err:", err)
67+
}
68+
69+
return rr.Result(), nil
70+
},
71+
},
72+
},
73+
}
74+
75+
// invoke the endpoint on Wireserver
76+
_, err := client.GetInterfaces(context.TODO())
77+
if err != nil {
78+
t.Fatal("unexpected error invoking GetInterfaces: err:", err)
79+
}
80+
81+
if test.expURL != gotURL {
82+
t.Error("received request URL to wireserve does not match expectation:\n\texp:", test.expURL, "\n\tgot:", gotURL)
83+
}
84+
})
85+
}
86+
}

0 commit comments

Comments
 (0)