package installation
import (
"fmt"
"strings"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"gitcode.com/openFuyao/e2e-auto-test/e2e/framework/executor"
config "gitcode.com/openFuyao/e2e-auto-test/e2e/installation/bke-config"
)
特性:引导节点安装优化
用例:
1)安装参数校验
2)使用oci格式制备引导节点安装包,并进行引导节点初始化
*/
var _ = Describe("引导节点安装优化", Label("bootstrap-deploy-opt", "pre-init", "online"), func() {
var (
sshExecutor *executor.SSHExecutor
guideConfig *config.GuideNodeConfig
)
BeforeEach(func() {
guideConfig = config.LoadGuideNodeFromEnv()
Expect(guideConfig.Host).NotTo(BeEmpty(), "GUIDE_NODE_HOST 环境变量必须设置")
Expect(guideConfig.Password).NotTo(BeEmpty(), "GUIDE_NODE_PASSWORD 环境变量必须设置")
var err error
sshExecutor, err = executor.NewSSHExecutor(
guideConfig.Host,
guideConfig.Port,
guideConfig.Username,
guideConfig.Password,
)
Expect(err).NotTo(HaveOccurred(), "应该成功连接到引导节点")
})
用例名称:1)安装参数校验
用例步骤:1)bke init携带参数--installConsole(为true)和--imageFilePath,预期结果1
2)bke init携带参数--otherRepo(为空)和--imageFilePath,预期结果1
3)bke init携带参数--imageFilePath,模拟路径不对或者非tar.gz/tgz文件,预期结果1
预期结果:1)参数校验失败,无法继续执行命令
*/
Describe("安装参数校验", Label("init-param-check"), func() {
It("安装参数校验,使用错误的参数就无法下发引导节点的初始化命令", SpecTimeout(InstallationItTimeout), func(ctx SpecContext) {
By("执行 bke init 命令")
initCmd := "bke init --installConsole=true --imageFilePath=/tmp/a.tar.gz"
_, err := sshExecutor.Exec(initCmd)
GinkgoWriter.Printf("%s 使用引导节点安装优化,无法安装带openFuyao管理面的引导节点\n", initCmd)
Expect(err).To(HaveOccurred(), "使用引导节点安装优化,无法安装带openFuyao管理面的引导节点")
initCmd = "bke init --imageFilePath=/tmp/a.tar.gz"
_, err = sshExecutor.Exec(initCmd)
GinkgoWriter.Printf("%s 使用引导节点安装优化,离线场景下不能使用\n", initCmd)
Expect(err).To(HaveOccurred(), "使用引导节点安装优化,离线场景下不能使用")
initCmd = "bke init --otherRepo=test.image.cn --imageFilePath=/tmp/a.ta"
_, err = sshExecutor.Exec(initCmd)
GinkgoWriter.Printf("%s imageFilePath指定的文件格式有误\n", initCmd)
Expect(err).To(HaveOccurred(), "imageFilePath指定的文件格式有误")
initCmd = "bke init --otherRepo=test.image.cn --imageFilePath=/tmp/a.tar.gz"
_, err = sshExecutor.Exec(initCmd)
GinkgoWriter.Printf("%s imageFilePath指定的文件不存在\n", initCmd)
Expect(err).To(HaveOccurred(), "imageFilePath指定的文件不存在")
})
})
})
var _ = Describe("使用oci格式制备引导节点安装包,并进行引导节点初始化", Label("bootstrap-deploy-opt", "bootstrap-init", "pre-init", "online"), func() {
var (
sshExecutor *executor.SSHExecutor
guideNode *config.GuideNodeConfig
)
BeforeEach(func() {
guideNode = config.LoadGuideNodeFromEnv()
var err error
sshExecutor, err = executor.NewSSHExecutor(guideNode.Host, guideNode.Port, guideNode.Username, guideNode.Password)
Expect(err).NotTo(HaveOccurred(), "连接引导节点失败")
By("重置引导节点环境 (bke reset)")
resetCmd := "echo Y | bke reset --all --mount"
result, err := sshExecutor.Exec(resetCmd)
if err != nil {
GinkgoWriter.Printf("Reset 命令执行返回错误 (可能环境已清理): %v\nStderr: %s\n", err, result.Stderr)
} else {
GinkgoWriter.Printf("Reset 命令执行成功: %s\n", result.Stdout)
}
time.Sleep(10 * time.Second)
By("在引导节点生成 sample.yaml(默认 amd64)")
yamlContent := `
repos:
- architecture:
- amd64
needDownload: true
subImages:
- sourceRepo: hub.oepkgs.net/openfuyao
targetRepo: kubernetes
imageTrack: ""
images:
- name: registry
tag:
- 2.8.1
- name: nginx
tag:
- 1.23.0-alpine
- name: helm/chartmuseum
tag:
- v0.16.2
- name: rancher/k3s
tag:
- v1.25.16-k3s4
- name: rancher/mirrored-pause
tag:
- 3.6
- architecture:
- amd64
needDownload: true
subImages:
- sourceRepo: hub.oepkgs.net/openfuyao/cluster-api
targetRepo: kubernetes/cluster-api
images:
- name: cluster-api-controller
usedPodInfo:
- podPrefix: capi-controller-manager
namespace: cluster-system
tag:
- v1.4.3
- name: kubeadm-bootstrap-controller
usedPodInfo:
- podPrefix: capi-controller-manager
namespace: cluster-system
tag:
- v1.4.3
- name: kubeadm-control-plane-controller
usedPodInfo:
- podPrefix: capi-controller-manager
namespace: cluster-system
tag:
- v1.4.3
- architecture:
- amd64
needDownload: true
subImages:
- sourceRepo: cr.openfuyao.cn/openfuyao
targetRepo: kubernetes
images:
- name: cluster-api-provider-bke
tag:
- latest
- name: bke-manifests
tag:
- latest
patches:
- address: https://openfuyao.obs.cn-north-4.myhuaweicloud.com/openFuyao/version-config/
files:
- fileName: VersionConfig-latest.yaml
files:
- address: https://openfuyao.obs.cn-north-4.myhuaweicloud.com/registry/
files:
- fileName: registry.image-amd64
- address: https://openfuyao.obs.cn-north-4.myhuaweicloud.com/containerd/containerd/releases/download/v2.1.1-origin/
files:
- fileName: containerd-v2.1.1-linux-amd64.tar.gz
- address: https://openfuyao.obs.cn-north-4.myhuaweicloud.com/containernetworking/plugins/releases/download/v1.4.1/
files:
- fileName: cni-plugins-linux-amd64-v1.4.1.tgz
`
writeYamlCmd := fmt.Sprintf("cat > /tmp/sample.yaml << 'EOF_SAMPLE'\n%s\nEOF_SAMPLE", yamlContent)
_, err = sshExecutor.Exec(writeYamlCmd)
Expect(err).NotTo(HaveOccurred(), "写入 /tmp/sample.yaml 应该成功")
By("根据引导节点架构调整 YAML(arm64 时替换 amd64 为 arm64)")
archAdjustCmd := `
ARCH=$(uname -m)
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
sed -i 's/amd64/arm64/g' /tmp/sample.yaml
fi
`
_, err = sshExecutor.Exec(archAdjustCmd)
Expect(err).NotTo(HaveOccurred(), "根据架构调整 sample.yaml 应该成功")
By("使用 OCI 策略制备安装包")
buildCmd := "cd /tmp && bke build patch -f sample.yaml -t image.tar.gz --strategy=oci"
buildRes, err := sshExecutor.Exec(buildCmd)
if err != nil {
GinkgoWriter.Printf("制备安装包失败: %v\nstdout:\n%s\nstderr:\n%s\n", err, buildRes.Stdout, buildRes.Stderr)
}
Expect(err).NotTo(HaveOccurred(), "bke build patch 应成功")
Expect(buildRes.ExitCode).To(Equal(0), "bke build patch 退出码应为 0")
})
AfterEach(func() {
By("测试后清理:重置引导节点环境")
resetCmd := "echo Y | bke reset --all --mount"
result, err := sshExecutor.Exec(resetCmd)
if err != nil {
GinkgoWriter.Printf("AfterEach Reset 失败: %v\nStderr: %s\n", err, result.Stderr)
} else {
GinkgoWriter.Printf("AfterEach Reset 成功: %s\n", result.Stdout)
}
By("测试后清理:删除制备的安装包与 YAML")
cleanupPkgCmd := "rm -f /tmp/image.tar.gz /tmp/sample.yaml"
_, _ = sshExecutor.Exec(cleanupPkgCmd)
time.Sleep(5 * time.Second)
})
runBootstrapInit := func(args string) (*executor.ExecResult, error) {
cmd := fmt.Sprintf("echo Y | bke init %s", args)
GinkgoWriter.Printf("执行引导节点初始化命令: %s\n", cmd)
res, err := sshExecutor.Exec(cmd)
if res != nil {
lines := strings.Split(res.Stdout, "\n")
start := 0
if len(lines) > 20 {
start = len(lines) - 20
}
GinkgoWriter.Printf("====== Last 20 lines of Stdout ======\n%s\n", strings.Join(lines[start:], "\n"))
if res.Stderr != "" {
GinkgoWriter.Printf("====== Stderr ======\n%s\n", res.Stderr)
}
GinkgoWriter.Printf("=====================================\n")
}
return res, err
}
checkPodsReady := func() {
By("检查引导集群 Pod 状态")
Eventually(func() bool {
cmd := "kubectl get pod -A --no-headers"
res, err := sshExecutor.Exec(cmd)
if err != nil || res.ExitCode != 0 {
return false
}
lines := strings.Split(strings.TrimSpace(res.Stdout), "\n")
if len(lines) == 0 {
return false
}
allReady := true
for _, line := range lines {
if strings.TrimSpace(line) == "" {
continue
}
fields := strings.Fields(line)
if len(fields) < 4 {
continue
}
status := fields[3]
if status != "Running" && status != "Completed" {
GinkgoWriter.Printf("Pod 未就绪: %s (Status: %s)\n", fields[1], status)
allReady = false
}
}
return allReady
}, 20*time.Minute, 10*time.Second).Should(BeTrue(), "引导集群所有 Pod 应该处于 Running 或 Completed 状态")
}
It("OCI 安装包制备并初始化应成功", SpecTimeout(InstallationItTimeout), func(ctx SpecContext) {
By("使用制备的安装包进行引导节点初始化")
initArgs := "--installConsole=false --otherRepo cr.openfuyao.cn/openfuyao --imageFilePath=/tmp/image.tar.gz"
res, err := runBootstrapInit(initArgs)
Expect(err).NotTo(HaveOccurred())
Expect(res.ExitCode).To(Equal(0), "安装命令应该成功退出")
Expect(res.Stdout).To(ContainSubstring("BKE initialization is complete"), "日志应包含初始化完成信息")
checkPodsReady()
By("校验所用镜像来自本地仓库(127.0.0.1)")
checkImageCmd := `nerdctl images --format "{{.Repository}}:{{.Tag}}" 2>/dev/null | grep -E '(^|/)127\.0\.0\.1([/:]|$)' || true`
imgRes, _ := sshExecutor.Exec(checkImageCmd)
GinkgoWriter.Printf("本地镜像检查输出:\n%s\n", imgRes.Stdout)
Expect(strings.TrimSpace(imgRes.Stdout)).NotTo(BeEmpty(), "应当存在来自 127.0.0.1 的镜像条目")
})
})