Files
1Panel/agent/app/service/vm-iso.go
ssongliu c06c4e338b feat(vm): enable community virtual machines and migrate xpack data (#14279)
* feat(vm): enable community virtual machines and migrate xpack data

* fix(vm): stop ISO download polling on failure or cancellation

* fix(vm): respect deleteFile when deleting failed ISO imports
2026-11-02 18:22:58 +08:00

172 lines
5.2 KiB
Go

package service
import (
"os"
"path/filepath"
"strings"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/jinzhu/copier"
)
func (v *VMService) SearchIso(req dto.SearchPageWithType) (int64, interface{}, error) {
options := []repo.DBOption{repo.WithOrderDesc("created_at"), repo.WithOrderDesc("id")}
if req.Type != "" {
options = append(options, repo.WithByType(req.Type))
}
if req.Info != "" {
options = append(options, repo.WithByLikeName(req.Info))
}
total, isos, err := vmRepo.PageIso(req.Page, req.PageSize, options...)
if err != nil {
return 0, nil, err
}
dtoIsos := make([]dto.VMIsoInfo, 0, len(isos))
for _, iso := range isos {
var item dto.VMIsoInfo
if err := copier.Copy(&item, &iso); err != nil {
return 0, nil, buserr.WithDetail("ErrStructTransform", err.Error(), nil)
}
item.IsExist, item.Size = loadVMResourceFileInfo(item.Path)
dtoIsos = append(dtoIsos, item)
}
return total, dtoIsos, err
}
func (v *VMService) ListIso() ([]dto.VMIsoInfo, error) {
isos, err := vmRepo.ListIsos()
if err != nil {
return nil, err
}
dtoIsos := make([]dto.VMIsoInfo, 0, len(isos))
for _, iso := range isos {
if iso.Status == "uploading" || iso.Status == "downloading" || iso.Status == "failed" {
continue
}
var item dto.VMIsoInfo
if err := copier.Copy(&item, &iso); err != nil {
return nil, buserr.WithDetail("ErrStructTransform", err.Error(), nil)
}
item.IsExist, item.Size = loadVMResourceFileInfo(item.Path)
dtoIsos = append(dtoIsos, item)
}
return dtoIsos, nil
}
func (v *VMService) CreateIso(req dto.VMIsoCreate) error {
isoItem, _ := vmRepo.GetIso(repo.WithByName(req.Name))
if isoItem.ID != 0 {
return buserr.New("ErrRecordExist")
}
exists, _ := loadVMResourceFileInfo(req.Path)
if !exists {
return buserr.WithDetail("ErrResourceNotExists", "Resource file does not exist", nil)
}
return vmRepo.CreateIso(&model.VMIso{
Type: req.Type, Name: req.Name, Path: req.Path, Status: "ready", Description: req.Description,
})
}
func (v *VMService) StartIsoImport(req dto.VMIsoImportStart) (uint, error) {
isoItem, _ := vmRepo.GetIso(repo.WithByName(req.Name))
if isoItem.ID != 0 {
return 0, buserr.New("ErrRecordExist")
}
item := model.VMIso{Type: req.Type, Name: req.Name, Path: req.Path, Status: req.Status, TempFileID: req.TempFileID, TempFileType: req.TempFileType, Description: req.Description}
if err := vmRepo.CreateIso(&item); err != nil {
return 0, err
}
return item.ID, nil
}
func (v *VMService) UpdateIsoImport(req dto.VMIsoImportUpdate) error {
isoItem, err := vmRepo.GetIso(repo.WithByID(req.ID))
if err != nil || isoItem.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
if isoItem.Status != "uploading" && isoItem.Status != "downloading" {
return buserr.WithDetail("ErrVMInvalidInput", "ISO import is not active", nil)
}
if (isoItem.Status == "uploading" && req.Status == "downloading") ||
(isoItem.Status == "downloading" && req.Status == "uploading") {
return buserr.WithDetail("ErrVMInvalidInput", "ISO import type cannot be changed", nil)
}
vars := map[string]interface{}{"status": req.Status, "error": req.Error}
if req.TempFileID != "" {
vars["temp_file_id"] = req.TempFileID
}
if req.TempFileType != "" {
vars["temp_file_type"] = req.TempFileType
}
if req.Status == "ready" {
exists, _ := loadVMResourceFileInfo(isoItem.Path)
if !exists {
return buserr.WithDetail("ErrResourceNotExists", "Resource file does not exist", nil)
}
vars["error"] = ""
}
if req.Status == "ready" {
vars["temp_file_id"] = ""
vars["temp_file_type"] = ""
}
return vmRepo.UpdateIso(req.ID, vars)
}
func (v *VMService) UpdateIso(req dto.VMIsoUpdate) error {
isoItem, err := vmRepo.GetIso(repo.WithByID(req.ID))
if err != nil {
return err
}
if isoItem.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
exists, _ := loadVMResourceFileInfo(req.Path)
if !exists {
return buserr.WithDetail("ErrResourceNotExists", "Resource file does not exist", nil)
}
return vmRepo.UpdateIso(req.ID, map[string]interface{}{
"name": req.Name, "type": req.Type, "path": req.Path, "description": req.Description,
})
}
func (v *VMService) DeleteIso(req dto.VMIsoDelete) error {
isoItem, err := vmRepo.GetIso(repo.WithByID(req.ID))
if err != nil {
return err
}
if isoItem.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
if req.DeleteFile && isoItem.Path != "" {
exists, _ := loadVMResourceFileInfo(isoItem.Path)
if exists {
if err := os.Remove(isoItem.Path); err != nil && !os.IsNotExist(err) {
return err
}
}
}
if isoItem.TempFileType == "upload" && validTempFileID(isoItem.TempFileID) {
tmpPath := filepath.Join(global.CONF.Base.InstallDir, "1panel", "tmp", "upload", isoItem.TempFileID)
if err := os.RemoveAll(tmpPath); err != nil {
return err
}
}
return vmRepo.DeleteIso(repo.WithByID(req.ID))
}
func loadVMResourceFileInfo(filePath string) (bool, int64) {
info, err := os.Stat(filePath)
if err != nil || info.IsDir() {
return false, 0
}
return true, info.Size()
}
func validTempFileID(value string) bool {
return value != "" && filepath.Base(value) == value && !strings.ContainsAny(value, `/\\`)
}