@@ -32,6 +32,7 @@ import (
3232	"github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/providers" 
3333	. "github.com/onsi/gomega"  //nolint:revive 
3434	"go.uber.org/mock/gomock" 
35+ 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 
3536	"k8s.io/utils/ptr" 
3637	clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" 
3738
@@ -926,3 +927,254 @@ func Test_getOrCreateAPILoadBalancer(t *testing.T) {
926927		})
927928	}
928929}
930+ 
931+ func  Test_ReconcileLoadBalancerMember (t  * testing.T ) {
932+ 	g  :=  NewWithT (t )
933+ 	mockCtrl  :=  gomock .NewController (t )
934+ 	defer  mockCtrl .Finish ()
935+ 
936+ 	const  (
937+ 		clusterName          =  "AAAAA" 
938+ 		clusterResourceName  =  "k8s-clusterapi-cluster-AAAAA" 
939+ 		memberIP             =  "10.0.0.1" 
940+ 		wrongMemberIP        =  "10.0.0.20" 
941+ 		port                 =  6443 
942+ 		machineName          =  "machine-1" 
943+ 
944+ 		clusterNetID  =  "aaaaaaaa-bbbb-cccc-dddd-111111111111" 
945+ 		subnetID      =  "aaaaaaaa-bbbb-cccc-dddd-222222222222" 
946+ 		lbID          =  "aaaaaaaa-bbbb-cccc-dddd-333333333333" 
947+ 		listenerID    =  "aaaaaaaa-bbbb-cccc-dddd-444444444444" 
948+ 		poolID        =  "aaaaaaaa-bbbb-cccc-dddd-555555555555" 
949+ 		memberID      =  "aaaaaaaa-bbbb-cccc-dddd-666666666666" 
950+ 		lbNetOtherID  =  "aaaaaaaa-bbbb-cccc-dddd-999999999999" 
951+ 	)
952+ 
953+ 	makeCluster  :=  func (provider  * string , lbNetworkID  string ) * infrav1.OpenStackCluster  {
954+ 		return  & infrav1.OpenStackCluster {
955+ 			Spec : infrav1.OpenStackClusterSpec {
956+ 				APIServerLoadBalancer : & infrav1.APIServerLoadBalancer {
957+ 					Enabled :  ptr .To (true ),
958+ 					Provider : provider ,
959+ 					Network : & infrav1.NetworkParam {
960+ 						ID : & lbNetworkID ,
961+ 					},
962+ 				},
963+ 				DisableAPIServerFloatingIP : ptr .To (true ),
964+ 				ControlPlaneEndpoint : & clusterv1beta1.APIEndpoint {
965+ 					Host : apiHostname ,
966+ 					Port : port ,
967+ 				},
968+ 				Tags : []string {"k8s" , "clusterapi" },
969+ 			},
970+ 			Status : infrav1.OpenStackClusterStatus {
971+ 				APIServerLoadBalancer : & infrav1.LoadBalancer {
972+ 					ID : lbID ,
973+ 					LoadBalancerNetwork : & infrav1.NetworkStatusWithSubnets {
974+ 						NetworkStatus : infrav1.NetworkStatus {
975+ 							ID : lbNetworkID ,
976+ 						},
977+ 					},
978+ 				},
979+ 				Network : & infrav1.NetworkStatusWithSubnets {
980+ 					NetworkStatus : infrav1.NetworkStatus {
981+ 						ID : clusterNetID ,
982+ 					},
983+ 					Subnets : []infrav1.Subnet {
984+ 						{ID : subnetID },
985+ 					},
986+ 				},
987+ 			},
988+ 		}
989+ 	}
990+ 
991+ 	openStackMachine  :=  & infrav1.OpenStackMachine {
992+ 		ObjectMeta : metav1.ObjectMeta {Name : machineName },
993+ 	}
994+ 
995+ 	lbtests  :=  []struct  {
996+ 		name                string 
997+ 		clusterSpec         * infrav1.OpenStackCluster 
998+ 		expectNetwork       func (m  * mock.MockNetworkClientMockRecorder )
999+ 		expectLoadBalancer  func (m  * mock.MockLbClientMockRecorder )
1000+ 		wantError           error 
1001+ 	}{
1002+ 		{
1003+ 			name :          "LB member exists, dont create" ,
1004+ 			clusterSpec :   makeCluster (nil , clusterNetID ),
1005+ 			expectNetwork : func (* mock.MockNetworkClientMockRecorder ) {},
1006+ 			expectLoadBalancer : func (m  * mock.MockLbClientMockRecorder ) {
1007+ 				activeLB  :=  loadbalancers.LoadBalancer {
1008+ 					ID :                 lbID ,
1009+ 					Name :               clusterResourceName  +  "-kubeapi" ,
1010+ 					ProvisioningStatus : "ACTIVE" ,
1011+ 				}
1012+ 				m .GetLoadBalancer (lbID ).Return (& activeLB , nil ).AnyTimes ()
1013+ 
1014+ 				pool  :=  pools.Pool {
1015+ 					ID :   poolID ,
1016+ 					Name : fmt .Sprintf ("%s-kubeapi-%d" , clusterResourceName , port ),
1017+ 				}
1018+ 				m .ListPools (pools.ListOpts {Name : pool .Name }).Return ([]pools.Pool {pool }, nil )
1019+ 
1020+ 				member  :=  pools.Member {
1021+ 					Name :    fmt .Sprintf ("%s-kubeapi-%d-%s" , clusterResourceName , port , machineName ),
1022+ 					Address : memberIP ,
1023+ 				}
1024+ 				m .ListPoolMember (poolID , pools.ListMembersOpts {Name : member .Name }).Return ([]pools.Member {member }, nil )
1025+ 
1026+ 			},
1027+ 			wantError : nil ,
1028+ 		},
1029+ 		{
1030+ 			name :          "No LB member, create" ,
1031+ 			clusterSpec :   makeCluster (nil , clusterNetID ),
1032+ 			expectNetwork : func (* mock.MockNetworkClientMockRecorder ) {},
1033+ 			expectLoadBalancer : func (m  * mock.MockLbClientMockRecorder ) {
1034+ 				activeLB  :=  loadbalancers.LoadBalancer {
1035+ 					ID :                 lbID ,
1036+ 					Name :               clusterResourceName  +  "-kubeapi" ,
1037+ 					ProvisioningStatus : "ACTIVE" ,
1038+ 				}
1039+ 				m .GetLoadBalancer (lbID ).Return (& activeLB , nil ).AnyTimes ()
1040+ 
1041+ 				pool  :=  pools.Pool {
1042+ 					ID :   poolID ,
1043+ 					Name : fmt .Sprintf ("%s-kubeapi-%d" , clusterResourceName , port ),
1044+ 				}
1045+ 				m .ListPools (pools.ListOpts {Name : pool .Name }).Return ([]pools.Pool {pool }, nil )
1046+ 
1047+ 				poolMemberName  :=  fmt .Sprintf ("%s-kubeapi-%d-%s" , clusterResourceName , port , machineName )
1048+ 				m .ListPoolMember (poolID , pools.ListMembersOpts {Name : poolMemberName }).Return ([]pools.Member {}, nil )
1049+ 
1050+ 				m .CreatePoolMember (
1051+ 					poolID ,
1052+ 					gomock .AssignableToTypeOf (pools.CreateMemberOpts {}),
1053+ 				).DoAndReturn (func (_  string , got  pools.CreateMemberOpts ) (* pools.Member , error ) {
1054+ 					// SubnetID must be empty here 
1055+ 					g .Expect (got .SubnetID ).To (Equal ("" ))
1056+ 					return  & pools.Member {ID : "member-2" }, nil 
1057+ 				})
1058+ 			},
1059+ 			wantError : nil ,
1060+ 		},
1061+ 		{
1062+ 			name :          "No pool found, return error" ,
1063+ 			clusterSpec :   makeCluster (nil , clusterNetID ),
1064+ 			expectNetwork : func (* mock.MockNetworkClientMockRecorder ) {},
1065+ 			expectLoadBalancer : func (m  * mock.MockLbClientMockRecorder ) {
1066+ 				activeLB  :=  loadbalancers.LoadBalancer {
1067+ 					ID :                 lbID ,
1068+ 					Name :               clusterResourceName  +  "-kubeapi" ,
1069+ 					ProvisioningStatus : "ACTIVE" ,
1070+ 				}
1071+ 				m .GetLoadBalancer (lbID ).Return (& activeLB , nil ).AnyTimes ()
1072+ 
1073+ 				poolName  :=  fmt .Sprintf ("%s-kubeapi-%d" , clusterResourceName , port )
1074+ 				m .ListPools (pools.ListOpts {Name : poolName }).Return ([]pools.Pool {}, nil )
1075+ 
1076+ 			},
1077+ 			wantError : errors .New ("load balancer pool does not exist yet" ),
1078+ 		},
1079+ 		{
1080+ 			name :          "LB member with wrong address, re-create" ,
1081+ 			clusterSpec :   makeCluster (nil , clusterNetID ),
1082+ 			expectNetwork : func (* mock.MockNetworkClientMockRecorder ) {},
1083+ 			expectLoadBalancer : func (m  * mock.MockLbClientMockRecorder ) {
1084+ 				activeLB  :=  loadbalancers.LoadBalancer {
1085+ 					ID :                 lbID ,
1086+ 					Name :               clusterResourceName  +  "-kubeapi" ,
1087+ 					ProvisioningStatus : "ACTIVE" ,
1088+ 				}
1089+ 				m .GetLoadBalancer (lbID ).Return (& activeLB , nil ).AnyTimes ()
1090+ 
1091+ 				pool  :=  pools.Pool {
1092+ 					ID :   poolID ,
1093+ 					Name : fmt .Sprintf ("%s-kubeapi-%d" , clusterResourceName , port ),
1094+ 				}
1095+ 				m .ListPools (pools.ListOpts {Name : pool .Name }).Return ([]pools.Pool {pool }, nil )
1096+ 
1097+ 				member  :=  pools.Member {
1098+ 					Name :    fmt .Sprintf ("%s-kubeapi-%d-%s" , clusterResourceName , port , machineName ),
1099+ 					Address : wrongMemberIP ,
1100+ 					ID :      memberID ,
1101+ 				}
1102+ 				m .ListPoolMember (poolID , pools.ListMembersOpts {Name : member .Name }).Return ([]pools.Member {member }, nil )
1103+ 
1104+ 				m .DeletePoolMember (poolID , memberID ).Return (nil )
1105+ 
1106+ 				m .CreatePoolMember (
1107+ 					poolID ,
1108+ 					gomock .AssignableToTypeOf (pools.CreateMemberOpts {}),
1109+ 				).DoAndReturn (func (_  string , got  pools.CreateMemberOpts ) (* pools.Member , error ) {
1110+ 					// SubnetID must be empty here 
1111+ 					g .Expect (got .SubnetID ).To (Equal ("" ))
1112+ 					return  & pools.Member {ID : "member-2" }, nil 
1113+ 				})
1114+ 			},
1115+ 			wantError : nil ,
1116+ 		},
1117+ 		{
1118+ 			name :        "ovn provider and different networks, set SubnetID on member create" ,
1119+ 			clusterSpec : makeCluster (ptr .To ("ovn" ), lbNetOtherID ),
1120+ 			expectNetwork : func (* mock.MockNetworkClientMockRecorder ) {
1121+ 				// not used by this path 
1122+ 			},
1123+ 			expectLoadBalancer : func (m  * mock.MockLbClientMockRecorder ) {
1124+ 				// LB initially ACTIVE whenever we wait 
1125+ 				activeLB  :=  loadbalancers.LoadBalancer {
1126+ 					ID :                 lbID ,
1127+ 					Name :               clusterResourceName  +  "-kubeapi" ,
1128+ 					ProvisioningStatus : "ACTIVE" ,
1129+ 				}
1130+ 				m .GetLoadBalancer (lbID ).Return (& activeLB , nil ).AnyTimes ()
1131+ 
1132+ 				pool  :=  pools.Pool {
1133+ 					ID :   poolID ,
1134+ 					Name : fmt .Sprintf ("%s-kubeapi-%d" , clusterResourceName , port ),
1135+ 				}
1136+ 				m .ListPools (pools.ListOpts {Name : pool .Name }).Return ([]pools.Pool {pool }, nil )
1137+ 
1138+ 				memberName  :=  fmt .Sprintf ("%s-kubeapi-%d-%s" , clusterResourceName , port , machineName )
1139+ 				m .ListPoolMember (poolID , pools.ListMembersOpts {Name : memberName }).Return ([]pools.Member {}, nil )
1140+ 
1141+ 				// Expect CreatePoolMember; capture opts to assert SubnetID is set 
1142+ 				m .CreatePoolMember (
1143+ 					poolID ,
1144+ 					gomock .AssignableToTypeOf (pools.CreateMemberOpts {}),
1145+ 				).DoAndReturn (func (_  string , got  pools.CreateMemberOpts ) (* pools.Member , error ) {
1146+ 					g .Expect (got .Address ).To (Equal (memberIP ))
1147+ 					g .Expect (got .ProtocolPort ).To (Equal (port ))
1148+ 					expName  :=  fmt .Sprintf ("%s-kubeapi-%d-%s" , clusterResourceName , port , machineName )
1149+ 					g .Expect (got .Name ).To (Equal (expName ))
1150+ 					g .Expect (got .SubnetID ).To (Equal (subnetID ))
1151+ 					// Tags should pass through 
1152+ 					g .Expect (got .Tags ).To (ConsistOf ("k8s" , "clusterapi" ))
1153+ 					return  & pools.Member {ID : "member-1" , Address : memberIP , ProtocolPort : port }, nil 
1154+ 				})
1155+ 			},
1156+ 			wantError : nil ,
1157+ 		},
1158+ 	}
1159+ 
1160+ 	for  _ , tt  :=  range  lbtests  {
1161+ 		t .Run (tt .name , func (t  * testing.T ) {
1162+ 			g  :=  NewWithT (t )
1163+ 			log  :=  testr .New (t )
1164+ 
1165+ 			mockScopeFactory  :=  scope .NewMockScopeFactory (mockCtrl , "" )
1166+ 			lbs , err  :=  NewService (scope .NewWithLogger (mockScopeFactory , log ))
1167+ 			g .Expect (err ).NotTo (HaveOccurred ())
1168+ 
1169+ 			tt .expectNetwork (mockScopeFactory .NetworkClient .EXPECT ())
1170+ 			tt .expectLoadBalancer (mockScopeFactory .LbClient .EXPECT ())
1171+ 
1172+ 			err  =  lbs .ReconcileLoadBalancerMember (tt .clusterSpec , openStackMachine , clusterName , memberIP )
1173+ 			if  tt .wantError  !=  nil  {
1174+ 				g .Expect (err ).To (MatchError (tt .wantError ))
1175+ 			} else  {
1176+ 				g .Expect (err ).NotTo (HaveOccurred ())
1177+ 			}
1178+ 		})
1179+ 	}
1180+ }
0 commit comments