798 lines
21 KiB
Go
798 lines
21 KiB
Go
package devcontainer
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"math"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
devcontainer_models "code.gitea.io/gitea/models/devcontainer"
|
|
"code.gitea.io/gitea/models/repo"
|
|
"code.gitea.io/gitea/models/user"
|
|
docker_module "code.gitea.io/gitea/modules/docker"
|
|
"code.gitea.io/gitea/modules/git"
|
|
"code.gitea.io/gitea/modules/log"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
gitea_context "code.gitea.io/gitea/services/context"
|
|
files_service "code.gitea.io/gitea/services/repository/files"
|
|
"github.com/docker/docker/api/types"
|
|
"xorm.io/builder"
|
|
)
|
|
|
|
func HasDevContainer(ctx context.Context, userID, repoID int64) (bool, error) {
|
|
var hasDevContainer bool
|
|
dbEngine := db.GetEngine(ctx)
|
|
hasDevContainer, err := dbEngine.
|
|
Table("devcontainer").
|
|
Select("*").
|
|
Where("user_id = ? AND repo_id = ?", userID, repoID).
|
|
Exist()
|
|
if err != nil {
|
|
return hasDevContainer, err
|
|
}
|
|
return hasDevContainer, nil
|
|
}
|
|
func HasDevContainerConfiguration(ctx context.Context, repo *gitea_context.Repository) (bool, error) {
|
|
_, err := FileExists(".devcontainer/devcontainer.json", repo)
|
|
if err != nil {
|
|
if git.IsErrNotExist(err) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
configurationString, err := GetDevcontainerConfigurationString(ctx, repo.Repository)
|
|
if err != nil {
|
|
return true, err
|
|
}
|
|
configurationModel, err := UnmarshalDevcontainerConfigContent(configurationString)
|
|
if err != nil {
|
|
return true, err
|
|
}
|
|
// 执行验证
|
|
if errs := configurationModel.Validate(); len(errs) > 0 {
|
|
log.Info("配置验证失败:")
|
|
for _, err := range errs {
|
|
fmt.Printf(" - %s\n", err.Error())
|
|
}
|
|
return true, fmt.Errorf("配置格式错误")
|
|
} else {
|
|
return true, nil
|
|
}
|
|
}
|
|
func HasDevContainerDockerFile(ctx context.Context, repo *gitea_context.Repository) (bool, error) {
|
|
_, err := FileExists(".devcontainer/devcontainer.json", repo)
|
|
if err != nil {
|
|
if git.IsErrNotExist(err) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
configurationString, err := GetDevcontainerConfigurationString(ctx, repo.Repository)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
configurationModel, err := UnmarshalDevcontainerConfigContent(configurationString)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
// 执行验证
|
|
if errs := configurationModel.Validate(); len(errs) > 0 {
|
|
log.Info("配置验证失败:")
|
|
for _, err := range errs {
|
|
fmt.Printf(" - %s\n", err.Error())
|
|
}
|
|
return false, fmt.Errorf("配置格式错误")
|
|
} else {
|
|
log.Info("%v", configurationModel)
|
|
if configurationModel.Build == nil || configurationModel.Build.Dockerfile == "" {
|
|
return false, nil
|
|
}
|
|
_, err := FileExists(".devcontainer/"+configurationModel.Build.Dockerfile, repo)
|
|
if err != nil {
|
|
if git.IsErrNotExist(err) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
}
|
|
func CreateDevcontainerConfiguration(repo *repo.Repository, doer *user.User) error {
|
|
jsonString := `{
|
|
"name":"template",
|
|
"image":"mcr.microsoft.com/devcontainers/base:dev-ubuntu-20.04",
|
|
"forwardPorts": ["8080"],
|
|
"containerEnv": {
|
|
"NODE_ENV": "development"
|
|
},
|
|
"initializeCommand": "echo \"init\";",
|
|
"postCreateCommand": [
|
|
"echo \"created\"",
|
|
"echo \"test\""
|
|
],
|
|
"runArgs": [
|
|
"-p 8888"
|
|
]
|
|
}`
|
|
_, err := files_service.ChangeRepoFiles(db.DefaultContext, repo, doer, &files_service.ChangeRepoFilesOptions{
|
|
Files: []*files_service.ChangeRepoFile{
|
|
{
|
|
Operation: "create",
|
|
TreePath: ".devcontainer/devcontainer.json",
|
|
ContentReader: bytes.NewReader([]byte(jsonString)),
|
|
},
|
|
},
|
|
OldBranch: "main",
|
|
NewBranch: "main",
|
|
Message: "add container configuration",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func GetWebTerminalURL(ctx context.Context, userID, repoID int64) (string, error) {
|
|
var devcontainerName string
|
|
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
dbEngine := db.GetEngine(ctx)
|
|
_, err = dbEngine.
|
|
Table("devcontainer").
|
|
Select("name").
|
|
Where("user_id = ? AND repo_id = ?", userID, repoID).
|
|
Get(&devcontainerName)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
/*
|
|
-1不存在
|
|
0 已创建数据库记录
|
|
1 正在拉取镜像
|
|
2 正在创建和启动容器
|
|
3 容器安装必要工具
|
|
4 容器正在运行
|
|
5 正在提交容器更新
|
|
6 正在重启
|
|
7 正在停止
|
|
8 容器已停止
|
|
9 正在删除
|
|
10已删除
|
|
*/
|
|
func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string, error) {
|
|
var id int
|
|
var containerName string
|
|
|
|
var status uint16
|
|
var realTimeStatus uint16
|
|
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
dbEngine := db.GetEngine(ctx)
|
|
_, err = dbEngine.
|
|
Table("devcontainer").
|
|
Select("devcontainer_status, id, name").
|
|
Where("user_id = ? AND repo_id = ?", userID, repoID).
|
|
Get(&status, &id, &containerName)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if id == 0 {
|
|
return fmt.Sprintf("%d", -1), nil
|
|
}
|
|
|
|
realTimeStatus = status
|
|
switch status {
|
|
//正在重启
|
|
case 6:
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
} else {
|
|
containerRealTimeStatus, err := GetDevContainerStatusFromDocker(ctx, containerName)
|
|
if err != nil {
|
|
return "", err
|
|
} else if containerRealTimeStatus == "running" {
|
|
realTimeStatus = 4
|
|
}
|
|
}
|
|
break
|
|
//正在关闭
|
|
case 7:
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
} else {
|
|
containerRealTimeStatus, err := GetDevContainerStatusFromDocker(ctx, containerName)
|
|
if err != nil {
|
|
return "", err
|
|
} else if containerRealTimeStatus == "exited" {
|
|
realTimeStatus = 8
|
|
}
|
|
}
|
|
break
|
|
case 9:
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
} else {
|
|
isContainerNotFound, err := IsContainerNotFound(ctx, containerName)
|
|
if err != nil {
|
|
return "", err
|
|
} else if isContainerNotFound {
|
|
realTimeStatus = 10
|
|
}
|
|
}
|
|
break
|
|
default:
|
|
log.Info("other status")
|
|
}
|
|
//状态更新
|
|
if realTimeStatus != status {
|
|
if realTimeStatus == 10 {
|
|
_, err = dbEngine.Table("devcontainer").
|
|
Where("user_id = ? AND repo_id = ? ", userID, repoID).
|
|
Delete()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_, err = dbEngine.Table("devcontainer_output").
|
|
Where("user_id = ? AND repo_id = ? ", userID, repoID).
|
|
Delete()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return "-1", nil
|
|
}
|
|
_, err = dbEngine.Table("devcontainer").
|
|
Where("user_id = ? AND repo_id = ? ", userID, repoID).
|
|
Update(&devcontainer_models.Devcontainer{DevcontainerStatus: realTimeStatus})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_, err = dbEngine.Table("devcontainer_output").
|
|
Where("user_id = ? AND repo_id = ? AND list_id = ?", userID, repoID, status).
|
|
Update(&devcontainer_models.DevcontainerOutput{Status: "finished"})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
return fmt.Sprintf("%d", realTimeStatus), nil
|
|
}
|
|
func CreateDevContainer(ctx context.Context, repo *repo.Repository, doer *user.User, publicKeyList []string, isWebTerminal bool) error {
|
|
containerName := getSanitizedDevcontainerName(doer.Name, repo.Name)
|
|
|
|
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
unixTimestamp := time.Now().Unix()
|
|
newDevcontainer := devcontainer_models.Devcontainer{
|
|
Name: containerName,
|
|
DevcontainerHost: cfg.Section("server").Key("DOMAIN").Value(),
|
|
DevcontainerUsername: "root",
|
|
DevcontainerWorkDir: "/workspace",
|
|
DevcontainerStatus: 0,
|
|
RepoId: repo.ID,
|
|
UserId: doer.ID,
|
|
CreatedUnix: unixTimestamp,
|
|
UpdatedUnix: unixTimestamp,
|
|
}
|
|
|
|
dbEngine := db.GetEngine(ctx)
|
|
|
|
_, err = dbEngine.
|
|
Table("devcontainer").
|
|
Insert(newDevcontainer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = dbEngine.
|
|
Table("devcontainer").
|
|
Select("*").
|
|
Where("user_id = ? AND repo_id = ?", doer.ID, repo.ID).
|
|
Get(&newDevcontainer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go func() {
|
|
otherCtx := context.Background()
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
} else {
|
|
imageName, err := CreateDevContainerByDockerCommand(otherCtx, &newDevcontainer, repo, publicKeyList)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if !isWebTerminal {
|
|
CreateDevContainerByDockerAPI(otherCtx, &newDevcontainer, imageName, repo, publicKeyList)
|
|
}
|
|
}
|
|
}()
|
|
return nil
|
|
}
|
|
func DeleteDevContainer(ctx context.Context, userID, repoID int64) error {
|
|
dbEngine := db.GetEngine(ctx)
|
|
var devContainerInfo devcontainer_models.Devcontainer
|
|
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = dbEngine.
|
|
Table("devcontainer").
|
|
Select("*").
|
|
Where("user_id = ? AND repo_id = ?", userID, repoID).
|
|
Get(&devContainerInfo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = dbEngine.Table("devcontainer").
|
|
Where("user_id = ? AND repo_id = ? ", userID, repoID).
|
|
Update(&devcontainer_models.Devcontainer{DevcontainerStatus: 9})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go func() {
|
|
otherCtx := context.Background()
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
} else {
|
|
|
|
err = DeleteDevContainerByDocker(otherCtx, &devContainerInfo)
|
|
if err != nil {
|
|
log.Info(err.Error())
|
|
}
|
|
}
|
|
}()
|
|
return nil
|
|
}
|
|
func RestartDevContainer(ctx context.Context, userID, repoID int64) error {
|
|
dbEngine := db.GetEngine(ctx)
|
|
var devContainerInfo devcontainer_models.Devcontainer
|
|
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = dbEngine.
|
|
Table("devcontainer").
|
|
Select("*").
|
|
Where("user_id = ? AND repo_id = ?", userID, repoID).
|
|
Get(&devContainerInfo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = dbEngine.Table("devcontainer").
|
|
Where("user_id = ? AND repo_id = ? ", userID, repoID).
|
|
Update(&devcontainer_models.Devcontainer{DevcontainerStatus: 6})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go func() {
|
|
otherCtx := context.Background()
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
} else {
|
|
err = RestartDevContainerByDocker(otherCtx, &devContainerInfo)
|
|
if err != nil {
|
|
log.Info(err.Error())
|
|
}
|
|
}
|
|
}()
|
|
return nil
|
|
}
|
|
func StopDevContainer(ctx context.Context, userID, repoID int64) error {
|
|
dbEngine := db.GetEngine(ctx)
|
|
var devContainerInfo devcontainer_models.Devcontainer
|
|
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = dbEngine.
|
|
Table("devcontainer").
|
|
Select("*").
|
|
Where("user_id = ? AND repo_id = ?", userID, repoID).
|
|
Get(&devContainerInfo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = dbEngine.Table("devcontainer").
|
|
Where("user_id = ? AND repo_id = ? ", userID, repoID).
|
|
Update(&devcontainer_models.Devcontainer{DevcontainerStatus: 7})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go func() {
|
|
otherCtx := context.Background()
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
} else {
|
|
err = StopDevContainerByDocker(otherCtx, &devContainerInfo)
|
|
if err != nil {
|
|
log.Info(err.Error())
|
|
}
|
|
}
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
func UpdateDevContainer(ctx context.Context, doer *user.User, repo *repo.Repository, updateInfo *UpdateInfo) error {
|
|
dbEngine := db.GetEngine(ctx)
|
|
var devContainerInfo devcontainer_models.Devcontainer
|
|
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = dbEngine.
|
|
Table("devcontainer").
|
|
Select("*").
|
|
Where("user_id = ? AND repo_id = ?", doer.ID, repo.ID).
|
|
Get(&devContainerInfo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = dbEngine.Table("devcontainer").
|
|
Where("user_id = ? AND repo_id = ? ", doer.ID, repo.ID).
|
|
Update(&devcontainer_models.Devcontainer{DevcontainerStatus: 5})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
otherCtx := context.Background()
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
} else {
|
|
updateErr := UpdateDevContainerByDocker(otherCtx, &devContainerInfo, updateInfo, repo, doer)
|
|
_, err = dbEngine.Table("devcontainer").
|
|
Where("user_id = ? AND repo_id = ? ", doer.ID, repo.ID).
|
|
Update(&devcontainer_models.Devcontainer{DevcontainerStatus: 4})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if updateErr != nil {
|
|
return updateErr
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
func GetTerminalCommand(ctx context.Context, userID string, repo *repo.Repository) (string, string, error) {
|
|
|
|
dbEngine := db.GetEngine(ctx)
|
|
var devContainerInfo devcontainer_models.Devcontainer
|
|
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
_, err = dbEngine.
|
|
Table("devcontainer").
|
|
Select("*").
|
|
Where("user_id = ? AND repo_id = ?", userID, repo.ID).
|
|
Get(&devContainerInfo)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
realTimeStatus := devContainerInfo.DevcontainerStatus
|
|
var cmd string
|
|
|
|
switch devContainerInfo.DevcontainerStatus {
|
|
case 0:
|
|
if devContainerInfo.Id > 0 {
|
|
realTimeStatus = 1
|
|
}
|
|
break
|
|
case 1:
|
|
//正在拉取镜像,当镜像拉取成功,则状态转移
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
} else {
|
|
configurationString, err := GetDevcontainerConfigurationString(ctx, repo)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
configurationModel, err := UnmarshalDevcontainerConfigContent(configurationString)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
var imageName string
|
|
if configurationModel.Build == nil || configurationModel.Build.Dockerfile == "" {
|
|
imageName = configurationModel.Image
|
|
} else {
|
|
imageName = userID + "-" + fmt.Sprintf("%d", repo.ID) + "-dockerfile"
|
|
}
|
|
isExist, err := ImageExists(ctx, imageName)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
if isExist {
|
|
realTimeStatus = 2
|
|
}
|
|
|
|
}
|
|
break
|
|
case 2:
|
|
//正在创建容器,创建容器成功,则状态转移
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
} else {
|
|
status, err := GetDevContainerStatusFromDocker(ctx, devContainerInfo.Name)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
if status == "running" {
|
|
//添加脚本文件
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
|
|
} else {
|
|
|
|
var scriptContent []byte
|
|
_, err = os.Stat("webTerminal.sh")
|
|
if os.IsNotExist(err) {
|
|
_, err = os.Stat("/app/gitea/webTerminal.sh")
|
|
if os.IsNotExist(err) {
|
|
return "", "", err
|
|
} else {
|
|
scriptContent, err = os.ReadFile("/app/gitea/webTerminal.sh")
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
}
|
|
} else {
|
|
scriptContent, err = os.ReadFile("webTerminal.sh")
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
}
|
|
// 创建 tar 归档文件
|
|
var buf bytes.Buffer
|
|
tw := tar.NewWriter(&buf)
|
|
defer tw.Close()
|
|
|
|
// 添加文件到 tar 归档
|
|
AddFileToTar(tw, "webTerminal.sh", string(scriptContent), 0777)
|
|
// 创建 Docker 客户端
|
|
cli, err := docker_module.CreateDockerClient(ctx)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
// 获取容器 ID
|
|
containerID, err := docker_module.GetContainerID(cli, devContainerInfo.Name)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
err = cli.CopyToContainer(ctx, containerID, "/home", bytes.NewReader(buf.Bytes()), types.CopyToContainerOptions{})
|
|
if err != nil {
|
|
log.Info("%v", err)
|
|
return "", "", err
|
|
}
|
|
}
|
|
realTimeStatus = 3
|
|
}
|
|
}
|
|
break
|
|
case 3:
|
|
//正在初始化容器,初始化容器成功,则状态转移
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
} else {
|
|
|
|
status, err := CheckDirExistsFromDocker(ctx, devContainerInfo.Name, devContainerInfo.DevcontainerWorkDir)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
if status {
|
|
realTimeStatus = 4
|
|
}
|
|
}
|
|
break
|
|
case 4:
|
|
//正在连接容器
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
} else {
|
|
_, err = dbEngine.Table("devcontainer_output").
|
|
Select("command").
|
|
Where("user_id = ? AND repo_id = ? AND list_id = ?", userID, repo.ID, realTimeStatus).
|
|
Get(&cmd)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
}
|
|
break
|
|
}
|
|
|
|
if realTimeStatus != devContainerInfo.DevcontainerStatus {
|
|
//下一条指令
|
|
_, err = dbEngine.Table("devcontainer_output").
|
|
Select("command").
|
|
Where("user_id = ? AND repo_id = ? AND list_id = ?", userID, repo.ID, realTimeStatus).
|
|
Get(&cmd)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
_, err = dbEngine.Table("devcontainer").
|
|
Where("user_id = ? AND repo_id = ? ", userID, repo.ID).
|
|
Update(&devcontainer_models.Devcontainer{DevcontainerStatus: realTimeStatus})
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
}
|
|
return cmd, fmt.Sprintf("%d", realTimeStatus), nil
|
|
}
|
|
func GetDevContainerOutput(ctx context.Context, doer *user.User, repo *repo.Repository) (OutputResponse, error) {
|
|
var devContainerOutput []devcontainer_models.DevcontainerOutput
|
|
dbEngine := db.GetEngine(ctx)
|
|
resp := OutputResponse{}
|
|
var status string
|
|
var containerName string
|
|
_, err := dbEngine.
|
|
Table("devcontainer").
|
|
Select("devcontainer_status, name").
|
|
Where("user_id = ? AND repo_id = ?", doer.ID, repo.ID).
|
|
Get(&status, &containerName)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
err = dbEngine.Table("devcontainer_output").
|
|
Where("user_id = ? AND repo_id = ?", doer.ID, repo.ID).
|
|
Find(&devContainerOutput)
|
|
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
|
|
if len(devContainerOutput) > 0 {
|
|
|
|
resp.CurrentJob.Title = repo.Name + " Devcontainer Info"
|
|
resp.CurrentJob.Detail = status
|
|
if status == "4" {
|
|
// 获取WebSSH服务端口
|
|
webTerminalURL, err := GetWebTerminalURL(ctx, doer.ID, repo.ID)
|
|
if err == nil {
|
|
return resp, err
|
|
}
|
|
// 解析URL
|
|
u, err := url.Parse(webTerminalURL)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
// 分离主机和端口
|
|
terminalHost, terminalPort, err := net.SplitHostPort(u.Host)
|
|
resp.CurrentJob.IP = terminalHost
|
|
resp.CurrentJob.Port = terminalPort
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
}
|
|
for _, item := range devContainerOutput {
|
|
logLines := []ViewStepLogLine{}
|
|
logLines = append(logLines, ViewStepLogLine{
|
|
Index: 1,
|
|
Message: item.Output,
|
|
})
|
|
resp.CurrentJob.Steps = append(resp.CurrentJob.Steps, &ViewJobStep{
|
|
Summary: item.Command,
|
|
Status: item.Status,
|
|
Logs: logLines,
|
|
})
|
|
|
|
}
|
|
}
|
|
return resp, nil
|
|
}
|
|
func GetMappedPort(ctx context.Context, containerName string, port string) (uint16, error) {
|
|
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
|
|
//k8s的逻辑
|
|
return 0, nil
|
|
} else {
|
|
port, err := docker_module.GetMappedPort(ctx, containerName, port)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return port, nil
|
|
}
|
|
}
|
|
func GetDevcontainersList(ctx context.Context, doer *user.User, pageNum, pageSize int) (DevcontainerList, error) {
|
|
|
|
// 0. 构造异常返回时的空数据
|
|
var resultDevContainerListVO = DevcontainerList{
|
|
Page: 0,
|
|
PageSize: 50,
|
|
PageTotalNum: 0,
|
|
ItemTotalNum: 0,
|
|
DevContainers: []devcontainer_models.Devcontainer{},
|
|
}
|
|
resultDevContainerListVO.UserID = doer.ID
|
|
resultDevContainerListVO.Username = doer.Name
|
|
|
|
paginationOption := db.ListOptions{
|
|
Page: pageNum,
|
|
PageSize: pageSize,
|
|
}
|
|
|
|
paginationOption.ListAll = false // 强制使用分页查询,禁止一次性列举所有 devContainers
|
|
if paginationOption.Page <= 0 { // 未指定页码/无效页码:查询第 1 页
|
|
paginationOption.Page = 1
|
|
}
|
|
if paginationOption.PageSize <= 0 || paginationOption.PageSize > 50 {
|
|
paginationOption.PageSize = 50 // /无效页面大小/超过每页最大限制:自动调整到系统最大开发容器页面大小
|
|
}
|
|
resultDevContainerListVO.Page = paginationOption.Page
|
|
resultDevContainerListVO.PageSize = paginationOption.PageSize
|
|
|
|
// 2. SQL 条件构建
|
|
|
|
sqlCondition := builder.Eq{"user_id": doer.ID}
|
|
// 执行数据库事务
|
|
err := db.WithTx(ctx, func(ctx context.Context) error {
|
|
// 查询总数
|
|
count, err := db.GetEngine(ctx).
|
|
Table("devcontainer").
|
|
Where(sqlCondition).
|
|
Count()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resultDevContainerListVO.ItemTotalNum = count
|
|
|
|
// 无记录直接返回
|
|
if count == 0 {
|
|
return nil
|
|
}
|
|
|
|
// 计算分页参数
|
|
pageSize := int64(resultDevContainerListVO.PageSize)
|
|
resultDevContainerListVO.PageTotalNum = int(math.Ceil(float64(count) / float64(pageSize)))
|
|
|
|
// 查询分页数据
|
|
sess := db.GetEngine(ctx).
|
|
Table("devcontainer").
|
|
Join("INNER", "repository", "devcontainer.repo_id = repository.id").
|
|
Where(sqlCondition).
|
|
OrderBy("devcontainer_id DESC").
|
|
Select(`devcontainer.id AS devcontainer_id,
|
|
devcontainer.name AS devcontainer_name,
|
|
devcontainer.devcontainer_host AS devcontainer_host,
|
|
devcontainer.devcontainer_username AS devcontainer_username,
|
|
devcontainer.devcontainer_work_dir AS devcontainer_work_dir,
|
|
devcontainer.repo_id AS repo_id,
|
|
repository.name AS repo_name,
|
|
repository.owner_name AS repo_owner_name,
|
|
repository.description AS repo_description,
|
|
CONCAT('/', repository.owner_name, '/', repository.name) AS repo_link`)
|
|
|
|
resultDevContainerListVO.DevContainers = make([]devcontainer_models.Devcontainer, 0, pageSize)
|
|
err = db.SetSessionPagination(sess, &paginationOption).
|
|
Find(&resultDevContainerListVO.DevContainers)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return resultDevContainerListVO, err
|
|
}
|
|
|
|
return resultDevContainerListVO, nil
|
|
}
|