!12 [Feature][Fix][Doc] repo devcontainer.json parser, WeChat QR Expiration Mask, Zombie Process Mitigation
* [Fix] Zombie/Defunct Processes caused by SSH Daemon * [Fix] WeChat QR Expiration Mask with a refresh button * [Doc] Updated API response field `publicKeySsh` of GET `/api/devstar_ssh/key_pair/new_temp` * [Feature] Get or default DevContainer image from `.devcontainer/devcontainer.json` parser
This commit is contained in:
@@ -1,20 +1,24 @@
|
||||
package devstar_devcontainer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
devstar_devcontainer_models "code.gitea.io/gitea/models/devstar_devcontainer"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
git_module "code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
DevcontainersVO "code.gitea.io/gitea/routers/api/devcontainer/vo"
|
||||
devcontainer_service_dto "code.gitea.io/gitea/services/devstar_devcontainer/dto"
|
||||
devcontainer_service_errors "code.gitea.io/gitea/services/devstar_devcontainer/errors"
|
||||
devcontainer_k8s_agent_service "code.gitea.io/gitea/services/devstar_devcontainer/k8s_agent"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
@@ -106,6 +110,7 @@ func CreateRepoDevcontainer(ctx context.Context, opts *DevcontainersVO.CreateRep
|
||||
CreatedUnix: unixTimestamp,
|
||||
UpdatedUnix: unixTimestamp,
|
||||
},
|
||||
Image: GetDefaultDevcontainerImageFromRepoDevcontainerJSON(ctx, opts.Repository),
|
||||
GitRepositoryURL: strings.TrimSuffix(setting.AppURL, "/") + opts.Repository.Link(),
|
||||
}
|
||||
|
||||
@@ -262,7 +267,7 @@ func getSanitizedDevcontainerName(username, repoName string) string {
|
||||
// purgeDevcontainersResource 辅助函数,用于goroutine后台执行,回收DevContainer资源
|
||||
func purgeDevcontainersResource(ctx *context.Context, devcontainersList *[]devstar_devcontainer_models.DevstarDevcontainer) error {
|
||||
// 1. 检查 DevContainer 功能是否启用,若禁用,则直接结束,不会真正执行删除操作
|
||||
if setting.Devstar.Devcontainer.Enabled == false {
|
||||
if !setting.Devstar.Devcontainer.Enabled {
|
||||
// 如果用户设置禁用 DevContainer,无法删除资源,会直接忽略,而数据库相关记录会继续清空、不会发生回滚
|
||||
log.Warn("Orphan DevContainers in namespace `%s` left undeleted: %v", setting.Devstar.Devcontainer.Namespace, devcontainersList)
|
||||
return nil
|
||||
@@ -283,7 +288,7 @@ func purgeDevcontainersResource(ctx *context.Context, devcontainersList *[]devst
|
||||
// claimDevcontainerResource 分发创建 DevContainer 任务到配置文件指定的执行器
|
||||
func claimDevcontainerResource(ctx *context.Context, newDevContainer *devcontainer_service_dto.CreateDevcontainerDTO) error {
|
||||
// 1. 检查 DevContainer 功能是否启用,若禁用,则直接结束
|
||||
if setting.Devstar.Devcontainer.Enabled == false {
|
||||
if !setting.Devstar.Devcontainer.Enabled {
|
||||
return devstar_devcontainer_models.ErrFailedToOperateDevstarDevcontainerDB{
|
||||
Action: "Check for DevContainer functionality switch",
|
||||
Message: "DevContainer is disabled globally, please check your configuration files",
|
||||
@@ -303,3 +308,52 @@ func claimDevcontainerResource(ctx *context.Context, newDevContainer *devcontain
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetDefaultDevcontainerImageFromRepoDevcontainerJSON 从 .devcontainer/devcontainer.json 中获取 devContainer image
|
||||
func GetDefaultDevcontainerImageFromRepoDevcontainerJSON(ctx context.Context, repo *repo_model.Repository) string {
|
||||
|
||||
// 1. 获取默认分支名
|
||||
branchName := repo.DefaultBranch
|
||||
if len(branchName) == 0 {
|
||||
branchName = setting.Devstar.Devcontainer.DefaultGitBranchName
|
||||
}
|
||||
|
||||
// 2. 打开默认分支
|
||||
gitRepoEntity, err := git_module.OpenRepository(ctx, repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error("Failed to open repository %s: %v", repo.RepoPath(), err)
|
||||
return setting.Devstar.Devcontainer.DefaultDevcontainerImageName
|
||||
}
|
||||
defer func(gitRepoEntity *git_module.Repository) {
|
||||
_ = gitRepoEntity.Close()
|
||||
}(gitRepoEntity)
|
||||
|
||||
// 3. 获取分支名称
|
||||
commit, err := gitRepoEntity.GetBranchCommit(branchName)
|
||||
if err != nil {
|
||||
return setting.Devstar.Devcontainer.DefaultDevcontainerImageName
|
||||
}
|
||||
|
||||
// 4. 读取 .devcontainer/devcontainer.json 文件
|
||||
const maxDevcontainerJSONSize = 10 * 1024 * 1024 // 设置最大允许的文件大小 1MB
|
||||
devcontainerJSONContent, err := commit.GetFileContent(".devcontainer/devcontainer.json", maxDevcontainerJSONSize)
|
||||
if err != nil {
|
||||
log.Error("Failed to get .devcontainer/devcontainer.json file: %v", err)
|
||||
return setting.Devstar.Devcontainer.DefaultDevcontainerImageName
|
||||
}
|
||||
|
||||
// 5. 解析 JSON
|
||||
var devcontainerJSON map[string]interface{}
|
||||
err = json.Unmarshal([]byte(devcontainerJSONContent), &devcontainerJSON)
|
||||
if err != nil {
|
||||
log.Error("Failed to unmarshal .devcontainer/devcontainer.json: %v", err)
|
||||
return setting.Devstar.Devcontainer.DefaultDevcontainerImageName
|
||||
}
|
||||
|
||||
// 6. 解析并返回
|
||||
devcontainerImage, ok := devcontainerJSON["image"].(string)
|
||||
if !ok || len(devcontainerImage) == 0 {
|
||||
return setting.Devstar.Devcontainer.DefaultDevcontainerImageName
|
||||
}
|
||||
return devcontainerImage
|
||||
}
|
||||
|
||||
@@ -8,4 +8,5 @@ type CreateDevcontainerDTO struct {
|
||||
devstar_devcontainer_models.DevstarDevcontainer
|
||||
SSHPublicKeyList []string
|
||||
GitRepositoryURL string
|
||||
Image string
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package k8s_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
devcontainer_dto "code.gitea.io/gitea/modules/devstar_devcontainer/k8s_agent/dto"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
devcontainer_service_dto "code.gitea.io/gitea/services/devstar_devcontainer/dto"
|
||||
"context"
|
||||
apimachinery_meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
devcontainer_k8s_agent_module "code.gitea.io/gitea/modules/devstar_devcontainer/k8s_agent"
|
||||
@@ -31,12 +32,29 @@ func AssignDevcontainerCreation2K8sOperator(ctx *context.Context, newDevContaine
|
||||
CreateOptions: apimachinery_meta_v1.CreateOptions{},
|
||||
Name: newDevContainer.Name,
|
||||
Namespace: setting.Devstar.Devcontainer.Namespace,
|
||||
// TODO: 后期根据 .devcontainer/devcontainer.json 或默认设置选项,指定 devContainer image 和 初始化命令
|
||||
Image: "devstar.cn/public/base-ssh-devcontainer:ubuntu-20.04-20241014",
|
||||
Image: newDevContainer.Image,
|
||||
/**
|
||||
* 配置 Kubernetes 主容器启动命令注意事项:
|
||||
* 1. 确保 Image 中已安装 OpenSSH Server
|
||||
* 2. 容器启动后必须拉起 OpenSSH 后台服务
|
||||
* 3. 请勿使用 sleep infinity 或者 tail -f /dev/null 等无限等待命令,否则将造成大量僵尸(<defunct>)进程:
|
||||
* $ ps aux | grep "<defunct>" # 列举僵尸进程
|
||||
* USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
|
||||
* pollina+ 2336 0.0 0.0 0 0 ? Z 17:22 0:00 [sshd] <defunct>
|
||||
* pollina+ 10986 0.0 0.0 0 0 ? Z 16:12 0:00 [sshd] <defunct>
|
||||
* pollina+ 24722 0.0 0.0 0 0 ? Z 18:36 0:00 [sshd] <defunct>
|
||||
* pollina+ 26773 0.0 0.0 0 0 ? Z 18:37 0:00 [sshd] <defunct>
|
||||
* $ ubuntu@node2:~$ ps o ppid 2336 10986 24722 26773 # 查询僵尸进程父进程PID
|
||||
* PPID
|
||||
* 21826
|
||||
* $ ps aux | grep # 列举僵尸进程父进程详情
|
||||
* USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
|
||||
* root 21826 0.0 0.0 2520 408 ? Ss 18:36 0:00 sleep infinity
|
||||
*/
|
||||
CommandList: []string{
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
"service ssh restart && sleep infinity",
|
||||
"service ssh restart",
|
||||
},
|
||||
ContainerPort: 22,
|
||||
ServicePort: 22,
|
||||
|
||||
Reference in New Issue
Block a user