summaryrefslogtreecommitdiff
path: root/pkg/machine
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/machine')
-rw-r--r--pkg/machine/fcos.go40
-rw-r--r--pkg/machine/fcos_arm64.go16
-rw-r--r--pkg/machine/pull.go115
-rw-r--r--pkg/machine/qemu/machine.go82
-rw-r--r--pkg/machine/qemu/options_linux_arm64.go41
5 files changed, 236 insertions, 58 deletions
diff --git a/pkg/machine/fcos.go b/pkg/machine/fcos.go
index 0c6a2485e..32f943c87 100644
--- a/pkg/machine/fcos.go
+++ b/pkg/machine/fcos.go
@@ -2,17 +2,13 @@ package machine
import (
"crypto/sha256"
- "io"
"io/ioutil"
url2 "net/url"
- "os"
"path/filepath"
"runtime"
"strings"
- "github.com/containers/storage/pkg/archive"
digest "github.com/opencontainers/go-digest"
- "github.com/sirupsen/logrus"
)
// These should eventually be moved into machine/qemu as
@@ -75,41 +71,7 @@ func (f FcosDownload) DownloadImage() error {
return err
}
}
- uncompressedFileWriter, err := os.OpenFile(f.getLocalUncompressedName(), os.O_CREATE|os.O_RDWR, 0600)
- if err != nil {
- return err
- }
- sourceFile, err := ioutil.ReadFile(f.LocalPath)
- if err != nil {
- return err
- }
- compressionType := archive.DetectCompression(sourceFile)
- f.CompressionType = compressionType.Extension()
-
- switch f.CompressionType {
- case "tar.xz":
- return decompressXZ(f.LocalPath, uncompressedFileWriter)
- default:
- // File seems to be uncompressed, make a copy
- if err := copyFile(f.LocalPath, uncompressedFileWriter); err != nil {
- return err
- }
- }
- return nil
-}
-
-func copyFile(src string, dest *os.File) error {
- source, err := os.Open(src)
- if err != nil {
- return err
- }
- defer func() {
- if err := source.Close(); err != nil {
- logrus.Error(err)
- }
- }()
- _, err = io.Copy(dest, source)
- return err
+ return Decompress(f.LocalPath, f.getLocalUncompressedName())
}
func (f FcosDownload) Get() *Download {
diff --git a/pkg/machine/fcos_arm64.go b/pkg/machine/fcos_arm64.go
index ab50ca874..f5cd5a505 100644
--- a/pkg/machine/fcos_arm64.go
+++ b/pkg/machine/fcos_arm64.go
@@ -2,9 +2,9 @@ package machine
import (
"encoding/json"
- "fmt"
"io/ioutil"
"net/http"
+ url2 "net/url"
"github.com/sirupsen/logrus"
)
@@ -14,9 +14,7 @@ const aarchBaseURL = "https://fedorapeople.org/groups/fcos-images/builds/latest/
// Total hack until automation is possible.
// We need a proper json file at least to automate
func getFCOSDownload() (*fcosDownloadInfo, error) {
-
meta := Build{}
- fmt.Println(aarchBaseURL + "meta.json")
resp, err := http.Get(aarchBaseURL + "meta.json")
if err != nil {
return nil, err
@@ -33,8 +31,18 @@ func getFCOSDownload() (*fcosDownloadInfo, error) {
if err := json.Unmarshal(body, &meta); err != nil {
return nil, err
}
+ pathURL, err := url2.Parse(meta.BuildArtifacts.Qemu.Path)
+ if err != nil {
+ return nil, err
+ }
+
+ baseURL, err := url2.Parse(aarchBaseURL)
+ if err != nil {
+ return nil, err
+ }
+ pullURL := baseURL.ResolveReference(pathURL)
return &fcosDownloadInfo{
- Location: "https://fedorapeople.org/groups/fcos-images/builds/latest/aarch64/fedora-coreos-33.20210310.dev.0-qemu.aarch64.qcow2",
+ Location: pullURL.String(),
Release: "",
Sha256Sum: meta.BuildArtifacts.Qemu.Sha256,
}, nil
diff --git a/pkg/machine/pull.go b/pkg/machine/pull.go
index 39dde15b8..41abe6993 100644
--- a/pkg/machine/pull.go
+++ b/pkg/machine/pull.go
@@ -3,17 +3,94 @@ package machine
import (
"fmt"
"io"
+ "io/ioutil"
"net/http"
+ url2 "net/url"
"os"
"os/exec"
+ "path/filepath"
"strings"
"time"
+ "github.com/containers/image/v5/pkg/compression"
+ "github.com/docker/docker/pkg/archive"
"github.com/sirupsen/logrus"
"github.com/vbauerster/mpb/v6"
"github.com/vbauerster/mpb/v6/decor"
)
+// GenericDownload is used when a user provides a URL
+// or path for an image
+type GenericDownload struct {
+ Download
+}
+
+// NewGenericDownloader is used when the disk image is provided by the user
+func NewGenericDownloader(vmType, vmName, pullPath string) (DistributionDownload, error) {
+ var (
+ imageName string
+ )
+ dataDir, err := GetDataDir(vmType)
+ if err != nil {
+ return nil, err
+ }
+ dl := Download{}
+ // Is pullpath a file or url?
+ getURL, err := url2.Parse(pullPath)
+ if err != nil {
+ return nil, err
+ }
+ if len(getURL.Scheme) > 0 {
+ urlSplit := strings.Split(pullPath, "/")
+ imageName = urlSplit[len(urlSplit)-1]
+ dl.LocalUncompressedFile = filepath.Join(dataDir, imageName)
+ dl.URL = getURL
+ dl.LocalPath = filepath.Join(dataDir, imageName)
+ } else {
+ // Dealing with FilePath
+ imageName = filepath.Base(pullPath)
+ dl.LocalUncompressedFile = filepath.Join(dataDir, imageName)
+ dl.LocalPath = pullPath
+ }
+ dl.VMName = vmName
+ dl.ImageName = imageName
+ // The download needs to be pulled into the datadir
+
+ gd := GenericDownload{Download: dl}
+ gd.LocalUncompressedFile = gd.getLocalUncompressedName()
+ return gd, nil
+}
+
+func (g GenericDownload) getLocalUncompressedName() string {
+ var (
+ extension string
+ )
+ switch {
+ case strings.HasSuffix(g.LocalPath, ".bz2"):
+ extension = ".bz2"
+ case strings.HasSuffix(g.LocalPath, ".gz"):
+ extension = ".gz"
+ case strings.HasSuffix(g.LocalPath, ".xz"):
+ extension = ".xz"
+ }
+ uncompressedFilename := filepath.Join(filepath.Dir(g.LocalUncompressedFile), g.VMName+"_"+g.ImageName)
+ return strings.TrimSuffix(uncompressedFilename, extension)
+}
+
+func (g GenericDownload) DownloadImage() error {
+ // If we have a URL for this "downloader", we now pull it
+ if g.URL != nil {
+ if err := DownloadVMImage(g.URL, g.LocalPath); err != nil {
+ return err
+ }
+ }
+ return Decompress(g.LocalPath, g.getLocalUncompressedName())
+}
+
+func (g GenericDownload) Get() *Download {
+ return &g.Download
+}
+
// DownloadVMImage downloads a VM image from url to given path
// with download status
func DownloadVMImage(downloadURL fmt.Stringer, localImagePath string) error {
@@ -38,7 +115,7 @@ func DownloadVMImage(downloadURL fmt.Stringer, localImagePath string) error {
}()
if resp.StatusCode != http.StatusOK {
- return fmt.Errorf("error downloading VM image: %s", resp.Status)
+ return fmt.Errorf("error downloading VM image %s: %s", downloadURL, resp.Status)
}
size := resp.ContentLength
urlSplit := strings.Split(downloadURL.String(), "/")
@@ -75,6 +152,22 @@ func DownloadVMImage(downloadURL fmt.Stringer, localImagePath string) error {
return nil
}
+func Decompress(localPath, uncompressedPath string) error {
+ uncompressedFileWriter, err := os.OpenFile(uncompressedPath, os.O_CREATE|os.O_RDWR, 0600)
+ if err != nil {
+ return err
+ }
+ sourceFile, err := ioutil.ReadFile(localPath)
+ if err != nil {
+ return err
+ }
+
+ if compressionType := archive.DetectCompression(sourceFile); compressionType.Extension() == "tar.xz" {
+ return decompressXZ(localPath, uncompressedFileWriter)
+ }
+ return decompressEverythingElse(localPath, uncompressedFileWriter)
+}
+
// Will error out if file without .xz already exists
// Maybe extracting then renameing is a good idea here..
// depends on xz: not pre-installed on mac, so it becomes a brew dependecy
@@ -95,3 +188,23 @@ func decompressXZ(src string, output io.Writer) error {
}()
return cmd.Run()
}
+
+func decompressEverythingElse(src string, output io.Writer) error {
+ fmt.Println("Extracting compressed file")
+ f, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ uncompressStream, _, err := compression.AutoDecompress(f)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err := uncompressStream.Close(); err != nil {
+ logrus.Error(err)
+ }
+ }()
+
+ _, err = io.Copy(output, uncompressStream)
+ return err
+}
diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go
index fe155750f..fdb528a86 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -12,9 +12,8 @@ import (
"strconv"
"time"
- "github.com/containers/podman/v3/utils"
-
"github.com/containers/podman/v3/pkg/machine"
+ "github.com/containers/podman/v3/utils"
"github.com/containers/storage/pkg/homedir"
"github.com/digitalocean/go-qemu/qmp"
"github.com/pkg/errors"
@@ -83,7 +82,7 @@ func NewMachine(opts machine.InitOptions) (machine.VM, error) {
return nil, err
}
vm.QMPMonitor = monitor
- cmd = append(cmd, []string{"-qmp", monitor.Network + ":/" + monitor.Address + ",server,nowait"}...)
+ cmd = append(cmd, []string{"-qmp", monitor.Network + ":/" + monitor.Address + ",server=on,wait=off"}...)
// Add network
cmd = append(cmd, "-nic", "user,model=virtio,hostfwd=tcp::"+strconv.Itoa(vm.Port)+"-:22")
@@ -96,7 +95,7 @@ func NewMachine(opts machine.InitOptions) (machine.VM, error) {
// Add serial port for readiness
cmd = append(cmd, []string{
"-device", "virtio-serial",
- "-chardev", "socket,path=" + virtualSocketPath + ",server,nowait,id=" + vm.Name + "_ready",
+ "-chardev", "socket,path=" + virtualSocketPath + ",server=on,wait=off,id=" + vm.Name + "_ready",
"-device", "virtserialport,chardev=" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0"}...)
vm.CmdLine = cmd
return vm, nil
@@ -135,15 +134,29 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
jsonFile := filepath.Join(vmConfigDir, v.Name) + ".json"
v.IdentityPath = filepath.Join(sshDir, v.Name)
- dd, err := machine.NewFcosDownloader(vmtype, v.Name)
- if err != nil {
- return err
+ // The user has provided an alternate image which can be a file path
+ // or URL.
+ if len(opts.ImagePath) > 0 {
+ g, err := machine.NewGenericDownloader(vmtype, v.Name, opts.ImagePath)
+ if err != nil {
+ return err
+ }
+ v.ImagePath = g.Get().LocalUncompressedFile
+ if err := g.DownloadImage(); err != nil {
+ return err
+ }
+ } else {
+ // Get the image as usual
+ dd, err := machine.NewFcosDownloader(vmtype, v.Name)
+ if err != nil {
+ return err
+ }
+ v.ImagePath = dd.Get().LocalUncompressedFile
+ if err := dd.DownloadImage(); err != nil {
+ return err
+ }
}
- v.ImagePath = dd.Get().LocalUncompressedFile
- if err := dd.DownloadImage(); err != nil {
- return err
- }
// Add arch specific options including image location
v.CmdLine = append(v.CmdLine, v.addArchOptions()...)
@@ -171,10 +184,20 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
return err
}
+ originalDiskSize, err := getDiskSize(v.ImagePath)
+ if err != nil {
+ return err
+ }
// Resize the disk image to input disk size
- resize := exec.Command("qemu-img", []string{"resize", v.ImagePath, strconv.Itoa(int(opts.DiskSize)) + "G"}...)
- if err := resize.Run(); err != nil {
- return errors.Errorf("error resizing image: %q", err)
+ // only if the virtualdisk size is less than
+ // the given disk size
+ if opts.DiskSize<<(10*3) > originalDiskSize {
+ resize := exec.Command("qemu-img", []string{"resize", v.ImagePath, strconv.Itoa(int(opts.DiskSize)) + "G"}...)
+ resize.Stdout = os.Stdout
+ resize.Stderr = os.Stderr
+ if err := resize.Run(); err != nil {
+ return errors.Errorf("error resizing image: %q", err)
+ }
}
// Write the ignition file
ign := machine.DynamicIgnition{
@@ -372,3 +395,34 @@ func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error {
return cmd.Run()
}
+
+// executes qemu-image info to get the virtual disk size
+// of the diskimage
+func getDiskSize(path string) (uint64, error) {
+ diskInfo := exec.Command("qemu-img", "info", "--output", "json", path)
+ stdout, err := diskInfo.StdoutPipe()
+ if err != nil {
+ return 0, err
+ }
+ if err := diskInfo.Start(); err != nil {
+ return 0, err
+ }
+ tmpInfo := struct {
+ VirtualSize uint64 `json:"virtual-size"`
+ Filename string `json:"filename"`
+ ClusterSize int64 `json:"cluster-size"`
+ Format string `json:"format"`
+ FormatSpecific struct {
+ Type string `json:"type"`
+ Data map[string]string `json:"data"`
+ }
+ DirtyFlag bool `json:"dirty-flag"`
+ }{}
+ if err := json.NewDecoder(stdout).Decode(&tmpInfo); err != nil {
+ return 0, err
+ }
+ if err := diskInfo.Wait(); err != nil {
+ return 0, err
+ }
+ return tmpInfo.VirtualSize, nil
+}
diff --git a/pkg/machine/qemu/options_linux_arm64.go b/pkg/machine/qemu/options_linux_arm64.go
new file mode 100644
index 000000000..948117653
--- /dev/null
+++ b/pkg/machine/qemu/options_linux_arm64.go
@@ -0,0 +1,41 @@
+package qemu
+
+import (
+ "os"
+ "path/filepath"
+)
+
+var (
+ QemuCommand = "qemu-system-aarch64"
+)
+
+func (v *MachineVM) addArchOptions() []string {
+ opts := []string{
+ "-accel", "kvm",
+ "-cpu", "host",
+ "-M", "virt,gic-version=max",
+ "-bios", getQemuUefiFile("QEMU_EFI.fd"),
+ }
+ return opts
+}
+
+func (v *MachineVM) prepare() error {
+ return nil
+}
+
+func (v *MachineVM) archRemovalFiles() []string {
+ return []string{}
+}
+
+func getQemuUefiFile(name string) string {
+ dirs := []string{
+ "/usr/share/qemu-efi-aarch64",
+ "/usr/share/edk2/aarch64",
+ }
+ for _, dir := range dirs {
+ if _, err := os.Stat(dir); err == nil {
+ return filepath.Join(dir, name)
+ }
+ }
+ return name
+}