Skip to content

Commit c3a71ec

Browse files
Fix IsDefaultNSXProject always returning false after SDK field rename (#1389)
Use Cluster.HttpGet to read the "default" field directly from the NSX REST API JSON response, bypassing the SDK struct deserialization which cannot populate the unexported model.Project._Default field. Change-Id: I79cfd117c95abe17ef7032525e69cd1695e8822e Co-authored-by: Oz <oz-agent@warp.dev>
1 parent ea2eb58 commit c3a71ec

2 files changed

Lines changed: 54 additions & 80 deletions

File tree

pkg/nsx/services/vpc/vpc.go

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ import (
44
"context"
55
"errors"
66
"fmt"
7-
"reflect"
87
"strings"
98
"sync"
10-
"unsafe"
119

1210
stderrors "github.com/vmware/vsphere-automation-sdk-go/lib/vapi/std/errors"
1311
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model"
@@ -54,8 +52,9 @@ var (
5452

5553
type VPCService struct {
5654
common.Service
57-
VpcStore *VPCStore
58-
LbsStore *LBSStore
55+
VpcStore *VPCStore
56+
LbsStore *LBSStore
57+
defaultProjectCache sync.Map // cache for IsDefaultNSXProject results, key: "orgID/projectID", value: bool
5958
}
6059

6160
func (s *VPCService) GetDefaultNetworkConfig() (*v1alpha1.VPCNetworkConfiguration, error) {
@@ -1168,27 +1167,29 @@ func IsPreCreatedVPC(nc *v1alpha1.VPCNetworkConfiguration) bool {
11681167
return nc.Spec.VPC != ""
11691168
}
11701169

1171-
func getProjectDefault(p *model.Project) *bool {
1172-
v := reflect.ValueOf(p).Elem()
1173-
f := v.FieldByName("_Default")
1174-
if !f.IsValid() {
1175-
return nil
1170+
// IsDefaultNSXProject checks if the given project is a NSX default project.
1171+
// It queries the NSX Policy API directly and reads the "default" field from the
1172+
// JSON response, bypassing the Go SDK struct deserialization which cannot populate
1173+
// the unexported model.Project._Default field (CanSet=false for unexported fields).
1174+
// Results are cached since a project's default status does not change at runtime.
1175+
func (s *VPCService) IsDefaultNSXProject(orgID, projectID string) (bool, error) {
1176+
cacheKey := orgID + "/" + projectID
1177+
if val, ok := s.defaultProjectCache.Load(cacheKey); ok {
1178+
return val.(bool), nil
11761179
}
11771180

1178-
ptr := unsafe.Pointer(f.UnsafeAddr())
1179-
return *(**bool)(ptr) // read *bool safely
1180-
}
1181-
1182-
// IsDefaultNSXProject checks if the given project is a default project
1183-
func (s *VPCService) IsDefaultNSXProject(orgID, projectID string) (bool, error) {
1184-
proj, err := s.NSXClient.ProjectClient.Get(orgID, projectID, nil)
1181+
url := fmt.Sprintf("policy/api/v1/orgs/%s/projects/%s", orgID, projectID)
1182+
resp, err := s.NSXClient.Cluster.HttpGet(url)
11851183
if err != nil {
1186-
log.Error(err, "Failed to get project", "ProjectID", projectID)
1184+
log.Error(err, "Failed to get project from NSX", "OrgID", orgID, "ProjectID", projectID)
11871185
return false, err
11881186
}
1189-
defaultVal := getProjectDefault(&proj)
1190-
if defaultVal != nil && *defaultVal {
1191-
return true, nil
1187+
isDefault := false
1188+
if defaultVal, ok := resp["default"]; ok {
1189+
if boolVal, ok := defaultVal.(bool); ok {
1190+
isDefault = boolVal
1191+
}
11921192
}
1193-
return false, nil
1193+
s.defaultProjectCache.Store(cacheKey, isDefault)
1194+
return isDefault, nil
11941195
}

pkg/nsx/services/vpc/vpc_test.go

Lines changed: 32 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,13 @@ import (
77
"reflect"
88
"strings"
99
"testing"
10-
"unsafe"
1110

1211
"github.com/agiledragon/gomonkey/v2"
1312
"github.com/golang/mock/gomock"
1413
"github.com/stretchr/testify/assert"
1514
"github.com/stretchr/testify/require"
1615
"github.com/vmware/vsphere-automation-sdk-go/runtime/data"
17-
nsx_client "github.com/vmware/vsphere-automation-sdk-go/runtime/protocol/client"
1816
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model"
19-
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/orgs"
2017
v1 "k8s.io/api/core/v1"
2118
k8sapierrors "k8s.io/apimachinery/pkg/api/errors"
2219
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -2921,83 +2918,59 @@ func Test_isNamespaceReady(t *testing.T) {
29212918
assert.False(t, isNamespaceReady(nsUnready))
29222919
}
29232920

2924-
func SetProjectDefault(p *model.Project, b bool) {
2925-
v := reflect.ValueOf(p).Elem()
2926-
f := v.FieldByName("_Default")
2927-
if !f.IsValid() {
2928-
return
2929-
}
2930-
2931-
val := b // bool value
2932-
ptr := unsafe.Pointer(f.UnsafeAddr())
2933-
realPtr := (*unsafe.Pointer)(ptr)
2934-
*realPtr = unsafe.Pointer(&val)
2935-
}
2936-
29372921
func TestIsDefaultNSXProject(t *testing.T) {
2938-
connector := nsx_client.NewConnector("localhost")
2939-
projectClient := orgs.NewProjectsClient(connector)
29402922
tests := []struct {
29412923
name string
29422924
orgID string
29432925
projectID string
2944-
prepareFunc func(*VPCService) *gomonkey.Patches
2926+
httpGetResp map[string]interface{}
2927+
httpGetErr error
29452928
expectedResult bool
2946-
expectedErrStr string
2929+
expectedErr bool
29472930
}{
29482931
{
2949-
name: "Project is default",
2950-
orgID: "default",
2951-
projectID: "project-1",
2952-
prepareFunc: func(service *VPCService) *gomonkey.Patches {
2953-
patches := gomonkey.ApplyMethod(reflect.TypeOf(projectClient), "Get", func(_ orgs.ProjectsClient, _ string, _ string, _ *bool) (model.Project, error) {
2954-
pro := model.Project{}
2955-
SetProjectDefault(&pro, true)
2956-
return pro, nil
2957-
})
2958-
return patches
2959-
},
2932+
name: "project is default",
2933+
orgID: "default",
2934+
projectID: "default",
2935+
httpGetResp: map[string]interface{}{"default": true, "id": "default"},
29602936
expectedResult: true,
29612937
},
29622938
{
2963-
name: "Project is not default",
2964-
orgID: "default",
2965-
projectID: "project-2",
2966-
prepareFunc: func(service *VPCService) *gomonkey.Patches {
2967-
patches := gomonkey.ApplyMethod(reflect.TypeOf(projectClient), "Get", func(_ orgs.ProjectsClient, _ string, _ string, _ *bool) (model.Project, error) {
2968-
pro := model.Project{}
2969-
return pro, nil
2970-
})
2971-
return patches
2972-
},
2939+
name: "project is not default",
2940+
orgID: "default",
2941+
projectID: "customer-project",
2942+
httpGetResp: map[string]interface{}{"default": false, "id": "customer-project"},
29732943
expectedResult: false,
29742944
},
29752945
{
2976-
name: "Get project error",
2977-
orgID: "default",
2978-
projectID: "project-3",
2979-
prepareFunc: func(service *VPCService) *gomonkey.Patches {
2980-
patches := gomonkey.ApplyMethod(reflect.TypeOf(projectClient), "Get", func(_ orgs.ProjectsClient, _ string, _ string, _ *bool) (model.Project, error) {
2981-
return model.Project{}, fmt.Errorf("failed to get project")
2982-
})
2983-
return patches
2984-
},
2985-
expectedErrStr: "failed to get project",
2946+
name: "response missing default field",
2947+
orgID: "default",
2948+
projectID: "some-project",
2949+
httpGetResp: map[string]interface{}{"id": "some-project"},
2950+
expectedResult: false,
2951+
},
2952+
{
2953+
name: "HTTP error",
2954+
orgID: "default",
2955+
projectID: "default",
2956+
httpGetErr: fmt.Errorf("connection refused"),
2957+
expectedErr: true,
29862958
},
29872959
}
29882960

29892961
for _, tt := range tests {
29902962
t.Run(tt.name, func(t *testing.T) {
29912963
service, _, _, _, _ := createService(t)
2992-
if tt.prepareFunc != nil {
2993-
patches := tt.prepareFunc(service)
2994-
defer patches.Reset()
2995-
}
2996-
service.NSXClient.ProjectClient = projectClient
2997-
result, err := service.IsDefaultNSXProject(tt.orgID, tt.projectID)
2964+
service.NSXClient.Cluster = &nsx.Cluster{}
2965+
patches := gomonkey.ApplyMethod(reflect.TypeOf(service.NSXClient.Cluster), "HttpGet",
2966+
func(_ *nsx.Cluster, _ string) (map[string]interface{}, error) {
2967+
return tt.httpGetResp, tt.httpGetErr
2968+
})
2969+
defer patches.Reset()
29982970

2999-
if tt.expectedErrStr != "" {
3000-
assert.ErrorContains(t, err, tt.expectedErrStr)
2971+
result, err := service.IsDefaultNSXProject(tt.orgID, tt.projectID)
2972+
if tt.expectedErr {
2973+
assert.Error(t, err)
30012974
} else {
30022975
assert.NoError(t, err)
30032976
assert.Equal(t, tt.expectedResult, result)

0 commit comments

Comments
 (0)