* Copyright (c) 2024 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 cmd
import (
"errors"
"fmt"
"net"
"os"
"regexp"
"strconv"
"strings"
"github.com/spf13/cobra"
configinit "gopkg.openfuyao.cn/cluster-api-provider-bke/common/cluster/initialize"
"gopkg.openfuyao.cn/bkeadm/pkg/cluster"
"gopkg.openfuyao.cn/bkeadm/pkg/initialize"
"gopkg.openfuyao.cn/bkeadm/utils"
)
var initOption initialize.Options
var confirm bool
func isValidImageName(name string) bool {
if name == "" {
return false
}
invalidPrefixes := []string{".", "-", "_"}
invalidSuffixes := []string{".", "-", "_"}
for _, prefix := range invalidPrefixes {
if strings.HasPrefix(name, prefix) {
return false
}
}
for _, suffix := range invalidSuffixes {
if strings.HasSuffix(name, suffix) {
return false
}
}
return true
}
func isValidImageFormat(image string) bool {
if image == "" {
return false
}
imagePattern := `^([a-zA-Z0-9][a-zA-Z0-9-_.]*/)?([a-zA-Z0-9][a-zA-Z0-9-_.]*/)*[a-zA-Z0-9][a-zA-Z0-9-_.]*(:[a-zA-Z0-9][a-zA-Z0-9-_.-]*)?$`
matched, err := regexp.MatchString(imagePattern, image)
if err != nil {
return false
}
if strings.Contains(image, "..") || strings.Contains(image, "//") {
return false
}
parts := strings.Split(image, "/")
if len(parts) > 0 {
lastPart := parts[len(parts)-1]
if strings.Contains(lastPart, ":") {
lastPart = strings.Split(lastPart, ":")[0]
}
if !isValidImageName(lastPart) {
return false
}
}
return matched
}
var initCmd = &cobra.Command{
Use: "init",
Short: "Initialize the boot node",
Long: `Initialize the boot node, including node check, warehouse start, cluster installation, and so on`,
Example: `
# Initialize the boot node
bke init
# Initialize with console installation enabled
bke init --installConsole=true
# Initialize with console installation disabled
bke init --installConsole=false
# Initialize the boot node and deploy the management cluster
bke init --file bkecluster.yaml
# Download files from the specified image and install the cluster
bke init --otherRepo cr.openfuyao.cn/openfuyao/bke-online-installed:latest
# Use external mirror repositories and source repositories
bke init --otherRepo cr.openfuyao.cn/openfuyao --otherSource http://192.168.1.120:40080
bke init --otherRepo cr.openfuyao.cn/openfuyao --otherSource http://192.168.100.48:8080(端口号)
`,
PreRunE: func(cmd *cobra.Command, args []string) error {
if initOption.HostIP == "" {
ip, _ := utils.GetOutBoundIP()
initOption.HostIP = ip
}
if initOption.HostIP == "" {
ip, _ := utils.GetIntranetIp()
initOption.HostIP = ip
}
if initOption.Domain == "" {
initOption.Domain = configinit.DefaultImageRepo
}
if initOption.ImageRepoPort == "" {
initOption.ImageRepoPort = configinit.DefaultImageRepoPort
}
if initOption.File != "" {
bkeCluster, err := cluster.NewBKEClusterFromFile(initOption.File)
if err != nil {
return err
}
imageRepo := bkeCluster.Spec.ClusterConfig.Cluster.ImageRepo
imgPort := ":443"
if len(imageRepo.Port) > 0 {
imgPort = ":" + imageRepo.Port
}
if len(imageRepo.Ip) > 0 {
if imageRepo.Ip != initOption.HostIP {
if imageRepo.Domain == "" || imageRepo.Domain == configinit.DefaultImageRepo {
initOption.OtherRepo = imageRepo.Ip + imgPort + "/" + imageRepo.Prefix
} else {
initOption.OtherRepo = imageRepo.Domain + imgPort + "/" + imageRepo.Prefix
}
}
} else {
if len(imageRepo.Domain) > 0 {
_, err := utils.LoopIP(imageRepo.Domain)
if err != nil {
return err
}
initOption.OtherRepo = imageRepo.Domain + imgPort + "/" + imageRepo.Prefix
}
}
httpRepo := bkeCluster.Spec.ClusterConfig.Cluster.HTTPRepo
httpPort := ":80"
if len(httpRepo.Port) > 0 {
httpPort = ":" + httpRepo.Port
}
if len(httpRepo.Ip) > 0 {
if httpRepo.Ip != initOption.HostIP {
initOption.OtherSource = "http://" + httpRepo.Ip + httpPort
}
} else {
if len(httpRepo.Domain) > 0 {
if net.ParseIP(httpRepo.Domain) == nil {
initOption.OtherSource = "http://" + httpRepo.Domain + httpPort
} else {
_, err := utils.LoopIP(httpRepo.Domain)
if err != nil {
return err
}
initOption.OtherSource = "http://" + httpRepo.Domain + httpPort
}
}
}
if len(httpRepo.Port) > 0 {
initOption.YumRepoPort = httpRepo.Port
}
chartRepo := bkeCluster.Spec.ClusterConfig.Cluster.ChartRepo
chartPort := ":443"
if len(chartRepo.Port) > 0 {
chartPort = ":" + chartRepo.Port
}
if len(chartRepo.Ip) > 0 {
if chartRepo.Domain == configinit.DefaultChartRepo {
initOption.OtherChart = configinit.DefaultChartRepo + "/" + chartRepo.Prefix
} else {
initOption.OtherChart = chartRepo.Ip + chartPort + "/" + chartRepo.Prefix
}
} else {
if len(chartRepo.Domain) > 0 {
_, err := utils.LoopIP(chartRepo.Domain)
if err != nil {
return err
}
initOption.OtherChart = chartRepo.Domain + chartPort + "/" + chartRepo.Prefix
}
}
if len(bkeCluster.Spec.ClusterConfig.Cluster.NTPServer) > 0 {
initOption.NtpServer = bkeCluster.Spec.ClusterConfig.Cluster.NTPServer
}
}
if len(initOption.ClusterAPI) == 0 {
initOption.ClusterAPI = "latest"
}
if len(initOption.OFVersion) == 0 {
initOption.OFVersion = "latest"
}
if len(initOption.VersionUrl) == 0 {
initOption.VersionUrl = utils.DefaultPatchDownURL
}
if len(initOption.NtpServer) == 0 {
initOption.NtpServer = configinit.DefaultNTPServer
}
if len(initOption.Runtime) == 0 {
initOption.Runtime = "docker"
}
if len(initOption.RuntimeStorage) == 0 {
if initOption.Runtime == "docker" {
initOption.RuntimeStorage = configinit.DefaultCRIDockerDataRootDir
} else {
initOption.RuntimeStorage = configinit.DefaultCRIContainerdDataRootDir
}
}
fmt.Println(fmt.Sprintf("--hostIP: %s", initOption.HostIP))
fmt.Println(fmt.Sprintf("--domain: %s", initOption.Domain))
fmt.Println(fmt.Sprintf("--kubernetesPort: %s", initOption.KubernetesPort))
fmt.Println(fmt.Sprintf("--imageRepoPort: %s", initOption.ImageRepoPort))
fmt.Println(fmt.Sprintf("--yumRepoPort: %s", initOption.YumRepoPort))
fmt.Println(fmt.Sprintf("--chartRepoPort: %s", initOption.ChartRepoPort))
fmt.Println(fmt.Sprintf("--ntpServer: %s", initOption.NtpServer))
fmt.Println(fmt.Sprintf("--runtime: %s", initOption.Runtime))
fmt.Println(fmt.Sprintf("--runtimeStorage: %s", initOption.RuntimeStorage))
fmt.Println(fmt.Sprintf("--clusterAPI: %s", initOption.ClusterAPI))
fmt.Println(fmt.Sprintf("--oFVersion: %s", initOption.OFVersion))
fmt.Println(fmt.Sprintf("--versionUrl: %s", initOption.VersionUrl))
fmt.Println(fmt.Sprintf("--enableNTP: %v", initOption.EnableNTP))
fmt.Println(fmt.Sprintf("--agentHealthPort: %v", initOption.AgentHealthPort))
port, err := strconv.Atoi(initOption.AgentHealthPort)
if err != nil || port < utils.MinPort || port > utils.MaxPort {
return errors.New("The agent health port must be legal. ")
}
if initOption.ImageFilePath != "" {
fmt.Println(fmt.Sprintf("--imageFilePath: %v", initOption.ImageFilePath))
}
if initOption.File != "" {
fmt.Println(fmt.Sprintf("--file: %s", initOption.File))
}
if initOption.OnlineImage != "" {
fmt.Println(fmt.Sprintf("--onlineImage: %s", initOption.OnlineImage))
}
if initOption.OtherRepo != "" {
fmt.Println(fmt.Sprintf("--otherRepo: %s", initOption.OtherRepo))
}
if initOption.OtherSource != "" {
fmt.Println(fmt.Sprintf("--otherSource: %s", initOption.OtherSource))
}
if initOption.HostIP == "" {
return errors.New("The host IP address must be set. ")
}
if initOption.KubernetesPort == "" {
return errors.New("The kubernetes port must be set. ")
}
if initOption.Domain == "" {
return errors.New("The host domain name is a must. ")
}
if initOption.ImageRepoPort == "" {
return errors.New("The image server port must be set. ")
}
if initOption.ChartRepoPort == "" {
return errors.New("The yum chart port must be set. ")
}
if initOption.YumRepoPort == "" {
return errors.New("The yum server port must be set. ")
}
if !utils.IsNum(initOption.ImageRepoPort) {
return errors.New("The image server port is not a number. ")
}
if !utils.IsNum(initOption.YumRepoPort) {
return errors.New("The yum server port is not a number. ")
}
if !utils.IsNum(initOption.ChartRepoPort) {
return errors.New("The chart server port is not a number. ")
}
if len(initOption.OnlineImage) > 0 {
if !isValidImageFormat(initOption.OnlineImage) {
return errors.New("the parameter `onlineImage` must be a valid Docker image reference format (e.g., registry/repository/image:tag)")
}
}
if len(initOption.OtherSource) > 0 {
if !strings.HasPrefix(initOption.OtherSource, "http://") {
return errors.New("The parameter `otherSource` must start with http:// ")
}
}
if initOption.ImageFilePath != "" {
if initOption.InstallConsole {
return errors.New("local image file and install console is mutually exclusive. ")
}
if initOption.OtherRepo == "" {
return errors.New("local image file and offline deploy is mutually exclusive. ")
}
filename := strings.ToLower(initOption.ImageFilePath)
if !strings.HasSuffix(filename, ".tar.gz") && !strings.HasSuffix(filename, ".tgz") {
return errors.New("local image file is not tar.gz or tgz format")
}
_, err := os.Stat(initOption.ImageFilePath)
if err != nil {
return errors.New("local image file not exist")
}
}
if !utils.PromptForConfirmation(confirm) {
return fmt.Errorf("operation cancelled by user")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
initOption.Args = args
initOption.Options = options
initOption.Initialize()
},
}
func registerInitCommand() {
rootCmd.AddCommand(initCmd)
initCmd.Flags().StringVarP(&initOption.File, "file", "f", "", "bkecluster.yaml")
initCmd.Flags().StringVar(&initOption.Domain,
"domain", configinit.DefaultImageRepo, "The domain name of the host is customized")
initCmd.Flags().StringVar(&initOption.ImageRepoPort,
"imageRepoPort", configinit.DefaultImageRepoPort, "image repository port")
initCmd.Flags().StringVar(&initOption.HostIP, "hostIP", "", "local kubernetes api server")
initCmd.Flags().StringVar(&initOption.KubernetesPort,
"kubernetesPort", utils.DefaultKubernetesPort, "local kubernetes port")
initCmd.Flags().StringVar(&initOption.YumRepoPort,
"yumRepoPort", configinit.DefaultYumRepoPort, "yum repository port")
initCmd.Flags().StringVar(&initOption.ChartRepoPort,
"chartRepoPort", utils.DefaultChartRegistryPort, "chart repository port")
initCmd.Flags().StringVar(&initOption.NtpServer,
"ntpServer", configinit.DefaultNTPServer, "value is `local`, an ntp service is started on the host")
initCmd.Flags().StringVar(&initOption.Runtime, "runtime", "containerd", "docker/containerd")
initCmd.Flags().StringVarP(&initOption.RuntimeStorage,
"runtimeStorage", "s", "", "default `/var/lib/docker or /var/lib/containerd`")
initCmd.Flags().StringVar(&initOption.OnlineImage, "onlineImage", "", "Online image to provide binary files and os rpm")
initCmd.Flags().StringVar(&initOption.OtherRepo, "otherRepo", "", "Provides the private source address for image mirror installation")
initCmd.Flags().StringVar(&initOption.OtherSource, "otherSource", "", "Provides the private source address for system package installation")
initCmd.Flags().StringVar(&initOption.OtherChart, "otherChart", "", "Provides the private chart address for helm chart installation")
initCmd.Flags().StringVar(&initOption.ClusterAPI, "clusterAPI", "latest", "cluster-api-bke version")
initCmd.Flags().BoolVar(&confirm, "confirm", false, "Skip the confirmation")
initCmd.Flags().StringVarP(&initOption.OFVersion,
"oFVersion", "v", "latest", "openfuyao version")
initCmd.Flags().StringVar(&initOption.VersionUrl,
"versionUrl", utils.DefaultPatchDownURL, "online openfuyao version config download url")
initCmd.Flags().BoolVar(&initOption.InstallConsole,
"installConsole", true, "Whether to install bkeconsole (default: true)")
initCmd.Flags().BoolVar(&initOption.EnableNTP,
"enableNTP", true, "enable or disable NTP service, default is true")
initCmd.Flags().BoolVar(&initOption.ImageRepoTLSVerify,
"imageRepoTLSVerify", true, "Enable TLS verification for image repository")
initCmd.Flags().StringVar(&initOption.ImageRepoCAFile,
"imageRepoCAFile", "", "CA certificate file for image repository")
initCmd.Flags().StringVar(&initOption.ImageRepoUsername,
"imageRepoUsername", "", "Username for image repository authentication")
initCmd.Flags().StringVar(&initOption.ImageRepoPassword,
"imageRepoPassword", "", "Password for image repository authentication")
initCmd.Flags().StringVar(&initOption.ImageFilePath, "imageFilePath", "", "image file path")
initCmd.Flags().StringVar(&initOption.AgentHealthPort, "agentHealthPort", utils.DefaultAgentHealthPort, "agent listen health port")
}