* 修复k8s/devcontainer/suite_test * 改进了DevStar Controller Manager镜像相关代码 * 修改了Dockerfile.rootless以解决go版本问题 * 移动Dockerfile.cotroller-manager位置 * Merge remote-tracking branch 'origin/dev' into devcontainer-on-k8s * Merge remote-tracking branch 'origin/add_k8s' into AppOnK8s * Merge remote-tracking branch 'origin/add-dockerfile-method-and-start-s… * 添加了k8s下的停止、重启devcontainer和webterminal * Merge branch 'add-dockerfile-method-and-start-stop-container' of https… * 更新了容器镜像方式的构建、安装和使用方法,但是devcontainer功能还有问题 * fix run postCreateCommand bug * sh文件方式管理启动脚本 * Merge branch 'add-dockerfile-method-and-start-stop-container' of https… * add restart command and fix bug * chore: 补充添加k8s controller的go.mod和go.sum文件 * Merge branch 'add-dockerfile-method-and-start-stop-container' of https… * 为devstar添加k8s上的controller-manager * add dockerfile method to create container and save container .restart …
321 lines
10 KiB
Go
321 lines
10 KiB
Go
package k8s_agent
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"testing"
|
||
"time"
|
||
|
||
"code.gitea.io/gitea/modules/setting"
|
||
"github.com/stretchr/testify/assert"
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||
)
|
||
|
||
func TestGetKubernetesClient(t *testing.T) {
|
||
t.Log("====== 开始测试 GetKubernetesClient 功能 ======")
|
||
|
||
// 设置测试环境
|
||
setting.Devcontainer.Enabled = true
|
||
t.Log("Devcontainer.Enabled 已设置为:", setting.Devcontainer.Enabled)
|
||
|
||
// 创建上下文
|
||
ctx := context.Background()
|
||
ctxPtr := &ctx
|
||
|
||
t.Log("尝试连接Kubernetes集群...")
|
||
startTime := time.Now()
|
||
|
||
// 尝试获取Kubernetes客户端
|
||
dynamicClient, err := GetKubernetesClient(ctxPtr)
|
||
|
||
elapsed := time.Since(startTime).Seconds()
|
||
t.Logf("连接尝试耗时: %.2f 秒", elapsed)
|
||
|
||
// 检查结果
|
||
if err != nil {
|
||
t.Logf("⚠️ 获取Kubernetes动态客户端失败: %v", err)
|
||
t.Logf("这可能是因为测试环境没有适当的Kubernetes配置或无法访问集群")
|
||
assert.False(t, setting.Devcontainer.Enabled,
|
||
"当Kubernetes连接失败时,Devcontainer.Enabled应被设置为false")
|
||
t.Log("====== 测试完成 - 连接失败 ======")
|
||
return
|
||
}
|
||
|
||
t.Log("✅ 成功获取Kubernetes动态客户端")
|
||
assert.NotNil(t, dynamicClient, "Kubernetes动态客户端不应为nil")
|
||
|
||
// 使用动态客户端获取节点信息
|
||
t.Log("使用动态客户端获取集群节点信息...")
|
||
|
||
// 定义Node资源的GVR (Group, Version, Resource)
|
||
nodesGVR := schema.GroupVersionResource{
|
||
Group: "",
|
||
Version: "v1",
|
||
Resource: "nodes",
|
||
}
|
||
|
||
// 使用动态客户端获取节点列表
|
||
nodesList, err := dynamicClient.Resource(nodesGVR).List(*ctxPtr, metav1.ListOptions{})
|
||
if err != nil {
|
||
t.Logf("⚠️ 获取节点信息失败: %v", err)
|
||
} else {
|
||
// 处理和打印节点信息
|
||
t.Logf("📊 集群共有 %d 个节点:", len(nodesList.Items))
|
||
for i, nodeUnstructured := range nodesList.Items {
|
||
nodeName, _, _ := unstructured.NestedString(nodeUnstructured.Object, "metadata", "name")
|
||
|
||
// 尝试获取节点状态
|
||
var nodeStatus string = "Unknown"
|
||
conditions, exists, _ := unstructured.NestedSlice(nodeUnstructured.Object, "status", "conditions")
|
||
if exists {
|
||
for _, conditionObj := range conditions {
|
||
condition, ok := conditionObj.(map[string]interface{})
|
||
if !ok {
|
||
continue
|
||
}
|
||
|
||
conditionType, typeExists, _ := unstructured.NestedString(condition, "type")
|
||
if typeExists && conditionType == "Ready" {
|
||
status, statusExists, _ := unstructured.NestedString(condition, "status")
|
||
if statusExists {
|
||
if status == "True" {
|
||
nodeStatus = "Ready"
|
||
} else {
|
||
nodeStatus = "NotReady"
|
||
}
|
||
}
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
// 尝试获取节点版本
|
||
var kubeletVersion string = "Unknown"
|
||
version, exists, _ := unstructured.NestedString(nodeUnstructured.Object, "status", "nodeInfo", "kubeletVersion")
|
||
if exists {
|
||
kubeletVersion = version
|
||
}
|
||
|
||
t.Logf(" 节点 %d: 名称=%s, 状态=%s, 版本=%s",
|
||
i+1,
|
||
nodeName,
|
||
nodeStatus,
|
||
kubeletVersion)
|
||
}
|
||
}
|
||
|
||
// 使用动态客户端获取命名空间信息
|
||
namespacesGVR := schema.GroupVersionResource{
|
||
Group: "",
|
||
Version: "v1",
|
||
Resource: "namespaces",
|
||
}
|
||
|
||
namespacesList, err := dynamicClient.Resource(namespacesGVR).List(*ctxPtr, metav1.ListOptions{})
|
||
if err != nil {
|
||
t.Logf("⚠️ 获取命名空间信息失败: %v", err)
|
||
} else {
|
||
t.Logf("📊 集群共有 %d 个命名空间:", len(namespacesList.Items))
|
||
for i, nsUnstructured := range namespacesList.Items {
|
||
nsName, _, _ := unstructured.NestedString(nsUnstructured.Object, "metadata", "name")
|
||
|
||
// 尝试获取命名空间状态
|
||
nsStatus, exists, _ := unstructured.NestedString(nsUnstructured.Object, "status", "phase")
|
||
if !exists {
|
||
nsStatus = "Unknown"
|
||
}
|
||
|
||
t.Logf(" 命名空间 %d: %s (状态: %s)",
|
||
i+1,
|
||
nsName,
|
||
nsStatus)
|
||
}
|
||
}
|
||
|
||
// 测试DevcontainerApp资源列表
|
||
t.Log("尝试列出指定命名空间的DevcontainerApp资源...")
|
||
namespaceToTest := "default" // 使用默认命名空间测试
|
||
opts := &ListDevcontainersOptions{
|
||
Namespace: namespaceToTest,
|
||
}
|
||
devcontainers, listErr := ListDevcontainers(ctxPtr, dynamicClient, opts)
|
||
|
||
if listErr != nil {
|
||
t.Logf("⚠️ 列出DevcontainerApp资源失败: %v", listErr)
|
||
t.Log("这可能是因为缺少权限或CRD未定义")
|
||
} else {
|
||
t.Logf("✅ 成功获取DevcontainerApp列表,命名空间'%s'中有 %d 个DevcontainerApp资源",
|
||
namespaceToTest,
|
||
len(devcontainers.Items))
|
||
|
||
// 打印每个DevcontainerApp的基本信息
|
||
for i, devcontainer := range devcontainers.Items {
|
||
t.Logf(" DevcontainerApp %d:", i+1)
|
||
t.Logf(" 名称: %s", devcontainer.Name)
|
||
t.Logf(" 创建时间: %v", devcontainer.CreationTimestamp)
|
||
t.Logf(" Ready状态: %v", devcontainer.Status.Ready)
|
||
t.Logf(" NodePort: %d", devcontainer.Status.NodePortAssigned)
|
||
t.Logf(" 镜像: %s", devcontainer.Spec.StatefulSet.Image)
|
||
}
|
||
}
|
||
|
||
// 列出所有CRD
|
||
t.Log("列出集群中的所有CRD...")
|
||
crdsGVR := schema.GroupVersionResource{
|
||
Group: "apiextensions.k8s.io",
|
||
Version: "v1",
|
||
Resource: "customresourcedefinitions",
|
||
}
|
||
|
||
crdsList, err := dynamicClient.Resource(crdsGVR).List(*ctxPtr, metav1.ListOptions{})
|
||
if err != nil {
|
||
t.Logf("⚠️ 获取CRD列表失败: %v", err)
|
||
} else {
|
||
t.Logf("📊 集群中共有 %d 个CRD定义", len(crdsList.Items))
|
||
|
||
// 查找DevcontainerApp CRD
|
||
found := false
|
||
for _, crdObj := range crdsList.Items {
|
||
crdName, exists, _ := unstructured.NestedString(crdObj.Object, "metadata", "name")
|
||
if exists && crdName == "devcontainerapps.devcontainer.devstar.cn" {
|
||
found = true
|
||
t.Log("✅ 找到DevcontainerApp CRD定义")
|
||
|
||
// 尝试获取CRD版本和组
|
||
group, _, _ := unstructured.NestedString(crdObj.Object, "spec", "group")
|
||
version, _, _ := unstructured.NestedString(crdObj.Object, "spec", "versions", "0", "name")
|
||
scope, _, _ := unstructured.NestedString(crdObj.Object, "spec", "scope")
|
||
|
||
t.Logf(" 组: %s", group)
|
||
t.Logf(" 版本: %s", version)
|
||
t.Logf(" 作用域: %s", scope)
|
||
break
|
||
}
|
||
}
|
||
|
||
if !found {
|
||
t.Log("⚠️ 未找到DevcontainerApp CRD定义,这可能导致后续测试失败")
|
||
}
|
||
}
|
||
|
||
t.Log("====== 测试完成 - 连接成功 ======")
|
||
}
|
||
|
||
// 模拟创建和删除Devcontainer的集成测试
|
||
func TestCreateAndDeleteDevcontainer(t *testing.T) {
|
||
t.Log("====== 开始测试 CreateAndDeleteDevcontainer 功能 ======")
|
||
|
||
if testing.Short() {
|
||
t.Skip("在短模式下跳过集成测试")
|
||
return
|
||
}
|
||
|
||
// 创建上下文
|
||
ctx := context.Background()
|
||
ctxPtr := &ctx
|
||
|
||
// 获取客户端
|
||
t.Log("尝试获取Kubernetes客户端...")
|
||
client, err := GetKubernetesClient(ctxPtr)
|
||
if err != nil {
|
||
t.Logf("⚠️ 获取Kubernetes客户端失败: %v", err)
|
||
t.Skip("跳过后续测试,因为无法创建Kubernetes客户端")
|
||
return
|
||
}
|
||
t.Log("✅ 成功获取Kubernetes客户端")
|
||
|
||
// 创建唯一的测试名称
|
||
currentTime := time.Now().UnixNano() / int64(time.Millisecond)
|
||
testName := fmt.Sprintf("studio-test-%d", currentTime)
|
||
t.Logf("创建测试Devcontainer: %s", testName)
|
||
|
||
// 创建测试Devcontainer的配置
|
||
createOpts := &CreateDevcontainerOptions{
|
||
Name: testName,
|
||
Namespace: "default",
|
||
Image: "devstar.cn/public/base-ssh-devcontainer:ubuntu-20.04-20241014",
|
||
GitRepositoryURL: "https://gitee.com/panshuxiao/test-devcontainer.git",
|
||
CommandList: []string{
|
||
"/bin/bash",
|
||
"-c",
|
||
"service ssh start && while true; do sleep 60; done",
|
||
},
|
||
ContainerPort: 22,
|
||
ServicePort: 22,
|
||
SSHPublicKeyList: []string{
|
||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOmlOiVc18CjXKmVxDwSEqQ8fA2ikZ3p8NqdGV1Gw2cQ panshuxiao@mail.ustc.edu.cn",
|
||
},
|
||
}
|
||
|
||
t.Log("开始创建Devcontainer...")
|
||
startTime := time.Now()
|
||
|
||
// 创建Devcontainer
|
||
devcontainer, err := CreateDevcontainer(ctxPtr, client, createOpts)
|
||
|
||
elapsed := time.Since(startTime).Seconds()
|
||
|
||
if err != nil {
|
||
t.Logf("⚠️ 创建测试Devcontainer失败 (耗时: %.2f秒): %v", elapsed, err)
|
||
t.Skip("由于创建失败跳过测试的其余部分")
|
||
return
|
||
}
|
||
|
||
t.Logf("✅ 成功创建Devcontainer (耗时: %.2f秒)", elapsed)
|
||
t.Logf(" 名称: %s", devcontainer.Name)
|
||
t.Logf(" 命名空间: %s", devcontainer.Namespace)
|
||
t.Logf(" 镜像: %s", devcontainer.Spec.StatefulSet.Image)
|
||
|
||
// 确保测试结束时删除资源
|
||
defer func() {
|
||
t.Logf("清理 - 删除测试Devcontainer: %s", testName)
|
||
deleteOpts := &DeleteDevcontainerOptions{
|
||
Name: createOpts.Name,
|
||
Namespace: createOpts.Namespace,
|
||
}
|
||
deleteStartTime := time.Now()
|
||
err := DeleteDevcontainer(ctxPtr, client, deleteOpts)
|
||
deleteElapsed := time.Since(deleteStartTime).Seconds()
|
||
|
||
if err != nil {
|
||
t.Logf("⚠️ 清理测试Devcontainer失败 (耗时: %.2f秒): %v", deleteElapsed, err)
|
||
} else {
|
||
t.Logf("✅ 成功删除测试Devcontainer (耗时: %.2f秒)", deleteElapsed)
|
||
}
|
||
}()
|
||
|
||
// 验证创建的Devcontainer
|
||
assert.Equal(t, createOpts.Name, devcontainer.Name, "创建的Devcontainer名称应与请求的名称匹配")
|
||
|
||
// 等待一小段时间,让K8s有时间处理资源创建
|
||
t.Log("等待15秒钟,让Kubernetes有时间处理资源...")
|
||
time.Sleep(15 * time.Second)
|
||
|
||
// 测试获取刚创建的Devcontainer
|
||
t.Logf("尝试获取刚创建的Devcontainer: %s", testName)
|
||
getOpts := &GetDevcontainerOptions{
|
||
Name: createOpts.Name,
|
||
Namespace: createOpts.Namespace,
|
||
Wait: true,
|
||
}
|
||
|
||
getStartTime := time.Now()
|
||
retrieved, err := GetDevcontainer(ctxPtr, client, getOpts)
|
||
getElapsed := time.Since(getStartTime).Seconds()
|
||
|
||
if err != nil {
|
||
t.Logf("⚠️ 获取Devcontainer失败 (耗时: %.2f秒): %v", getElapsed, err)
|
||
t.Log("注意: 如果Devcontainer尚未Ready,这可能是正常的")
|
||
} else {
|
||
t.Logf("✅ 成功获取Devcontainer (耗时: %.2f秒)", getElapsed)
|
||
t.Logf(" 名称: %s", retrieved.Name)
|
||
t.Logf(" Ready状态: %v", retrieved.Status.Ready)
|
||
t.Logf(" NodePort: %d", retrieved.Status.NodePortAssigned)
|
||
assert.Equal(t, createOpts.Name, retrieved.Name, "获取的Devcontainer名称应与创建的名称匹配")
|
||
}
|
||
|
||
t.Log("====== 测试完成 - CreateAndDeleteDevcontainer ======")
|
||
}
|