Skip to content

Add listener based on the machine label and listener label #2155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions api/v1beta2/ibmvpccluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ type AdditionalListenerSpec struct {
// Will default to TCP protocol if not specified.
// +optional
Protocol *VPCLoadBalancerListenerProtocol `json:"protocol,omitempty"`

// The selector is used to find IBMPowerVSMachines with matching labels.
// If the label matches, the machine is then added to the load balancer listener configuration.
// +kubebuilder:validation:Optional
Selector metav1.LabelSelector `json:"selector,omitempty"`
}

// VPCLoadBalancerBackendPoolSpec defines the desired configuration of a VPC Load Balancer Backend Pool.
Expand Down
1 change: 1 addition & 0 deletions api/v1beta2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

85 changes: 66 additions & 19 deletions cloud/scope/powervs_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import (
"github.com/IBM/vpc-go-sdk/vpcv1"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/tools/cache"
Expand Down Expand Up @@ -996,7 +998,49 @@ func (m *PowerVSMachineScope) CreateVPCLoadBalancerPoolMember(ctx context.Contex

internalIP := m.GetMachineInternalIP()

// lbAdditionalListeners is a mapping of additionalListener's port-protocol to the additionalListener as defined in the specification
// It will be used later to get the default pool associated with the listener
lbAdditionalListeners := map[string]infrav1beta2.AdditionalListenerSpec{}
for _, additionalListener := range lb.AdditionalListeners {
if additionalListener.Protocol == nil {
additionalListener.Protocol = &infrav1beta2.VPCLoadBalancerListenerProtocolTCP
}
lbAdditionalListeners[fmt.Sprintf("%d-%s", additionalListener.Port, *additionalListener.Protocol)] = additionalListener
}

// loadBalancerListeners is a mapping of the loadBalancer listener's defaultPoolName to the additionalListener
// as the default pool name might be empty in spec and should be fetched from the cloud's listener
loadBalancerListeners := map[string]infrav1beta2.AdditionalListenerSpec{}
for _, listener := range loadBalancer.Listeners {
listenerOptions := &vpcv1.GetLoadBalancerListenerOptions{}
listenerOptions.SetLoadBalancerID(*loadBalancer.ID)
listenerOptions.SetID(*listener.ID)
loadBalancerListener, _, err := m.IBMVPCClient.GetLoadBalancerListener(listenerOptions)
if err != nil {
return nil, fmt.Errorf("failed to list %s load balancer listener: %w", *listener.ID, err)
}
if additionalListener, ok := lbAdditionalListeners[fmt.Sprintf("%d-%s", *loadBalancerListener.Port, *loadBalancerListener.Protocol)]; ok {
if loadBalancerListener.DefaultPool != nil {
loadBalancerListeners[*loadBalancerListener.DefaultPool.Name] = additionalListener
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Shilpa-Gokul @Karthik-K-N , one question here, the AdditionalListener in Spec has DefaultPoolName field, is it not possible to populate the field somehow and use the value here?

I'm unable to recall if the pool name follows a unique pattern when it is created

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think currently that field is never considered, also we expects spec to be set by user and not to alter from controller

}
// loadBalancerListeners map is created only with the listeners provided in the spec,
// and targetPort is populated only if there is an entry in the map.
// Inorder for the default pool 6443 to be added to all control plane machines, creating an entry in the map for the same.
} else if loadBalancerListener.Port != nil && *loadBalancerListener.Port == int64(6443) {
protocol := infrav1beta2.VPCLoadBalancerListenerProtocol(*loadBalancerListener.Protocol)
listener := infrav1beta2.AdditionalListenerSpec{
Port: *loadBalancerListener.Port,
Protocol: &protocol,
}
if loadBalancerListener.DefaultPool != nil {
loadBalancerListeners[*loadBalancerListener.DefaultPool.Name] = listener
} else {
log.V(3).Error(fmt.Errorf("unable to get the default pool details"), "default pool is nil", "port", loadBalancerListener.Port)
}
}
}
// Update each LoadBalancer pool
// For each pool, get the additionalListener associated with the pool from the loadBalancerListeners map.
for _, pool := range loadBalancer.Pools {
log.V(3).Info("Updating LoadBalancer pool member", "pool", *pool.Name, "loadBalancerName", *loadBalancer.Name, "IP", internalIP)
listOptions := &vpcv1.ListLoadBalancerPoolMembersOptions{}
Expand All @@ -1009,32 +1053,35 @@ func (m *PowerVSMachineScope) CreateVPCLoadBalancerPoolMember(ctx context.Contex
var targetPort int64
var alreadyRegistered bool

if len(listLoadBalancerPoolMembers.Members) == 0 {
// For adding the first member to the pool we depend on the pool name to get the target port
// pool name will have port number appended at the end
lbNameSplit := strings.Split(*pool.Name, "-")
if len(lbNameSplit) == 0 {
// user might have created additional pool
log.V(3).Info("Not updating pool as it might be created externally", "poolName", *pool.Name)
if loadBalancerListener, ok := loadBalancerListeners[*pool.Name]; ok {
targetPort = loadBalancerListener.Port
log.V(3).Info("Checking if machine label matches with the label selector in listener", "machineLabel", m.IBMPowerVSMachine.Labels, "labelSelector", loadBalancerListener.Selector)
selector, err := metav1.LabelSelectorAsSelector(&loadBalancerListener.Selector)
if err != nil {
log.V(5).Error(err, "Skipping listener addition, failed to get label selector from spec selector")
continue
}
targetPort, err = strconv.ParseInt(lbNameSplit[len(lbNameSplit)-1], 10, 64)
if err != nil {
// user might have created additional pool
log.Error(err, "unable to fetch target port from pool name", "poolName", *pool.Name)

if selector.Empty() && !util.IsControlPlaneMachine(m.Machine) {
log.V(3).Info("Skipping listener addition as the selector is empty and not a control plane machine")
continue
}
} else {
for _, member := range listLoadBalancerPoolMembers.Members {
if target, ok := member.Target.(*vpcv1.LoadBalancerPoolMemberTarget); ok {
targetPort = *member.Port
if *target.Address == internalIP {
alreadyRegistered = true
log.V(3).Info("Target IP already configured for pool", "IP", internalIP, "poolName", *pool.Name)
}
// Skip adding the listener if the selector does not match
if !selector.Empty() && !selector.Matches(labels.Set(m.IBMPowerVSMachine.Labels)) {
log.V(3).Info("Skip adding listener, machine label doesn't match with the listener label selector", "pool", *pool.Name, "IP", internalIP)
continue
}
}

for _, member := range listLoadBalancerPoolMembers.Members {
if target, ok := member.Target.(*vpcv1.LoadBalancerPoolMemberTarget); ok {
if *target.Address == internalIP {
alreadyRegistered = true
log.V(3).Info("Target IP already configured for pool", "IP", internalIP, "poolName", *pool.Name)
}
}
}

if alreadyRegistered {
log.V(3).Info("PoolMember already exist", "poolName", *pool.Name, "IP", internalIP, "targetPort", targetPort)
continue
Expand Down
Loading