diff --git a/cmd/run.go b/cmd/run.go index 53926342..f8df0d56 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -1,10 +1,7 @@ package cmd import ( - "context" - "errors" "fmt" - "os" "time" "github.com/containers/podman-bootc/pkg/bootc" @@ -13,7 +10,6 @@ import ( "github.com/containers/podman-bootc/pkg/utils" "github.com/containers/podman-bootc/pkg/vm" - "github.com/containers/podman/v5/pkg/bindings" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -66,42 +62,16 @@ func doRun(flags *cobra.Command, args []string) error { return fmt.Errorf("unable to get user: %w", err) } - //podman machine connection - machineInfo, err := utils.GetMachineInfo() - if err != nil { - return err - } - - if machineInfo == nil { - println(utils.PodmanMachineErrorMessage) - return errors.New("rootful podman machine is required, please run 'podman machine init --rootful'") - } - - if !machineInfo.Rootful { - println(utils.PodmanMachineErrorMessage) - return errors.New("rootful podman machine is required, please run 'podman machine set --rootful'") - } - - if _, err := os.Stat(machineInfo.PodmanSocket); err != nil { - println(utils.PodmanMachineErrorMessage) - logrus.Errorf("podman machine socket is missing. Is podman machine running?\n%s", err) - return err - } - - ctx, err := bindings.NewConnectionWithIdentity( - context.Background(), - fmt.Sprintf("unix://%s", machineInfo.PodmanSocket), - machineInfo.SSHIdentityPath, - true) + machine, err := utils.GetMachineContext() if err != nil { println(utils.PodmanMachineErrorMessage) - logrus.Errorf("failed to connect to the podman socket. Is podman machine running?\n%s", err) + logrus.Errorf("failed to connect to podman machine. Is podman machine running?\n%s", err) return err } // create the disk image idOrName := args[0] - bootcDisk := bootc.NewBootcDisk(idOrName, ctx, user) + bootcDisk := bootc.NewBootcDisk(idOrName, machine.Ctx, user) err = bootcDisk.Install(vmConfig.Quiet, diskImageConfigInstance) if err != nil { @@ -143,7 +113,7 @@ func doRun(flags *cobra.Command, args []string) error { RemoveVm: vmConfig.RemoveVm, Background: vmConfig.Background, SSHPort: sshPort, - SSHIdentity: machineInfo.SSHIdentityPath, + SSHIdentity: machine.SSHIdentityPath, VMUser: vmConfig.User, }) diff --git a/pkg/bootc/bootc_disk.go b/pkg/bootc/bootc_disk.go index d8f27c71..ce9bfb13 100644 --- a/pkg/bootc/bootc_disk.go +++ b/pkg/bootc/bootc_disk.go @@ -18,7 +18,6 @@ import ( "github.com/containers/podman-bootc/pkg/utils" "github.com/containers/podman/v5/pkg/bindings/containers" - "github.com/containers/podman/v5/pkg/bindings/images" "github.com/containers/podman/v5/pkg/domain/entities/types" "github.com/containers/podman/v5/pkg/specgen" "github.com/docker/go-units" @@ -277,32 +276,17 @@ func (p *BootcDisk) bootcInstallImageToDisk(quiet bool, diskConfig DiskImageConf } // pullImage fetches the container image if not present -func (p *BootcDisk) pullImage() (err error) { - pullPolicy := "missing" - ids, err := images.Pull(p.Ctx, p.ImageNameOrId, &images.PullOptions{Policy: &pullPolicy}) +func (p *BootcDisk) pullImage() error { + imageData, err := utils.PullAndInspect(p.Ctx, p.ImageNameOrId) if err != nil { - return fmt.Errorf("failed to pull image: %w", err) - } - - if len(ids) == 0 { - return fmt.Errorf("no ids returned from image pull") - } - - if len(ids) > 1 { - return fmt.Errorf("multiple ids returned from image pull") - } - - image, err := images.GetImage(p.Ctx, p.ImageNameOrId, &images.GetOptions{}) - if err != nil { - return fmt.Errorf("failed to get image: %w", err) + return err } - p.imageData = image - imageId := ids[0] - p.ImageId = imageId - p.RepoTag = image.RepoTags[0] + p.imageData = imageData + p.ImageId = imageData.ID + p.RepoTag = imageData.RepoTags[0] - return + return nil } // runInstallContainer runs the bootc installer in a container to create a disk image diff --git a/pkg/utils/podman.go b/pkg/utils/podman.go index 35c0d3b6..7e2663e5 100644 --- a/pkg/utils/podman.go +++ b/pkg/utils/podman.go @@ -1,12 +1,17 @@ package utils import ( + "context" "encoding/json" "errors" "fmt" + "github.com/containers/podman/v5/pkg/bindings/images" + "github.com/containers/podman/v5/pkg/domain/entities/types" + "os" "os/exec" "strings" + "github.com/containers/podman/v5/pkg/bindings" "github.com/containers/podman/v5/pkg/machine" "github.com/containers/podman/v5/pkg/machine/define" "github.com/containers/podman/v5/pkg/machine/env" @@ -14,14 +19,70 @@ import ( "github.com/containers/podman/v5/pkg/machine/vmconfigs" ) -type MachineInfo struct { - PodmanSocket string +type MachineContext struct { + Ctx context.Context SSHIdentityPath string - Rootful bool } -func GetMachineInfo() (*MachineInfo, error) { - minfo, err := getMachineInfo() +type machineInfo struct { + podmanSocket string + sshIdentityPath string + rootful bool +} + +// PullAndInspect inpects the image, pulling in if the image if required +func PullAndInspect(ctx context.Context, imageNameOrId string) (*types.ImageInspectReport, error) { + pullPolicy := "missing" + _, err := images.Pull(ctx, imageNameOrId, &images.PullOptions{Policy: &pullPolicy}) + if err != nil { + return nil, fmt.Errorf("failed to pull image: %w", err) + } + + imageInfo, err := images.GetImage(ctx, imageNameOrId, &images.GetOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to inspect image: %w", err) + } + + return imageInfo, nil +} + +func GetMachineContext() (*MachineContext, error) { + //podman machine connection + machineInfo, err := getMachineInfo() + if err != nil { + return nil, fmt.Errorf("unable to get podman machine info: %w", err) + } + + if machineInfo == nil { + return nil, errors.New("rootful podman machine is required, please run 'podman machine init --rootful'") + } + + if !machineInfo.rootful { + return nil, errors.New("rootful podman machine is required, please run 'podman machine set --rootful'") + } + + if _, err := os.Stat(machineInfo.podmanSocket); err != nil { + return nil, fmt.Errorf("podman machine socket is missing: %w", err) + } + + ctx, err := bindings.NewConnectionWithIdentity( + context.Background(), + fmt.Sprintf("unix://%s", machineInfo.podmanSocket), + machineInfo.sshIdentityPath, + true) + if err != nil { + return nil, fmt.Errorf("failed to connect to the podman socket: %w", err) + } + + mc := &MachineContext{ + Ctx: ctx, + SSHIdentityPath: machineInfo.sshIdentityPath, + } + return mc, nil +} + +func getMachineInfo() (*machineInfo, error) { + minfo, err := getPv5MachineInfo() if err != nil { var errIncompatibleMachineConfig *define.ErrIncompatibleMachineConfig var errVMDoesNotExist *define.ErrVMDoesNotExist @@ -39,7 +100,7 @@ func GetMachineInfo() (*MachineInfo, error) { } // Get podman v5 machine info -func getMachineInfo() (*MachineInfo, error) { +func getPv5MachineInfo() (*machineInfo, error) { prov, err := provider.Get() if err != nil { return nil, fmt.Errorf("getting podman machine provider: %w", err) @@ -60,16 +121,16 @@ func getMachineInfo() (*MachineInfo, error) { return nil, fmt.Errorf("getting podman machine connection info: %w", err) } - pmi := MachineInfo{ - PodmanSocket: podmanSocket.GetPath(), - SSHIdentityPath: pm.SSH.IdentityPath, - Rootful: pm.HostUser.Rootful, + pmi := machineInfo{ + podmanSocket: podmanSocket.GetPath(), + sshIdentityPath: pm.SSH.IdentityPath, + rootful: pm.HostUser.Rootful, } return &pmi, nil } // Just to support podman v4.9, it will be removed in the future -func getPv4MachineInfo() (*MachineInfo, error) { +func getPv4MachineInfo() (*machineInfo, error) { //check if a default podman machine exists listCmd := exec.Command("podman", "machine", "list", "--format", "json") var listCmdOutput strings.Builder @@ -135,9 +196,9 @@ func getPv4MachineInfo() (*MachineInfo, error) { return nil, errors.New("no podman machine found") } - return &MachineInfo{ - PodmanSocket: machineInspect[0].ConnectionInfo.PodmanSocket.Path, - SSHIdentityPath: machineInspect[0].SSHConfig.IdentityPath, - Rootful: machineInspect[0].Rootful, + return &machineInfo{ + podmanSocket: machineInspect[0].ConnectionInfo.PodmanSocket.Path, + sshIdentityPath: machineInspect[0].SSHConfig.IdentityPath, + rootful: machineInspect[0].Rootful, }, nil } diff --git a/podman-bootc.go b/podman-bootc.go index d7f418dc..93992be8 100644 --- a/podman-bootc.go +++ b/podman-bootc.go @@ -1,8 +1,6 @@ package main import ( - "context" - "fmt" "os" "os/signal" "syscall" @@ -12,7 +10,6 @@ import ( "github.com/containers/podman-bootc/pkg/user" "github.com/containers/podman-bootc/pkg/utils" - "github.com/containers/podman/v5/pkg/bindings" "github.com/sirupsen/logrus" ) @@ -23,41 +20,15 @@ func cleanup() { os.Exit(0) } - //podman machine connection - machineInfo, err := utils.GetMachineInfo() - if err != nil { - logrus.Errorf("unable to get podman machine info: %s", err) - os.Exit(1) - } - - if machineInfo == nil { - logrus.Errorf("rootful podman machine is required, please run 'podman machine init --rootful'") - os.Exit(1) - } - - if !machineInfo.Rootful { - logrus.Errorf("rootful podman machine is required, please run 'podman machine set --rootful'") - os.Exit(1) - } - - if _, err := os.Stat(machineInfo.PodmanSocket); err != nil { - logrus.Errorf("podman machine socket is missing. Is podman machine running?\n%s", err) - os.Exit(1) - } - - ctx, err := bindings.NewConnectionWithIdentity( - context.Background(), - fmt.Sprintf("unix://%s", machineInfo.PodmanSocket), - machineInfo.SSHIdentityPath, - true) + machine, err := utils.GetMachineContext() if err != nil { println(utils.PodmanMachineErrorMessage) - logrus.Errorf("failed to connect to the podman socket. Is podman machine running?\n%s", err) + logrus.Errorf("failed to connect to podman machine. Is podman machine running?\n%s", err) os.Exit(1) } //delete the disk image - err = bootc.NewBootcDisk("", ctx, user).Cleanup() + err = bootc.NewBootcDisk("", machine.Ctx, user).Cleanup() if err != nil { logrus.Errorf("unable to get podman machine info: %s", err) os.Exit(0)