diff options
Diffstat (limited to 'pkg/machine')
-rw-r--r-- | pkg/machine/config.go | 19 | ||||
-rw-r--r-- | pkg/machine/e2e/basic_test.go | 4 | ||||
-rw-r--r-- | pkg/machine/e2e/config_basic_test.go (renamed from pkg/machine/e2e/config_basic.go) | 3 | ||||
-rw-r--r-- | pkg/machine/e2e/config_info_test.go (renamed from pkg/machine/e2e/config_info.go) | 2 | ||||
-rw-r--r-- | pkg/machine/e2e/config_init_test.go (renamed from pkg/machine/e2e/config_init.go) | 6 | ||||
-rw-r--r-- | pkg/machine/e2e/config_inspect_test.go (renamed from pkg/machine/e2e/config_inspect.go) | 2 | ||||
-rw-r--r-- | pkg/machine/e2e/config_list_test.go (renamed from pkg/machine/e2e/config_list.go) | 2 | ||||
-rw-r--r-- | pkg/machine/e2e/config_rm_test.go (renamed from pkg/machine/e2e/config_rm.go) | 8 | ||||
-rw-r--r-- | pkg/machine/e2e/config_set_test.go (renamed from pkg/machine/e2e/config_set.go) | 2 | ||||
-rw-r--r-- | pkg/machine/e2e/config_ssh_test.go (renamed from pkg/machine/e2e/config_ssh.go) | 8 | ||||
-rw-r--r-- | pkg/machine/e2e/config_start_test.go (renamed from pkg/machine/e2e/config_start.go) | 3 | ||||
-rw-r--r-- | pkg/machine/e2e/config_stop_test.go (renamed from pkg/machine/e2e/config_stop.go) | 3 | ||||
-rw-r--r-- | pkg/machine/e2e/config_test.go (renamed from pkg/machine/e2e/config.go) | 26 | ||||
-rw-r--r-- | pkg/machine/e2e/info_test.go | 6 | ||||
-rw-r--r-- | pkg/machine/e2e/init_test.go | 8 | ||||
-rw-r--r-- | pkg/machine/e2e/inspect_test.go | 8 | ||||
-rw-r--r-- | pkg/machine/e2e/list_test.go | 10 | ||||
-rw-r--r-- | pkg/machine/e2e/machine_test.go | 4 | ||||
-rw-r--r-- | pkg/machine/e2e/rm_test.go | 2 | ||||
-rw-r--r-- | pkg/machine/e2e/set_test.go | 8 | ||||
-rw-r--r-- | pkg/machine/e2e/ssh_test.go | 14 | ||||
-rw-r--r-- | pkg/machine/e2e/start_test.go | 2 | ||||
-rw-r--r-- | pkg/machine/e2e/stop_test.go | 2 | ||||
-rw-r--r-- | pkg/machine/fcos.go | 19 | ||||
-rw-r--r-- | pkg/machine/fedora.go | 89 | ||||
-rw-r--r-- | pkg/machine/keys.go | 3 | ||||
-rw-r--r-- | pkg/machine/pull.go | 61 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 34 | ||||
-rw-r--r-- | pkg/machine/wsl/machine.go | 28 |
29 files changed, 205 insertions, 181 deletions
diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 29cd7bc00..253601dad 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -73,6 +73,7 @@ type Download struct { Arch string Artifact string CompressionType string + CacheDir string Format string ImageName string LocalPath string @@ -139,6 +140,7 @@ type VM interface { type DistributionDownload interface { HasUsableCache() (bool, error) Get() *Download + CleanCache() error } type InspectInfo struct { ConfigPath VMFile @@ -172,6 +174,19 @@ func (rc RemoteConnectionType) MakeSSHURL(host, path, port, userName string) url return uri } +// GetCacheDir returns the dir where VM images are downladed into when pulled +func GetCacheDir(vmType string) (string, error) { + dataDir, err := GetDataDir(vmType) + if err != nil { + return "", err + } + cacheDir := filepath.Join(dataDir, "cache") + if _, err := os.Stat(cacheDir); !errors.Is(err, os.ErrNotExist) { + return cacheDir, nil + } + return cacheDir, os.MkdirAll(cacheDir, 0755) +} + // GetDataDir returns the filepath where vm images should // live for podman-machine. func GetDataDir(vmType string) (string, error) { @@ -180,7 +195,7 @@ func GetDataDir(vmType string) (string, error) { return "", err } dataDir := filepath.Join(dataDirPrefix, vmType) - if _, err := os.Stat(dataDir); !os.IsNotExist(err) { + if _, err := os.Stat(dataDir); !errors.Is(err, os.ErrNotExist) { return dataDir, nil } mkdirErr := os.MkdirAll(dataDir, 0755) @@ -205,7 +220,7 @@ func GetConfDir(vmType string) (string, error) { return "", err } confDir := filepath.Join(confDirPrefix, vmType) - if _, err := os.Stat(confDir); !os.IsNotExist(err) { + if _, err := os.Stat(confDir); !errors.Is(err, os.ErrNotExist) { return confDir, nil } mkdirErr := os.MkdirAll(confDir, 0755) diff --git a/pkg/machine/e2e/basic_test.go b/pkg/machine/e2e/basic_test.go index f67fb4c67..da0310485 100644 --- a/pkg/machine/e2e/basic_test.go +++ b/pkg/machine/e2e/basic_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( . "github.com/onsi/ginkgo" @@ -20,7 +20,7 @@ var _ = Describe("run basic podman commands", func() { }) It("Basic ops", func() { - name := randomString(12) + name := randomString() i := new(initMachine) session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath).withNow()).run() Expect(err).To(BeNil()) diff --git a/pkg/machine/e2e/config_basic.go b/pkg/machine/e2e/config_basic_test.go index be0896156..d1cb24174 100644 --- a/pkg/machine/e2e/config_basic.go +++ b/pkg/machine/e2e/config_basic_test.go @@ -1,8 +1,7 @@ -package e2e +package e2e_test type basicMachine struct { args []string - cmd []string } func (s basicMachine) buildCmd(m *machineTestBuilder) []string { diff --git a/pkg/machine/e2e/config_info.go b/pkg/machine/e2e/config_info_test.go index 410c7e518..4da40ab99 100644 --- a/pkg/machine/e2e/config_info.go +++ b/pkg/machine/e2e/config_info_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test type infoMachine struct { format string diff --git a/pkg/machine/e2e/config_init.go b/pkg/machine/e2e/config_init_test.go index 7f18cce7d..d6c7990b0 100644 --- a/pkg/machine/e2e/config_init.go +++ b/pkg/machine/e2e/config_init_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( "strconv" @@ -25,7 +25,7 @@ type initMachine struct { memory *uint now bool timezone string - rootful bool + rootful bool //nolint:unused,structcheck volumes []string cmd []string @@ -71,7 +71,7 @@ func (i *initMachine) withDiskSize(size uint) *initMachine { return i } -func (i *initMachine) withIgnitionPath(path string) *initMachine { +func (i *initMachine) withIgnitionPath(path string) *initMachine { //nolint:unused i.ignitionPath = path return i } diff --git a/pkg/machine/e2e/config_inspect.go b/pkg/machine/e2e/config_inspect_test.go index 74c9a5d9c..ffd74220f 100644 --- a/pkg/machine/e2e/config_inspect.go +++ b/pkg/machine/e2e/config_inspect_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test type inspectMachine struct { /* diff --git a/pkg/machine/e2e/config_list.go b/pkg/machine/e2e/config_list_test.go index 150f984bc..78f9edc62 100644 --- a/pkg/machine/e2e/config_list.go +++ b/pkg/machine/e2e/config_list_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test type listMachine struct { /* diff --git a/pkg/machine/e2e/config_rm.go b/pkg/machine/e2e/config_rm_test.go index 6cf262a22..1f9c9b4ec 100644 --- a/pkg/machine/e2e/config_rm.go +++ b/pkg/machine/e2e/config_rm_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test type rmMachine struct { /* @@ -40,17 +40,17 @@ func (i *rmMachine) withForce() *rmMachine { return i } -func (i *rmMachine) withSaveIgnition() *rmMachine { +func (i *rmMachine) withSaveIgnition() *rmMachine { //nolint:unused i.saveIgnition = true return i } -func (i *rmMachine) withSaveImage() *rmMachine { +func (i *rmMachine) withSaveImage() *rmMachine { //nolint:unused i.saveImage = true return i } -func (i *rmMachine) withSaveKeys() *rmMachine { +func (i *rmMachine) withSaveKeys() *rmMachine { //nolint:unused i.saveKeys = true return i } diff --git a/pkg/machine/e2e/config_set.go b/pkg/machine/e2e/config_set_test.go index b310ab1b9..3c773b970 100644 --- a/pkg/machine/e2e/config_set.go +++ b/pkg/machine/e2e/config_set_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( "strconv" diff --git a/pkg/machine/e2e/config_ssh.go b/pkg/machine/e2e/config_ssh_test.go index b09eed47d..f062625fa 100644 --- a/pkg/machine/e2e/config_ssh.go +++ b/pkg/machine/e2e/config_ssh_test.go @@ -1,14 +1,12 @@ -package e2e +package e2e_test type sshMachine struct { /* --username string Username to use when ssh-ing into the VM. */ - username string + username string //nolint:unused sshCommand []string - - cmd []string } func (s sshMachine) buildCmd(m *machineTestBuilder) []string { @@ -22,7 +20,7 @@ func (s sshMachine) buildCmd(m *machineTestBuilder) []string { return cmd } -func (s *sshMachine) withUsername(name string) *sshMachine { +func (s *sshMachine) withUsername(name string) *sshMachine { //nolint:unused s.username = name return s } diff --git a/pkg/machine/e2e/config_start.go b/pkg/machine/e2e/config_start_test.go index 86b1721f8..d9efbf489 100644 --- a/pkg/machine/e2e/config_start.go +++ b/pkg/machine/e2e/config_start_test.go @@ -1,10 +1,9 @@ -package e2e +package e2e_test type startMachine struct { /* No command line args other than a machine vm name (also not required) */ - cmd []string } func (s startMachine) buildCmd(m *machineTestBuilder) []string { diff --git a/pkg/machine/e2e/config_stop.go b/pkg/machine/e2e/config_stop_test.go index 04dcfb524..41142ec7e 100644 --- a/pkg/machine/e2e/config_stop.go +++ b/pkg/machine/e2e/config_stop_test.go @@ -1,10 +1,9 @@ -package e2e +package e2e_test type stopMachine struct { /* No command line args other than a machine vm name (also not required) */ - cmd []string } func (s stopMachine) buildCmd(m *machineTestBuilder) []string { diff --git a/pkg/machine/e2e/config.go b/pkg/machine/e2e/config_test.go index b3fe74b0c..9940e711b 100644 --- a/pkg/machine/e2e/config.go +++ b/pkg/machine/e2e/config_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( "encoding/json" @@ -10,13 +10,11 @@ import ( "time" "github.com/containers/podman/v4/pkg/machine" - "github.com/containers/podman/v4/pkg/machine/qemu" "github.com/containers/podman/v4/pkg/util" "github.com/containers/storage/pkg/stringid" - . "github.com/onsi/ginkgo" //nolint:golint,stylecheck + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/onsi/gomega/gexec" - . "github.com/onsi/gomega/gexec" //nolint:golint,stylecheck + . "github.com/onsi/gomega/gexec" ) var originalHomeDir = os.Getenv("HOME") @@ -36,7 +34,7 @@ type MachineTestBuilder interface { run() (*machineSession, error) } type machineSession struct { - *gexec.Session + *Session } type machineTestBuilder struct { @@ -47,10 +45,6 @@ type machineTestBuilder struct { podmanBinary string timeout time.Duration } -type qemuMachineInspectInfo struct { - State machine.Status - VM qemu.MachineVM -} // waitWithTimeout waits for a command to complete for a given // number of seconds @@ -121,7 +115,7 @@ func (m *machineTestBuilder) setCmd(mc machineCommand) *machineTestBuilder { // If no name for the machine exists, we set a random name. if !util.StringInSlice(m.name, m.names) { if len(m.name) < 1 { - m.name = randomString(12) + m.name = randomString() } m.names = append(m.names, m.name) } @@ -136,10 +130,10 @@ func (m *machineTestBuilder) setTimeout(timeout time.Duration) *machineTestBuild // toQemuInspectInfo is only for inspecting qemu machines. Other providers will need // to make their own. -func (mb *machineTestBuilder) toQemuInspectInfo() ([]machine.InspectInfo, int, error) { +func (m *machineTestBuilder) toQemuInspectInfo() ([]machine.InspectInfo, int, error) { args := []string{"machine", "inspect"} - args = append(args, mb.names...) - session, err := runWrapper(mb.podmanBinary, args, defaultTimeout, true) + args = append(args, m.names...) + session, err := runWrapper(m.podmanBinary, args, defaultTimeout, true) if err != nil { return nil, -1, err } @@ -175,9 +169,7 @@ func runWrapper(podmanBinary string, cmdArgs []string, timeout time.Duration, wa return &ms, nil } -func (m *machineTestBuilder) init() {} - // randomString returns a string of given length composed of random characters -func randomString(n int) string { +func randomString() string { return stringid.GenerateRandomID()[0:12] } diff --git a/pkg/machine/e2e/info_test.go b/pkg/machine/e2e/info_test.go index eeabb78af..fe0cfba32 100644 --- a/pkg/machine/e2e/info_test.go +++ b/pkg/machine/e2e/info_test.go @@ -1,7 +1,7 @@ -package e2e +package e2e_test import ( - "github.com/containers/podman/v4/cmd/podman/machine" + "github.com/containers/podman/v4/pkg/domain/entities" jsoniter "github.com/json-iterator/go" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -51,7 +51,7 @@ var _ = Describe("podman machine info", func() { Expect(err).NotTo(HaveOccurred()) Expect(infoSession).Should(Exit(0)) - infoReport := &machine.Info{} + infoReport := &entities.MachineInfo{} err = jsoniter.Unmarshal(infoSession.Bytes(), infoReport) Expect(err).To(BeNil()) }) diff --git a/pkg/machine/e2e/init_test.go b/pkg/machine/e2e/init_test.go index 40f140cae..b246dc4da 100644 --- a/pkg/machine/e2e/init_test.go +++ b/pkg/machine/e2e/init_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( "io/ioutil" @@ -78,7 +78,7 @@ var _ = Describe("podman machine init", func() { }) It("machine init with cpus, disk size, memory, timezone", func() { - name := randomString(12) + name := randomString() i := new(initMachine) session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath).withCPUs(2).withDiskSize(102).withMemory(4000).withTimezone("Pacific/Honolulu")).run() Expect(err).To(BeNil()) @@ -108,7 +108,7 @@ var _ = Describe("podman machine init", func() { switch runtime.GOOS { // os's handle memory differently case "linux": - Expect(memorySession.outputToString()).To(ContainSubstring("3821")) + Expect(memorySession.outputToString()).To(ContainSubstring("3822")) case "darwin": Expect(memorySession.outputToString()).To(ContainSubstring("3824")) default: @@ -130,7 +130,7 @@ var _ = Describe("podman machine init", func() { mount := tmpDir + ":/testmountdir" defer os.RemoveAll(tmpDir) - name := randomString(12) + name := randomString() i := new(initMachine) session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath).withVolume(mount)).run() Expect(err).To(BeNil()) diff --git a/pkg/machine/e2e/inspect_test.go b/pkg/machine/e2e/inspect_test.go index 93fb8cc2b..0ab928205 100644 --- a/pkg/machine/e2e/inspect_test.go +++ b/pkg/machine/e2e/inspect_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( "strings" @@ -52,15 +52,15 @@ var _ = Describe("podman machine stop", func() { }) It("inspect with go format", func() { - name := randomString(12) + name := randomString() i := new(initMachine) session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run() Expect(err).To(BeNil()) Expect(session).To(Exit(0)) // regular inspect should - inspectJson := new(inspectMachine) - inspectSession, err := mb.setName(name).setCmd(inspectJson).run() + inspectJSON := new(inspectMachine) + inspectSession, err := mb.setName(name).setCmd(inspectJSON).run() Expect(err).To(BeNil()) Expect(inspectSession).To(Exit(0)) diff --git a/pkg/machine/e2e/list_test.go b/pkg/machine/e2e/list_test.go index fb855c61e..5c7ae6c5e 100644 --- a/pkg/machine/e2e/list_test.go +++ b/pkg/machine/e2e/list_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( "strings" @@ -45,8 +45,8 @@ var _ = Describe("podman machine list", func() { It("list machines with quiet or noheading", func() { // Random names for machines to test list - name1 := randomString(12) - name2 := randomString(12) + name1 := randomString() + name2 := randomString() list := new(listMachine) firstList, err := mb.setCmd(list.withQuiet()).run() @@ -109,7 +109,7 @@ var _ = Describe("podman machine list", func() { It("list with --format", func() { // Random names for machines to test list - name1 := randomString(12) + name1 := randomString() i := new(initMachine) session, err := mb.setName(name1).setCmd(i.withImagePath(mb.imagePath)).run() @@ -135,7 +135,7 @@ var _ = Describe("podman machine list", func() { Expect(listSession2).To(Exit(0)) var listResponse []*entities.ListReporter - err = jsoniter.Unmarshal(listSession.Bytes(), &listResponse) + err = jsoniter.Unmarshal(listSession2.Bytes(), &listResponse) Expect(err).To(BeNil()) // table format includes the header diff --git a/pkg/machine/e2e/machine_test.go b/pkg/machine/e2e/machine_test.go index 7b063937d..5de04b9f7 100644 --- a/pkg/machine/e2e/machine_test.go +++ b/pkg/machine/e2e/machine_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( "fmt" @@ -57,7 +57,7 @@ var _ = BeforeSuite(func() { Fail(fmt.Sprintf("unable to create url for download: %q", err)) } now := time.Now() - if err := machine.DownloadVMImage(getMe, fqImageName+".xz"); err != nil { + if err := machine.DownloadVMImage(getMe, suiteImageName, fqImageName+".xz"); err != nil { Fail(fmt.Sprintf("unable to download machine image: %q", err)) } fmt.Println("Download took: ", time.Since(now).String()) diff --git a/pkg/machine/e2e/rm_test.go b/pkg/machine/e2e/rm_test.go index 43b8c594c..e33eaf702 100644 --- a/pkg/machine/e2e/rm_test.go +++ b/pkg/machine/e2e/rm_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( . "github.com/onsi/ginkgo" diff --git a/pkg/machine/e2e/set_test.go b/pkg/machine/e2e/set_test.go index 80cb89488..4839e33da 100644 --- a/pkg/machine/e2e/set_test.go +++ b/pkg/machine/e2e/set_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( "runtime" @@ -22,7 +22,7 @@ var _ = Describe("podman machine set", func() { }) It("set machine cpus, disk, memory", func() { - name := randomString(12) + name := randomString() i := new(initMachine) session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run() Expect(err).To(BeNil()) @@ -62,7 +62,7 @@ var _ = Describe("podman machine set", func() { switch runtime.GOOS { // it seems macos and linux handle memory differently case "linux": - Expect(memorySession.outputToString()).To(ContainSubstring("3821")) + Expect(memorySession.outputToString()).To(ContainSubstring("3822")) case "darwin": Expect(memorySession.outputToString()).To(ContainSubstring("3824")) default: @@ -75,7 +75,7 @@ var _ = Describe("podman machine set", func() { }) It("no settings should change if no flags", func() { - name := randomString(12) + name := randomString() i := new(initMachine) session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run() Expect(err).To(BeNil()) diff --git a/pkg/machine/e2e/ssh_test.go b/pkg/machine/e2e/ssh_test.go index 9ee31ac26..52d714c91 100644 --- a/pkg/machine/e2e/ssh_test.go +++ b/pkg/machine/e2e/ssh_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( . "github.com/onsi/ginkgo" @@ -20,17 +20,16 @@ var _ = Describe("podman machine ssh", func() { }) It("bad machine name", func() { - name := randomString(12) + name := randomString() ssh := sshMachine{} session, err := mb.setName(name).setCmd(ssh).run() Expect(err).To(BeNil()) Expect(session).To(Exit(125)) - // TODO seems like stderr is not being returned; re-enabled when fixed - //Expect(session.outputToString()).To(ContainSubstring("not exist")) + Expect(session.errorToString()).To(ContainSubstring("not exist")) }) It("ssh to non-running machine", func() { - name := randomString(12) + name := randomString() i := new(initMachine) session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run() Expect(err).To(BeNil()) @@ -39,13 +38,12 @@ var _ = Describe("podman machine ssh", func() { ssh := sshMachine{} sshSession, err := mb.setName(name).setCmd(ssh).run() Expect(err).To(BeNil()) - // TODO seems like stderr is not being returned; re-enabled when fixed - //Expect(sshSession.outputToString()).To(ContainSubstring("is not running")) + Expect(sshSession.errorToString()).To(ContainSubstring("is not running")) Expect(sshSession).To(Exit(125)) }) It("ssh to running machine and check os-type", func() { - name := randomString(12) + name := randomString() i := new(initMachine) session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath).withNow()).run() Expect(err).To(BeNil()) diff --git a/pkg/machine/e2e/start_test.go b/pkg/machine/e2e/start_test.go index 1de66eb9a..1f9405569 100644 --- a/pkg/machine/e2e/start_test.go +++ b/pkg/machine/e2e/start_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( "github.com/containers/podman/v4/pkg/machine" diff --git a/pkg/machine/e2e/stop_test.go b/pkg/machine/e2e/stop_test.go index 0c27045a6..621bbdb16 100644 --- a/pkg/machine/e2e/stop_test.go +++ b/pkg/machine/e2e/stop_test.go @@ -1,4 +1,4 @@ -package e2e +package e2e_test import ( . "github.com/onsi/ginkgo" diff --git a/pkg/machine/fcos.go b/pkg/machine/fcos.go index 4ccb99e96..246f92a19 100644 --- a/pkg/machine/fcos.go +++ b/pkg/machine/fcos.go @@ -13,6 +13,7 @@ import ( "path/filepath" "runtime" "strings" + "time" "github.com/coreos/stream-metadata-go/fedoracoreos" "github.com/coreos/stream-metadata-go/release" @@ -53,7 +54,7 @@ func NewFcosDownloader(vmType, vmName, imageStream string) (DistributionDownload return nil, err } - dataDir, err := GetDataDir(vmType) + cacheDir, err := GetCacheDir(vmType) if err != nil { return nil, err } @@ -62,15 +63,20 @@ func NewFcosDownloader(vmType, vmName, imageStream string) (DistributionDownload Download: Download{ Arch: getFcosArch(), Artifact: artifact, + CacheDir: cacheDir, Format: Format, ImageName: imageName, - LocalPath: filepath.Join(dataDir, imageName), + LocalPath: filepath.Join(cacheDir, imageName), Sha256sum: info.Sha256Sum, URL: url, VMName: vmName, }, } - fcd.Download.LocalUncompressedFile = fcd.getLocalUncompressedName() + dataDir, err := GetDataDir(vmType) + if err != nil { + return nil, err + } + fcd.Download.LocalUncompressedFile = fcd.getLocalUncompressedFile(dataDir) return fcd, nil } @@ -108,6 +114,13 @@ func (f FcosDownload) HasUsableCache() (bool, error) { return sum.Encoded() == f.Sha256sum, nil } +func (f FcosDownload) CleanCache() error { + // Set cached image to expire after 2 weeks + // FCOS refreshes around every 2 weeks, assume old images aren't needed + expire := 14 * 24 * time.Hour + return removeImageAfterExpire(f.CacheDir, expire) +} + func getFcosArch() string { var arch string // TODO fill in more architectures diff --git a/pkg/machine/fedora.go b/pkg/machine/fedora.go index 46e450418..497265269 100644 --- a/pkg/machine/fedora.go +++ b/pkg/machine/fedora.go @@ -7,50 +7,55 @@ import ( "errors" "fmt" "io" - "io/ioutil" + "os" + "path" + "strings" + "net/http" "net/url" - "os" "path/filepath" - "regexp" - - "github.com/sirupsen/logrus" + "time" ) const ( - githubURL = "http://github.com/fedora-cloud/docker-brew-fedora/" + githubLatestReleaseURL = "https://github.com/containers/podman-wsl-fedora/releases/latest/download/rootfs.tar.xz" ) -var fedoraxzRegex = regexp.MustCompile(`fedora[^\"]+xz`) - type FedoraDownload struct { Download } func NewFedoraDownloader(vmType, vmName, releaseStream string) (DistributionDownload, error) { - imageName, downloadURL, size, err := getFedoraDownload(releaseStream) + downloadURL, version, size, err := getFedoraDownload(githubLatestReleaseURL) if err != nil { return nil, err } - dataDir, err := GetDataDir(vmType) + cacheDir, err := GetCacheDir(vmType) if err != nil { return nil, err } + imageName := fmt.Sprintf("fedora-podman-%s.tar.xz", version) + f := FedoraDownload{ Download: Download{ Arch: getFcosArch(), Artifact: artifact, + CacheDir: cacheDir, Format: Format, ImageName: imageName, - LocalPath: filepath.Join(dataDir, imageName), + LocalPath: filepath.Join(cacheDir, imageName), URL: downloadURL, VMName: vmName, Size: size, }, } - f.Download.LocalUncompressedFile = f.getLocalUncompressedName() + dataDir, err := GetDataDir(vmType) + if err != nil { + return nil, err + } + f.Download.LocalUncompressedFile = f.getLocalUncompressedFile(dataDir) return f, nil } @@ -69,56 +74,42 @@ func (f FedoraDownload) HasUsableCache() (bool, error) { return info.Size() == f.Size, nil } -func truncRead(url string) ([]byte, error) { - resp, err := http.Get(url) - if err != nil { - return nil, err - } - - defer func() { - if err := resp.Body.Close(); err != nil { - logrus.Error(err) - } - }() +func (f FedoraDownload) CleanCache() error { + // Set cached image to expire after 2 weeks + expire := 14 * 24 * time.Hour + return removeImageAfterExpire(f.CacheDir, expire) +} - body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 10*1024*1024)) +func getFedoraDownload(releaseURL string) (*url.URL, string, int64, error) { + downloadURL, err := url.Parse(releaseURL) if err != nil { - return nil, err + return nil, "", -1, fmt.Errorf("invalid URL generated from discovered Fedora file: %s: %w", releaseURL, err) } - _, _ = io.Copy(io.Discard, resp.Body) - - return body, nil -} - -func getFedoraDownload(releaseStream string) (string, *url.URL, int64, error) { - dirURL := githubURL + "tree/" + releaseStream + "/" + getFcosArch() + "/" - body, err := truncRead(dirURL) + resp, err := http.Head(releaseURL) if err != nil { - return "", nil, -1, err + return nil, "", -1, fmt.Errorf("head request failed: %s: %w", releaseURL, err) } + _ = resp.Body.Close() + contentLen := resp.ContentLength - file := fedoraxzRegex.FindString(string(body)) - if len(file) == 0 { - return "", nil, -1, fmt.Errorf("could not locate Fedora download at %s", dirURL) + if resp.StatusCode != http.StatusOK { + return nil, "", -1, fmt.Errorf("head request failed: %s: %w", releaseURL, err) } - rawURL := githubURL + "raw/" + releaseStream + "/" + getFcosArch() + "/" - newLocation := rawURL + file - downloadURL, err := url.Parse(newLocation) - if err != nil { - return "", nil, -1, fmt.Errorf("invalid URL generated from discovered Fedora file: %s: %w", newLocation, err) - } + verURL := *downloadURL + verURL.Path = path.Join(path.Dir(downloadURL.Path), "version") - resp, err := http.Head(newLocation) + resp, err = http.Get(verURL.String()) if err != nil { - return "", nil, -1, fmt.Errorf("head request failed: %s: %w", newLocation, err) + return nil, "", -1, fmt.Errorf("get request failed: %s: %w", verURL.String(), err) } - _ = resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return "", nil, -1, fmt.Errorf("head request failed [%d] on download: %s", resp.StatusCode, newLocation) + defer resp.Body.Close() + bytes, err := io.ReadAll(&io.LimitedReader{R: resp.Body, N: 1024}) + if err != nil { + return nil, "", -1, fmt.Errorf("failed reading: %s: %w", verURL.String(), err) } - return file, downloadURL, resp.ContentLength, nil + return downloadURL, strings.TrimSpace(string(bytes)), contentLen, nil } diff --git a/pkg/machine/keys.go b/pkg/machine/keys.go index 0c7d7114e..94cbdac04 100644 --- a/pkg/machine/keys.go +++ b/pkg/machine/keys.go @@ -21,6 +21,9 @@ var sshCommand = []string{"ssh-keygen", "-N", "", "-t", "ed25519", "-f"} // CreateSSHKeys makes a priv and pub ssh key for interacting // the a VM. func CreateSSHKeys(writeLocation string) (string, error) { + if err := os.MkdirAll(filepath.Dir(writeLocation), 0700); err != nil { + return "", err + } if err := generatekeys(writeLocation); err != nil { return "", err } diff --git a/pkg/machine/pull.go b/pkg/machine/pull.go index 7e6f01bad..26b6adc67 100644 --- a/pkg/machine/pull.go +++ b/pkg/machine/pull.go @@ -5,6 +5,7 @@ package machine import ( "bufio" + "errors" "fmt" "io" "io/ioutil" @@ -39,6 +40,10 @@ func NewGenericDownloader(vmType, vmName, pullPath string) (DistributionDownload if err != nil { return nil, err } + cacheDir, err := GetCacheDir(vmType) + if err != nil { + return nil, err + } dl := Download{} // Is pullpath a file or url? getURL, err := url2.Parse(pullPath) @@ -48,25 +53,23 @@ func NewGenericDownloader(vmType, vmName, pullPath string) (DistributionDownload if len(getURL.Scheme) > 0 { urlSplit := strings.Split(getURL.Path, "/") imageName = urlSplit[len(urlSplit)-1] - dl.LocalUncompressedFile = filepath.Join(dataDir, imageName) dl.URL = getURL - dl.LocalPath = filepath.Join(dataDir, imageName) + dl.LocalPath = filepath.Join(cacheDir, imageName) } else { // Dealing with FilePath imageName = filepath.Base(pullPath) - dl.LocalUncompressedFile = filepath.Join(dataDir, imageName) dl.LocalPath = pullPath } dl.VMName = vmName dl.ImageName = imageName + dl.LocalUncompressedFile = filepath.Join(dataDir, imageName) // The download needs to be pulled into the datadir gd := GenericDownload{Download: dl} - gd.LocalUncompressedFile = gd.getLocalUncompressedName() return gd, nil } -func (d Download) getLocalUncompressedName() string { +func (d Download) getLocalUncompressedFile(dataDir string) string { var ( extension string ) @@ -78,8 +81,8 @@ func (d Download) getLocalUncompressedName() string { case strings.HasSuffix(d.LocalPath, ".xz"): extension = ".xz" } - uncompressedFilename := filepath.Join(filepath.Dir(d.LocalPath), d.VMName+"_"+d.ImageName) - return strings.TrimSuffix(uncompressedFilename, extension) + uncompressedFilename := d.VMName + "_" + d.ImageName + return filepath.Join(dataDir, strings.TrimSuffix(uncompressedFilename, extension)) } func (g GenericDownload) Get() *Download { @@ -91,6 +94,18 @@ func (g GenericDownload) HasUsableCache() (bool, error) { return g.URL == nil, nil } +// CleanCache cleans out downloaded uncompressed image files +func (g GenericDownload) CleanCache() error { + // Remove any image that has been downloaded via URL + // We never read from cache for generic downloads + if g.URL != nil { + if err := os.Remove(g.LocalPath); err != nil && !errors.Is(err, os.ErrNotExist) { + return err + } + } + return nil +} + func DownloadImage(d DistributionDownload) error { // check if the latest image is already present ok, err := d.HasUsableCache() @@ -98,16 +113,22 @@ func DownloadImage(d DistributionDownload) error { return err } if !ok { - if err := DownloadVMImage(d.Get().URL, d.Get().LocalPath); err != nil { + if err := DownloadVMImage(d.Get().URL, d.Get().ImageName, d.Get().LocalPath); err != nil { return err } + // Clean out old cached images, since we didn't find needed image in cache + defer func() { + if err = d.CleanCache(); err != nil { + logrus.Warnf("error cleaning machine image cache: %s", err) + } + }() } - return Decompress(d.Get().LocalPath, d.Get().getLocalUncompressedName()) + return Decompress(d.Get().LocalPath, d.Get().LocalUncompressedFile) } // DownloadVMImage downloads a VM image from url to given path // with download status -func DownloadVMImage(downloadURL *url2.URL, localImagePath string) error { +func DownloadVMImage(downloadURL *url2.URL, imageName string, localImagePath string) error { out, err := os.Create(localImagePath) if err != nil { return err @@ -132,8 +153,7 @@ func DownloadVMImage(downloadURL *url2.URL, localImagePath string) error { return fmt.Errorf("downloading VM image %s: %s", downloadURL, resp.Status) } size := resp.ContentLength - urlSplit := strings.Split(downloadURL.Path, "/") - prefix := "Downloading VM image: " + urlSplit[len(urlSplit)-1] + prefix := "Downloading VM image: " + imageName onComplete := prefix + ": done" p := mpb.New( @@ -253,3 +273,20 @@ func decompressEverythingElse(src string, output io.WriteCloser) error { _, err = io.Copy(output, uncompressStream) return err } + +func removeImageAfterExpire(dir string, expire time.Duration) error { + now := time.Now() + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + // Delete any cache files that are older than expiry date + if !info.IsDir() && (now.Sub(info.ModTime()) > expire) { + err := os.Remove(path) + if err != nil && !errors.Is(err, os.ErrNotExist) { + logrus.Warnf("unable to clean up cached image: %s", path) + } else { + logrus.Debugf("cleaning up cached image: %s", path) + } + } + return nil + }) + return err +} diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 6134e69e1..7974c261e 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -240,20 +240,6 @@ func (p *Provider) LoadVMByName(name string) (machine.VM, error) { return nil, err } - // It is here for providing the ability to propagate - // proxy settings (e.g. HTTP_PROXY and others) on a start - // and avoid a need of re-creating/re-initiating a VM - if proxyOpts := machine.GetProxyVariables(); len(proxyOpts) > 0 { - proxyStr := "name=opt/com.coreos/environment,string=" - var proxies string - for k, v := range proxyOpts { - proxies = fmt.Sprintf("%s%s=\"%s\"|", proxies, k, v) - } - proxyStr = fmt.Sprintf("%s%s", proxyStr, base64.StdEncoding.EncodeToString([]byte(proxies))) - vm.CmdLine = append(vm.CmdLine, "-fw_cfg", proxyStr) - } - - logrus.Debug(vm.CmdLine) return vm, nil } @@ -573,15 +559,29 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { attr := new(os.ProcAttr) files := []*os.File{dnr, dnw, dnw, fd} attr.Files = files - logrus.Debug(v.CmdLine) cmdLine := v.CmdLine + // It is here for providing the ability to propagate + // proxy settings (e.g. HTTP_PROXY and others) on a start + // and avoid a need of re-creating/re-initiating a VM + if proxyOpts := machine.GetProxyVariables(); len(proxyOpts) > 0 { + proxyStr := "name=opt/com.coreos/environment,string=" + var proxies string + for k, v := range proxyOpts { + proxies = fmt.Sprintf("%s%s=\"%s\"|", proxies, k, v) + } + proxyStr = fmt.Sprintf("%s%s", proxyStr, base64.StdEncoding.EncodeToString([]byte(proxies))) + cmdLine = append(cmdLine, "-fw_cfg", proxyStr) + } + // Disable graphic window when not in debug mode // Done in start, so we're not suck with the debug level we used on init if !logrus.IsLevelEnabled(logrus.DebugLevel) { cmdLine = append(cmdLine, "-display", "none") } + logrus.Debugf("qemu cmd: %v", cmdLine) + stderrBuf := &bytes.Buffer{} cmd := &exec.Cmd{ @@ -670,11 +670,11 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { // because / is immutable, we have to monkey around with permissions // if we dont mount in /home or /mnt args := []string{"-q", "--"} - if !strings.HasPrefix(mount.Target, "/home") || !strings.HasPrefix(mount.Target, "/mnt") { + if !strings.HasPrefix(mount.Target, "/home") && !strings.HasPrefix(mount.Target, "/mnt") { args = append(args, "sudo", "chattr", "-i", "/", ";") } args = append(args, "sudo", "mkdir", "-p", mount.Target) - if !strings.HasPrefix(mount.Target, "/home") || !strings.HasPrefix(mount.Target, "/mnt") { + if !strings.HasPrefix(mount.Target, "/home") && !strings.HasPrefix(mount.Target, "/mnt") { args = append(args, ";", "sudo", "chattr", "+i", "/", ";") } err = v.SSH(name, machine.SSHOptions{Args: args}) diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index 450d8b877..189723ac7 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -386,7 +386,7 @@ func downloadDistro(v *MachineVM, opts machine.InitOptions) error { if _, e := strconv.Atoi(opts.ImagePath); e == nil { v.ImageStream = opts.ImagePath - dd, err = machine.NewFedoraDownloader(vmtype, v.Name, v.ImageStream) + dd, err = machine.NewFedoraDownloader(vmtype, v.Name, opts.ImagePath) } else { v.ImageStream = "custom" dd, err = machine.NewGenericDownloader(vmtype, v.Name, opts.ImagePath) @@ -449,34 +449,14 @@ func provisionWSLDist(v *MachineVM) (string, error) { } dist := toDist(v.Name) - fmt.Println("Importing operating system into WSL (this may take 5+ minutes on a new WSL install)...") + fmt.Println("Importing operating system into WSL (this may take a few minutes on a new WSL install)...") if err = runCmdPassThrough("wsl", "--import", dist, distTarget, v.ImagePath); err != nil { return "", fmt.Errorf("the WSL import of guest OS failed: %w", err) } - fmt.Println("Installing packages (this will take awhile)...") - if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "upgrade", "-y"); err != nil { - return "", fmt.Errorf("package upgrade on guest OS failed: %w", err) - } - - fmt.Println("Enabling Copr") - if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "install", "-y", "'dnf-command(copr)'"); err != nil { - return "", fmt.Errorf("enabling copr failed: %w", err) - } - - fmt.Println("Enabling podman4 repo") - if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "-y", "copr", "enable", "rhcontainerbot/podman4"); err != nil { - return "", fmt.Errorf("enabling copr failed: %w", err) - } - - if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "install", - "podman", "podman-docker", "openssh-server", "procps-ng", "-y"); err != nil { - return "", fmt.Errorf("package installation on guest OS failed: %w", err) - } - // Fixes newuidmap - if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "reinstall", "shadow-utils", "-y"); err != nil { - return "", fmt.Errorf("package reinstallation of shadow-utils on guest OS failed: %w", err) + if err = runCmdPassThrough("wsl", "-d", dist, "rpm", "-q", "--restore", "shadow-utils", "2>/dev/null"); err != nil { + return "", fmt.Errorf("package permissions restore of shadow-utils on guest OS failed: %w", err) } // Windows 11 (NT Version = 10, Build 22000) generates harmless but scary messages on every |