* Copyright (c) 2025 Bocloud Technologies Co., Ltd.
* installer is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain n copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
******************************************************************/
package command
import (
"fmt"
"strings"
"time"
"github.com/pkg/errors"
agentv1beta1 "gopkg.openfuyao.cn/cluster-api-provider-bke/api/bkeagent/v1beta1"
bkenode "gopkg.openfuyao.cn/cluster-api-provider-bke/common/cluster/node"
"gopkg.openfuyao.cn/cluster-api-provider-bke/utils"
)
type HA struct {
BaseCommand
MasterNodes bkenode.Nodes
IngressNodes bkenode.Nodes
IngressVIP string
ControlPlaneEndpointPort int32
ControlPlaneEndpointVIP string
ThirdImageRepo string
FuyaoImageRepo string
ManifestsDir string
VirtualRouterId string
WaitVIP bool
isMasterHa bool
}
func (l *HA) Validate() error {
if l.ThirdImageRepo == "" {
return errors.New("ThirdImageRepo is empty")
}
if l.FuyaoImageRepo == "" {
return errors.New("FuyaoImageRepo is empty")
}
if l.ManifestsDir == "" {
return errors.New("manifestsDir is empty")
}
if l.MasterNodes.Length() != 0 && l.IngressNodes.Length() != 0 {
return errors.New("loadbalance command except configure one type of Ha, but both types (master ha,ingress ha) are configured")
}
if l.MasterNodes.Length() != 0 {
l.isMasterHa = true
if l.ControlPlaneEndpointPort == 0 {
return errors.New("controlPlaneEndpointPort is empty")
}
if l.ControlPlaneEndpointVIP == "" {
return errors.New("controlPlaneEndpointVIP is empty")
}
}
if l.IngressNodes.Length() != 0 {
l.isMasterHa = false
if l.IngressVIP == "" {
return errors.New("ingressVIP is empty")
}
}
if l.MasterNodes.Length() == 0 && l.IngressNodes.Length() == 0 {
return errors.New("loadbalance command except at least one node but got 0")
}
return l.BaseCommand.validate()
}
func (l *HA) New() error {
if err := l.Validate(); err != nil {
return err
}
manifestsDirParam := fmt.Sprintf("manifestsDir=%s", l.ManifestsDir)
thirdImageRepoParam := fmt.Sprintf("thirdImageRepo=%s", l.ThirdImageRepo)
fuyaoImageRepoParam := fmt.Sprintf("fuyaoImageRepo=%s", l.FuyaoImageRepo)
commandName := fmt.Sprintf("%s-%d", HACommandName, time.Now().Unix())
commandSpec := GenerateDefaultCommandSpec()
if l.isMasterHa {
l.setupMasterHACommand(commandSpec, manifestsDirParam, thirdImageRepoParam, fuyaoImageRepoParam)
commandSpec.NodeSelector = getNodeSelector(l.MasterNodes)
} else {
l.setupIngressHACommand(commandSpec, manifestsDirParam, thirdImageRepoParam, fuyaoImageRepoParam)
commandSpec.NodeSelector = getNodeSelector(l.IngressNodes)
}
return l.newCommand(commandName, BKEClusterLabel, commandSpec)
}
func (l *HA) setupMasterHACommand(commandSpec *agentv1beta1.CommandSpec, manifestsDirParam, thirdImageRepoParam, fuyaoImageRepoParam string) {
haNodesParam := l.getHaNodesParam(l.MasterNodes)
controlPlaneEndpointPortParam := fmt.Sprintf("controlPlaneEndpointPort=%d", l.ControlPlaneEndpointPort)
controlPlaneEndpointVIPParam := fmt.Sprintf("controlPlaneEndpointVIP=%s", l.ControlPlaneEndpointVIP)
virtualRouterIdParam := fmt.Sprintf("virtualRouterId=%s", l.VirtualRouterId)
waitVIPParam := fmt.Sprintf("wait=%t", l.WaitVIP)
commandSpec.Commands = []agentv1beta1.ExecCommand{
{
ID: "master-ha",
Command: []string{
"HA",
haNodesParam,
controlPlaneEndpointPortParam,
controlPlaneEndpointVIPParam,
virtualRouterIdParam,
thirdImageRepoParam,
fuyaoImageRepoParam,
manifestsDirParam,
waitVIPParam,
},
Type: agentv1beta1.CommandBuiltIn,
},
}
}
func (l *HA) setupIngressHACommand(commandSpec *agentv1beta1.CommandSpec, manifestsDirParam, thirdImageRepoParam, fuyaoImageRepoParam string) {
haNodesParam := l.getHaNodesParam(l.IngressNodes)
ingressVIPParam := fmt.Sprintf("ingressVIP=%s", l.IngressVIP)
virtualRouterIdParam := fmt.Sprintf("virtualRouterId=%s", l.VirtualRouterId)
waitVIPParam := fmt.Sprintf("wait=%t", l.WaitVIP)
commandSpec.Commands = []agentv1beta1.ExecCommand{
{
ID: "ingress-ha",
Command: []string{
"HA",
haNodesParam,
ingressVIPParam,
thirdImageRepoParam,
fuyaoImageRepoParam,
virtualRouterIdParam,
manifestsDirParam,
waitVIPParam,
},
Type: agentv1beta1.CommandBuiltIn,
},
}
}
func (l *HA) getHaNodesParam(nodes bkenode.Nodes) string {
var haNodesList []string
for _, node := range nodes {
haNodesList = append(haNodesList, fmt.Sprintf("%s:%s", node.Hostname, node.IP))
}
return fmt.Sprintf("haNodes=%s", strings.Join(haNodesList, ","))
}
func (l *HA) Wait() (error, []string, []string) {
err, complete, nodes := l.waitCommandComplete()
if !complete && len(nodes.FailedNodes) == 0 {
for _, node := range l.MasterNodes {
if utils.ContainsString(nodes.SuccessNodes, node.Hostname) {
continue
}
nodes.FailedNodes = append(nodes.FailedNodes, node.Hostname)
}
}
return err, nodes.SuccessNodes, nodes.FailedNodes
}