Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions cns/NetworkContainerContract.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
// Container Network Service DNC Contract
const (
SetOrchestratorType = "/network/setorchestratortype"
GetHomeAz = "/homeaz"
CreateOrUpdateNetworkContainer = "/network/createorupdatenetworkcontainer"
DeleteNetworkContainer = "/network/deletenetworkcontainer"
PublishNetworkContainer = "/network/publishnetworkcontainer"
Expand Down
12 changes: 12 additions & 0 deletions cns/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type IPConfigurationStatus struct {
// Equals compares a subset of the IPConfigurationStatus fields since a direct
// DeepEquals or otherwise complete comparison of two IPConfigurationStatus objects
// compares internal state details that don't impact their functional equality.
//
//nolint:gocritic // it's safer to pass this by value
func (i *IPConfigurationStatus) Equals(o IPConfigurationStatus) bool {
if i.PodInfo != nil && o.PodInfo != nil {
Expand Down Expand Up @@ -107,6 +108,7 @@ func (i *IPConfigurationStatus) String() string {
// a struct that has public fields for the original struct's private fields,
// embed the original struct in an anonymous struct as the alias type, and then
// let the default marshaller do its magic.
//
//nolint:gocritic // ignore hugeParam it's a value receiver on purpose
func (i IPConfigurationStatus) MarshalJSON() ([]byte, error) {
type alias IPConfigurationStatus
Expand Down Expand Up @@ -335,3 +337,13 @@ type NmAgentSupportedApisResponse struct {
Response Response
SupportedApis []string
}

type HomeAzResponse struct {
IsSupported bool `json:"isSupported"`
HomeAz uint `json:"homeAz"`
}

type GetHomeAzResponse struct {
Response Response `json:"response"`
HomeAzResponse HomeAzResponse `json:"homeAzResponse"`
}
36 changes: 36 additions & 0 deletions cns/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var clientPaths = []string{
cns.NMAgentSupportedAPIs,
cns.DeleteNetworkContainer,
cns.NetworkContainersURLPath,
cns.GetHomeAz,
}

type do interface {
Expand Down Expand Up @@ -811,3 +812,38 @@ func (c *Client) PostAllNetworkContainers(ctx context.Context, createNcRequest c

return nil
}

// GetHomeAz gets home AZ of host
func (c *Client) GetHomeAz(ctx context.Context) (*cns.GetHomeAzResponse, error) {
// build the request
u := c.routes[cns.GetHomeAz]
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), http.NoBody)
if err != nil {
return nil, errors.Wrap(err, "building http request")
}

// submit the request
resp, err := c.client.Do(req)
if err != nil {
return nil, errors.Wrap(err, "sending HTTP request")
}
defer resp.Body.Close()

// decode the response
var getHomeAzResponse cns.GetHomeAzResponse
err = json.NewDecoder(resp.Body).Decode(&getHomeAzResponse)
if err != nil {
return nil, errors.Wrap(err, "decoding response as JSON")
}

// if the return code is non-zero, something went wrong and it should be
// surfaced to the caller
if getHomeAzResponse.Response.ReturnCode != 0 {
return nil, &CNSClientError{
Code: getHomeAzResponse.Response.ReturnCode,
Err: errors.New(getHomeAzResponse.Response.Message),
}
}

return &getHomeAzResponse, nil
}
65 changes: 64 additions & 1 deletion cns/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func TestMain(m *testing.M) {
logger.InitLogger(logName, 0, 0, tmpLogDir+"/")
config := common.ServiceConfig{}

httpRestService, err := restserver.NewHTTPRestService(&config, &fakes.WireserverClientFake{}, &fakes.NMAgentClientFake{}, nil, nil)
httpRestService, err := restserver.NewHTTPRestService(&config, &fakes.WireserverClientFake{}, &fakes.NMAgentClientFake{}, nil, nil, nil)
svc = httpRestService.(*restserver.HTTPRestService)
svc.Name = "cns-test-server"
fakeNNC := v1alpha.NodeNetworkConfig{
Expand Down Expand Up @@ -2260,3 +2260,66 @@ func TestPostAllNetworkContainers(t *testing.T) {
})
}
}

func TestGetHomeAz(t *testing.T) {
emptyRoutes, _ := buildRoutes(defaultBaseURL, clientPaths)
tests := []struct {
name string
shouldErr bool
exp *cns.GetHomeAzResponse
}{
{
"happy path",
false,
&cns.GetHomeAzResponse{
Response: cns.Response{
ReturnCode: 0,
Message: "success",
},
HomeAzResponse: cns.HomeAzResponse{
IsSupported: true,
HomeAz: uint(1),
},
},
},
{
"error",
true,
&cns.GetHomeAzResponse{
Response: cns.Response{
ReturnCode: types.UnexpectedError,
Message: "unexpected error",
},
},
},
}

for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()

client := &Client{
client: &mockdo{
errToReturn: nil,
objToReturn: test.exp,
httpStatusCodeToReturn: http.StatusOK,
},
routes: emptyRoutes,
}

got, err := client.GetHomeAz(context.Background())
if err != nil && !test.shouldErr {
t.Fatal("unexpected error: err:", err)
}

if err == nil && test.shouldErr {
t.Fatal("expected an error but received none")
}

if !test.shouldErr && !cmp.Equal(got, test.exp) {
t.Error("received response differs from expectation: diff:", cmp.Diff(got, test.exp))
}
})
}
}
3 changes: 2 additions & 1 deletion cns/configuration/cns_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@
},
"MSISettings": {
"ResourceID": ""
}
},
"PopulateHomeAzCacheRetryIntervalSecs": 15
}
5 changes: 5 additions & 0 deletions cns/configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type CNSConfig struct {
CNIConflistScenario string
EnableCNIConflistGeneration bool
CNIConflistFilepath string
PopulateHomeAzCacheRetryIntervalSecs int
}

