aarch64 EFI image management

This commit is contained in:
AlexSSD7 2023-08-30 13:13:08 +01:00
commit 50df5197d4
6 changed files with 156 additions and 33 deletions

View file

@ -15,7 +15,7 @@ import (
"github.com/pkg/errors"
)
func (s *Storage) download(url string, hash []byte, out string) error {
func (s *Storage) download(url string, hash []byte, out string, applyReaderMiddleware func(io.Reader) io.Reader) error {
var created, success bool
defer func() {
@ -51,21 +51,41 @@ func (s *Storage) download(url string, hash []byte, out string) error {
defer func() { _ = resp.Body.Close() }()
_, err = copyWithProgressAndHash(f, resp.Body, 1024, resp.ContentLength, hash, func(i int, f float64) {
s.logger.Info("Downloading file", "out", out, "percent", math.Round(f*100*100)/100, "size", humanize.Bytes(uint64(resp.ContentLength)))
knownSize := resp.ContentLength
var readFrom io.Reader
if applyReaderMiddleware != nil {
readFrom = applyReaderMiddleware(resp.Body)
knownSize = 0
} else {
readFrom = resp.Body
}
n, err := copyWithProgressAndHash(f, readFrom, 1024, hash, func(downloaded int) {
var percent float64
if knownSize != 0 {
percent = float64(downloaded) / float64(knownSize)
}
lg := s.logger.With("out", out, "done", humanize.Bytes(uint64(downloaded)))
if percent != 0 {
lg.Info("Downloading file", "percent", math.Round(percent*100*100)/100)
} else {
lg.Info("Downloading compressed file", "percent", "N/A")
}
})
if err != nil {
return errors.Wrap(err, "copy resp to file")
}
s.logger.Info("Successfully downloaded file", "from", url, "to", out)
s.logger.Info("Successfully downloaded file", "from", url, "to", out, "out-size", humanize.Bytes(uint64(n)))
success = true
return nil
}
func copyWithProgressAndHash(dst io.Writer, src io.Reader, blockSize int, length int64, wantHash []byte, report func(int, float64)) (int, error) {
func copyWithProgressAndHash(dst io.Writer, src io.Reader, blockSize int, wantHash []byte, report func(int)) (int, error) {
block := make([]byte, blockSize)
var h hash.Hash
@ -98,11 +118,7 @@ func copyWithProgressAndHash(dst io.Writer, src io.Reader, blockSize int, length
}
if progress%1000000 == 0 {
var percent float64
if length != 0 {
percent = float64(progress) / float64(length)
}
report(progress, percent)
report(progress)
}
}

View file

@ -1,10 +1,13 @@
package storage
import (
"compress/bzip2"
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
"runtime"
"github.com/AlexSSD7/linsk/constants"
"github.com/AlexSSD7/linsk/imgbuilder"
@ -41,7 +44,7 @@ func (s *Storage) CheckDownloadBaseImage() (string, error) {
}
// Image doesn't exist. Download one.
err := s.download(constants.GetAlpineBaseImageURL(), constants.GetAlpineBaseImageHash(), baseImagePath)
err := s.download(constants.GetAlpineBaseImageURL(), constants.GetAlpineBaseImageHash(), baseImagePath, nil)
if err != nil {
return "", errors.Wrap(err, "download base alpine image")
}
@ -62,34 +65,30 @@ func (s *Storage) GetVMImagePath() string {
return filepath.Join(s.path, constants.GetVMImageTags()+".qcow2")
}
func (s *Storage) GetAarch64EFIImagePath() string {
return filepath.Join(s.path, constants.GetAarch64EFIImageName())
}
func (s *Storage) BuildVMImageWithInterruptHandler(showBuilderVMDisplay bool, overwrite bool) error {
var overwriting bool
vmImagePath := s.GetVMImagePath()
_, err := os.Stat(vmImagePath)
removed, err := checkExistsOrRemove(vmImagePath, overwrite)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return errors.Wrap(err, "stat vm image path")
}
} else {
if overwrite {
overwriting = true
err = os.Remove(vmImagePath)
if err != nil {
return errors.Wrap(err, "remove existing vm image")
}
} else {
return ErrImageAlreadyExists
}
return errors.Wrap(err, "check exists or remove")
}
baseImagePath, err := s.CheckDownloadBaseImage()
if err != nil {
return errors.Wrap(err, "check download base image")
return errors.Wrap(err, "check/download base image")
}
s.logger.Info("Building VM image", "tags", constants.GetAlpineBaseImageTags(), "overwriting", overwriting, "dst", vmImagePath)
biosPath, err := s.CheckDownloadCPUArchSpecifics()
if err != nil {
return errors.Wrap(err, "check/download cpu arch specifics")
}
buildCtx, err := imgbuilder.NewBuildContext(s.logger.With("subcaller", "imgbuilder"), baseImagePath, vmImagePath, showBuilderVMDisplay)
s.logger.Info("Building VM image", "tags", constants.GetAlpineBaseImageTags(), "overwriting", removed, "dst", vmImagePath)
buildCtx, err := imgbuilder.NewBuildContext(s.logger.With("subcaller", "imgbuilder"), baseImagePath, vmImagePath, showBuilderVMDisplay, biosPath)
if err != nil {
return errors.Wrap(err, "create new img build context")
}
@ -116,3 +115,44 @@ func (s *Storage) CheckVMImageExists() (string, error) {
func (s *Storage) DataDirPath() string {
return s.path
}
func (s *Storage) CheckDownloadCPUArchSpecifics() (string, error) {
if runtime.GOARCH == "arm64" {
p, err := s.CheckDownloadAarch64EFIImage()
if err != nil {
return "", errors.Wrap(err, "check/download aarch64 efi image")
}
return p, nil
}
return "", nil
}
func (s *Storage) CheckDownloadAarch64EFIImage() (string, error) {
efiImagePath := s.GetAarch64EFIImagePath()
_, err := os.Stat(efiImagePath)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return "", errors.Wrap(err, "stat base image path")
}
// EFI image doesn't exist. Download one.
err := s.download(constants.GetAarch64EFIImageBZ2URL(), constants.GetAarch64EFIImageHash(), efiImagePath, func(r io.Reader) io.Reader {
return bzip2.NewReader(r)
})
if err != nil {
return "", errors.Wrap(err, "download base alpine image")
}
return efiImagePath, nil
}
// EFI image exists. Ensure that the hash is correct.
err = validateFileHash(efiImagePath, constants.GetAarch64EFIImageHash())
if err != nil {
return "", errors.Wrap(err, "validate hash of existing image")
}
return efiImagePath, nil
}

30
storage/utils.go Normal file
View file

@ -0,0 +1,30 @@
package storage
import (
"os"
"github.com/pkg/errors"
)
func checkExistsOrRemove(path string, overwriteRemove bool) (bool, error) {
var removed bool
_, err := os.Stat(path)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return removed, errors.Wrap(err, "stat file")
}
} else {
if overwriteRemove {
err = os.Remove(path)
if err != nil {
return removed, errors.Wrap(err, "remove file")
}
removed = true
} else {
return removed, ErrImageAlreadyExists
}
}
return removed, nil
}