From f768ad5471f2508fb75d5505e059a3fabd88f49b Mon Sep 17 00:00:00 2001 From: ssongliu Date: Mon, 14 Nov 2022 19:19:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=20docker=20=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/docker.go | 48 +++++ backend/app/api/v1/entry.go | 1 + backend/app/dto/docker.go | 15 ++ backend/app/service/docker.go | 125 +++++++++++++ backend/app/service/docker_test.go | 32 ++++ backend/app/service/entry.go | 1 + backend/app/service/image_repo.go | 2 +- backend/router/ro_container.go | 4 + frontend/src/api/interface/container.ts | 14 ++ frontend/src/api/modules/container.ts | 11 ++ frontend/src/lang/modules/zh.ts | 6 + frontend/src/routers/modules/container.ts | 11 +- frontend/src/views/container/index.vue | 8 + .../src/views/container/setting/index.vue | 172 ++++++++++++++++++ .../views/database/redis/setting/index.vue | 4 +- 15 files changed, 449 insertions(+), 5 deletions(-) create mode 100644 backend/app/api/v1/docker.go create mode 100644 backend/app/dto/docker.go create mode 100644 backend/app/service/docker.go create mode 100644 backend/app/service/docker_test.go create mode 100644 frontend/src/views/container/setting/index.vue diff --git a/backend/app/api/v1/docker.go b/backend/app/api/v1/docker.go new file mode 100644 index 000000000..46669edea --- /dev/null +++ b/backend/app/api/v1/docker.go @@ -0,0 +1,48 @@ +package v1 + +import ( + "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" + "github.com/1Panel-dev/1Panel/backend/app/dto" + "github.com/1Panel-dev/1Panel/backend/constant" + "github.com/gin-gonic/gin" +) + +func (b *BaseApi) LoadDaemonJson(c *gin.Context) { + conf, err := dockerService.LoadDockerConf() + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, conf) +} + +func (b *BaseApi) UpdateDaemonJson(c *gin.Context) { + var req dto.DaemonJsonConf + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + if err := dockerService.UpdateConf(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) UpdateDaemonJsonByFile(c *gin.Context) { + var req dto.DaemonJsonUpdateByFile + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + if err := dockerService.UpdateConfByFile(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, nil) +} diff --git a/backend/app/api/v1/entry.go b/backend/app/api/v1/entry.go index 11525f8a3..a7e59d34e 100644 --- a/backend/app/api/v1/entry.go +++ b/backend/app/api/v1/entry.go @@ -18,6 +18,7 @@ var ( composeTemplateService = service.ServiceGroupApp.ComposeTemplateService imageRepoService = service.ServiceGroupApp.ImageRepoService imageService = service.ServiceGroupApp.ImageService + dockerService = service.ServiceGroupApp.DockerService mysqlService = service.ServiceGroupApp.MysqlService redisService = service.ServiceGroupApp.RedisService diff --git a/backend/app/dto/docker.go b/backend/app/dto/docker.go new file mode 100644 index 000000000..fb8cf06d3 --- /dev/null +++ b/backend/app/dto/docker.go @@ -0,0 +1,15 @@ +package dto + +type DaemonJsonUpdateByFile struct { + Path string `json:"path" validate:"required"` + File string `json:"file"` +} + +type DaemonJsonConf struct { + Status string `json:"status"` + Mirrors []string `json:"registryMirrors"` + Registries []string `json:"insecureRegistries"` + Bip string `json:"bip"` + LiveRestore bool `json:"liveRestore"` + CgroupDriver string `json:"cgroupDriver"` +} diff --git a/backend/app/service/docker.go b/backend/app/service/docker.go new file mode 100644 index 000000000..2277ee972 --- /dev/null +++ b/backend/app/service/docker.go @@ -0,0 +1,125 @@ +package service + +import ( + "bufio" + "encoding/json" + "io/ioutil" + "os" + "strings" + + "github.com/1Panel-dev/1Panel/backend/app/dto" +) + +type DockerService struct{} + +type IDockerService interface { + UpdateConf(req dto.DaemonJsonConf) error + UpdateConfByFile(info dto.DaemonJsonUpdateByFile) error + LoadDockerConf() (*dto.DaemonJsonConf, error) +} + +func NewIDockerService() IDockerService { + return &DockerService{} +} + +type daemonJsonItem struct { + Status string `json:"status"` + Mirrors []string `json:"registry-mirrors"` + Registries []string `json:"insecure-registries"` + Bip string `json:"bip"` + LiveRestore bool `json:"live-restore"` + ExecOpts []string `json:"exec-opts"` +} + +func (u *DockerService) LoadDockerConf() (*dto.DaemonJsonConf, error) { + file, err := ioutil.ReadFile("/opt/1Panel/docker/daemon.json") + if err != nil { + return nil, err + } + var conf daemonJsonItem + deamonMap := make(map[string]interface{}) + if err := json.Unmarshal(file, &deamonMap); err != nil { + return nil, err + } + arr, err := json.Marshal(deamonMap) + if err != nil { + return nil, err + } + if err := json.Unmarshal(arr, &conf); err != nil { + return nil, err + } + driver := "cgroupfs" + for _, opt := range conf.ExecOpts { + if strings.HasPrefix(opt, "native.cgroupdriver=") { + driver = strings.ReplaceAll(opt, "native.cgroupdriver=", "") + break + } + } + data := dto.DaemonJsonConf{ + Status: conf.Status, + Mirrors: conf.Mirrors, + Registries: conf.Registries, + Bip: conf.Bip, + LiveRestore: conf.LiveRestore, + CgroupDriver: driver, + } + + return &data, nil +} + +func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error { + file, err := ioutil.ReadFile("/opt/1Panel/docker/daemon.json") + if err != nil { + return err + } + deamonMap := make(map[string]interface{}) + if err := json.Unmarshal(file, &deamonMap); err != nil { + return err + } + deamonMap["registry-mirrors"] = req.Mirrors + deamonMap["insecure-registries"] = req.Registries + deamonMap["bip"] = req.Bip + deamonMap["live-restore"] = req.LiveRestore + if opts, ok := deamonMap["exec-opts"]; ok { + if optsValue, isArray := opts.([]interface{}); isArray { + for i := 0; i < len(optsValue); i++ { + if opt, isStr := optsValue[i].(string); isStr { + if strings.HasPrefix(opt, "native.cgroupdriver=") { + optsValue[i] = "native.cgroupdriver=" + req.CgroupDriver + break + } + } + } + } + } else { + if req.CgroupDriver == "systemd" { + deamonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"} + } + } + newJson, err := json.MarshalIndent(deamonMap, "", "\t") + if err != nil { + return err + } + if err := ioutil.WriteFile("/opt/1Panel/docker/daemon.json", newJson, 0640); err != nil { + return err + } + return nil +} + +func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error { + file, err := os.OpenFile(req.Path, os.O_WRONLY|os.O_TRUNC, 0640) + if err != nil { + return err + } + defer file.Close() + write := bufio.NewWriter(file) + _, _ = write.WriteString(req.File) + write.Flush() + + // cmd := exec.Command("systemctl", "restart", "docker") + // stdout, err := cmd.CombinedOutput() + // if err != nil { + // return errors.New(string(stdout)) + // } + return nil +} diff --git a/backend/app/service/docker_test.go b/backend/app/service/docker_test.go new file mode 100644 index 000000000..35ea8f031 --- /dev/null +++ b/backend/app/service/docker_test.go @@ -0,0 +1,32 @@ +package service + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "strings" + "testing" +) + +func TestDocker(t *testing.T) { + file, err := ioutil.ReadFile("/opt/1Panel/docker/daemon.json") + if err != nil { + fmt.Println(err) + } + var conf daemonJsonItem + deamonMap := make(map[string]interface{}) + if err := json.Unmarshal(file, &deamonMap); err != nil { + fmt.Println(err) + } + arr, err := json.Marshal(deamonMap) + if err != nil { + fmt.Println(err) + } + _ = json.Unmarshal(arr, &conf) + + for _, opt := range conf.ExecOpts { + if strings.HasPrefix(opt, "native.cgroupdriver=") { + fmt.Println(strings.ReplaceAll(opt, "native.cgroupdriver=", "")) + } + } +} diff --git a/backend/app/service/entry.go b/backend/app/service/entry.go index 07f266e4e..c0e50aaec 100644 --- a/backend/app/service/entry.go +++ b/backend/app/service/entry.go @@ -12,6 +12,7 @@ type ServiceGroup struct { ImageService ImageRepoService ComposeTemplateService + DockerService MysqlService RedisService diff --git a/backend/app/service/image_repo.go b/backend/app/service/image_repo.go index 31c897d59..633022410 100644 --- a/backend/app/service/image_repo.go +++ b/backend/app/service/image_repo.go @@ -75,7 +75,7 @@ func (u *ImageRepoService) Create(imageRepoDto dto.ImageRepoCreate) error { if err != nil { return err } - if err := ioutil.WriteFile(constant.DaemonJsonDir, newJson, 0777); err != nil { + if err := ioutil.WriteFile(constant.DaemonJsonDir, newJson, 0640); err != nil { return err } } diff --git a/backend/router/ro_container.go b/backend/router/ro_container.go index 23f487068..a572aa141 100644 --- a/backend/router/ro_container.go +++ b/backend/router/ro_container.go @@ -62,5 +62,9 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) { baRouter.POST("/volume/search", baseApi.SearchVolume) baRouter.GET("/volume", baseApi.ListVolume) baRouter.POST("/volume", baseApi.CreateVolume) + + baRouter.GET("/daemonjson", baseApi.LoadDaemonJson) + baRouter.POST("/daemonjson/update", baseApi.UpdateDaemonJson) + baRouter.POST("/daemonjson/update/byfile", baseApi.UpdateDaemonJsonByFile) } } diff --git a/frontend/src/api/interface/container.ts b/frontend/src/api/interface/container.ts index 70634a88c..778ecb9c5 100644 --- a/frontend/src/api/interface/container.ts +++ b/frontend/src/api/interface/container.ts @@ -223,4 +223,18 @@ export namespace Container { export interface BatchDelete { ids: Array; } + + export interface DaemonJsonUpdateByFile { + path: string; + file: string; + } + + export interface DaemonJsonConf { + status: string; + bip: string; + registryMirrors: Array; + insecureRegistries: Array; + liveRestore: boolean; + cgroupDriver: string; + } } diff --git a/frontend/src/api/modules/container.ts b/frontend/src/api/modules/container.ts index 1c19ac278..bf7c7932b 100644 --- a/frontend/src/api/modules/container.ts +++ b/frontend/src/api/modules/container.ts @@ -119,3 +119,14 @@ export const upCompose = (params: Container.ComposeCreate) => { export const ComposeOperator = (params: Container.ComposeOpration) => { return http.post(`/containers/compose/operate`, params); }; + +// docker +export const loadDaemonJson = () => { + return http.get(`/containers/daemonjson`); +}; +export const updateDaemonJson = (params: Container.DaemonJsonConf) => { + return http.post(`/containers/daemonjson/update`, params); +}; +export const updateDaemonJsonByfile = (params: Container.DaemonJsonUpdateByFile) => { + return http.post(`/containers/daemonjson/update/byfile`, params); +}; diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index d422ec808..8aa28a62c 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -387,6 +387,12 @@ export default { down: '删除', up: '启动', operatorComposeHelper: '将对选中 Compose 进行 {0} 操作,是否继续?', + + setting: '容器配置', + mirrors: '镜像加速', + mirrorsHelper: '为空则关闭镜像加速;优先使用加速 URL 执行操作,请求超时将跳过使用默认加速方式', + registries: '私有仓库', + liveHelper: '停止 docker 服务时,是否关闭所有容器', }, cronjob: { cronTask: '计划任务', diff --git a/frontend/src/routers/modules/container.ts b/frontend/src/routers/modules/container.ts index b7554b758..370ca97ff 100644 --- a/frontend/src/routers/modules/container.ts +++ b/frontend/src/routers/modules/container.ts @@ -67,13 +67,22 @@ const containerRouter = { }, { path: 'template', - name: 'composeTemplate', + name: 'ComposeTemplate', component: () => import('@/views/container/template/index.vue'), hidden: true, meta: { activeMenu: '/containers', }, }, + { + path: 'setting', + name: 'ContainerSetting', + component: () => import('@/views/container/setting/index.vue'), + hidden: true, + meta: { + activeMenu: '/containers', + }, + }, ], }; diff --git a/frontend/src/views/container/index.vue b/frontend/src/views/container/index.vue index 4a20d7b83..39410a977 100644 --- a/frontend/src/views/container/index.vue +++ b/frontend/src/views/container/index.vue @@ -38,6 +38,14 @@ > {{ $t('container.composeTemplate') }} + + {{ $t('container.setting') }} + diff --git a/frontend/src/views/container/setting/index.vue b/frontend/src/views/container/setting/index.vue new file mode 100644 index 000000000..9a97eaa7c --- /dev/null +++ b/frontend/src/views/container/setting/index.vue @@ -0,0 +1,172 @@ + + + diff --git a/frontend/src/views/database/redis/setting/index.vue b/frontend/src/views/database/redis/setting/index.vue index 1bc7561d3..de67053f3 100644 --- a/frontend/src/views/database/redis/setting/index.vue +++ b/frontend/src/views/database/redis/setting/index.vue @@ -83,7 +83,6 @@ import { ChangePort } from '@/api/modules/app'; const extensions = [javascript(), oneDark]; const confShowType = ref('base'); -const restartNow = ref(false); const form = reactive({ name: '', port: 6379, @@ -166,10 +165,9 @@ const onSaveFile = async () => { const onSubmitSave = async () => { let param = { file: mysqlConf.value, - restartNow: restartNow.value, + restartNow: true, }; await updateRedisConfByFile(param); - restartNow.value = false; ElMessage.success(i18n.global.t('commons.msg.operationSuccess')); };