type TelemetrySettings struct {
Expand Down Expand Up @@ -239,4 +240,8 @@ func SetCNSConfigDefaults(config *CNSConfig) {
if config.SyncHostNCTimeoutMs == 0 {
config.SyncHostNCTimeoutMs = 500 //nolint:gomnd // default times
}
if config.PopulateHomeAzCacheRetryIntervalSecs == 0 {
// set the default PopulateHomeAzCache retry interval to 15 seconds
config.PopulateHomeAzCacheRetryIntervalSecs = 15
}
}
3 changes: 3 additions & 0 deletions cns/configuration/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ func TestSetCNSConfigDefaults(t *testing.T) {
KeyVaultSettings: KeyVaultSettings{
RefreshIntervalInHrs: 12,
},
PopulateHomeAzCacheRetryIntervalSecs: 15,
},
},
{
Expand All @@ -229,6 +230,7 @@ func TestSetCNSConfigDefaults(t *testing.T) {
KeyVaultSettings: KeyVaultSettings{
RefreshIntervalInHrs: 3,
},
PopulateHomeAzCacheRetryIntervalSecs: 10,
},
want: CNSConfig{
ChannelMode: "Other",
Expand All @@ -248,6 +250,7 @@ func TestSetCNSConfigDefaults(t *testing.T) {
KeyVaultSettings: KeyVaultSettings{
RefreshIntervalInHrs: 3,
},
PopulateHomeAzCacheRetryIntervalSecs: 10,
},
},
}
Expand Down
29 changes: 17 additions & 12 deletions cns/fakes/nmagentclientfake.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,33 @@ type NMAgentClientFake struct {
SupportedAPIsF func(context.Context) ([]string, error)
GetNCVersionF func(context.Context, nmagent.NCVersionRequest) (nmagent.NCVersion, error)
GetNCVersionListF func(context.Context) (nmagent.NCVersionList, error)
GetHomeAzF func(context.Context) (nmagent.AzResponse, error)
}

