Some work on storage

This commit is contained in:
AlexSSD7 2023-08-29 20:56:21 +01:00
commit e09ad7d1c9
4 changed files with 175 additions and 2 deletions

1
go.mod
View file

@ -6,6 +6,7 @@ require (
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/alessio/shellescape v1.4.2 github.com/alessio/shellescape v1.4.2
github.com/bramvdbogaerde/go-scp v1.2.1 github.com/bramvdbogaerde/go-scp v1.2.1
github.com/dustin/go-humanize v1.0.1
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/sethvargo/go-password v0.2.0 github.com/sethvargo/go-password v0.2.0

2
go.sum
View file

@ -7,6 +7,8 @@ github.com/bramvdbogaerde/go-scp v1.2.1/go.mod h1:s4ZldBoRAOgUg8IrRP2Urmq5qqd2yP
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=

171
storage/storage.go Normal file
View file

@ -0,0 +1,171 @@
package storage
import (
"bytes"
"crypto/sha512"
"encoding/hex"
"fmt"
"io"
"log/slog"
"math"
"net/http"
"os"
"path/filepath"
"github.com/dustin/go-humanize"
"github.com/pkg/errors"
)
const imageURL = "http://localhost:8000/linsk-base.qcow2"
var imageHash = [64]byte{96, 134, 26, 122, 43, 140, 212, 78, 44, 123, 103, 209, 21, 36, 81, 152, 9, 177, 47, 114, 225, 117, 64, 198, 50, 151, 71, 100, 1, 92, 106, 24, 224, 254, 157, 125, 188, 118, 84, 200, 47, 11, 215, 252, 100, 173, 64, 202, 132, 110, 15, 240, 234, 223, 56, 125, 94, 94, 179, 39, 193, 215, 41, 109}
type Storage struct {
logger *slog.Logger
path string
}
func NewStorage(logger *slog.Logger, dataDir string) (*Storage, error) {
dataDir = filepath.Clean(dataDir)
err := os.MkdirAll(dataDir, 0700)
if err != nil {
return nil, fmt.Errorf("mkdir all data dir")
}
return &Storage{
logger: logger,
path: dataDir,
}, nil
}
func (s *Storage) getLocalImagePath() string {
return filepath.Join(s.path, hex.EncodeToString(imageHash[:])+".qcow2")
}
func (s *Storage) DownloadImage() error {
localImagePath := s.getLocalImagePath()
var created, success bool
defer func() {
if created && !success {
_ = os.Remove(localImagePath)
}
}()
_, err := os.Stat(localImagePath)
if err == nil {
err = os.Remove(localImagePath)
if err != nil {
return errors.Wrap(err, "remove existing image")
}
} else {
if !errors.Is(err, os.ErrNotExist) {
return errors.Wrap(err, "stat local image path")
}
}
f, err := os.OpenFile(localImagePath, os.O_CREATE|os.O_WRONLY, 0400)
if err != nil {
return errors.Wrap(err, "open file")
}
created = true
defer func() { _ = f.Close() }()
resp, err := http.Get(imageURL)
if err != nil {
return errors.Wrap(err, "http get image")
}
defer func() { _ = resp.Body.Close() }()
_, err = copyWithProgress(f, resp.Body, 1024, resp.ContentLength, func(i int, f float64) {
s.logger.Info("Downloading image", "url", imageURL, "percent", math.Round(f*100*100)/100, "content-length", humanize.Bytes(uint64(resp.ContentLength)))
})
if err != nil {
return errors.Wrap(err, "copy resp to file")
}
err = s.ValidateImageHash()
if err != nil {
return errors.Wrap(err, "validate image hash")
}
success = true
return nil
}
func (s *Storage) ValidateImageHash() error {
localImagePath := s.getLocalImagePath()
f, err := os.OpenFile(localImagePath, os.O_RDONLY, 0400)
if err != nil {
return errors.Wrap(err, "open file")
}
defer func() { _ = f.Close() }()
h := sha512.New()
block := make([]byte, 1024)
for {
read, err := f.Read(block)
if read > 0 {
h.Write(block[:read])
}
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return errors.Wrap(err, "read file block")
}
}
sum := h.Sum(nil)
if !bytes.Equal(sum, imageHash[:]) {
return fmt.Errorf("hash mismatch: want '%v', have '%v'", hex.EncodeToString(imageHash[:]), hex.EncodeToString(sum))
}
return nil
}
func copyWithProgress(dst io.Writer, src io.Reader, blockSize int, length int64, report func(int, float64)) (int, error) {
block := make([]byte, blockSize)
var progress int
for {
read, err := src.Read(block)
if read > 0 {
written, err := dst.Write(block[:read])
if err != nil {
return progress, errors.Wrap(err, "write")
}
progress += written
}
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return progress, errors.Wrap(err, "read")
}
if progress%1000000 == 0 {
var percent float64
if length != 0 {
percent = float64(progress) / float64(length)
}
report(progress, percent)
}
}
return progress, nil
}

View file

@ -144,7 +144,6 @@ func NewVM(logger *slog.Logger, cfg VMConfig) (*VM, error) {
if !cfg.ShowDisplay { if !cfg.ShowDisplay {
cmdArgs = append(cmdArgs, "-display", "none") cmdArgs = append(cmdArgs, "-display", "none")
} else if runtime.GOARCH == "arm64" { } else if runtime.GOARCH == "arm64" {
// No video is configured by default in ARM. This will enable it. // No video is configured by default in ARM. This will enable it.
// TODO: This doesn't really work on arm64. It just shows a blank viewer. // TODO: This doesn't really work on arm64. It just shows a blank viewer.
@ -174,7 +173,7 @@ func NewVM(logger *slog.Logger, cfg VMConfig) (*VM, error) {
} }
if len(cfg.PassthroughConfig.Block) != 0 { if len(cfg.PassthroughConfig.Block) != 0 {
logger.Warn("Detected raw block device passthrough, please note that it's YOUR responsibility to ensure that no device is mounted in your OS and the VM at the same time") logger.Warn("Detected raw block device passthrough. Please note that it's YOUR responsibility to ensure that no device is mounted in your OS and the VM at the same time. Otherwise, you run serious risks. No further warnings will be issued.")
} }
for _, dev := range cfg.PassthroughConfig.Block { for _, dev := range cfg.PassthroughConfig.Block {