Files
devstar/modules/devstar_devcontainer/k8s_agent/CreateDevcontainer.go
戴明辰 4b6f1b9cb5
All checks were successful
DevStar Studio CI Pipeline - dev branch / build-and-push-x86-64-docker-image (push) Successful in 21m49s
!6 k8s Agent for DevStar DevContainer
* DELETE /api/devcontainer?repoId=${repoId} 删除 DevContainer
* refactor
* GET /api/devcontainer?repoId=${repoId}&wait=true 阻塞式等待打开就绪的 DevContainer
* POST /api/devcontainer 创建 DevContainer
* refactored the code
* Updated context usage with cancel function
* 预留接口,适配单机版 DevStar DevContainer
* bugFix: context canceled while deleting k8s CRD DevcontainerApp
* 用户界面删除 k8s CRD DevContainer
* 用户界面创建 DevContainer 并更新 NodePort
* 完成用户界面创建 DevContainer
* transplant test code into DevStar Studio
* refactored API router to /routers/api
* 更改 DevContainer Doc
* 更改 DevContainer namespace
* 特殊仓库重定向
* [Doc] 更新 Kubernetes 部署 DevStar Studio 文档说明,特别是 namespace 管理
* [Doc] 更新 CI脚本说明
* Revert "optimized CI workflow"
* optimized CI workflow
* fix typo
* [feature test]: 测试 Pod 内使用 Kubernetes Operator 功能
* [Optimization] error msg for archived repo
* [Optimization]: display detailed err msg on creating devContainer for …
2024-09-30 06:48:01 +00:00

127 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package k8s_agent
import (
"context"
"encoding/json"
"fmt"
devcontainer_api_v1 "code.gitea.io/gitea/modules/devstar_devcontainer/k8s_agent/api/v1"
devcontainer_dto "code.gitea.io/gitea/modules/devstar_devcontainer/k8s_agent/dto"
devcontainer_k8s_agent_modules_errors "code.gitea.io/gitea/modules/devstar_devcontainer/k8s_agent/errors"
apimachinery_apis_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apimachinery_apis_v1_unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
apimachinery_watch "k8s.io/apimachinery/pkg/watch"
dynamic_client "k8s.io/client-go/dynamic"
)
// CreateDevcontainer 创建开发容器
func CreateDevcontainer(ctx *context.Context, client dynamic_client.Interface, opts *devcontainer_dto.CreateDevcontainerOptions) (*devcontainer_api_v1.DevcontainerApp, error) {
if ctx == nil || opts == nil {
return nil, devcontainer_k8s_agent_modules_errors.ErrIllegalDevcontainerParameters{
FieldList: []string{"ctx", "opts"},
Message: "cannot be nil",
}
}
devcontainerApp := &devcontainer_api_v1.DevcontainerApp{
TypeMeta: apimachinery_apis_v1.TypeMeta{
Kind: "DevcontainerApp",
APIVersion: "devcontainer.devstar.cn/v1",
},
ObjectMeta: apimachinery_apis_v1.ObjectMeta{
Name: opts.Name,
Namespace: opts.Namespace,
},
Spec: devcontainer_api_v1.DevcontainerAppSpec{
StatefulSet: devcontainer_api_v1.StatefulSetSpec{
Image: opts.Image,
Command: opts.CommandList,
ContainerPort: opts.ContainerPort,
},
},
}
jsonData, err := json.Marshal(devcontainerApp)
if err != nil {
return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{
Action: "Marshal JSON",
Message: err.Error(),
}
}
unstructuredObj := &apimachinery_apis_v1_unstructured.Unstructured{}
_, _, err = apimachinery_apis_v1_unstructured.UnstructuredJSONScheme.Decode(jsonData, &groupVersionKind, unstructuredObj)
if err != nil {
return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{
Action: "build unstructured obj",
Message: err.Error(),
}
}
// 创建 DevContainer Status.nodePortAssigned 信息
_, err = client.Resource(groupVersionResource).Namespace(opts.Namespace).Create(*ctx, unstructuredObj, opts.CreateOptions)
if err != nil {
return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{
Action: "create DevContainer via Dynamic Client",
Message: err.Error(),
}
}
// 注册 watcher 监听 DevContainer Status.nodePortAssigned 信息
watcherTimeoutSeconds := int64(3)
watcher, err := client.Resource(groupVersionResource).Namespace(opts.Namespace).Watch(*ctx, apimachinery_apis_v1.ListOptions{
FieldSelector: fmt.Sprintf("metadata.name=%s", opts.Name),
//Watch: false,
TimeoutSeconds: &watcherTimeoutSeconds,
Limit: 1,
})
if err != nil {
return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{
Action: "register watcher of DevContainer NodePort",
Message: err.Error(),
}
}
defer watcher.Stop()
//log.Info(" ===== 开始监听 DevContainer NodePort 分配信息 =====")
var nodePortAssigned int64
for event := range watcher.ResultChan() {
switch event.Type {
case apimachinery_watch.Modified:
if devcontainerUnstructured, ok := event.Object.(*apimachinery_apis_v1_unstructured.Unstructured); ok {
statusDevcontainer, ok, err := apimachinery_apis_v1_unstructured.NestedMap(devcontainerUnstructured.Object, "status")
if err == nil && ok {
nodePortAssigned = statusDevcontainer["nodePortAssigned"].(int64)
if 30000 <= nodePortAssigned && nodePortAssigned <= 32767 {
devcontainerApp.Status.NodePortAssigned = uint16(nodePortAssigned)
//log.Info("DevContainer NodePort Status 更新完成,最新 NodePort = %v", nodePortAssigned)
//break
// 收到 NodePort Service MODIFIED 消息后,更新 NodePort直接返回不再处理后续 Event (否则必须超时3秒才得到 NodePort)
return devcontainerApp, nil
}
}
}
}
}
//log.Info(" ===== 结束监听 DevContainer NodePort 分配信息 =====")
/*
// 目前需要更新的字段只有 devcontainerInCluster.Status.NodePortAssigned
// 如果后续有其他较多的需要更新的字段,可以考虑重新查询,目前,暂时只考虑 直接更新内存缓存对象,然后返回即可
// 避免对 k8s API Server 造成访问压力
//response, err := client.Resource(groupVersionResource).Namespace(opts.Namespace).Get(*ctx, opts.Name, apimachinery_apis_v1.GetOptions{})
//jsonData, err = response.MarshalJSON()
//devcontainerInCluster := &devcontainer_api_v1.DevcontainerApp{}
//err = json.Unmarshal(jsonData, devcontainerInCluster)
//if err != nil {
// return nil, devcontainer_errors.ErrOperateDevcontainer{
// Action: "parse response result of in-cluster DevContainer",
// Message: err.Error(),
// }
//}
*/
return devcontainerApp, nil
}