diff --git a/internal/db/migrations/0004_image_iso_path.sql b/internal/db/migrations/0004_image_iso_path.sql new file mode 100644 index 0000000..9b75af0 --- /dev/null +++ b/internal/db/migrations/0004_image_iso_path.sql @@ -0,0 +1 @@ +ALTER TABLE images ADD COLUMN iso_path TEXT NOT NULL DEFAULT ''; diff --git a/internal/image/extract.go b/internal/image/extract.go index fd29f0c..a254310 100644 --- a/internal/image/extract.go +++ b/internal/image/extract.go @@ -13,6 +13,7 @@ import ( type ExtractResult struct { KernelFilename string InitrdFilename string + ISOFilename string } type ProgressFunc func(stage string, detail string) @@ -33,22 +34,21 @@ func ExtractFromISOWithProgress(r io.Reader, destDir string, progress ProgressFu report("receiving", "Writing ISO to disk...") - tmp, err := os.CreateTemp(filepath.Dir(destDir), "iso-upload-*.tmp") + isoPath := filepath.Join(destDir, "original.iso") + tmp, err := os.Create(isoPath) if err != nil { - return nil, fmt.Errorf("create temp file: %w", err) + return nil, fmt.Errorf("create ISO file: %w", err) } - tmpPath := tmp.Name() - defer os.Remove(tmpPath) if _, err := io.Copy(tmp, r); err != nil { tmp.Close() - return nil, fmt.Errorf("write ISO to temp file: %w", err) + return nil, fmt.Errorf("write ISO to disk: %w", err) } tmp.Close() report("parsing", "Parsing ISO image...") - f, err := os.Open(tmpPath) + f, err := os.Open(isoPath) if err != nil { return nil, fmt.Errorf("open temp ISO: %w", err) } @@ -102,6 +102,7 @@ func ExtractFromISOWithProgress(r io.Reader, destDir string, progress ProgressFu return &ExtractResult{ KernelFilename: kernelName, InitrdFilename: initrdName, + ISOFilename: "original.iso", }, nil } diff --git a/internal/image/service.go b/internal/image/service.go index cbe8c02..7e86000 100644 --- a/internal/image/service.go +++ b/internal/image/service.go @@ -55,6 +55,7 @@ func (s *Service) Upload(ctx context.Context, p UploadParams) (*model.Image, err kernelPath := filepath.Join(p.Name, result.KernelFilename) initrdPath := filepath.Join(p.Name, result.InitrdFilename) + isoPath := filepath.Join(p.Name, result.ISOFilename) id, err := s.Store.Create(ctx, model.Image{ Name: p.Name, @@ -62,6 +63,7 @@ func (s *Service) Upload(ctx context.Context, p UploadParams) (*model.Image, err Version: p.Version, KernelPath: kernelPath, InitrdPath: initrdPath, + ISOPath: isoPath, }) if err != nil { os.RemoveAll(destDir) diff --git a/internal/model/model.go b/internal/model/model.go index 332d5dd..0990819 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -62,6 +62,7 @@ type Image struct { Version string KernelPath string InitrdPath string + ISOPath string IsDefault bool CreatedAt time.Time } diff --git a/internal/pxe/ipxe.go b/internal/pxe/ipxe.go index 4c981a1..d49f996 100644 --- a/internal/pxe/ipxe.go +++ b/internal/pxe/ipxe.go @@ -9,14 +9,15 @@ import ( func BuildIPXEScript(publicURL string, img *model.Image, mac string) string { kernelURL := fmt.Sprintf("%s/images/boot/%s", publicURL, img.KernelPath) initrdURL := fmt.Sprintf("%s/images/boot/%s", publicURL, img.InitrdPath) + isoURL := fmt.Sprintf("%s/images/boot/%s", publicURL, img.ISOPath) return fmt.Sprintf(`#!ipxe echo Provisioning: booting %s on ${mac} echo Loading kernel... -kernel %s ramdisk_size=16777216 vga=791 video=vesafb:lfb:on rw quiet splash=verbose initrd=initrd +kernel %s ramdisk_size=16777216 vga=791 video=vesafb:lfb:on rw quiet splash=verbose proxmox-iso-url=%s initrd=initrd echo Loading initrd... initrd --name initrd %s echo Booting... boot -`, img.Name, kernelURL, initrdURL) +`, img.Name, kernelURL, isoURL, initrdURL) } diff --git a/internal/store/images.go b/internal/store/images.go index 9664fac..b4c22d8 100644 --- a/internal/store/images.go +++ b/internal/store/images.go @@ -15,9 +15,9 @@ type Images struct { func (s *Images) Create(ctx context.Context, img model.Image) (int64, error) { res, err := s.DB.ExecContext(ctx, ` - INSERT INTO images(name, kind, version, kernel_path, initrd_path, is_default) - VALUES(?,?,?,?,?,?) - `, img.Name, img.Kind, img.Version, img.KernelPath, img.InitrdPath, boolToInt(img.IsDefault)) + INSERT INTO images(name, kind, version, kernel_path, initrd_path, iso_path, is_default) + VALUES(?,?,?,?,?,?,?) + `, img.Name, img.Kind, img.Version, img.KernelPath, img.InitrdPath, img.ISOPath, boolToInt(img.IsDefault)) if err != nil { return 0, fmt.Errorf("insert image: %w", err) } @@ -25,7 +25,7 @@ func (s *Images) Create(ctx context.Context, img model.Image) (int64, error) { } func (s *Images) List(ctx context.Context) ([]model.Image, error) { - rows, err := s.DB.QueryContext(ctx, `SELECT id, name, kind, version, kernel_path, initrd_path, is_default, created_at FROM images ORDER BY name`) + rows, err := s.DB.QueryContext(ctx, `SELECT id, name, kind, version, kernel_path, initrd_path, iso_path, is_default, created_at FROM images ORDER BY name`) if err != nil { return nil, fmt.Errorf("list images: %w", err) } @@ -35,7 +35,7 @@ func (s *Images) List(ctx context.Context) ([]model.Image, error) { var img model.Image var isDefault int var createdAt string - if err := rows.Scan(&img.ID, &img.Name, &img.Kind, &img.Version, &img.KernelPath, &img.InitrdPath, &isDefault, &createdAt); err != nil { + if err := rows.Scan(&img.ID, &img.Name, &img.Kind, &img.Version, &img.KernelPath, &img.InitrdPath, &img.ISOPath, &isDefault, &createdAt); err != nil { return nil, fmt.Errorf("scan image: %w", err) } img.IsDefault = isDefault == 1 @@ -46,11 +46,11 @@ func (s *Images) List(ctx context.Context) ([]model.Image, error) { } func (s *Images) GetDefault(ctx context.Context) (*model.Image, error) { - row := s.DB.QueryRowContext(ctx, `SELECT id, name, kind, version, kernel_path, initrd_path, is_default, created_at FROM images WHERE is_default = 1 LIMIT 1`) + row := s.DB.QueryRowContext(ctx, `SELECT id, name, kind, version, kernel_path, initrd_path, iso_path, is_default, created_at FROM images WHERE is_default = 1 LIMIT 1`) var img model.Image var isDefault int var createdAt string - if err := row.Scan(&img.ID, &img.Name, &img.Kind, &img.Version, &img.KernelPath, &img.InitrdPath, &isDefault, &createdAt); err != nil { + if err := row.Scan(&img.ID, &img.Name, &img.Kind, &img.Version, &img.KernelPath, &img.InitrdPath, &img.ISOPath, &isDefault, &createdAt); err != nil { if err == sql.ErrNoRows { return nil, ErrNotFound } @@ -62,11 +62,11 @@ func (s *Images) GetDefault(ctx context.Context) (*model.Image, error) { } func (s *Images) Get(ctx context.Context, id int64) (*model.Image, error) { - row := s.DB.QueryRowContext(ctx, `SELECT id, name, kind, version, kernel_path, initrd_path, is_default, created_at FROM images WHERE id = ?`, id) + row := s.DB.QueryRowContext(ctx, `SELECT id, name, kind, version, kernel_path, initrd_path, iso_path, is_default, created_at FROM images WHERE id = ?`, id) var img model.Image var isDefault int var createdAt string - if err := row.Scan(&img.ID, &img.Name, &img.Kind, &img.Version, &img.KernelPath, &img.InitrdPath, &isDefault, &createdAt); err != nil { + if err := row.Scan(&img.ID, &img.Name, &img.Kind, &img.Version, &img.KernelPath, &img.InitrdPath, &img.ISOPath, &isDefault, &createdAt); err != nil { if err == sql.ErrNoRows { return nil, ErrNotFound } @@ -94,11 +94,11 @@ func (s *Images) SetDefault(ctx context.Context, id int64) error { } func (s *Images) GetByName(ctx context.Context, name string) (*model.Image, error) { - row := s.DB.QueryRowContext(ctx, `SELECT id, name, kind, version, kernel_path, initrd_path, is_default, created_at FROM images WHERE name = ?`, name) + row := s.DB.QueryRowContext(ctx, `SELECT id, name, kind, version, kernel_path, initrd_path, iso_path, is_default, created_at FROM images WHERE name = ?`, name) var img model.Image var isDefault int var createdAt string - if err := row.Scan(&img.ID, &img.Name, &img.Kind, &img.Version, &img.KernelPath, &img.InitrdPath, &isDefault, &createdAt); err != nil { + if err := row.Scan(&img.ID, &img.Name, &img.Kind, &img.Version, &img.KernelPath, &img.InitrdPath, &img.ISOPath, &isDefault, &createdAt); err != nil { if err == sql.ErrNoRows { return nil, ErrNotFound }