Skip to content

Commit 2f55d53

Browse files
committed
Ensure pool member reach active state
When (re)creating a pool member, wait until its provisioning_status is ACTIVE. This avoids scenarios were CAPO contrinues removing members before new ones are actually active
1 parent ee82a04 commit 2f55d53

File tree

4 files changed

+81
-7
lines changed

4 files changed

+81
-7
lines changed

pkg/clients/loadbalancer.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type LbClient interface {
5151
DeletePool(id string) error
5252
CreatePoolMember(poolID string, opts pools.CreateMemberOptsBuilder) (*pools.Member, error)
5353
ListPoolMember(poolID string, opts pools.ListMembersOptsBuilder) ([]pools.Member, error)
54+
GetPoolMember(poolID string, lbMemberID string) (*pools.Member, error)
5455
DeletePoolMember(poolID string, lbMemberID string) error
5556
CreateMonitor(opts monitors.CreateOptsBuilder) (*monitors.Monitor, error)
5657
ListMonitors(opts monitors.ListOptsBuilder) ([]monitors.Monitor, error)
@@ -213,6 +214,15 @@ func (l lbClient) ListPoolMember(poolID string, opts pools.ListMembersOptsBuilde
213214
return pools.ExtractMembers(allPages)
214215
}
215216

217+
func (l lbClient) GetPoolMember(poolID string, lbMemberID string) (*pools.Member, error) {
218+
mc := metrics.NewMetricPrometheusContext("loadbalancer_member", "get")
219+
member, err := pools.GetMember(context.TODO(), l.serviceClient, poolID, lbMemberID).Extract()
220+
if mc.ObserveRequest(err) != nil {
221+
return nil, fmt.Errorf("error getting lbmember: %s", err)
222+
}
223+
return member, nil
224+
}
225+
216226
func (l lbClient) DeletePoolMember(poolID string, lbMemberID string) error {
217227
mc := metrics.NewMetricPrometheusContext("loadbalancer_member", "delete")
218228
err := pools.DeleteMember(context.TODO(), l.serviceClient, poolID, lbMemberID).ExtractErr()

pkg/clients/mock/loadbalancer.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cloud/services/loadbalancer/loadbalancer.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const (
5252
const (
5353
loadBalancerProvisioningStatusActive = "ACTIVE"
5454
loadBalancerProvisioningStatusPendingDelete = "PENDING_DELETE"
55+
poolMembeProvisioningStatusActive = "ACTIVE"
5556
)
5657

5758
// Default values for Monitor, sync with `kubebuilder:default` annotations on APIServerLoadBalancerMonitor object.
@@ -718,7 +719,12 @@ func (s *Service) ReconcileLoadBalancerMember(openStackCluster *infrav1.OpenStac
718719
return err
719720
}
720721

721-
if _, err := s.loadbalancerClient.CreatePoolMember(pool.ID, lbMemberOpts); err != nil {
722+
member, err := s.loadbalancerClient.CreatePoolMember(pool.ID, lbMemberOpts)
723+
if err != nil {
724+
return err
725+
}
726+
727+
if _, err := s.waitForPoolMemberActive(pool.ID, member.ID); err != nil {
722728
return err
723729
}
724730

@@ -932,6 +938,25 @@ func (s *Service) waitForLoadBalancerActive(id string) (*loadbalancers.LoadBalan
932938
return lb, nil
933939
}
934940

941+
// Possible Pool Member states are documented here: https://docs.openstack.org/api-ref/load-balancer/v2/#prov-status
942+
func (s *Service) waitForPoolMemberActive(poolID, memberID string) (*pools.Member, error) {
943+
var member *pools.Member
944+
945+
s.scope.Logger().Info("Waiting for pool member", "pool_id", poolID, "member_id", memberID, "targetStatus", "ACTIVE")
946+
err := wait.ExponentialBackoff(backoff, func() (bool, error) {
947+
var err error
948+
member, err = s.loadbalancerClient.GetPoolMember(poolID, memberID)
949+
if err != nil {
950+
return false, err
951+
}
952+
return member.ProvisioningStatus == poolMembeProvisioningStatusActive, nil
953+
})
954+
if err != nil {
955+
return nil, err
956+
}
957+
return member, nil
958+
}
959+
935960
func (s *Service) waitForListener(id, target string) error {
936961
s.scope.Logger().Info("Waiting for load balancer listener", "id", id, "targetStatus", target)
937962
return wait.ExponentialBackoff(backoff, func() (bool, error) {

pkg/cloud/services/loadbalancer/loadbalancer_test.go

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,8 +1052,15 @@ func Test_ReconcileLoadBalancerMember(t *testing.T) {
10521052
).DoAndReturn(func(_ string, got pools.CreateMemberOpts) (*pools.Member, error) {
10531053
// SubnetID must be empty here
10541054
g.Expect(got.SubnetID).To(Equal(""))
1055-
return &pools.Member{ID: "member-2"}, nil
1055+
return &pools.Member{ID: memberID}, nil
10561056
})
1057+
1058+
activeMember := pools.Member{
1059+
ID: memberID,
1060+
Name: poolMemberName,
1061+
ProvisioningStatus: "ACTIVE",
1062+
}
1063+
m.GetPoolMember(poolID, memberID).Return(&activeMember, nil).AnyTimes()
10571064
},
10581065
wantError: nil,
10591066
},
@@ -1092,8 +1099,10 @@ func Test_ReconcileLoadBalancerMember(t *testing.T) {
10921099
}
10931100
m.ListPools(pools.ListOpts{Name: pool.Name}).Return([]pools.Pool{pool}, nil)
10941101

1102+
poolMemberName := fmt.Sprintf("%s-kubeapi-%d-%s", clusterResourceName, port, machineName)
1103+
10951104
member := pools.Member{
1096-
Name: fmt.Sprintf("%s-kubeapi-%d-%s", clusterResourceName, port, machineName),
1105+
Name: poolMemberName,
10971106
Address: wrongMemberIP,
10981107
ID: memberID,
10991108
}
@@ -1107,8 +1116,15 @@ func Test_ReconcileLoadBalancerMember(t *testing.T) {
11071116
).DoAndReturn(func(_ string, got pools.CreateMemberOpts) (*pools.Member, error) {
11081117
// SubnetID must be empty here
11091118
g.Expect(got.SubnetID).To(Equal(""))
1110-
return &pools.Member{ID: "member-2"}, nil
1119+
return &pools.Member{ID: memberID}, nil
11111120
})
1121+
1122+
activeMember := pools.Member{
1123+
ID: memberID,
1124+
Name: poolMemberName,
1125+
ProvisioningStatus: "ACTIVE",
1126+
}
1127+
m.GetPoolMember(poolID, memberID).Return(&activeMember, nil).AnyTimes()
11121128
},
11131129
wantError: nil,
11141130
},
@@ -1133,8 +1149,8 @@ func Test_ReconcileLoadBalancerMember(t *testing.T) {
11331149
}
11341150
m.ListPools(pools.ListOpts{Name: pool.Name}).Return([]pools.Pool{pool}, nil)
11351151

1136-
memberName := fmt.Sprintf("%s-kubeapi-%d-%s", clusterResourceName, port, machineName)
1137-
m.ListPoolMember(poolID, pools.ListMembersOpts{Name: memberName}).Return([]pools.Member{}, nil)
1152+
poolMemberName := fmt.Sprintf("%s-kubeapi-%d-%s", clusterResourceName, port, machineName)
1153+
m.ListPoolMember(poolID, pools.ListMembersOpts{Name: poolMemberName}).Return([]pools.Member{}, nil)
11381154

11391155
// Expect CreatePoolMember; capture opts to assert SubnetID is set
11401156
m.CreatePoolMember(
@@ -1148,8 +1164,16 @@ func Test_ReconcileLoadBalancerMember(t *testing.T) {
11481164
g.Expect(got.SubnetID).To(Equal(subnetID))
11491165
// Tags should pass through
11501166
g.Expect(got.Tags).To(ConsistOf("k8s", "clusterapi"))
1151-
return &pools.Member{ID: "member-1", Address: memberIP, ProtocolPort: port}, nil
1167+
return &pools.Member{ID: memberID, Address: memberIP, ProtocolPort: port}, nil
11521168
})
1169+
1170+
activeMember := pools.Member{
1171+
ID: memberID,
1172+
Name: poolMemberName,
1173+
ProvisioningStatus: "ACTIVE",
1174+
}
1175+
m.GetPoolMember(poolID, memberID).Return(&activeMember, nil).AnyTimes()
1176+
11531177
},
11541178
wantError: nil,
11551179
},

0 commit comments

Comments
 (0)