@@ -32,6 +32,8 @@ import (
3232 "github.com/gophercloud/gophercloud/v2/openstack"
3333 "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes"
3434 "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers"
35+ "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/loadbalancers"
36+ "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/monitors"
3537 "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers"
3638 "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups"
3739 "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks"
@@ -1029,6 +1031,128 @@ var _ = Describe("e2e tests [PR-Blocking]", func() {
10291031 ), "OpenStackMachine should be marked not ready with InstanceDeletedReason" )
10301032 })
10311033 })
1034+
1035+ Describe ("Workload cluster (health monitor)" , func () {
1036+ It ("should configure load balancer health monitor with custom settings" , func (ctx context.Context ) {
1037+ shared .Logf ("Creating a cluster with custom health monitor configuration" )
1038+ clusterName := fmt .Sprintf ("cluster-%s" , namespace .Name )
1039+ configCluster := defaultConfigCluster (clusterName , namespace .Name )
1040+ configCluster .ControlPlaneMachineCount = ptr .To (int64 (1 ))
1041+ configCluster .WorkerMachineCount = ptr .To (int64 (1 ))
1042+ configCluster .Flavor = shared .FlavorHealthMonitor
1043+ createCluster (ctx , configCluster , clusterResources )
1044+
1045+ openStackCluster , err := shared .ClusterForSpec (ctx , e2eCtx , namespace )
1046+ Expect (err ).NotTo (HaveOccurred ())
1047+
1048+ Expect (openStackCluster .Spec .APIServerLoadBalancer ).ToNot (BeNil ())
1049+ Expect (openStackCluster .Spec .APIServerLoadBalancer .Monitor ).ToNot (BeNil ())
1050+ Expect (openStackCluster .Spec .APIServerLoadBalancer .Monitor .Delay ).ToNot (BeNil ())
1051+ Expect (openStackCluster .Spec .APIServerLoadBalancer .Monitor .Delay ).To (Equal (15 ))
1052+ Expect (openStackCluster .Spec .APIServerLoadBalancer .Monitor .Timeout ).ToNot (BeNil ())
1053+ Expect (openStackCluster .Spec .APIServerLoadBalancer .Monitor .Timeout ).To (Equal (10 ))
1054+ Expect (openStackCluster .Spec .APIServerLoadBalancer .Monitor .MaxRetries ).ToNot (BeNil ())
1055+ Expect (openStackCluster .Spec .APIServerLoadBalancer .Monitor .MaxRetries ).To (Equal (3 ))
1056+ Expect (openStackCluster .Spec .APIServerLoadBalancer .Monitor .MaxRetriesDown ).ToNot (BeNil ())
1057+ Expect (openStackCluster .Spec .APIServerLoadBalancer .Monitor .MaxRetriesDown ).To (Equal (2 ))
1058+
1059+ shared .Logf ("Looking for load balancer for cluster %s" , clusterName )
1060+ expectedLBName := fmt .Sprintf ("k8s-clusterapi-cluster-%s-%s-kubeapi" , namespace .Name , clusterName )
1061+ loadBalancers , err := shared .DumpOpenStackLoadBalancers (e2eCtx , loadbalancers.ListOpts {
1062+ Name : expectedLBName ,
1063+ })
1064+ Expect (err ).NotTo (HaveOccurred ())
1065+
1066+ if len (loadBalancers ) == 0 {
1067+ shared .Logf ("Load balancer not found by name, trying by tags" )
1068+ loadBalancers , err = shared .DumpOpenStackLoadBalancers (e2eCtx , loadbalancers.ListOpts {
1069+ Tags : []string {clusterName },
1070+ })
1071+ Expect (err ).NotTo (HaveOccurred ())
1072+ }
1073+ Expect (loadBalancers ).ToNot (BeEmpty (), "Load balancer should exist for cluster" )
1074+
1075+ loadBalancer := loadBalancers [0 ]
1076+ shared .Logf ("Found load balancer %s with ID %s" , loadBalancer .Name , loadBalancer .ID )
1077+
1078+ shared .Logf ("Looking for health monitors for load balancer %s" , loadBalancer .ID )
1079+ monitorList , err := shared .DumpOpenStackLoadBalancerMonitors (e2eCtx , monitors.ListOpts {})
1080+ Expect (err ).NotTo (HaveOccurred ())
1081+
1082+ expectedMonitorName := fmt .Sprintf ("%s-6443" , loadBalancer .Name )
1083+
1084+ var clusterMonitor * monitors.Monitor
1085+ for i := range monitorList {
1086+ monitor := & monitorList [i ]
1087+ if monitor .Name == expectedMonitorName || strings .Contains (monitor .Name , loadBalancer .Name ) {
1088+ clusterMonitor = monitor
1089+ break
1090+ }
1091+ }
1092+ Expect (clusterMonitor ).ToNot (BeNil (), "Health monitor should exist for the cluster load balancer" )
1093+
1094+ shared .Logf ("Found health monitor %s with ID %s" , clusterMonitor .Name , clusterMonitor .ID )
1095+
1096+ Expect (clusterMonitor .Delay ).To (Equal (15 ), "Monitor delay should match configured value" )
1097+ Expect (clusterMonitor .Timeout ).To (Equal (10 ), "Monitor timeout should match configured value" )
1098+ Expect (clusterMonitor .MaxRetries ).To (Equal (3 ), "Monitor maxRetries should match configured value" )
1099+ Expect (clusterMonitor .MaxRetriesDown ).To (Equal (2 ), "Monitor maxRetriesDown should match configured value" )
1100+ Expect (clusterMonitor .Type ).To (Equal ("TCP" ), "Monitor should be TCP type" )
1101+
1102+ shared .Logf ("Testing health monitor configuration update" )
1103+ openStackCluster , err = shared .ClusterForSpec (ctx , e2eCtx , namespace )
1104+ Expect (err ).NotTo (HaveOccurred ())
1105+
1106+ updatedCluster := openStackCluster .DeepCopy ()
1107+ updatedCluster .Spec .APIServerLoadBalancer .Monitor .Delay = 20
1108+ updatedCluster .Spec .APIServerLoadBalancer .Monitor .MaxRetries = 4
1109+
1110+ Expect (e2eCtx .Environment .BootstrapClusterProxy .GetClient ().Update (ctx , updatedCluster )).To (Succeed ())
1111+
1112+ Eventually (func () (bool , error ) {
1113+ updatedMonitor , err := shared .GetOpenStackLoadBalancerMonitor (e2eCtx , clusterMonitor .ID )
1114+ if err != nil {
1115+ return false , err
1116+ }
1117+ return updatedMonitor .Delay == 20 && updatedMonitor .MaxRetries == 4 , nil
1118+ }, e2eCtx .E2EConfig .GetIntervals (specName , "wait-cluster" )... ).Should (BeTrue (), "Monitor should be updated with new configuration" )
1119+
1120+ finalMonitor , err := shared .GetOpenStackLoadBalancerMonitor (e2eCtx , clusterMonitor .ID )
1121+ Expect (err ).NotTo (HaveOccurred ())
1122+ Expect (finalMonitor .Delay ).To (Equal (20 ), "Monitor delay should be updated" )
1123+ Expect (finalMonitor .MaxRetries ).To (Equal (4 ), "Monitor maxRetries should be updated" )
1124+ Expect (finalMonitor .Timeout ).To (Equal (10 ), "Monitor timeout should remain unchanged" )
1125+ Expect (finalMonitor .MaxRetriesDown ).To (Equal (2 ), "Monitor maxRetriesDown should remain unchanged" )
1126+
1127+ shared .Logf ("Testing monitor configuration removal and default value reversion" )
1128+ openStackCluster , err = shared .ClusterForSpec (ctx , e2eCtx , namespace )
1129+ Expect (err ).NotTo (HaveOccurred ())
1130+
1131+ clusterWithRemovedMonitor := openStackCluster .DeepCopy ()
1132+ clusterWithRemovedMonitor .Spec .APIServerLoadBalancer .Monitor = nil
1133+ if clusterWithRemovedMonitor .Annotations == nil {
1134+ clusterWithRemovedMonitor .Annotations = make (map [string ]string )
1135+ }
1136+ clusterWithRemovedMonitor .Annotations ["test.e2e/monitor-update" ] = fmt .Sprintf ("%d" , time .Now ().Unix ())
1137+ Expect (e2eCtx .Environment .BootstrapClusterProxy .GetClient ().Update (ctx , clusterWithRemovedMonitor )).To (Succeed ())
1138+
1139+ Eventually (func () (bool , error ) {
1140+ revertedMonitor , err := shared .GetOpenStackLoadBalancerMonitor (e2eCtx , clusterMonitor .ID )
1141+ if err != nil {
1142+ return false , err
1143+ }
1144+ return revertedMonitor .Delay == 10 && revertedMonitor .Timeout == 5 &&
1145+ revertedMonitor .MaxRetries == 5 && revertedMonitor .MaxRetriesDown == 3 , nil
1146+ }, e2eCtx .E2EConfig .GetIntervals (specName , "wait-cluster" )... ).Should (BeTrue (), "Monitor should revert to all default values when configuration is removed" )
1147+
1148+ revertedMonitor , err := shared .GetOpenStackLoadBalancerMonitor (e2eCtx , clusterMonitor .ID )
1149+ Expect (err ).NotTo (HaveOccurred ())
1150+ Expect (revertedMonitor .Delay ).To (Equal (10 ), "Monitor delay should revert to default value (10)" )
1151+ Expect (revertedMonitor .Timeout ).To (Equal (5 ), "Monitor timeout should revert to default value (5)" )
1152+ Expect (revertedMonitor .MaxRetries ).To (Equal (5 ), "Monitor maxRetries should revert to default value (5)" )
1153+ Expect (revertedMonitor .MaxRetriesDown ).To (Equal (3 ), "Monitor maxRetriesDown should revert to default value (3)" )
1154+ })
1155+ })
10321156})
10331157
10341158func defaultConfigCluster (clusterName , namespace string ) clusterctl.ConfigClusterInput {
0 commit comments