Skip to content

Commit

Permalink
Encapsulate Keepalived config in a new type
Browse files Browse the repository at this point in the history
In the future CPLB may want to support other backends. Encapsulate all
keepalived's configuration in a new struct, just like NLLB.

Signed-off-by: Juan-Luis de Sousa-Valadas Castaño <[email protected]>
  • Loading branch information
juanluisvaladas committed Apr 30, 2024
1 parent a6b3443 commit b9c8803
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 163 deletions.
5 changes: 4 additions & 1 deletion cmd/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,13 @@ func (c *command) start(ctx context.Context) error {
if c.SingleNode {
return errors.New("control plane load balancing cannot be used in a single-node cluster")
}
if cplb.Type != v1beta1.CPLBTypeKeepalived {
return errors.New("invalid control plane load balancing type. Only 'keepalived' is supported")
}

nodeComponents.Add(ctx, &controller.Keepalived{
K0sVars: c.K0sVars,
Config: cplb,
Config: cplb.Keepalived,
DetailedLogging: c.Debug,
KubeConfigPath: c.K0sVars.AdminKubeConfigPath,
APISpec: nodeConfig.Spec.API,
Expand Down
12 changes: 7 additions & 5 deletions inttest/cplb/cplb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ spec:
network:
controlPlaneLoadBalancing:
enabled: true
vrrpInstances:
- virtualIPs: ["%s/16"]
authPass: "123456"
virtualServers:
- ipAddress: %s
type: Keepalived
keepalived:
vrrpInstances:
- virtualIPs: ["%s/16"]
authPass: "123456"
virtualServers:
- ipAddress: %s
nodeLocalLoadBalancing:
enabled: true
type: EnvoyProxy
Expand Down
86 changes: 54 additions & 32 deletions pkg/apis/k0s/v1beta1/cplb.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ type ControlPlaneLoadBalancingSpec struct {
// +optional
Enabled bool `json:"enabled,omitempty"`

// type indicates the type of the node-local load balancer to deploy on
// worker nodes. Currently, the only supported type is "Keepalived".
// +kubebuilder:default=Keepalived
// +optional
Type CPLBType `json:"type,omitempty"`

// Keepalived contains configuration options related to the "Keepalived" type
// of load balancing.
Keepalived *KeepalivedSpec `json:"keepalived,omitempty"`
}

// NllbType describes which type of load balancer should be deployed for the
// node-local load balancing. The default is [CPLBTypeKeepalived].
// +kubebuilder:validation:Enum=keepalived
type CPLBType string

const (
// CPLBTypeKeepalived selects Keepalived as the backing load balancer.
CPLBTypeKeepalived CPLBType = "Keepalived"
)

type KeepalivedSpec struct {
// Configuration options related to the VRRP. This is an array which allows
// to configure multiple virtual IPs.
VRRPInstances VRRPInstances `json:"vrrpInstances,omitempty"`
Expand Down Expand Up @@ -84,46 +106,46 @@ type VirtualIPs []string

// ValidateVRRPInstances validates existing configuration and sets the default
// values of undefined fields.
func (c *ControlPlaneLoadBalancingSpec) ValidateVRRPInstances(getDefaultNICFn func() (string, error)) error {
func (k *KeepalivedSpec) ValidateVRRPInstances(getDefaultNICFn func() (string, error)) error {
if getDefaultNICFn == nil {
getDefaultNICFn = getDefaultNIC
}
for i := range c.VRRPInstances {
if c.VRRPInstances[i].Name == "" {
c.VRRPInstances[i].Name = fmt.Sprintf("k0s-vip-%d", i)
for i := range k.VRRPInstances {
if k.VRRPInstances[i].Name == "" {
k.VRRPInstances[i].Name = fmt.Sprintf("k0s-vip-%d", i)
}

if c.VRRPInstances[i].Interface == "" {
if k.VRRPInstances[i].Interface == "" {
nic, err := getDefaultNICFn()
if err != nil {
return fmt.Errorf("failed to get default NIC: %w", err)
}
c.VRRPInstances[i].Interface = nic
k.VRRPInstances[i].Interface = nic
}

if c.VRRPInstances[i].VirtualRouterID == nil {
if k.VRRPInstances[i].VirtualRouterID == nil {
vrid := int32(defaultVirtualRouterID + i)
c.VRRPInstances[i].VirtualRouterID = &vrid
} else if *c.VRRPInstances[i].VirtualRouterID < 0 || *c.VRRPInstances[i].VirtualRouterID > 255 {
k.VRRPInstances[i].VirtualRouterID = &vrid
} else if *k.VRRPInstances[i].VirtualRouterID < 0 || *k.VRRPInstances[i].VirtualRouterID > 255 {
return errors.New("VirtualRouterID must be in the range of 1-255")
}

if c.VRRPInstances[i].AdvertInterval == nil {
if k.VRRPInstances[i].AdvertInterval == nil {
advInt := int32(defaultAdvertInterval)
c.VRRPInstances[i].AdvertInterval = &advInt
k.VRRPInstances[i].AdvertInterval = &advInt
}

if c.VRRPInstances[i].AuthPass == "" {
if k.VRRPInstances[i].AuthPass == "" {
return errors.New("AuthPass must be defined")
}
if len(c.VRRPInstances[i].AuthPass) > 8 {
if len(k.VRRPInstances[i].AuthPass) > 8 {
return errors.New("AuthPass must be 8 characters or less")
}

if len(c.VRRPInstances[i].VirtualIPs) == 0 {
if len(k.VRRPInstances[i].VirtualIPs) == 0 {
return errors.New("VirtualIPs must be defined")
}
for _, vip := range c.VRRPInstances[i].VirtualIPs {
for _, vip := range k.VRRPInstances[i].VirtualIPs {
if _, _, err := net.ParseCIDR(vip); err != nil {
return fmt.Errorf("VirtualIPs must be a CIDR. Got: %s", vip)
}
Expand Down Expand Up @@ -186,44 +208,44 @@ type RealServer struct {
Weight int `json:"weight,omitempty"`
}

func (c *ControlPlaneLoadBalancingSpec) ValidateVirtualServers() error {
for i := range c.VirtualServers {
if c.VirtualServers[i].IPAddress == "" {
func (k *KeepalivedSpec) ValidateVirtualServers() error {
for i := range k.VirtualServers {
if k.VirtualServers[i].IPAddress == "" {
return errors.New("IPAddress must be defined")
}
if net.ParseIP(c.VirtualServers[i].IPAddress) == nil {
return fmt.Errorf("invalid IP address: %s", c.VirtualServers[i].IPAddress)
if net.ParseIP(k.VirtualServers[i].IPAddress) == nil {
return fmt.Errorf("invalid IP address: %s", k.VirtualServers[i].IPAddress)
}

if c.VirtualServers[i].LBAlgo == "" {
c.VirtualServers[i].LBAlgo = RRAlgo
if k.VirtualServers[i].LBAlgo == "" {
k.VirtualServers[i].LBAlgo = RRAlgo
} else {
switch c.VirtualServers[i].LBAlgo {
switch k.VirtualServers[i].LBAlgo {
case RRAlgo, WRRAlgo, LCAlgo, WLCAlgo, LBLCAlgo, DHAlgo, SHAlgo, SEDAlgo, NQAlgo:
// valid LBAlgo
default:
return fmt.Errorf("invalid LBAlgo: %s ", c.VirtualServers[i].LBAlgo)
return fmt.Errorf("invalid LBAlgo: %s ", k.VirtualServers[i].LBAlgo)
}
}

if c.VirtualServers[i].LBKind == "" {
c.VirtualServers[i].LBKind = DRLBKind
if k.VirtualServers[i].LBKind == "" {
k.VirtualServers[i].LBKind = DRLBKind
} else {
switch c.VirtualServers[i].LBKind {
switch k.VirtualServers[i].LBKind {
case NATLBKind, DRLBKind, TUNLBKind:
// valid LBKind
default:
return fmt.Errorf("invalid LBKind: %s ", c.VirtualServers[i].LBKind)
return fmt.Errorf("invalid LBKind: %s ", k.VirtualServers[i].LBKind)
}
}

if c.VirtualServers[i].PersistenceTimeout == 0 {
c.VirtualServers[i].PersistenceTimeout = 360
} else if c.VirtualServers[i].PersistenceTimeout < 0 {
if k.VirtualServers[i].PersistenceTimeout == 0 {
k.VirtualServers[i].PersistenceTimeout = 360
} else if k.VirtualServers[i].PersistenceTimeout < 0 {
return errors.New("PersistenceTimeout must be a positive integer")
}

if c.VirtualServers[i].DelayLoop < 0 {
if k.VirtualServers[i].DelayLoop < 0 {
return errors.New("DelayLoop must be a positive integer")
}
}
Expand Down
29 changes: 14 additions & 15 deletions pkg/apis/k0s/v1beta1/cplb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ type CPLBSuite struct {
}

func (s *CPLBSuite) TestValidateVRRPInstances() {

tests := []struct {
name string
vrrps []VRRPInstance
Expand Down Expand Up @@ -125,21 +124,21 @@ func (s *CPLBSuite) TestValidateVRRPInstances() {

for _, tt := range tests {
s.Run(tt.name, func() {
elb := &ControlPlaneLoadBalancingSpec{
k := &KeepalivedSpec{
VRRPInstances: tt.vrrps,
}
err := elb.ValidateVRRPInstances(returnNIC)
err := k.ValidateVRRPInstances(returnNIC)
if tt.wantErr {
s.Require().Errorf(err, "Test case %s expected error. Got none", tt.name)
} else {
s.Require().NoErrorf(err, "Test case %s expected no error. Got: %v", tt.name, err)
s.T().Log(elb.VRRPInstances)
s.Require().Equal(len(tt.expectedVRRPs), len(elb.VRRPInstances), "Expected and actual VRRPInstances length mismatch")
s.T().Log(k.VRRPInstances)
s.Require().Equal(len(tt.expectedVRRPs), len(k.VRRPInstances), "Expected and actual VRRPInstances length mismatch")
for i := 0; i < len(tt.expectedVRRPs); i++ {
s.Require().Equal(tt.expectedVRRPs[i].Name, elb.VRRPInstances[i].Name, "Name mismatch")
s.Require().Equal(tt.expectedVRRPs[i].Interface, elb.VRRPInstances[i].Interface, "Interface mismatch")
s.Require().Equal(*tt.expectedVRRPs[i].VirtualRouterID, *elb.VRRPInstances[i].VirtualRouterID, "Virtual router ID mismatch")
s.Require().Equal(*tt.expectedVRRPs[i].AdvertInterval, *elb.VRRPInstances[i].AdvertInterval, "Virtual router ID mismatch")
s.Require().Equal(tt.expectedVRRPs[i].Name, k.VRRPInstances[i].Name, "Name mismatch")
s.Require().Equal(tt.expectedVRRPs[i].Interface, k.VRRPInstances[i].Interface, "Interface mismatch")
s.Require().Equal(*tt.expectedVRRPs[i].VirtualRouterID, *k.VRRPInstances[i].VirtualRouterID, "Virtual router ID mismatch")
s.Require().Equal(*tt.expectedVRRPs[i].AdvertInterval, *k.VRRPInstances[i].AdvertInterval, "Virtual router ID mismatch")
}
}
})
Expand Down Expand Up @@ -250,17 +249,17 @@ func (s *CPLBSuite) TestValidateVirtualServers() {
}
for _, tt := range tests {
s.Run(tt.name, func() {
cplb := &ControlPlaneLoadBalancingSpec{VirtualServers: tt.vss}
err := cplb.ValidateVirtualServers()
k := &KeepalivedSpec{VirtualServers: tt.vss}
err := k.ValidateVirtualServers()
if tt.wantErr {
s.Require().Errorf(err, "Test case %s expected error. Got none", tt.name)
} else {
s.Require().NoErrorf(err, "Tedst case %s expected no error. Got: %v", tt.name, err)
for i := range tt.expectedVSS {
s.Require().Equal(tt.expectedVSS[i].DelayLoop, cplb.VirtualServers[i].DelayLoop, "DelayLoop mismatch")
s.Require().Equal(tt.expectedVSS[i].LBAlgo, cplb.VirtualServers[i].LBAlgo, "LBalgo mismatch")
s.Require().Equal(tt.expectedVSS[i].LBKind, cplb.VirtualServers[i].LBKind, "LBKind mismatch")
s.Require().Equal(tt.expectedVSS[i].PersistenceTimeout, cplb.VirtualServers[i].PersistenceTimeout, "PersistenceTimeout mismatch")
s.Require().Equal(tt.expectedVSS[i].DelayLoop, k.VirtualServers[i].DelayLoop, "DelayLoop mismatch")
s.Require().Equal(tt.expectedVSS[i].LBAlgo, k.VirtualServers[i].LBAlgo, "LBalgo mismatch")
s.Require().Equal(tt.expectedVSS[i].LBKind, k.VirtualServers[i].LBKind, "LBKind mismatch")
s.Require().Equal(tt.expectedVSS[i].PersistenceTimeout, k.VirtualServers[i].PersistenceTimeout, "PersistenceTimeout mismatch")
}
}
})
Expand Down
42 changes: 31 additions & 11 deletions pkg/apis/k0s/v1beta1/zz_generated.deepcopy.go

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

2 changes: 1 addition & 1 deletion pkg/component/controller/cplb_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import (
// Keepalived is the controller for the keepalived process in the control plane load balancing
type Keepalived struct {
K0sVars *config.CfgVars
Config *k0sAPI.ControlPlaneLoadBalancingSpec
Config *k0sAPI.KeepalivedSpec
DetailedLogging bool
uid int
supervisor *supervisor.Supervisor
Expand Down
Loading

0 comments on commit b9c8803

Please sign in to comment.