Skip to content

Commit

Permalink
Check all resources realized status when creating subnet
Browse files Browse the repository at this point in the history
Subnet includes child resources which also need checking realized status.
Remove entityType from CheckRealizeState. Since we're not sure if the
realized items for intentPath will be changed or not
  • Loading branch information
TaoZou1 committed Dec 10, 2024
1 parent 278aaf3 commit 7a03bc2
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 20 deletions.
21 changes: 12 additions & 9 deletions pkg/nsx/services/realizestate/realize_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ func IsRealizeStateError(err error) bool {
return ok
}

// CheckRealizeState allows the caller to check realize status of entityType with retries.
// backoff defines the maximum retries and the wait interval between two retries.
func (service *RealizeStateService) CheckRealizeState(backoff wait.Backoff, intentPath, entityType string) error {
// CheckRealizeState allows the caller to check realize status of intentPath with retries.
// Backoff defines the maximum retries and the wait interval between two retries.
// Check all the entities, all entities should be in the REALIZED state to be treated as REALIZED
func (service *RealizeStateService) CheckRealizeState(backoff wait.Backoff, intentPath string) error {
// TODO, ask NSX if there were multiple realize states could we check only the latest one?
return retry.OnError(backoff, func(err error) bool {
// Won't retry when realized state is `ERROR`.
Expand All @@ -54,12 +55,11 @@ func (service *RealizeStateService) CheckRealizeState(backoff wait.Backoff, inte
if err != nil {
return err
}
entitiesRealized := 0
for _, result := range results.Results {
if entityType != "" && result.EntityType != nil && *result.EntityType != entityType {
continue
}
if *result.State == model.GenericPolicyRealizedResource_STATE_REALIZED {
return nil
entitiesRealized++
continue
}
if *result.State == model.GenericPolicyRealizedResource_STATE_ERROR {
var errMsg []string
Expand All @@ -68,9 +68,12 @@ func (service *RealizeStateService) CheckRealizeState(backoff wait.Backoff, inte
errMsg = append(errMsg, *alarm.Message)
}
}
return NewRealizeStateError(fmt.Sprintf("%s realized with errors: %s", entityType, errMsg))
return NewRealizeStateError(fmt.Sprintf("%s realized with errors: %s", intentPath, errMsg))
}
}
return fmt.Errorf("%s not realized", entityType)
if entitiesRealized == len(results.Results) {
return nil
}
return fmt.Errorf("%s not realized", intentPath)
})
}
107 changes: 102 additions & 5 deletions pkg/nsx/services/realizestate/realize_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ func TestRealizeStateService_CheckRealizeState(t *testing.T) {
},
}, nil
})
defer patches.Reset()

