diff --git a/api/api_routers/router.go b/api/api_routers/router.go new file mode 100644 index 0000000000..2e820de89b --- /dev/null +++ b/api/api_routers/router.go @@ -0,0 +1,22 @@ +package api_routers + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +type RouteStruct struct { + // Add any necessary fields here +} + +func (r *RouteStruct) SetRoutes(engine *gin.Engine) { + // 应用 CORS 中间件到所有路由 + engine.Use(func(c *gin.Context) { + controller.CORS(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c.Next() + })).ServeHTTP(c.Writer, c.Request) + }) + + // ... 其余路由注册代码 +} \ No newline at end of file diff --git a/api/api_routers/websocket/websocket.go b/api/api_routers/websocket/websocket.go index 4ea1cff147..de8b0aa1c8 100644 --- a/api/api_routers/websocket/websocket.go +++ b/api/api_routers/websocket/websocket.go @@ -19,9 +19,12 @@ package websocket import ( + "net/http" + "github.com/go-chi/chi" "github.com/goodrain/rainbond/api/controller" "github.com/goodrain/rainbond/pkg/component/eventlog" + "github.com/sirupsen/logrus" ) // Routes routes @@ -64,10 +67,39 @@ func PackageBuildRoutes() chi.Router { // FileOperateRoutes 共享存储的文件操作路由 func FileOperateRoutes() chi.Router { r := chi.NewRouter() - r.Get("/download/{fileName}", controller.GetFileManage().Get) - r.Options("/download/{fileName}", controller.GetFileManage().Get) - r.Post("/upload", controller.GetFileManage().Get) - r.Options("/upload", controller.GetFileManage().Get) + r.Use(func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + logrus.Debugf("处理请求: %s %s", r.Method, r.URL.Path) + + // 获取 Origin + origin := r.Header.Get("Origin") + if origin == "" { + origin = "*" + } + + // 设置 CORS 头 + w.Header().Set("Access-Control-Allow-Origin", origin) + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Custom-Header, X-Requested-With") + w.Header().Set("Access-Control-Allow-Credentials", "true") + w.Header().Set("Access-Control-Max-Age", "3600") + + // 处理 OPTIONS 请求 + if r.Method == "OPTIONS" { + logrus.Debug("处理 OPTIONS 预检请求") + w.WriteHeader(http.StatusOK) + return + } + + next.ServeHTTP(w, r) + }) + }) + + r.Route("/", func(r chi.Router) { + r.Get("/download/{fileName}", controller.GetFileManage().DownloadFile) + r.Post("/upload", controller.GetFileManage().UploadFile) + r.Post("/mkdir", controller.GetFileManage().CreateDirectory) + }) return r } diff --git a/api/controller/apigateway/api_gateway_route.go b/api/controller/apigateway/api_gateway_route.go index 92777cf144..9c01deeaa6 100644 --- a/api/controller/apigateway/api_gateway_route.go +++ b/api/controller/apigateway/api_gateway_route.go @@ -207,26 +207,26 @@ func (g Struct) CreateHTTPAPIRoute(w http.ResponseWriter, r *http.Request) { routeName = strings.ReplaceAll(routeName, "/", "p-p") routeName = strings.ReplaceAll(routeName, "*", "s-s") - name := r.URL.Query().Get("name") + //name := r.URL.Query().Get("name") for _, host := range apisixRouteHTTP.Match.Hosts { labels[host] = "host" - labelSelector := host + "=host" - roueList, err := c.ApisixRoutes(tenant.Namespace).List(r.Context(), v1.ListOptions{ - LabelSelector: labelSelector, - }) - if err != nil { - logrus.Errorf("list check route failure: %v", err) - httputil.ReturnBcodeError(r, w, bcode.ErrRouteNotFound) - return - } - parts := strings.Split(name, "-") - bName := strings.Join(parts[:len(parts)-1], "-") - if roueList != nil && len(roueList.Items) > 0 && r.URL.Query().Get("intID")+roueList.Items[0].Name != bName && !defaultDomain { - logrus.Errorf("list check route failure: %v", err) - httputil.ReturnBcodeError(r, w, bcode.ErrRouteExist) - return - } + //labelSelector := host + "=host" + //roueList, err := c.ApisixRoutes(tenant.Namespace).List(r.Context(), v1.ListOptions{ + // LabelSelector: labelSelector, + //}) + //if err != nil { + // logrus.Errorf("list check route failure: %v", err) + // httputil.ReturnBcodeError(r, w, bcode.ErrRouteNotFound) + // return + //} + //parts := strings.Split(name, "-") + //bName := strings.Join(parts[:len(parts)-1], "-") + //if roueList != nil && len(roueList.Items) > 0 && r.URL.Query().Get("intID")+roueList.Items[0].Name != bName && !defaultDomain { + // logrus.Errorf("list check route failure: %v", err) + // httputil.ReturnBcodeError(r, w, bcode.ErrRouteExist) + // return + //} } apisixRouteHTTP.Name = uuid.NewV4().String()[0:8] //每次都让他变化,让 apisix controller去更新 diff --git a/api/controller/apigateway/api_gateway_service.go b/api/controller/apigateway/api_gateway_service.go index 5742abe845..801a75336c 100644 --- a/api/controller/apigateway/api_gateway_service.go +++ b/api/controller/apigateway/api_gateway_service.go @@ -83,6 +83,7 @@ func (g Struct) CreateAPIService(w http.ResponseWriter, r *http.Request) { httputil.ReturnSuccess(r, w, marshalApisixUpstream(create)) return } + logrus.Errorf("create ApisixUpstreams failure: %v", err) // 去更新 yaml get, err := c.ApisixUpstreams(tenant.Namespace).Get(r.Context(), chi.URLParam(r, "name"), v1.GetOptions{}) if err != nil { diff --git a/api/controller/service_monitor.go b/api/controller/service_monitor.go index 298a5f9488..ffd461ad9d 100644 --- a/api/controller/service_monitor.go +++ b/api/controller/service_monitor.go @@ -2,27 +2,29 @@ package controller import ( "archive/tar" + "bytes" "fmt" + "io" + "mime/multipart" + "net/http" + "os" + "path" + "path/filepath" + "regexp" + "strings" + + "github.com/goodrain/rainbond/api/client/prometheus" "github.com/goodrain/rainbond/api/proxy" "github.com/goodrain/rainbond/api/util" dbmodel "github.com/goodrain/rainbond/db/model" "github.com/goodrain/rainbond/pkg/component/k8s" "github.com/goodrain/rainbond/pkg/component/storage" "github.com/sirupsen/logrus" - "io" corev1 "k8s.io/api/core/v1" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" - cmdutil "k8s.io/kubectl/pkg/cmd/util" - "net/http" - "os" - "path" - "path/filepath" - "strings" - - "github.com/goodrain/rainbond/api/client/prometheus" "github.com/go-chi/chi" "github.com/goodrain/rainbond/api/handler" @@ -160,25 +162,6 @@ func GetFileManage() *FileManage { return fileManage } -// Get get -func (f FileManage) Get(w http.ResponseWriter, r *http.Request) { - origin := r.Header.Get("Origin") - w.Header().Add("Access-Control-Allow-Origin", origin) - w.Header().Add("Access-Control-Allow-Methods", "GET,POST,OPTIONS") - w.Header().Add("Access-Control-Allow-Credentials", "true") - w.Header().Add("Access-Control-Allow-Headers", "x-requested-with,Content-Type,X-Custom-Header") - switch r.Method { - case "GET": - w.Header().Set("Content-Type", "application/octet-stream") - f.DownloadFile(w, r) - case "POST": - f.UploadFile(w, r) - f.UploadEvent(w, r) - case "OPTIONS": - httputil.ReturnSuccess(r, w, nil) - } -} - // UploadEvent volume file upload event func (f FileManage) UploadEvent(w http.ResponseWriter, r *http.Request) { volumeName := w.Header().Get("volume_name") @@ -197,95 +180,236 @@ func (f FileManage) UploadEvent(w http.ResponseWriter, r *http.Request) { } func (f FileManage) UploadFile(w http.ResponseWriter, r *http.Request) { - w.Header().Add("volume_name", r.FormValue("volume_name")) - w.Header().Add("user_name", r.FormValue("user_name")) - w.Header().Add("tenant_id", r.FormValue("tenant_id")) - w.Header().Add("service_id", r.FormValue("service_id")) - w.Header().Add("status", "failed") + // 设置 CORS 头 + origin := r.Header.Get("Origin") + if origin == "" { + origin = "*" + } + + w.Header().Set("Access-Control-Allow-Origin", origin) + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Custom-Header") + w.Header().Set("Access-Control-Allow-Credentials", "true") + w.Header().Set("Access-Control-Max-Age", "3600") + + // 处理预检请求 + if r.Method == "OPTIONS" { + w.WriteHeader(http.StatusOK) + return + } + + logrus.Debugf("开始处理文件上传请求,Method: %s, ContentType: %s, Origin: %s", + r.Method, r.Header.Get("Content-Type"), origin) + + // 设置响应头 + w.Header().Set("volume_name", r.FormValue("volume_name")) + w.Header().Set("user_name", r.FormValue("user_name")) + w.Header().Set("tenant_id", r.FormValue("tenant_id")) + w.Header().Set("service_id", r.FormValue("service_id")) + w.Header().Set("status", "failed") + + // 获取上传参数 destPath := r.FormValue("path") podName := r.FormValue("pod_name") namespace := r.FormValue("namespace") containerName := r.FormValue("container_name") + + logrus.Debugf("上传参数: destPath=%s, podName=%s, namespace=%s, containerName=%s", + destPath, podName, namespace, containerName) + if destPath == "" { - httputil.ReturnError(r, w, 400, "Path cannot be empty") + httputil.ReturnError(r, w, 400, "目标路径不能为空") return } - reader, header, err := r.FormFile("file") + + // 解析multipart form数据 + err := r.ParseMultipartForm(32 << 20) // 32MB if err != nil { - logrus.Errorf("Failed to parse upload file: %s", err.Error()) - httputil.ReturnError(r, w, 501, "Failed to parse upload file.") + logrus.Errorf("解析multipart form失败: %v", err) + httputil.ReturnError(r, w, 400, fmt.Sprintf("解析上传数据失败: %v", err)) + return + } + + // 检查form是否为nil + if r.MultipartForm == nil { + logrus.Error("MultipartForm为空") + httputil.ReturnError(r, w, 400, "未找到上传文件") + return + } + + // 打印所有可用的form字段 + logrus.Debugf("Form字段: %+v", r.MultipartForm.Value) + logrus.Debugf("文件字段: %+v", r.MultipartForm.File) + + // 尝试获取文件 + var files []*multipart.FileHeader + if formFiles := r.MultipartForm.File["files"]; len(formFiles) > 0 { + files = formFiles + } else if formFiles := r.MultipartForm.File["file"]; len(formFiles) > 0 { + files = formFiles + } + + if len(files) == 0 { + logrus.Error("未找到上传文件") + httputil.ReturnError(r, w, 400, "未找到上传文件") return } - defer reader.Close() - w.Header().Add("file_name", header.Filename) - srcPath := path.Join("./", header.Filename) - file, err := os.OpenFile(srcPath, os.O_WRONLY|os.O_CREATE, 0644) + + // 创建临时目录存储上传的文件 + tempDir, err := os.MkdirTemp("", "upload-*") if err != nil { - logrus.Errorf("upload file open %v failure: %v", header.Filename, err.Error()) - httputil.ReturnError(r, w, 502, "Failed to open file: "+err.Error()) - } - defer os.Remove(srcPath) - defer file.Close() - if _, err := io.Copy(file, reader); err != nil { - logrus.Errorf("upload file write %v failure: %v", srcPath, err.Error()) - httputil.ReturnError(r, w, 503, "Failed to write file: "+err.Error()) + logrus.Errorf("创建临时目录失败: %v", err) + httputil.ReturnError(r, w, 500, fmt.Sprintf("创建临时目录失败: %v", err)) return } + defer os.RemoveAll(tempDir) + + // 保存文件到临时目录 + for _, fileHeader := range files { + logrus.Debugf("处理文件: %s", fileHeader.Filename) + + // 获取文件的相对路径 + var relativePath string + contentDisposition := fileHeader.Header.Get("Content-Disposition") + filenameRegex := regexp.MustCompile(`filename="([^"]+)"`) + matches := filenameRegex.FindStringSubmatch(contentDisposition) + if len(matches) > 1 { + relativePath = matches[1] // 使用正则表达式从Content-Disposition中提取filename + logrus.Debugf("使用 Content-Disposition 中的 filename: %s", relativePath) + } else if webkitPath := fileHeader.Header.Get("webkitRelativePath"); webkitPath != "" { + relativePath = webkitPath + logrus.Debugf("使用 webkitRelativePath: %s", relativePath) + } else { + relativePath = fileHeader.Filename + logrus.Debugf("使用文件名作为路径: %s", relativePath) + } + + file, err := fileHeader.Open() + if err != nil { + logrus.Errorf("打开上传文件失败: %v", err) + httputil.ReturnError(r, w, 500, fmt.Sprintf("打开上传文件失败: %v", err)) + return + } + defer file.Close() + + // 构建临时目录中的完整路径 + tempPath := filepath.Join(tempDir, relativePath) + logrus.Debugf("临时文件路径: %s", tempPath) + + // 确保目标目录存在 + if err := os.MkdirAll(filepath.Dir(tempPath), 0755); err != nil { + logrus.Errorf("创建目录失败: %v", err) + httputil.ReturnError(r, w, 500, fmt.Sprintf("创建目录失败: %v", err)) + return + } + + dst, err := os.Create(tempPath) + if err != nil { + logrus.Errorf("创建文件失败: %v", err) + httputil.ReturnError(r, w, 500, fmt.Sprintf("创建文件失败: %v", err)) + return + } + defer dst.Close() + + if _, err = io.Copy(dst, file); err != nil { + logrus.Errorf("保存文件失败: %v", err) + httputil.ReturnError(r, w, 500, fmt.Sprintf("保存文件失败: %v", err)) + return + } + logrus.Debugf("成功保存文件: %s", tempPath) + } + + // 上传文件到容器 + logrus.Debugf("开始上传文件到容器,源目录: %s,目标路径: %s", tempDir, destPath) + // 获取第一个文件的目录结构信息来判断是单文件还是目录上传 + firstFile := files[0] + if webkitPath := firstFile.Header.Get("webkitRelativePath"); webkitPath != "" { + // 如果是目录上传(有webkitRelativePath),使用临时目录内容 + err = f.AppFileUpload(containerName, podName, tempDir, destPath, namespace) + } else if len(files) == 1 { + // 如果是单文件上传 + uploadPath := filepath.Join(tempDir, firstFile.Filename) + err = f.AppFileUpload(containerName, podName, uploadPath, destPath, namespace) + } else { + // 多文件上传 + err = f.AppFileUpload(containerName, podName, tempDir, destPath, namespace) + } - err = f.AppFileUpload(containerName, podName, srcPath, destPath, namespace) if err != nil { - logrus.Errorf("upload file %v to %v %v failure: %v", header.Filename, podName, destPath, err.Error()) - httputil.ReturnError(r, w, 503, "Failed to write file: "+err.Error()) + logrus.Errorf("上传到容器失败: %v", err) + httputil.ReturnError(r, w, 500, fmt.Sprintf("上传失败: %v", err)) return } + + logrus.Debug("文件上传成功") w.Header().Set("status", "success") + httputil.ReturnSuccess(r, w, nil) } func (f FileManage) DownloadFile(w http.ResponseWriter, r *http.Request) { + logrus.Debugf("接收到文件下载请求: Method=%s, ContentType=%s", r.Method, r.Header.Get("Content-Type")) + + // 设置响应头 + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("volume_name", r.FormValue("volume_name")) + w.Header().Set("user_name", r.FormValue("user_name")) + w.Header().Set("tenant_id", r.FormValue("tenant_id")) + w.Header().Set("service_id", r.FormValue("service_id")) + w.Header().Set("status", "failed") + + // 获取请求参数 podName := r.FormValue("pod_name") p := r.FormValue("path") namespace := r.FormValue("namespace") fileName := strings.TrimSpace(chi.URLParam(r, "fileName")) + containerName := r.FormValue("container_name") + + logrus.Debugf("下载文件参数: podName=%s, path=%s, namespace=%s, fileName=%s, containerName=%s", + podName, p, namespace, fileName, containerName) + + if fileName == "" { + logrus.Error("文件名为空") + httputil.ReturnError(r, w, 400, "文件名不能为空") + return + } filePath := path.Join(p, fileName) - containerName := r.FormValue("container_name") + logrus.Debugf("完整文件路径: %s", filePath) err := f.AppFileDownload(containerName, podName, filePath, namespace) if err != nil { - logrus.Errorf("downloading file from Pod failure: %v", err) - http.Error(w, "Error downloading file from Pod", http.StatusInternalServerError) + logrus.Errorf("从Pod下载文件失败: %v", err) + httputil.ReturnError(r, w, 500, fmt.Sprintf("下载文件失败: %v", err)) return } - defer os.Remove(path.Join("./", fileName)) + + defer func() { + if err := os.Remove(path.Join("./", fileName)); err != nil { + logrus.Errorf("删除临时文件失败: %v", err) + } + }() + + // 设置成功状态和文件下载头 + w.Header().Set("status", "success") w.Header().Set("Content-Disposition", "attachment;filename="+fileName) - storage.Default().StorageCli.ServeFile(w, r, path.Join("./", fileName)) + + logrus.Debugf("开始传输文件: %s", fileName) + http.ServeFile(w, r, fileName) } -func (f FileManage) AppFileUpload(containerName, podName, srcPath, destPath, namespace string) error { - reader, writer := io.Pipe() - if destPath != "/" && strings.HasSuffix(string(destPath[len(destPath)-1]), "/") { - destPath = destPath[:len(destPath)-1] - } - destPath = destPath + "/" + path.Base(srcPath) - go func() { - defer writer.Close() - cmdutil.CheckErr(cpMakeTar(srcPath, destPath, writer)) - }() - var cmdArr []string - cmdArr = []string{"tar", "-xmf", "-"} - destDir := path.Dir(destPath) - if len(destDir) > 0 { - cmdArr = append(cmdArr, "-C", destDir) - } - req := f.clientset.CoreV1().RESTClient().Post(). +func (f FileManage) AppFileDownload(containerName, podName, filePath, namespace string) error { + reader, outStream := io.Pipe() + // Check if the file exists first + checkCmd := []string{"test", "-e", filePath} + req := f.clientset.CoreV1().RESTClient().Get(). Resource("pods"). Name(podName). Namespace(namespace). SubResource("exec"). VersionedParams(&corev1.PodExecOptions{ Container: containerName, - Command: cmdArr, - Stdin: true, + Command: checkCmd, + Stdin: false, Stdout: true, Stderr: true, TTY: false, @@ -293,19 +417,20 @@ func (f FileManage) AppFileUpload(containerName, podName, srcPath, destPath, nam exec, err := remotecommand.NewSPDYExecutor(f.config, "POST", req.URL()) if err != nil { - return err + return fmt.Errorf("failed to create executor: %v", err) } - return exec.Stream(remotecommand.StreamOptions{ - Stdin: reader, - Stdout: os.Stdout, - Stderr: os.Stderr, - Tty: false, + + var checkStdout, checkStderr bytes.Buffer + err = exec.Stream(remotecommand.StreamOptions{ + Stdout: &checkStdout, + Stderr: &checkStderr, }) -} + if err != nil { + return fmt.Errorf("file not found: %s", filePath) + } -func (f FileManage) AppFileDownload(containerName, podName, filePath, namespace string) error { - reader, outStream := io.Pipe() - req := f.clientset.CoreV1().RESTClient().Get(). + // If file exists, proceed with tar + req = f.clientset.CoreV1().RESTClient().Get(). Resource("pods"). Name(podName). Namespace(namespace). @@ -319,10 +444,11 @@ func (f FileManage) AppFileDownload(containerName, podName, filePath, namespace TTY: false, }, scheme.ParameterCodec) - exec, err := remotecommand.NewSPDYExecutor(f.config, "POST", req.URL()) + exec, err = remotecommand.NewSPDYExecutor(f.config, "POST", req.URL()) if err != nil { - return err + return fmt.Errorf("failed to create executor: %v", err) } + go func() { defer outStream.Close() err = exec.Stream(remotecommand.StreamOptions{ @@ -331,35 +457,141 @@ func (f FileManage) AppFileDownload(containerName, podName, filePath, namespace Stderr: os.Stderr, Tty: false, }) - cmdutil.CheckErr(err) + if err != nil { + logrus.Errorf("stream error: %v", err) + } }() + prefix := getPrefix(filePath) prefix = path.Clean(prefix) destPath := path.Join("./", path.Base(prefix)) err = unTarAll(reader, destPath, prefix) if err != nil { - return err + return fmt.Errorf("failed to untar: %v", err) } return nil } -func cpMakeTar(srcPath string, destPath string, out io.Writer) error { - tw := tar.NewWriter(out) +func (f FileManage) AppFileUpload(containerName, podName, srcPath, destPath, namespace string) error { + logrus.Debugf("开始上传目录/文件: 源路径=%s, 目标路径=%s", srcPath, destPath) - defer func() { - if err := tw.Close(); err != nil { - logrus.Errorf("Error closing tar writer: %v\n", err) + // 检查源路径是否存在 + srcInfo, err := os.Stat(srcPath) + if err != nil { + return fmt.Errorf("源路径检查失败: %v", err) + } + + reader, writer := io.Pipe() + + // 在goroutine中处理tar打包 + go func() { + defer writer.Close() + + // 如果是目录,使用完整目录路径 + if srcInfo.IsDir() { + logrus.Debugf("正在打包目录: %s", srcPath) + err = cpMakeTar(srcPath, destPath, writer) + } else { + logrus.Debugf("正在打包文件: %s", srcPath) + err = cpMakeTar(filepath.Dir(srcPath), destPath, writer) + } + + if err != nil { + logrus.Errorf("tar打包失败: %v", err) + writer.CloseWithError(err) + return } }() + // 构建在容器中解压的命令 + var cmdArr []string + cmdArr = []string{"tar", "-xmf", "-"} + + // 确保目标路径存在 + if len(destPath) > 0 { + // 先创建目标目录 + mkdirCmd := []string{"mkdir", "-p", destPath} + mkdirReq := f.clientset.CoreV1().RESTClient().Post(). + Resource("pods"). + Name(podName). + Namespace(namespace). + SubResource("exec"). + VersionedParams(&corev1.PodExecOptions{ + Container: containerName, + Command: mkdirCmd, + Stdin: false, + Stdout: true, + Stderr: true, + TTY: false, + }, scheme.ParameterCodec) + + mkdirExec, err := remotecommand.NewSPDYExecutor(f.config, "POST", mkdirReq.URL()) + if err != nil { + return fmt.Errorf("创建目标目录执行器失败: %v", err) + } + + if err := mkdirExec.Stream(remotecommand.StreamOptions{ + Stdout: os.Stdout, + Stderr: os.Stderr, + }); err != nil { + return fmt.Errorf("在容器中创建目录失败: %v", err) + } + + cmdArr = append(cmdArr, "-C", destPath) + } + + // 执行解压命令 + req := f.clientset.CoreV1().RESTClient().Post(). + Resource("pods"). + Name(podName). + Namespace(namespace). + SubResource("exec"). + VersionedParams(&corev1.PodExecOptions{ + Container: containerName, + Command: cmdArr, + Stdin: true, + Stdout: true, + Stderr: true, + TTY: false, + }, scheme.ParameterCodec) + + exec, err := remotecommand.NewSPDYExecutor(f.config, "POST", req.URL()) + if err != nil { + return fmt.Errorf("创建解压执行器失败: %v", err) + } + + logrus.Debug("开始在容器中解压文件") + err = exec.Stream(remotecommand.StreamOptions{ + Stdin: reader, + Stdout: os.Stdout, + Stderr: os.Stderr, + Tty: false, + }) + if err != nil { + return fmt.Errorf("在容器中解压失败: %v", err) + } + + logrus.Debug("文件/目录上传完成") + return nil +} + +func cpMakeTar(srcPath string, destPath string, out io.Writer) error { + tw := tar.NewWriter(out) + defer tw.Close() + // 获取源路径的信息 srcInfo, err := os.Stat(srcPath) if err != nil { return fmt.Errorf("failed to get info for %s: %v", srcPath, err) } + // 确定基础路径 basePath := srcPath if srcInfo.IsDir() { + // 如果是目录,使用目录本身作为基础路径 + basePath = srcPath + } else { + // 如果是文件,使用父目录作为基础路径 basePath = filepath.Dir(srcPath) } @@ -368,34 +600,53 @@ func cpMakeTar(srcPath string, destPath string, out io.Writer) error { return err } - // 获取相对于源路径的相对路径 + // 获取相对路径 relPath, err := filepath.Rel(basePath, path) if err != nil { return err } - // 创建 tar 归档文件的文件头信息 - hdr, err := tar.FileInfoHeader(info, relPath) + // 如果是根目录,跳过 + if relPath == "." && srcInfo.IsDir() { + return nil + } + + // 创建header + hdr, err := tar.FileInfoHeader(info, "") if err != nil { return fmt.Errorf("failed to create header for %s: %v", path, err) } - // 写入文件头信息到 tar 归档 + // 修改header名称,使用相对路径 + if srcInfo.IsDir() { + // 如果源路径是目录,保持相对路径结构 + hdr.Name = relPath + } else { + // 如果源路径是文件,直接使用文件名 + hdr.Name = filepath.Base(srcPath) + } + + if info.IsDir() { + hdr.Name += "/" + } + + logrus.Debugf("Adding to tar: %s", hdr.Name) + + // 写入header if err := tw.WriteHeader(hdr); err != nil { return fmt.Errorf("failed to write header for %s: %v", path, err) } - if info.Mode().IsRegular() { - // 如果是普通文件,则将文件内容写入到 tar 归档 + // 如果是普通文件,写入文件内容 + if !info.IsDir() { file, err := os.Open(path) if err != nil { - return fmt.Errorf("failed to open %s: %v", path, err) + return err } defer file.Close() - _, err = io.Copy(tw, file) - if err != nil { - return fmt.Errorf("failed to write file %s to tar: %v", path, err) + if _, err := io.Copy(tw, file); err != nil { + return fmt.Errorf("failed to write file %s: %v", path, err) } } @@ -468,3 +719,71 @@ func unTarAll(reader io.Reader, destDir, prefix string) error { func getPrefix(file string) string { return strings.TrimLeft(file, "/") } + +// CreateDirectory 在容器内创建目录 +func (f FileManage) CreateDirectory(w http.ResponseWriter, r *http.Request) { + logrus.Debugf("接收到创建目录请求: Method=%s, ContentType=%s", r.Method, r.Header.Get("Content-Type")) + + // 获取请求参数 + podName := r.FormValue("pod_name") + namespace := r.FormValue("namespace") + containerName := r.FormValue("container_name") + dirPath := r.FormValue("path") + + logrus.Debugf("创建目录参数: podName=%s, namespace=%s, containerName=%s, dirPath=%s", + podName, namespace, containerName, dirPath) + + if dirPath == "" { + logrus.Error("目录路径为空") + httputil.ReturnError(r, w, 400, "目录路径不能为空") + return + } + + // 构建在容器中创建目录的命令 + req := f.clientset.CoreV1().RESTClient().Post(). + Resource("pods"). + Name(podName). + Namespace(namespace). + SubResource("exec"). + VersionedParams(&corev1.PodExecOptions{ + Container: containerName, + Command: []string{"mkdir", "-p", dirPath}, + Stdin: false, + Stdout: true, + Stderr: true, + TTY: false, + }, scheme.ParameterCodec) + + exec, err := remotecommand.NewSPDYExecutor(f.config, "POST", req.URL()) + if err != nil { + logrus.Errorf("创建目录执行器失败: %v", err) + httputil.ReturnError(r, w, 500, fmt.Sprintf("创建目录失败: %v", err)) + return + } + + // 创建缓冲区来捕获输出 + var stdout, stderr bytes.Buffer + err = exec.Stream(remotecommand.StreamOptions{ + Stdin: nil, + Stdout: &stdout, + Stderr: &stderr, + Tty: false, + }) + + // 记录命令输出 + if stdout.Len() > 0 { + logrus.Debugf("命令输出: %s", stdout.String()) + } + if stderr.Len() > 0 { + logrus.Debugf("错误输出: %s", stderr.String()) + } + + if err != nil { + logrus.Errorf("在容器中创建目录失败: %v", err) + httputil.ReturnError(r, w, 500, fmt.Sprintf("创建目录失败: %v", err)) + return + } + + logrus.Debugf("目录创建成功: %s", dirPath) + httputil.ReturnSuccess(r, w, nil) +} diff --git a/api/handler/service.go b/api/handler/service.go index 0cb77b1927..9aafcb45d8 100644 --- a/api/handler/service.go +++ b/api/handler/service.go @@ -23,6 +23,13 @@ import ( "context" "encoding/json" "fmt" + "io" + "net/http" + "os" + "strconv" + "strings" + "time" + apisixversioned "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned" "github.com/goodrain/rainbond/builder/sources/registry" "github.com/goodrain/rainbond/pkg/component/grpc" @@ -31,17 +38,11 @@ import ( "github.com/goodrain/rainbond/pkg/component/mq" "github.com/goodrain/rainbond/pkg/component/prom" "github.com/goodrain/rainbond/util/constants" - "io" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" v1 "kubevirt.io/api/core/v1" "kubevirt.io/client-go/kubecli" - "net/http" - "os" - "strconv" - "strings" - "time" "github.com/goodrain/rainbond/api/client/prometheus" apimodel "github.com/goodrain/rainbond/api/model" @@ -3059,30 +3060,42 @@ func TransStatus(eStatus string) string { func (s *ServiceAction) FileManageInfo(serviceID, podName, tarPath, namespace string) ([]apimodel.FileInfo, error) { var fileInfos []apimodel.FileInfo + + // 获取服务信息 service, err := db.GetManager().TenantServiceDao().GetServiceByID(serviceID) if err != nil { return nil, err } containerName := service.K8sComponentName - output, err := s.executeCommand(podName, namespace, containerName, []string{"ls", "-l", tarPath}) + + // 执行 shell 命令 `ls -p -1`,列出指定路径下的文件和目录 + output, err := s.executeCommand(podName, namespace, containerName, []string{"ls", "-p", "-1", tarPath}) if err != nil { return nil, err } + + // 按行解析命令输出 files := strings.Split(output, "\n") for _, file := range files { - fileElements := strings.Split(file, " ") - if strings.HasPrefix(fileElements[0], "d") { + file = strings.TrimSpace(file) // 去除行首尾空格 + if len(file) == 0 { + continue // 跳过空行 + } + + // 判断是否为目录(以 "/" 结尾) + if strings.HasSuffix(file, "/") { fileInfos = append(fileInfos, apimodel.FileInfo{ - Title: fileElements[len(fileElements)-1], - IsLeaf: true, + Title: strings.TrimSuffix(file, "/"), // 去掉末尾的 "/" + IsLeaf: true, // 目录 }) - } else if strings.HasPrefix(fileElements[0], "-") { + } else { fileInfos = append(fileInfos, apimodel.FileInfo{ - Title: fileElements[len(fileElements)-1], - IsLeaf: false, + Title: file, // 文件名 + IsLeaf: false, // 文件 }) } } + return fileInfos, nil } diff --git a/builder/exector/build_from_vm.go b/builder/exector/build_from_vm.go index 8fd4b6927a..a81f1eee1f 100644 --- a/builder/exector/build_from_vm.go +++ b/builder/exector/build_from_vm.go @@ -111,7 +111,21 @@ func (v *VMBuildItem) RunVMBuild() error { } func downloadFile(downPath, url string, Logger event.Logger) error { - rsp, err := http.Get(url) + // 创建一个 HTTP client 和 request + client := &http.Client{} + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + + // 添加请求头,例如设置 User-Agent + req.Header.Set("User-Agent", "MyCustomDownloader/1.0") + + // 发送请求 + rsp, err := client.Do(req) + if err != nil { + return err + } defer func() { _ = rsp.Body.Close() }() @@ -143,7 +157,7 @@ func downloadFile(downPath, url string, Logger event.Logger) error { _, err = io.Copy(f, myDownloader) if err != nil { - downError := fmt.Sprintf("download vm image %v failre: %v", url, err.Error()) + downError := fmt.Sprintf("download vm image %v failure: %v", url, err.Error()) Logger.Error(downError, map[string]string{"step": "builder-exector", "status": "failure"}) } return err diff --git a/builder/parser/vm_service.go b/builder/parser/vm_service.go index c4ff255df8..c8eae250ff 100644 --- a/builder/parser/vm_service.go +++ b/builder/parser/vm_service.go @@ -19,14 +19,15 @@ package parser import ( - "github.com/goodrain/rainbond/builder/parser/discovery" - "github.com/goodrain/rainbond/event" - "github.com/sirupsen/logrus" "io/ioutil" "net/http" "path" "path/filepath" "strings" + + "github.com/goodrain/rainbond/builder/parser/discovery" + "github.com/goodrain/rainbond/event" + "github.com/sirupsen/logrus" ) // VMServiceParse is one of the implematation of parser.Parser @@ -68,9 +69,19 @@ func (t *VMServiceParse) Parse() ParseErrorList { } fileExt = path.Ext(fileInfoList[0].Name()) } else { - rsp, err := http.Get(t.sourceBody) + req, err := http.NewRequest("GET", t.sourceBody, nil) + if err != nil { + logrus.Errorf("创建 HTTP 请求失败 %v: %v", t.sourceBody, err) + t.errappend(Errorf(FatalError, "create http request failed")) + return t.errors + } + + req.Header.Add("User-Agent", "RainbondBuilder/1.0") + req.Header.Add("Accept", "application/json") + + rsp, err := http.DefaultClient.Do(req) if err != nil { - logrus.Errorf("http get %v failure: %v", t.sourceBody, err) + logrus.Errorf("HTTP GET 请求失败 %v: %v", t.sourceBody, err) t.errappend(Errorf(FatalError, "http get failure")) return t.errors } @@ -81,7 +92,9 @@ func (t *VMServiceParse) Parse() ParseErrorList { return t.errors } defer func() { - _ = rsp.Body.Close() + if err := rsp.Body.Close(); err != nil { + logrus.Errorf("关闭响应体失败: %v", err) + } }() baseURL := filepath.Base(t.sourceBody) diff --git a/go.mod b/go.mod index a690ebdd63..8976be0210 100644 --- a/go.mod +++ b/go.mod @@ -103,6 +103,7 @@ require ( github.com/apache/apisix-ingress-controller v1.7.1 github.com/coreos/etcd v3.3.13+incompatible github.com/dustin/go-humanize v1.0.0 + github.com/gin-gonic/gin v1.9.1 github.com/google/go-containerregistry v0.5.1 github.com/google/uuid v1.3.0 github.com/grafana/pyroscope-go v1.2.0 @@ -130,8 +131,10 @@ require ( github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/blang/semver/v4 v4.0.0 // indirect + github.com/bytedance/sonic v1.9.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe // indirect github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b // indirect github.com/cockroachdb/apd/v2 v2.0.1 // indirect @@ -160,6 +163,7 @@ require ( github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-gorp/gorp/v3 v3.0.2 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect @@ -171,6 +175,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/gobwas/glob v0.2.3 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/btree v1.1.2 // indirect @@ -187,6 +192,7 @@ require ( github.com/jmoiron/sqlx v1.3.4 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/k8snetworkplumbingwg/network-attachment-definition-client v0.0.0-20191119172530-79f836b90111 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/kr/fs v0.1.0 // indirect github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect @@ -221,6 +227,7 @@ require ( github.com/openshift/client-go v0.0.0-20210112165513-ebc401615f47 // indirect github.com/openshift/custom-resource-status v1.1.2 // indirect github.com/pelletier/go-toml v1.9.4 // indirect + github.com/pelletier/go-toml/v2 v2.0.9 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect @@ -233,6 +240,8 @@ require ( github.com/src-d/gcfg v1.4.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect @@ -241,6 +250,7 @@ require ( go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.25.0 // indirect + golang.org/x/arch v0.3.0 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect @@ -281,7 +291,7 @@ require ( golang.org/x/tools v0.12.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20230815205213-6bfd019c3878 // indirect - k8s.io/kubectl v0.24.0 + k8s.io/kubectl v0.24.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect ) diff --git a/go.sum b/go.sum index b2abf983f7..ed9aab66f2 100644 --- a/go.sum +++ b/go.sum @@ -301,6 +301,9 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -325,6 +328,9 @@ github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3/ github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -596,12 +602,15 @@ github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/static v0.0.0-20200815103939-31fb0c56a3d1/go.mod h1:VhW/Ch/3FhimwZb8Oj+qJmdMmoB8r7lmJ5auRjm50oQ= github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= @@ -801,6 +810,8 @@ github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXs github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -1246,6 +1257,9 @@ github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0N github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -1645,6 +1659,8 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -1864,6 +1880,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -1907,6 +1924,8 @@ github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twinj/uuid v1.0.0 h1:fzz7COZnDrXGTAOHGuUGYd6sG+JMq+AoE7+Jlu0przk= github.com/twinj/uuid v1.0.0/go.mod h1:mMgcE1RHFUFqe5AfiwlINXisXfDGro23fWdPUfOMjRY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/uber/jaeger-client-go v2.20.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-client-go v2.23.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-client-go v2.23.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= @@ -1922,6 +1941,8 @@ github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2t github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU= github.com/ugorji/go/codec v1.2.1/go.mod h1:s/WxCRi46t8rA+fowL40EnmD7ec0XhR7ZypxeBNdzsM= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= @@ -2102,6 +2123,9 @@ go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -2456,6 +2480,7 @@ golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/worker/appm/conversion/conversion_test.go b/worker/appm/conversion/conversion_test.go deleted file mode 100644 index a6d44b2e97..0000000000 --- a/worker/appm/conversion/conversion_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// RAINBOND, Application Management Platform -// Copyright (C) 2014-2017 Goodrain Co., Ltd. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. For any non-GPL usage of Rainbond, -// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. -// must be obtained first. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package conversion - -import ( - gomock "github.com/golang/mock/gomock" - "github.com/goodrain/rainbond/db" - "github.com/goodrain/rainbond/db/dao" - "github.com/goodrain/rainbond/db/model" - "github.com/goodrain/rainbond/util" - "github.com/goodrain/rainbond/worker/appm/types/v1" - "testing" -) - -func TestTenantServiceBase(t *testing.T) { - t.Run("third-party service", func(t *testing.T) { - as := &v1.AppService{} - as.ServiceID = util.NewUUID() - as.TenantID = util.NewUUID() - as.TenantName = "abcdefg" - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - dbm := db.NewMockManager(ctrl) - // TenantServiceDao - tenantServiceDao := dao.NewMockTenantServiceDao(ctrl) - tenantService := &model.TenantServices{ - TenantID: as.TenantID, - ServiceID: as.ServiceID, - Kind: model.ServiceKindThirdParty.String(), - } - tenantServiceDao.EXPECT().GetServiceByID(as.ServiceID).Return(tenantService, nil) - dbm.EXPECT().TenantServiceDao().Return(tenantServiceDao) - // TenantDao - tenantDao := dao.NewMockTenantDao(ctrl) - tenant := &model.Tenants{ - UUID: as.TenantID, - Name: as.TenantName, - } - tenantDao.EXPECT().GetTenantByUUID(as.TenantID).Return(tenant, nil) - dbm.EXPECT().TenantDao().Return(tenantDao) - if err := TenantServiceBase(as, dbm); err != nil { - t.Errorf("Unexpected error: %v", err) - } - }) -} diff --git a/worker/appm/conversion/version.go b/worker/appm/conversion/version.go index f2bfb300c1..713270fa57 100644 --- a/worker/appm/conversion/version.go +++ b/worker/appm/conversion/version.go @@ -22,10 +22,6 @@ import ( "context" "encoding/json" "fmt" - v2 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2" - "github.com/goodrain/rainbond/pkg/component/k8s" - "github.com/twinj/uuid" - kubevirtv1 "kubevirt.io/api/core/v1" "net" "os" "sort" @@ -33,6 +29,12 @@ import ( "strings" "time" + v2 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2" + "github.com/goodrain/rainbond/pkg/component/k8s" + "github.com/twinj/uuid" + "k8s.io/apimachinery/pkg/api/resource" + kubevirtv1 "kubevirt.io/api/core/v1" + "github.com/goodrain/rainbond/builder/sources" "github.com/goodrain/rainbond/builder" @@ -199,7 +201,10 @@ func TenantServiceVersion(as *v1.AppService, dbmanager db.Manager) error { Domain: kubevirtv1.DomainSpec{ Resources: reource, CPU: &kubevirtv1.CPU{ - Cores: 2, + Cores: uint32(as.ContainerCPU / 1000), + }, + Memory: &kubevirtv1.Memory{ + Guest: resource.NewScaledQuantity(int64(as.ContainerMemory), resource.Mega), }, Machine: &kubevirtv1.Machine{Type: "q35"}, Devices: kubevirtv1.Devices{ @@ -207,7 +212,7 @@ func TenantServiceVersion(as *v1.AppService, dbmanager db.Manager) error { Interfaces: []kubevirtv1.Interface{ { Name: "default", - Model: "e1000", + Model: "virtio", InterfaceBindingMethod: kubevirtv1.InterfaceBindingMethod{ Masquerade: &kubevirtv1.InterfaceMasquerade{}, },