Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update for new home az contract from nma #3151

Merged
merged 12 commits into from
Dec 19, 2024
5 changes: 3 additions & 2 deletions cns/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,9 @@ type NmAgentSupportedApisResponse struct {
}

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

type GetHomeAzResponse struct {
Expand Down
2 changes: 1 addition & 1 deletion cns/restserver/homeazmonitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (h *HomeAzMonitor) Populate(ctx context.Context) {
h.update(returnCode, returnMessage, cns.HomeAzResponse{IsSupported: true})
return
}
h.update(types.Success, "Get Home Az succeeded", cns.HomeAzResponse{IsSupported: true, HomeAz: azResponse.HomeAz})
h.update(types.Success, "Get Home Az succeeded", cns.HomeAzResponse{IsSupported: true, HomeAz: azResponse.HomeAz, NmaAppliedTheIPV6Fix: azResponse.ContainsFixes(nmagent.HomeAZFixIPv6)})
}

// update constructs a GetHomeAzResponse entity and update its cache
Expand Down
33 changes: 23 additions & 10 deletions cns/restserver/homeazmonitor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,23 @@ func TestHomeAzMonitor(t *testing.T) {
{
"happy path",
&fakes.NMAgentClientFake{
SupportedAPIsF: func(ctx context.Context) ([]string, error) {
SupportedAPIsF: func(_ context.Context) ([]string, error) {
return []string{"GetHomeAz"}, nil
},
GetHomeAzF: func(ctx context.Context) (nmagent.AzResponse, error) {
return nmagent.AzResponse{HomeAz: uint(1)}, nil
GetHomeAzF: func(_ context.Context) (nmagent.AzResponse, error) {
return nmagent.AzResponse{HomeAz: uint(1), AppliedFixes: []nmagent.HomeAZFix{nmagent.HomeAZFixIPv6}}, nil
},
},
cns.HomeAzResponse{IsSupported: true, HomeAz: uint(1)},
cns.HomeAzResponse{IsSupported: true, HomeAz: uint(1), NmaAppliedTheIPV6Fix: true},
false,
},
{
"getHomeAz is not supported in nmagent",
&fakes.NMAgentClientFake{
SupportedAPIsF: func(ctx context.Context) ([]string, error) {
SupportedAPIsF: func(_ context.Context) ([]string, error) {
return []string{"dummy"}, nil
},
GetHomeAzF: func(ctx context.Context) (nmagent.AzResponse, error) {
GetHomeAzF: func(_ context.Context) (nmagent.AzResponse, error) {
return nmagent.AzResponse{}, nil
},
},
Expand All @@ -50,23 +50,36 @@ func TestHomeAzMonitor(t *testing.T) {
{
"api supported but home az value is not valid",
&fakes.NMAgentClientFake{
SupportedAPIsF: func(ctx context.Context) ([]string, error) {
SupportedAPIsF: func(_ context.Context) ([]string, error) {
return []string{GetHomeAzAPIName}, nil
},
GetHomeAzF: func(ctx context.Context) (nmagent.AzResponse, error) {
GetHomeAzF: func(_ context.Context) (nmagent.AzResponse, error) {
return nmagent.AzResponse{HomeAz: 0}, nil
},
},
cns.HomeAzResponse{IsSupported: true},
true,
},
{
"api supported but apiVersion value is not valid",
&fakes.NMAgentClientFake{
SupportedAPIsF: func(_ context.Context) ([]string, error) {
return []string{GetHomeAzAPIName}, nil
},
GetHomeAzF: func(_ context.Context) (nmagent.AzResponse, error) {
return nmagent.AzResponse{HomeAz: uint(1), AppliedFixes: []nmagent.HomeAZFix{nmagent.HomeAZFixInvalid}}, nil
},
},
cns.HomeAzResponse{IsSupported: true, HomeAz: uint(1), NmaAppliedTheIPV6Fix: false},
false,
},
{
"api supported but got unexpected errors",
&fakes.NMAgentClientFake{
SupportedAPIsF: func(ctx context.Context) ([]string, error) {
SupportedAPIsF: func(_ context.Context) ([]string, error) {
return []string{GetHomeAzAPIName}, nil
},
GetHomeAzF: func(ctx context.Context) (nmagent.AzResponse, error) {
GetHomeAzF: func(_ context.Context) (nmagent.AzResponse, error) {
return nmagent.AzResponse{}, errors.New("unexpected error")
},
},
Expand Down
13 changes: 12 additions & 1 deletion nmagent/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -751,14 +751,25 @@ func TestGetHomeAz(t *testing.T) {
}{
{
"happy path",
nmagent.AzResponse{HomeAz: uint(1)},
nmagent.AzResponse{HomeAz: uint(1), AppliedFixes: nil},
"/machine/plugins?comp=nmagent&type=GetHomeAz%2Fapi-version%2F1",
map[string]interface{}{
"httpStatusCode": "200",
"HomeAz": 1,
},
false,
},
{
"happy path with new version",
nmagent.AzResponse{HomeAz: uint(1), AppliedFixes: []nmagent.HomeAZFix{nmagent.HomeAZFixIPv6}},
"/machine/plugins?comp=nmagent&type=GetHomeAz%2Fapi-version%2F1",
map[string]interface{}{
"httpStatusCode": "200",
"HomeAz": 1,
"APIVersion": 2,
},
false,
},
{
"empty response",
nmagent.AzResponse{},
Expand Down
8 changes: 8 additions & 0 deletions nmagent/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ import (
pkgerrors "github.com/pkg/errors"
)

type HomeAzAPIVersionError struct {
ReceivedAPIVersion uint
}

func (h HomeAzAPIVersionError) Error() string {
return fmt.Sprintf("invalid homeaz api version (must be 0 or 2): received %d", h.ReceivedAPIVersion)
}

var deleteNetworkPattern = regexp.MustCompile(`/NetworkManagement/joinedVirtualNetworks/[^/]+/api-version/\d+/method/DELETE`)

// ContentError is encountered when an unexpected content type is obtained from
Expand Down
74 changes: 73 additions & 1 deletion nmagent/responses.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package nmagent

import (
"encoding/json"

"github.com/pkg/errors"
)

type VirtualNetwork struct {
CNetSpace string `json:"cnetSpace"`
DefaultGateway string `json:"defaultGateway"`
Expand Down Expand Up @@ -37,8 +43,74 @@ type NCVersionList struct {
Containers []NCVersion `json:"networkContainers"`
}

// HomeAZFix is an indication that a particular bugfix has been applied to some
// HomeAZ.
type HomeAZFix int

func (h HomeAZFix) String() string {
switch h {
case HomeAZFixInvalid:
return "HomeAZFixInvalid"
case HomeAZFixIPv6:
return "HomeAZFixIPv6"
default:
return "Unknown HomeAZ Fix"
}
}
smittal22 marked this conversation as resolved.
Show resolved Hide resolved

const (
HomeAZFixInvalid HomeAZFix = iota
HomeAZFixIPv6
)

type AzResponse struct {
HomeAz uint `json:"homeAz"`
HomeAz uint
AppliedFixes []HomeAZFix
timraymond marked this conversation as resolved.
Show resolved Hide resolved
}

func (az *AzResponse) UnmarshalJSON(in []byte) error {
type resp struct {
HomeAz uint `json:"homeAz"`
APIVersion uint `json:"apiVersion"`
}

var rsp resp
err := json.Unmarshal(in, &rsp)
if err != nil {
return errors.Wrap(err, "unmarshaling raw home az response")
}

if rsp.APIVersion != 0 && rsp.APIVersion != 2 {
return HomeAzAPIVersionError{
ReceivedAPIVersion: rsp.APIVersion,
}
}

az.HomeAz = rsp.HomeAz

if rsp.APIVersion == 2 { // nolint:gomnd // ignore magic number 2
az.AppliedFixes = append(az.AppliedFixes, HomeAZFixIPv6)
}

return nil
}

// ContainsFixes reports whether all fixes requested are present in the
// AzResponse returned.
func (az AzResponse) ContainsFixes(requestedFixes ...HomeAZFix) bool {
for _, requested := range requestedFixes {
found := false
for _, present := range az.AppliedFixes {
if requested == present {
found = true
}
}

if !found {
return false
}
}
return true
timraymond marked this conversation as resolved.
Show resolved Hide resolved
}

type NodeIP struct {
Expand Down
137 changes: 137 additions & 0 deletions nmagent/responses_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package nmagent_test

import (
"encoding/json"
"testing"

"github.com/Azure/azure-container-networking/nmagent"
"github.com/google/go-cmp/cmp"
)

func TestContainsFixes(t *testing.T) {
tests := []struct {
name string
resp nmagent.AzResponse
fixes []nmagent.HomeAZFix
exp bool
}{
{
"empty",
nmagent.AzResponse{},
[]nmagent.HomeAZFix{},
true,
},
{
"one present",
nmagent.AzResponse{
AppliedFixes: []nmagent.HomeAZFix{
nmagent.HomeAZFixIPv6,
},
},
[]nmagent.HomeAZFix{nmagent.HomeAZFixIPv6},
true,
},
{
"one absent",
nmagent.AzResponse{
AppliedFixes: []nmagent.HomeAZFix{},
},
[]nmagent.HomeAZFix{nmagent.HomeAZFixIPv6},
false,
},
{
"one with empty request",
nmagent.AzResponse{
AppliedFixes: []nmagent.HomeAZFix{
nmagent.HomeAZFixIPv6,
},
},
[]nmagent.HomeAZFix{},
true,
},
}

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

exp := test.exp
if got != exp {
t.Error("unexpected response from ContainsFixes: exp:", exp, "got:", got)
smittal22 marked this conversation as resolved.
Show resolved Hide resolved
}
})
}
}

func TestUnmarshalAzResponse(t *testing.T) {
tests := []struct {
name string
in string
exp nmagent.AzResponse
shouldErr bool
}{
{
"empty",
"{}",
nmagent.AzResponse{},
false,
},
{
"only homeaz",
`{"homeAz": 42}`,
nmagent.AzResponse{
HomeAz: 42,
},
false,
},
{
"valid apiversion",
`{"homeAz": 42, "apiVersion": 0}`,
nmagent.AzResponse{
HomeAz: 42,
},
false,
},
{
"valid apiversion ipv6",
`{"homeAz": 42, "apiVersion": 2}`,
nmagent.AzResponse{
HomeAz: 42,
AppliedFixes: []nmagent.HomeAZFix{
nmagent.HomeAZFixIPv6,
},
},
false,
},
{
"invalid apiversion",
`{"homeAz": 42, "apiVersion": 42}`,
nmagent.AzResponse{},
true,
},
}

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

var got nmagent.AzResponse
err := json.Unmarshal([]byte(test.in), &got)
if err != nil && !test.shouldErr {
t.Fatal("unexpected error unmarshaling JSON: err:", err)
}

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

exp := test.exp
if !cmp.Equal(got, exp) {
t.Error("received response differs from expected: diff:", cmp.Diff(got, exp))
}
})
}
}
Loading