backoff := wait.Backoff{
Duration: 1 * time.Second,
Expand All @@ -64,16 +63,114 @@ func TestRealizeStateService_CheckRealizeState(t *testing.T) {
Steps: 6,
}
// default project
err := s.CheckRealizeState(backoff, "/orgs/default/projects/default/vpcs/vpc/subnets/subnet/ports/port", "RealizedLogicalPort")
err := s.CheckRealizeState(backoff, "/orgs/default/projects/default/vpcs/vpc/subnets/subnet/ports/port")

realizeStateError, ok := err.(*RealizeStateError)
assert.True(t, ok)
assert.Equal(t, realizeStateError.Error(), "RealizedLogicalPort realized with errors: [mocked error]")
assert.Equal(t, realizeStateError.Error(), "/orgs/default/projects/default/vpcs/vpc/subnets/subnet/ports/port realized with errors: [mocked error]")

// non default project
err = s.CheckRealizeState(backoff, "/orgs/default/projects/project-quality/vpcs/vpc/subnets/subnet/ports/port", "RealizedLogicalPort")
err = s.CheckRealizeState(backoff, "/orgs/default/projects/project-quality/vpcs/vpc/subnets/subnet/ports/port")

realizeStateError, ok = err.(*RealizeStateError)
assert.True(t, ok)
assert.Equal(t, realizeStateError.Error(), "RealizedLogicalPort realized with errors: [mocked error]")
assert.Equal(t, realizeStateError.Error(), "/orgs/default/projects/project-quality/vpcs/vpc/subnets/subnet/ports/port realized with errors: [mocked error]")

// for subnet, RealizedLogicalPort realized with errors
patches.Reset()

patches = gomonkey.ApplyFunc((*fakeRealizedEntitiesClient).List, func(c *fakeRealizedEntitiesClient, intentPathParam string, sitePathParam *string) (model.GenericPolicyRealizedResourceListResult, error) {
return model.GenericPolicyRealizedResourceListResult{
Results: []model.GenericPolicyRealizedResource{
{
State: common.String(model.GenericPolicyRealizedResource_STATE_ERROR),
Alarms: []model.PolicyAlarmResource{
{Message: common.String("mocked error")},
},
EntityType: common.String("RealizedLogicalPort"),
},
{
State: common.String(model.GenericPolicyRealizedResource_STATE_UNREALIZED),
Alarms: []model.PolicyAlarmResource{
{Message: common.String("mocked error")},
},
EntityType: common.String("RealizedLogicalSwitch"),
},
{
State: common.String(model.GenericPolicyRealizedResource_STATE_REALIZED),
Alarms: []model.PolicyAlarmResource{},
EntityType: common.String("RealizedLogicalRouterPort"),
},
},
}, nil
})
err = s.CheckRealizeState(backoff, "/orgs/default/projects/project-quality/vpcs/vpc/subnets/subnet/")

realizeStateError, ok = err.(*RealizeStateError)
assert.True(t, ok)
assert.Equal(t, realizeStateError.Error(), "/orgs/default/projects/project-quality/vpcs/vpc/subnets/subnet/ realized with errors: [mocked error]")

// for subnet, realized successfully
patches.Reset()

patches = gomonkey.ApplyFunc((*fakeRealizedEntitiesClient).List, func(c *fakeRealizedEntitiesClient, intentPathParam string, sitePathParam *string) (model.GenericPolicyRealizedResourceListResult, error) {
return model.GenericPolicyRealizedResourceListResult{
Results: []model.GenericPolicyRealizedResource{
{
State: common.String(model.GenericPolicyRealizedResource_STATE_REALIZED),
Alarms: []model.PolicyAlarmResource{},
EntityType: common.String("RealizedLogicalPort"),
},
{
State: common.String(model.GenericPolicyRealizedResource_STATE_REALIZED),
Alarms: []model.PolicyAlarmResource{},
EntityType: common.String("RealizedLogicalSwitch"),
},
{
State: common.String(model.GenericPolicyRealizedResource_STATE_REALIZED),
Alarms: []model.PolicyAlarmResource{},
EntityType: common.String("RealizedLogicalRouterPort"),
},
},
}, nil
})
err = s.CheckRealizeState(backoff, "/orgs/default/projects/project-quality/vpcs/vpc/subnets/subnet/")
assert.Equal(t, err, nil)

// for subnet, need retry
patches.Reset()

patches = gomonkey.ApplyFunc((*fakeRealizedEntitiesClient).List, func(c *fakeRealizedEntitiesClient, intentPathParam string, sitePathParam *string) (model.GenericPolicyRealizedResourceListResult, error) {
return model.GenericPolicyRealizedResourceListResult{
Results: []model.GenericPolicyRealizedResource{
{
State: common.String(model.GenericPolicyRealizedResource_STATE_REALIZED),
Alarms: []model.PolicyAlarmResource{},
EntityType: common.String("RealizedLogicalPort"),
},
{
State: common.String(model.GenericPolicyRealizedResource_STATE_UNREALIZED),
Alarms: []model.PolicyAlarmResource{},
EntityType: common.String("RealizedLogicalSwitch"),
},
{
State: common.String(model.GenericPolicyRealizedResource_STATE_REALIZED),
Alarms: []model.PolicyAlarmResource{},
EntityType: common.String("RealizedLogicalRouterPort"),
},
},
}, nil
})
backoff = wait.Backoff{
Duration: 10 * time.Millisecond,
Factor: 1,
Jitter: 0,
Steps: 1,
}
err = s.CheckRealizeState(backoff, "/orgs/default/projects/project-quality/vpcs/vpc/subnets/subnet/")
assert.NotEqual(t, err, nil)
_, ok = err.(*RealizeStateError)
assert.Equal(t, ok, false)
patches.Reset()

}
3 changes: 2 additions & 1 deletion pkg/nsx/services/subnet/subnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ func (service *SubnetService) createOrUpdateSubnet(obj client.Object, nsxSubnet
// Failure of CheckRealizeState may result in the creation of an existing Subnet.
// For Subnets, it's important to reuse the already created NSXSubnet.
// For SubnetSets, since the ID includes a random value, the created NSX Subnet needs to be deleted and recreated.
if err = realizeService.CheckRealizeState(backoff, *nsxSubnet.Path, "RealizedLogicalSwitch"); err != nil {

if err = realizeService.CheckRealizeState(backoff, *nsxSubnet.Path); err != nil {
log.Error(err, "Failed to check subnet realization state", "ID", *nsxSubnet.Id)
// Delete the subnet if realization check fails, avoiding creating duplicate subnets continuously.
deleteErr := service.DeleteSubnet(*nsxSubnet)
Expand Down
3 changes: 2 additions & 1 deletion pkg/nsx/services/subnet/subnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ func (f fakeRealizedEntitiesClient) List(intentPathParam string, sitePathParam *
return model.GenericPolicyRealizedResourceListResult{
Results: []model.GenericPolicyRealizedResource{
{
State: &state,
State: &state,
EntityType: common.String("RealizedLogicalPort"),
},
},
}, nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/nsx/services/subnetport/subnetport.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func (service *SubnetPortService) CheckSubnetPortState(obj interface{}, nsxSubne
Jitter: 0,
Steps: 6,
}
if err := realizeService.CheckRealizeState(backoff, *nsxSubnetPort.Path, "RealizedLogicalPort"); err != nil {
if err := realizeService.CheckRealizeState(backoff, *nsxSubnetPort.Path); err != nil {
log.Error(err, "failed to get realized status", "subnetport path", *nsxSubnetPort.Path)
if realizestate.IsRealizeStateError(err) {
log.Error(err, "the created subnet port is in error realization state, cleaning the resource", "subnetport", portID)
Expand Down
6 changes: 3 additions & 3 deletions pkg/nsx/services/vpc/vpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ func (s *VPCService) createNSXVPC(createdVpc *model.Vpc, nc *common.VPCNetworkCo
func (s *VPCService) checkVPCRealizationState(createdVpc *model.Vpc, newVpcPath string) error {
log.V(2).Info("Check VPC realization state", "VPC", *createdVpc.Id)
realizeService := realizestate.InitializeRealizeState(s.Service)
if err := realizeService.CheckRealizeState(util.NSXTDefaultRetry, newVpcPath, "RealizedLogicalRouter"); err != nil {
if err := realizeService.CheckRealizeState(util.NSXTDefaultRetry, newVpcPath); err != nil {
log.Error(err, "Failed to check VPC realization state", "VPC", *createdVpc.Id)
if realizestate.IsRealizeStateError(err) {
log.Error(err, "The created VPC is in error realization state, cleaning the resource", "VPC", *createdVpc.Id)
Expand Down Expand Up @@ -882,7 +882,7 @@ func (s *VPCService) checkLBSRealization(createdLBS *model.LBService, createdVpc

log.V(2).Info("Check LBS realization state", "LBS", *createdLBS.Id)
realizeService := realizestate.InitializeRealizeState(s.Service)
if err = realizeService.CheckRealizeState(util.NSXTLBVSDefaultRetry, *newLBS.Path, ""); err != nil {
if err = realizeService.CheckRealizeState(util.NSXTLBVSDefaultRetry, *newLBS.Path); err != nil {
log.Error(err, "Failed to check LBS realization state", "LBS", *createdLBS.Id)
if realizestate.IsRealizeStateError(err) {
log.Error(err, "The created LBS is in error realization state, cleaning the resource", "LBS", *createdLBS.Id)
Expand All @@ -908,7 +908,7 @@ func (s *VPCService) checkVpcAttachmentRealization(createdAttachment *model.VpcA
}
log.V(2).Info("Check VPC attachment realization state", "VpcAttachment", *createdAttachment.Id)
realizeService := realizestate.InitializeRealizeState(s.Service)
if err = realizeService.CheckRealizeState(util.NSXTLBVSDefaultRetry, *newAttachment.Path, ""); err != nil {
if err = realizeService.CheckRealizeState(util.NSXTLBVSDefaultRetry, *newAttachment.Path); err != nil {
log.Error(err, "Failed to check VPC attachment realization state", "VpcAttachment", *createdAttachment.Id)
if realizestate.IsRealizeStateError(err) {
log.Error(err, "The created VPC attachment is in error realization state, cleaning the resource", "VpcAttachment", *createdAttachment.Id)
Expand Down

0 comments on commit 7a03bc2

Please sign in to comment.