func (c *NMAgentClientFake) PutNetworkContainer(ctx context.Context, req *nmagent.PutNetworkContainerRequest) error {
return c.PutNetworkContainerF(ctx, req)
func (n *NMAgentClientFake) PutNetworkContainer(ctx context.Context, req *nmagent.PutNetworkContainerRequest) error {
return n.PutNetworkContainerF(ctx, req)
}

func (c *NMAgentClientFake) DeleteNetworkContainer(ctx context.Context, req nmagent.DeleteContainerRequest) error {
return c.DeleteNetworkContainerF(ctx, req)
func (n *NMAgentClientFake) DeleteNetworkContainer(ctx context.Context, req nmagent.DeleteContainerRequest) error {
return n.DeleteNetworkContainerF(ctx, req)
}

func (c *NMAgentClientFake) JoinNetwork(ctx context.Context, req nmagent.JoinNetworkRequest) error {
return c.JoinNetworkF(ctx, req)
func (n *NMAgentClientFake) JoinNetwork(ctx context.Context, req nmagent.JoinNetworkRequest) error {
return n.JoinNetworkF(ctx, req)
}

func (c *NMAgentClientFake) SupportedAPIs(ctx context.Context) ([]string, error) {
return c.SupportedAPIsF(ctx)
func (n *NMAgentClientFake) SupportedAPIs(ctx context.Context) ([]string, error) {
return n.SupportedAPIsF(ctx)
}

func (c *NMAgentClientFake) GetNCVersion(ctx context.Context, req nmagent.NCVersionRequest) (nmagent.NCVersion, error) {
return c.GetNCVersionF(ctx, req)
func (n *NMAgentClientFake) GetNCVersion(ctx context.Context, req nmagent.NCVersionRequest) (nmagent.NCVersion, error) {
return n.GetNCVersionF(ctx, req)
}

func (c *NMAgentClientFake) GetNCVersionList(ctx context.Context) (nmagent.NCVersionList, error) {
return c.GetNCVersionListF(ctx)
func (n *NMAgentClientFake) GetNCVersionList(ctx context.Context) (nmagent.NCVersionList, error) {
return n.GetNCVersionListF(ctx)
}

func (n *NMAgentClientFake) GetHomeAz(ctx context.Context) (nmagent.AzResponse, error) {
return n.GetHomeAzF(ctx)
}
23 changes: 22 additions & 1 deletion cns/restserver/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ var (
// 3) the ncid parameter
// 4) the authentication token parameter
// 5) the optional delete path
const ncURLExpectedMatches = 5
const (
ncURLExpectedMatches = 5
)

// This file contains implementation of all HTTP APIs which are exposed to external clients.
// TODO: break it even further per module (network, nc, etc) like it is done for ipam
Expand Down Expand Up @@ -764,6 +766,25 @@ func (service *HTTPRestService) setOrchestratorType(w http.ResponseWriter, r *ht
logger.Response(service.Name, resp, resp.ReturnCode, err)
}

// getHomeAz retrieves home AZ of host
func (service *HTTPRestService) getHomeAz(w http.ResponseWriter, r *http.Request) {
logger.Printf("[Azure CNS] getHomeAz")
logger.Request(service.Name, "getHomeAz", nil)
ctx := r.Context()

switch r.Method {
case http.MethodGet:
getHomeAzResponse := service.homeAzMonitor.GetHomeAz(ctx)
service.setResponse(w, getHomeAzResponse.Response.ReturnCode, getHomeAzResponse)
default:
returnMessage := "[Azure CNS] Error. getHomeAz did not receive a GET."
returnCode := types.UnsupportedVerb
service.setResponse(w, returnCode, cns.GetHomeAzResponse{
Response: cns.Response{ReturnCode: returnCode, Message: returnMessage},
})
}
}

func (service *HTTPRestService) createOrUpdateNetworkContainer(w http.ResponseWriter, r *http.Request) {
logger.Printf("[Azure CNS] createOrUpdateNetworkContainer")

Expand Down
20 changes: 19 additions & 1 deletion cns/restserver/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,24 @@ func TestNmAgentSupportedApisHandler(t *testing.T) {
fmt.Printf("nmAgentSupportedApisHandler Responded with %+v\n", nmAgentSupportedApisResponse)
}

// Testing GetHomeAz API handler, return UnsupportedVerb if http method is not supported
func TestGetHomeAz_UnsupportedHttpMethod(t *testing.T) {
req, err := http.NewRequestWithContext(context.TODO(), http.MethodPost, cns.GetHomeAz, http.NoBody)
if err != nil {
t.Fatal(err)
}

w := httptest.NewRecorder()
mux.ServeHTTP(w, req)

var getHomeAzResponse cns.GetHomeAzResponse
err = decodeResponse(w, &getHomeAzResponse)
if err != nil && getHomeAzResponse.Response.ReturnCode != types.UnsupportedVerb {
t.Errorf("GetHomeAz not failing to unsupported http method with response %+v", getHomeAzResponse)
}
logger.Printf("GetHomeAz Responded with %+v\n", getHomeAzResponse)
}

func TestCreateHostNCApipaEndpoint(t *testing.T) {
fmt.Println("Test: createHostNCApipaEndpoint")

Expand Down Expand Up @@ -1359,7 +1377,7 @@ func startService() error {
}

nmagentClient := &fakes.NMAgentClientFake{}
service, err = NewHTTPRestService(&config, &fakes.WireserverClientFake{}, nmagentClient, nil, nil)
service, err = NewHTTPRestService(&config, &fakes.WireserverClientFake{}, nmagentClient, nil, nil, nil)
if err != nil {
return err
}
Expand Down
Loading