Skip to content

Commit 328ade0

Browse files
zhengxiexieoz-agent
andcommitted
Fix IsDefaultNSXProject always returning false after SDK field rename
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. Co-Authored-By: Oz <oz-agent@warp.dev> Change-Id: I79cfd117c95abe17ef7032525e69cd1695e8822e
1 parent 4ba0355 commit 328ade0

File tree

2 files changed

+43
-78
lines changed

2 files changed

+43
-78
lines changed

pkg/nsx/services/vpc/vpc.go

Lines changed: 11 additions & 19 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"
@@ -1168,27 +1166,21 @@ func IsPreCreatedVPC(nc *v1alpha1.VPCNetworkConfiguration) bool {
11681166
return nc.Spec.VPC != ""
11691167
}
11701168

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
1176-
}
1177-
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
1169+
// IsDefaultNSXProject checks if the given project is an NSX default project.
1170+
// It queries the NSX Policy API directly and reads the "default" field from the
1171+
// JSON response, bypassing the Go SDK struct deserialization which cannot populate
1172+
// the unexported model.Project._Default field (CanSet=false for unexported fields).
11831173
func (s *VPCService) IsDefaultNSXProject(orgID, projectID string) (bool, error) {
1184-
proj, err := s.NSXClient.ProjectClient.Get(orgID, projectID, nil)
1174+
url := fmt.Sprintf("policy/api/v1/orgs/%s/projects/%s", orgID, projectID)
1175+
resp, err := s.NSXClient.Cluster.HttpGet(url)
11851176
if err != nil {
1186-
log.Error(err, "Failed to get project", "ProjectID", projectID)
1177+
log.Error(err, "Failed to get project from NSX", "OrgID", orgID, "ProjectID", projectID)
11871178
return false, err
11881179
}
1189-
defaultVal := getProjectDefault(&proj)
1190-
if defaultVal != nil && *defaultVal {
1191-
return true, nil
1180+
if defaultVal, ok := resp["default"]; ok {
1181+
if boolVal, ok := defaultVal.(bool); ok {
1182+
return boolVal, nil
1183+
}
11921184
}
11931185
return false, nil
11941186
}

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)