From 28df0b9a3cd8bfcb61badcc309b18d9065669c90 Mon Sep 17 00:00:00 2001 From: ssongliu Date: Wed, 12 Oct 2022 13:42:58 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E9=95=9C=E5=83=8F?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=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/cronjob.go | 25 ----- backend/app/api/v1/file.go | 30 +++++- backend/app/api/v1/image.go | 5 +- backend/app/dto/common_req.go | 4 + backend/app/dto/cronjob.go | 4 - backend/app/dto/image.go | 7 +- backend/app/service/image.go | 80 ++++++++++----- backend/app/service/image_test.go | 99 ------------------- backend/constant/container.go | 3 +- backend/router/ro_container.go | 1 + backend/router/ro_cronjob.go | 1 - backend/router/ro_file.go | 1 + frontend/src/api/interface/container.ts | 2 + frontend/src/api/interface/file.ts | 4 + frontend/src/api/modules/container.ts | 2 +- frontend/src/api/modules/files.ts | 4 + .../src/views/container/image/build/index.vue | 78 ++++++++++++--- frontend/src/views/cronjob/record/index.vue | 5 +- 18 files changed, 180 insertions(+), 175 deletions(-) delete mode 100644 backend/app/service/image_test.go diff --git a/backend/app/api/v1/cronjob.go b/backend/app/api/v1/cronjob.go index aaf5f8ff0..5614e1f82 100644 --- a/backend/app/api/v1/cronjob.go +++ b/backend/app/api/v1/cronjob.go @@ -1,7 +1,6 @@ package v1 import ( - "os" "time" "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" @@ -144,30 +143,6 @@ func (b *BaseApi) UpdateCronjobStatus(c *gin.Context) { helper.SuccessWithData(c, nil) } -func (b *BaseApi) LoadRecordDetail(c *gin.Context) { - var req dto.DetailFile - if err := c.ShouldBindJSON(&req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - if err := global.VALID.Struct(req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - file, err := os.Open(req.Path) - if err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - defer file.Close() - buf := make([]byte, 1024*2) - if _, err := file.Read(buf); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - helper.SuccessWithData(c, string(buf)) -} - func (b *BaseApi) TargetDownload(c *gin.Context) { var req dto.CronjobDownload if err := c.ShouldBindJSON(&req); err != nil { diff --git a/backend/app/api/v1/file.go b/backend/app/api/v1/file.go index e1da5cb61..4cc24f976 100644 --- a/backend/app/api/v1/file.go +++ b/backend/app/api/v1/file.go @@ -2,6 +2,10 @@ package v1 import ( "fmt" + "net/http" + "os" + "path" + "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" @@ -9,8 +13,6 @@ import ( websocket2 "github.com/1Panel-dev/1Panel/backend/utils/websocket" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" - "net/http" - "path" ) func (b *BaseApi) ListFiles(c *gin.Context) { @@ -228,6 +230,30 @@ func (b *BaseApi) Size(c *gin.Context) { helper.SuccessWithData(c, res) } +func (b *BaseApi) LoadFromFile(c *gin.Context) { + var req dto.FilePath + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + file, err := os.Open(req.Path) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + defer file.Close() + buf := make([]byte, 1024*500) + if _, err := file.Read(buf); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, string(buf)) +} + var wsUpgrade = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true diff --git a/backend/app/api/v1/image.go b/backend/app/api/v1/image.go index a253de785..729ffbe46 100644 --- a/backend/app/api/v1/image.go +++ b/backend/app/api/v1/image.go @@ -42,12 +42,13 @@ func (b *BaseApi) ImageBuild(c *gin.Context) { return } - if err := imageService.ImageBuild(req); err != nil { + log, err := imageService.ImageBuild(req) + if err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } - helper.SuccessWithData(c, nil) + helper.SuccessWithData(c, log) } func (b *BaseApi) ImagePull(c *gin.Context) { diff --git a/backend/app/dto/common_req.go b/backend/app/dto/common_req.go index 1e1ebdd6e..8f72c3972 100644 --- a/backend/app/dto/common_req.go +++ b/backend/app/dto/common_req.go @@ -18,6 +18,10 @@ type BatchDeleteReq struct { Ids []uint `json:"ids" validate:"required"` } +type FilePath struct { + Path string `json:"path" validate:"required"` +} + type DeleteByName struct { Name string `json:"name" validate:"required"` } diff --git a/backend/app/dto/cronjob.go b/backend/app/dto/cronjob.go index 184344fd7..3e8f12a8e 100644 --- a/backend/app/dto/cronjob.go +++ b/backend/app/dto/cronjob.go @@ -49,10 +49,6 @@ type CronjobDownload struct { BackupAccountID uint `json:"backupAccountID" validate:"required"` } -type DetailFile struct { - Path string `json:"path" validate:"required"` -} - type CronjobInfo struct { ID uint `json:"id"` Name string `json:"name"` diff --git a/backend/app/dto/image.go b/backend/app/dto/image.go index ee511fe24..4433c3400 100644 --- a/backend/app/dto/image.go +++ b/backend/app/dto/image.go @@ -14,9 +14,10 @@ type ImageLoad struct { } type ImageBuild struct { - From string `josn:"from" validate:"required"` - Dockerfile string `josn:"dockerfile" validate:"required"` - Tags string `josn:"tags" validate:"required"` + From string `josn:"from" validate:"required"` + Name string `json:"name" validate:"required"` + Dockerfile string `josn:"dockerfile" validate:"required"` + Tags []string `josn:"tags"` } type ImagePull struct { diff --git a/backend/app/service/image.go b/backend/app/service/image.go index cebf98cf0..eb849b4df 100644 --- a/backend/app/service/image.go +++ b/backend/app/service/image.go @@ -1,6 +1,7 @@ package service import ( + "bufio" "bytes" "context" "encoding/base64" @@ -11,9 +12,11 @@ import ( "time" "github.com/1Panel-dev/1Panel/app/dto" + "github.com/1Panel-dev/1Panel/constant" "github.com/1Panel-dev/1Panel/global" "github.com/1Panel-dev/1Panel/utils/docker" "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/archive" ) type ImageService struct{} @@ -40,7 +43,7 @@ func (u *ImageService) Page(req dto.PageInfo) (int64, interface{}, error) { if err != nil { return 0, nil, err } - list, err = client.ImageList(context.Background(), types.ImageListOptions{}) + list, err = client.ImageList(context.Background(), types.ImageListOptions{All: true}) if err != nil { return 0, nil, err } @@ -67,27 +70,60 @@ func (u *ImageService) Page(req dto.PageInfo) (int64, interface{}, error) { return int64(total), backDatas, nil } -func (u *ImageService) ImageBuild(req dto.ImageBuild) error { - // client, err := docker.NewDockerClient() - // if err != nil { - // return err - // } - // if req.From == "path" { - // tar, err := archive.TarWithOptions("node-hello/", &archive.TarOptions{}) - // if err != nil { - // return err - // } +func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) { + client, err := docker.NewDockerClient() + if err != nil { + return "", err + } + if req.From == "edit" { + dir := fmt.Sprintf("%s/%s", constant.TmpDockerBuildDir, req.Name) + if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) { + if err = os.MkdirAll(dir, os.ModePerm); err != nil { + return "", err + } + } - // opts := types.ImageBuildOptions{ - // Dockerfile: "Dockerfile", - // Tags: []string{dockerRegistryUserID + "/node-hello"}, - // Remove: true, - // } - // if _, err := client.ImageBuild(context.TODO(), tar, opts); err != nil { - // return err - // } - // } - return nil + path := fmt.Sprintf("%s/Dockerfile", dir) + file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + if err != nil { + return "", err + } + defer file.Close() + write := bufio.NewWriter(file) + _, _ = write.WriteString(string(req.Dockerfile)) + write.Flush() + req.Dockerfile = dir + } + tar, err := archive.TarWithOptions(req.Dockerfile+"/", &archive.TarOptions{}) + if err != nil { + return "", err + } + + opts := types.ImageBuildOptions{ + Dockerfile: "Dockerfile", + Tags: []string{req.Name}, + Remove: true, + Labels: stringsToMap(req.Tags), + } + logName := fmt.Sprintf("%s/build.log", req.Dockerfile) + + path := logName + file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + if err != nil { + return "", err + } + go func() { + defer file.Close() + res, err := client.ImageBuild(context.TODO(), tar, opts) + if err != nil { + global.LOG.Errorf("build image %s failed, err: %v", req.Name, err) + return + } + global.LOG.Debugf("build image %s successful!", req.Name) + _, _ = io.Copy(file, res.Body) + }() + + return logName, nil } func (u *ImageService) ImagePull(req dto.ImagePull) error { @@ -240,7 +276,7 @@ func (u *ImageService) ImageRemove(req dto.BatchDelete) error { return err } for _, ids := range req.Ids { - if _, err := client.ImageRemove(context.TODO(), ids, types.ImageRemoveOptions{Force: true}); err != nil { + if _, err := client.ImageRemove(context.TODO(), ids, types.ImageRemoveOptions{Force: true, PruneChildren: true}); err != nil { return err } } diff --git a/backend/app/service/image_test.go b/backend/app/service/image_test.go deleted file mode 100644 index 634ea9f13..000000000 --- a/backend/app/service/image_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package service - -import ( - "context" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "testing" - - "github.com/1Panel-dev/1Panel/constant" - "github.com/1Panel-dev/1Panel/utils/docker" - "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/archive" -) - -func TestImage(t *testing.T) { - file, err := os.OpenFile(("/tmp/nginx.tar"), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) - if err != nil { - fmt.Println(err) - } - defer file.Close() - - client, err := docker.NewDockerClient() - if err != nil { - fmt.Println(err) - } - out, err := client.ImageSave(context.TODO(), []string{"nginx:1.14.2"}) - fmt.Println(err) - defer out.Close() - if _, err = io.Copy(file, out); err != nil { - fmt.Println(err) - } -} - -func TestBuild(t *testing.T) { - client, err := docker.NewDockerClient() - if err != nil { - fmt.Println(err) - } - tar, err := archive.TarWithOptions("/Users/slooop/Documents/neeko/", &archive.TarOptions{}) - if err != nil { - fmt.Println(err) - } - - opts := types.ImageBuildOptions{ - Dockerfile: "Dockerfile", - Tags: []string{"neeko" + "/test"}, - Remove: true, - } - res, err := client.ImageBuild(context.TODO(), tar, opts) - if err != nil { - fmt.Println(err) - } - defer res.Body.Close() -} - -func TestDeam(t *testing.T) { - file, err := ioutil.ReadFile(constant.DaemonJsonDir) - if err != nil { - fmt.Println(err) - } - deamonMap := make(map[string]interface{}) - err = json.Unmarshal(file, &deamonMap) - fmt.Println(err) - for k, v := range deamonMap { - fmt.Println(k, v) - } - if _, ok := deamonMap["insecure-registries"]; ok { - if k, v := deamonMap["insecure-registries"].(string); v { - fmt.Println("string ", k) - } - if k, v := deamonMap["insecure-registries"].([]interface{}); v { - fmt.Println("[]string ", k) - k = append(k, "172.16.10.111:8085") - deamonMap["insecure-registries"] = k - } - } - newss, err := json.Marshal(deamonMap) - if err != nil { - fmt.Println(err) - } - fmt.Println(string(newss)) - if err := ioutil.WriteFile(constant.DaemonJsonDir, newss, 0777); err != nil { - fmt.Println(err) - } -} - -func TestNetwork(t *testing.T) { - client, err := docker.NewDockerClient() - if err != nil { - fmt.Println(err) - } - _, err = client.NetworkCreate(context.TODO(), "test", types.NetworkCreate{}) - if err != nil { - fmt.Println(err) - } -} diff --git a/backend/constant/container.go b/backend/constant/container.go index fa264518f..bd80ea499 100644 --- a/backend/constant/container.go +++ b/backend/constant/container.go @@ -10,5 +10,6 @@ const ( ContainerOpRename = "reName" ContainerOpRemove = "remove" - DaemonJsonDir = "/System/Volumes/Data/Users/slooop/.docker/daemon.json" + DaemonJsonDir = "/System/Volumes/Data/Users/slooop/.docker/daemon.json" + TmpDockerBuildDir = "/opt/1Panel/build" ) diff --git a/backend/router/ro_container.go b/backend/router/ro_container.go index 20252f5fb..07136a52e 100644 --- a/backend/router/ro_container.go +++ b/backend/router/ro_container.go @@ -38,6 +38,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) { baRouter.POST("/image/load", baseApi.ImageLoad) baRouter.POST("/image/remove", baseApi.ImageRemove) baRouter.POST("/image/tag", baseApi.ImageTag) + baRouter.POST("/image/build", baseApi.ImageBuild) baRouter.POST("/network/del", baseApi.DeleteNetwork) baRouter.POST("/network/search", baseApi.SearchNetwork) diff --git a/backend/router/ro_cronjob.go b/backend/router/ro_cronjob.go index c21475f6a..96501641c 100644 --- a/backend/router/ro_cronjob.go +++ b/backend/router/ro_cronjob.go @@ -29,6 +29,5 @@ func (s *CronjobRouter) InitCronjobRouter(Router *gin.RouterGroup) { cmdRouter.POST("/download", baseApi.TargetDownload) cmdRouter.POST("/search", baseApi.SearchCronjob) cmdRouter.POST("/search/records", baseApi.SearchJobRecords) - cmdRouter.POST("/search/detail", baseApi.LoadRecordDetail) } } diff --git a/backend/router/ro_file.go b/backend/router/ro_file.go index dde6f7c3e..92961fa73 100644 --- a/backend/router/ro_file.go +++ b/backend/router/ro_file.go @@ -32,6 +32,7 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) { fileRouter.POST("/size", baseApi.Size) fileRouter.GET("/ws", baseApi.Ws) fileRouter.GET("/keys", baseApi.Keys) + fileRouter.POST("/loadfile", baseApi.LoadFromFile) } } diff --git a/frontend/src/api/interface/container.ts b/frontend/src/api/interface/container.ts index 93b8e7294..a78078a88 100644 --- a/frontend/src/api/interface/container.ts +++ b/frontend/src/api/interface/container.ts @@ -30,7 +30,9 @@ export namespace Container { } export interface ImageBuild { from: string; + name: string; dockerfile: string; + tags: Array; } export interface ImagePull { repoID: number; diff --git a/frontend/src/api/interface/file.ts b/frontend/src/api/interface/file.ts index 9964badea..1d4fd6486 100644 --- a/frontend/src/api/interface/file.ts +++ b/frontend/src/api/interface/file.ts @@ -108,4 +108,8 @@ export namespace File { export interface DirSizeRes { size: number; } + + export interface FilePath { + path: string; + } } diff --git a/frontend/src/api/modules/container.ts b/frontend/src/api/modules/container.ts index 0e0626aca..6bd6b57d5 100644 --- a/frontend/src/api/modules/container.ts +++ b/frontend/src/api/modules/container.ts @@ -23,7 +23,7 @@ export const getImagePage = (params: ReqPage) => { return http.post>(`/containers/image/search`, params); }; export const imageBuild = (params: Container.ImageBuild) => { - return http.post(`/containers/image/build`, params); + return http.post(`/containers/image/build`, params); }; export const imagePull = (params: Container.ImagePull) => { return http.post(`/containers/image/pull`, params); diff --git a/frontend/src/api/modules/files.ts b/frontend/src/api/modules/files.ts index e9408b5f2..1bf6df210 100644 --- a/frontend/src/api/modules/files.ts +++ b/frontend/src/api/modules/files.ts @@ -22,6 +22,10 @@ export const ChangeFileMode = (form: File.FileCreate) => { return http.post('files/mode', form); }; +export const LoadFile = (form: File.FilePath) => { + return http.post('files/loadfile', form); +}; + export const CompressFile = (form: File.FileCompress) => { return http.post('files/compress', form); }; diff --git a/frontend/src/views/container/image/build/index.vue b/frontend/src/views/container/image/build/index.vue index feb340ed3..fa3e25a07 100644 --- a/frontend/src/views/container/image/build/index.vue +++ b/frontend/src/views/container/image/build/index.vue @@ -1,21 +1,30 @@