From 9c5da23a384db9e0749be9728acc2cf3594affb0 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Wed, 16 Oct 2024 22:19:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E4=BB=8E=E5=91=BD=E4=BB=A4=E5=88=9B=E5=BB=BA=E5=AE=B9=E5=99=A8?= =?UTF-8?q?=EF=BC=8C=E8=A1=A8=E5=8D=95=E6=A0=B7=E5=BC=8F=E4=BC=98=E5=8C=96?= =?UTF-8?q?=20(#6741)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agent/app/api/v2/container.go | 21 + agent/app/dto/container.go | 5 + agent/app/service/container.go | 21 + agent/router/ro_container.go | 1 + frontend/src/api/modules/container.ts | 3 + .../src/components/codemirror-pro/index.vue | 13 +- frontend/src/lang/modules/en.ts | 5 + frontend/src/lang/modules/tw.ts | 5 + frontend/src/lang/modules/zh.ts | 5 + .../container/container/command/index.vue | 110 +++++ .../container/container/operate/index.vue | 457 ++++++++++-------- .../src/views/container/image/tag/index.vue | 10 +- 12 files changed, 451 insertions(+), 205 deletions(-) create mode 100644 frontend/src/views/container/container/command/index.vue diff --git a/agent/app/api/v2/container.go b/agent/app/api/v2/container.go index bdf7eae01..146a20882 100644 --- a/agent/app/api/v2/container.go +++ b/agent/app/api/v2/container.go @@ -257,6 +257,27 @@ func (b *BaseApi) ContainerCreate(c *gin.Context) { helper.SuccessWithData(c, nil) } +// @Tags Container +// @Summary Create container by command +// @Description 命令创建容器 +// @Accept json +// @Param request body dto.ContainerCreateByCommand true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Router /containers/command [post] +func (b *BaseApi) ContainerCreateByCommand(c *gin.Context) { + var req dto.ContainerCreateByCommand + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + + if err := containerService.ContainerCreateByCommand(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + // @Tags Container // @Summary Upgrade container // @Description 更新容器镜像 diff --git a/agent/app/dto/container.go b/agent/app/dto/container.go index 6170c4e42..28e2cdac1 100644 --- a/agent/app/dto/container.go +++ b/agent/app/dto/container.go @@ -90,6 +90,11 @@ type ContainerOperate struct { RestartPolicy string `json:"restartPolicy"` } +type ContainerCreateByCommand struct { + TaskID string `json:"taskID"` + Command string `json:"command"` +} + type ContainerUpgrade struct { Name string `json:"name" validate:"required"` Image string `json:"image" validate:"required"` diff --git a/agent/app/service/container.go b/agent/app/service/container.go index cf916f510..07592bd17 100644 --- a/agent/app/service/container.go +++ b/agent/app/service/container.go @@ -11,6 +11,7 @@ import ( "net/url" "os" "os/exec" + "path" "path/filepath" "sort" "strconv" @@ -63,6 +64,7 @@ type IContainerService interface { CreateCompose(req dto.ComposeCreate) error ComposeOperation(req dto.ComposeOperation) error ContainerCreate(req dto.ContainerOperate) error + ContainerCreateByCommand(req dto.ContainerCreateByCommand) error ContainerUpdate(req dto.ContainerOperate) error ContainerUpgrade(req dto.ContainerUpgrade) error ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error) @@ -313,6 +315,25 @@ func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error return datas, nil } +func (u *ContainerService) ContainerCreateByCommand(req dto.ContainerCreateByCommand) error { + if cmd.CheckIllegal(req.Command) { + return buserr.New(constant.ErrCmdIllegal) + } + taskItem, err := task.NewTaskWithOps("-", task.TaskCreate, task.TaskScopeContainer, req.TaskID, 1) + if err != nil { + global.LOG.Errorf("new task for create container failed, err: %v", err) + return err + } + go func() { + taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", "-"), func(t *task.Task) error { + logPath := path.Join(constant.LogDir, task.TaskScopeContainer, req.TaskID+".log") + return cmd.ExecShell(logPath, 5*time.Minute, "bash", "-c", req.Command) + }, nil) + _ = taskItem.Execute() + }() + return nil +} + func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) { client, err := docker.NewDockerClient() if err != nil { diff --git a/agent/router/ro_container.go b/agent/router/ro_container.go index 508550465..466c626c6 100644 --- a/agent/router/ro_container.go +++ b/agent/router/ro_container.go @@ -15,6 +15,7 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) { baRouter.GET("/stats/:id", baseApi.ContainerStats) baRouter.POST("", baseApi.ContainerCreate) + baRouter.POST("command", baseApi.ContainerCreateByCommand) baRouter.POST("/update", baseApi.ContainerUpdate) baRouter.POST("/upgrade", baseApi.ContainerUpgrade) baRouter.POST("/info", baseApi.ContainerInfo) diff --git a/frontend/src/api/modules/container.ts b/frontend/src/api/modules/container.ts index e044d74a3..51c17e5d9 100644 --- a/frontend/src/api/modules/container.ts +++ b/frontend/src/api/modules/container.ts @@ -18,6 +18,9 @@ export const loadResourceLimit = () => { export const createContainer = (params: Container.ContainerHelper) => { return http.post(`/containers`, params, TimeoutEnum.T_10M); }; +export const createContainerByCommand = (command: string, taskID: string) => { + return http.post(`/containers/command`, { command: command, taskID: taskID }); +}; export const updateContainer = (params: Container.ContainerHelper) => { return http.post(`/containers/update`, params, TimeoutEnum.T_10M); }; diff --git a/frontend/src/components/codemirror-pro/index.vue b/frontend/src/components/codemirror-pro/index.vue index e39ec9d67..63eb3e987 100644 --- a/frontend/src/components/codemirror-pro/index.vue +++ b/frontend/src/components/codemirror-pro/index.vue @@ -37,6 +37,10 @@ const props = defineProps({ type: String, default: '', }, + height: { + type: Number, + default: 0, + }, heightDiff: { type: Number, default: 200, @@ -45,6 +49,10 @@ const props = defineProps({ type: Number, default: 400, }, + lineWrapping: { + type: Boolean, + default: false, + }, }); const emit = defineEmits(['update:modelValue']); @@ -62,7 +70,7 @@ const initCodeMirror = () => { const defaultTheme = EditorView.theme({ '&.cm-editor': { minHeight: props.minHeight + 'px', - height: 'calc(100vh - ' + props.heightDiff + 'px)', + height: props.height ? props.height + 'px' : 'calc(100vh - ' + props.heightDiff + 'px)', }, }); @@ -78,6 +86,9 @@ const initCodeMirror = () => { placeholder(props.placeholder), EditorView.editable.of(!props.disabled), ]; + if (props.lineWrapping) { + extensions.push(EditorView.lineWrapping); + } switch (props.mode) { case 'dockerfile': extensions.push(StreamLanguage.define(dockerFile)); diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index fe378ae5a..832fda9ed 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -580,6 +580,10 @@ const message = { }, container: { create: 'Create', + createByCommand: 'Create by command', + commandInput: 'Command input', + commandRule: 'Please enter the correct docker run container creation command!', + commandHelper: 'This command will be executed on the server to create the container. Do you want to continue?', edit: 'Edit container', updateContainerHelper: 'Container editing requires rebuilding the container. Any data that has not been persisted will be lost. Do you want to continue?', @@ -669,6 +673,7 @@ const message = { imageLoadErr: 'No image name detected for the container', appHelper: 'This container is sourced from the app store; upgrading might render the service unavailable', + resource: 'Resource', input: 'Input', forcePull: 'forced image pull ', forcePullHelper: 'Ignore existing images on the server and pull again.', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 1aeab5533..dfd53e71e 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -562,6 +562,10 @@ const message = { }, container: { create: '創建容器', + createByCommand: '命令創建', + commandInput: '命令輸入', + commandRule: '請輸入正確的 docker run 容器創建命令!', + commandHelper: '將在伺服器上執行該條命令以創建容器,是否繼續?', edit: '編輯容器', updateContainerHelper: '容器編輯需要重建容器,任何未持久化的數據將會丟失,是否繼續?', containerList: '容器列表', @@ -643,6 +647,7 @@ const message = { imageLoadErr: '未檢測到容器的鏡像名稱', appHelper: '該容器來源於應用商店,升級可能導致該服務不可用', + resource: '資源', input: '手動輸入', forcePull: '強製拉取鏡像', forcePullHelper: '忽略服務器已存在的鏡像,重新拉取一次', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index f6d6e116b..c3ff8488d 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -562,6 +562,10 @@ const message = { }, container: { create: '创建容器', + createByCommand: '命令创建', + commandInput: '命令输入', + commandRule: '请输入正确的 docker run 容器创建命令!', + commandHelper: '将在服务器上执行该条命令以创建容器,是否继续?', edit: '编辑容器', updateContainerHelper: '容器编辑需要重建容器,任何未持久化的数据将会丢失,是否继续?', containerList: '容器列表', @@ -644,6 +648,7 @@ const message = { imageLoadErr: '未检测到容器的镜像名称', appHelper: '该容器来源于应用商店,升级可能导致该服务不可用', + resource: '资源', input: '手动输入', forcePull: '强制拉取镜像', forcePullHelper: '忽略服务器已存在的镜像,重新拉取一次', diff --git a/frontend/src/views/container/container/command/index.vue b/frontend/src/views/container/container/command/index.vue new file mode 100644 index 000000000..c48acd328 --- /dev/null +++ b/frontend/src/views/container/container/command/index.vue @@ -0,0 +1,110 @@ + + + diff --git a/frontend/src/views/container/container/operate/index.vue b/frontend/src/views/container/container/operate/index.vue index 34427d72d..17fa9956f 100644 --- a/frontend/src/views/container/container/operate/index.vue +++ b/frontend/src/views/container/container/operate/index.vue @@ -1,6 +1,9 @@