* fixed /api/devcontainer/user * 修改了models/devcontainer目录下的文件命名 * 整理了devcontainer的部分代码,只是修改了文件目录结构和命名,没有对代码进行本质上的改动
198 lines
6.8 KiB
Go
198 lines
6.8 KiB
Go
package devcontainer
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"regexp"
|
||
|
||
"code.gitea.io/gitea/models/db"
|
||
"code.gitea.io/gitea/models/repo"
|
||
"code.gitea.io/gitea/modules/setting"
|
||
|
||
gitea_web_context "code.gitea.io/gitea/services/context"
|
||
devcontainer_service_errors "code.gitea.io/gitea/services/devcontainer/errors"
|
||
)
|
||
|
||
// CreateDevcontainerAPIService API专用创建 DevContainer Service
|
||
func CreateDevcontainerAPIService(ctx *gitea_web_context.Context, opts *CreateDevcontainerOptions) error {
|
||
// 0. 检查用户传入参数
|
||
if ctx == nil || opts == nil || opts.Actor == nil || opts.RepoId <= 0 {
|
||
return devcontainer_service_errors.ErrIllegalParams{
|
||
FieldNameList: []string{"ctx", "opts", "opts.Actor", "opts.RepoId"},
|
||
}
|
||
}
|
||
|
||
// sanitize user-input SSH Public Key List
|
||
regexpInvalidSSHPublicKey := regexp.MustCompile(`[\r]`)
|
||
for _, publicKey := range opts.SSHPublicKeyList {
|
||
if len(publicKey) <= 4 || publicKey[0:4] != "ssh-" || regexpInvalidSSHPublicKey.MatchString(publicKey) {
|
||
// 遇到可能无效的 SSH Public Key,或者导致 k8s Operator YAML 解码失败的字符: `\r`,报错返回
|
||
// ERROR Reconciler error:
|
||
// {
|
||
// "controller": "devcontainerapp",
|
||
// "controllerGroup": "devcontainer.devstar.cn",
|
||
// "controllerKind": "DevcontainerApp",
|
||
// "DevcontainerApp": {
|
||
// "name": "leviyanx-16-4834a4c88c4511ef9c1a4e1bce2a7080",
|
||
// "namespace": "devstar-studio-ns"
|
||
// },
|
||
// "namespace": "devstar-studio-ns",
|
||
// "name": "leviyanx-16-4834a4c88c4511ef9c1a4e1bce2a7080",
|
||
// "reconcileID": "6af51347-7aae-4542-a5cf-2f9b57a202e6",
|
||
// "error": "panic: error converting YAML to JSON: yaml: line 46: could not find expected ':' [recovered]"
|
||
// }
|
||
return devcontainer_service_errors.ErrIllegalParams{
|
||
FieldNameList: []string{"SSHPublicKeyList"},
|
||
}
|
||
}
|
||
}
|
||
|
||
// 1. 开启事务
|
||
errTxn := db.WithTx(ctx, func(ctx context.Context) error {
|
||
|
||
// 1.1 调用 model层,查询数据库,将 repoId 变换为 Repository 对象
|
||
repositoryInDB, err := repo.GetRepositoryByID(ctx, opts.RepoId)
|
||
if err != nil || repositoryInDB == nil {
|
||
return devcontainer_service_errors.ErrIllegalParams{
|
||
FieldNameList: []string{"opts.RepoId"},
|
||
}
|
||
}
|
||
|
||
// 1.2 检查该用户在该仓库 是否已经创建过 DevContainer
|
||
optsRepoDevcontainer := &RepoDevcontainerOptions{
|
||
Actor: opts.Actor,
|
||
Repository: repositoryInDB,
|
||
}
|
||
devcontainerDetails, err := GetRepoDevcontainerDetails(ctx, optsRepoDevcontainer)
|
||
if err != nil || devcontainerDetails.DevContainerId > 0 {
|
||
return devcontainer_service_errors.ErrDevcontainerAlreadyCreated{
|
||
Actor: opts.Actor,
|
||
Repository: repositoryInDB,
|
||
}
|
||
}
|
||
|
||
// 1.3 调用 DevContainer Service 创建 DevContainer
|
||
optsCreateDevcontainer := &CreateRepoDevcontainerOptions{
|
||
Actor: opts.Actor,
|
||
Repository: repositoryInDB,
|
||
SSHPublicKeyList: opts.SSHPublicKeyList,
|
||
}
|
||
return CreateRepoDevcontainer(ctx, optsCreateDevcontainer)
|
||
})
|
||
|
||
return errTxn
|
||
}
|
||
|
||
// OpenDevcontainerAPIService API 专用获取 DevContainer Service
|
||
func OpenDevcontainerAPIService(ctx *gitea_web_context.Context, opts *AbstractOpenDevcontainerOptions) (*RepoDevContainer, error) {
|
||
// 0. 检查用户传入参数
|
||
if ctx == nil || opts == nil || opts.Actor == nil || opts.RepoId <= 0 {
|
||
return nil, devcontainer_service_errors.ErrIllegalParams{
|
||
FieldNameList: []string{"ctx", "opts", "opts.Actor", "opts.RepoId"},
|
||
}
|
||
}
|
||
|
||
var devcontainerDetails RepoDevContainer
|
||
|
||
// 1. 开启数据库事务,查询 某用户在某仓库的 DevContainer
|
||
errTxn := db.WithTx(ctx, func(ctx context.Context) error {
|
||
// 1.1 调用 model层,查询数据库,将 repoId 变换为 Repository 对象
|
||
repositoryInDB, err := repo.GetRepositoryByID(ctx, opts.RepoId)
|
||
if err != nil || repositoryInDB == nil {
|
||
return devcontainer_service_errors.ErrIllegalParams{
|
||
FieldNameList: []string{"opts.RepoId"},
|
||
}
|
||
}
|
||
|
||
// 1.2 检查该用户在该仓库 是否已经创建过 DevContainer
|
||
optsRepoDevcontainer := &RepoDevcontainerOptions{
|
||
Actor: opts.Actor,
|
||
Repository: repositoryInDB,
|
||
}
|
||
devcontainerDetails, err = GetRepoDevcontainerDetails(ctx, optsRepoDevcontainer)
|
||
if err != nil || devcontainerDetails.DevContainerId <= 0 {
|
||
return devcontainer_service_errors.ErrDevcontainerNotFound{
|
||
Actor: opts.Actor,
|
||
Repository: repositoryInDB,
|
||
}
|
||
}
|
||
|
||
// 1.3 查询 DevContainer 成功,结束数据库事务
|
||
return nil
|
||
})
|
||
if errTxn != nil {
|
||
return nil, errTxn
|
||
}
|
||
|
||
// 2. 调用抽象层获取 DevContainer 最新状态(需要根据用户传入的 wait 参数决定是否要阻塞等待 DevContainer 就绪)
|
||
optsOpenDevcontainer := &OpenDevcontainerAppDispatcherOptions{
|
||
Name: devcontainerDetails.DevContainerName,
|
||
Port: devcontainerDetails.DevContainerPort,
|
||
Wait: opts.Wait,
|
||
UserPublicKey: opts.UserPublicKey,
|
||
}
|
||
|
||
openDevcontainerAbstractAgentVO, err := OpenDevcontainerService(ctx, optsOpenDevcontainer)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 3. 更新VO,合并成为真正的 SSH连接信息
|
||
devcontainerDetails.DevContainerPort = openDevcontainerAbstractAgentVO.NodePortAssigned
|
||
return &devcontainerDetails, nil
|
||
}
|
||
|
||
func UpdateDevcontainerAPIService(ctx *gitea_web_context.Context, opts *UpdateDevcontainerOptions) error {
|
||
switch setting.Devcontainer.Agent {
|
||
case setting.KUBERNETES:
|
||
//k8s处理
|
||
return fmt.Errorf("暂时不支持的Agent")
|
||
|
||
case setting.DOCKER:
|
||
return SaveDevcontainer(ctx, opts)
|
||
default:
|
||
return fmt.Errorf("不支持的Agent")
|
||
//默认处理
|
||
}
|
||
}
|
||
|
||
// DeleteDevcontainerAPIService API 专用删除 DevContainer Service
|
||
func DeleteDevcontainerAPIService(ctx *gitea_web_context.Context, opts *AbstractDeleteDevcontainerOptions) error {
|
||
// 0. 检查用户传入参数
|
||
if ctx == nil || opts == nil || opts.Actor == nil || opts.RepoId <= 0 {
|
||
return devcontainer_service_errors.ErrIllegalParams{
|
||
FieldNameList: []string{"ctx", "opts", "opts.Actor", "opts.RepoId"},
|
||
}
|
||
}
|
||
|
||
// 1. 开启数据库事务,查询 某用户在某仓库的 DevContainer
|
||
errTxn := db.WithTx(ctx, func(ctx context.Context) error {
|
||
// 1.1 调用 model层,查询数据库,将 repoId 变换为 Repository 对象
|
||
repositoryInDB, err := repo.GetRepositoryByID(ctx, opts.RepoId)
|
||
if err != nil || repositoryInDB == nil {
|
||
return devcontainer_service_errors.ErrIllegalParams{
|
||
FieldNameList: []string{"opts.RepoId"},
|
||
}
|
||
}
|
||
|
||
// 1.2 直接尝试删除 DevContainer,若报错,则说明不存在 DevContainer
|
||
optsRepoDevcontainer := &RepoDevcontainerOptions{
|
||
Actor: opts.Actor,
|
||
Repository: repositoryInDB,
|
||
}
|
||
err = DeleteRepoDevcontainer(ctx, optsRepoDevcontainer)
|
||
if err != nil {
|
||
return devcontainer_service_errors.ErrDevcontainerNotFound{
|
||
Actor: opts.Actor,
|
||
Repository: repositoryInDB,
|
||
}
|
||
}
|
||
|
||
// 1.3 查询 DevContainer 成功,结束数据库事务
|
||
return nil
|
||
})
|
||
|
||
// 2. 返回删除结果
|
||
return errTxn
|
||
}
|