//go:build amd64 || arm64 // +build amd64 arm64 package machine import ( "fmt" "os" "github.com/containers/common/pkg/completion" "github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/pkg/machine" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" ) var ( initCmd = &cobra.Command{ Use: "init [options] [NAME]", Short: "Initialize a virtual machine", Long: "initialize a virtual machine ", RunE: initMachine, Args: cobra.MaximumNArgs(1), Example: `podman machine init myvm`, ValidArgsFunction: completion.AutocompleteNone, } ) var ( initOpts = machine.InitOptions{} defaultMachineName = machine.DefaultMachineName now bool ) // maxMachineNameSize is set to thirty to limit huge machine names primarily // because macos has a much smaller file size limit. const maxMachineNameSize = 30 func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Command: initCmd, Parent: machineCmd, }) flags := initCmd.Flags() cfg := registry.PodmanConfig() initOpts.Username = cfg.Config.Machine.User cpusFlagName := "cpus" flags.Uint64Var( &initOpts.CPUS, cpusFlagName, cfg.Machine.CPUs, "Number of CPUs", ) _ = initCmd.RegisterFlagCompletionFunc(cpusFlagName, completion.AutocompleteNone) diskSizeFlagName := "disk-size" flags.Uint64Var( &initOpts.DiskSize, diskSizeFlagName, cfg.Machine.DiskSize, "Disk size in GB", ) _ = initCmd.RegisterFlagCompletionFunc(diskSizeFlagName, completion.AutocompleteNone) memoryFlagName := "memory" flags.Uint64VarP( &initOpts.Memory, memoryFlagName, "m", cfg.Machine.Memory, "Memory in MB", ) _ = initCmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) flags.BoolVar( &now, "now", false, "Start machine now", ) timezoneFlagName := "timezone" defaultTz := cfg.TZ() if len(defaultTz) < 1 { defaultTz = "local" } flags.StringVar(&initOpts.TimeZone, timezoneFlagName, defaultTz, "Set timezone") _ = initCmd.RegisterFlagCompletionFunc(timezoneFlagName, completion.AutocompleteDefault) flags.BoolVar( &initOpts.ReExec, "reexec", false, "process was rexeced", ) _ = flags.MarkHidden("reexec") ImagePathFlagName := "image-path" flags.StringVar(&initOpts.ImagePath, ImagePathFlagName, cfg.Machine.Image, "Path to qcow image") _ = initCmd.RegisterFlagCompletionFunc(ImagePathFlagName, completion.AutocompleteDefault) VolumeFlagName := "volume" flags.StringArrayVarP(&initOpts.Volumes, VolumeFlagName, "v", cfg.Machine.Volumes, "Volumes to mount, source:target") _ = initCmd.RegisterFlagCompletionFunc(VolumeFlagName, completion.AutocompleteDefault) VolumeDriverFlagName := "volume-driver" flags.StringVar(&initOpts.VolumeDriver, VolumeDriverFlagName, "", "Optional volume driver") _ = initCmd.RegisterFlagCompletionFunc(VolumeDriverFlagName, completion.AutocompleteDefault) IgnitionPathFlagName := "ignition-path" flags.StringVar(&initOpts.IgnitionPath, IgnitionPathFlagName, "", "Path to ignition file") _ = initCmd.RegisterFlagCompletionFunc(IgnitionPathFlagName, completion.AutocompleteDefault) rootfullFlagName := "rootfull" flags.BoolVar(&initOpts.Rootfull, rootfullFlagName, false, "Whether this machine should prefer rootfull container execution") flags.SetNormalizeFunc(aliasFlags) } // aliasFlags is a function to handle backwards compatibility with old flags func aliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName { switch name { case "rootful": name = "rootfull" } return pflag.NormalizedName(name) } // TODO should we allow for a users to append to the qemu cmdline? func initMachine(cmd *cobra.Command, args []string) error { var ( err error vm machine.VM ) provider := getSystemDefaultProvider() initOpts.Name = defaultMachineName if len(args) > 0 { if len(args[0]) > maxMachineNameSize { return errors.New("machine name must be 30 characters or less") } initOpts.Name = args[0] } if _, err := provider.LoadVMByName(initOpts.Name); err == nil { return errors.Wrap(machine.ErrVMAlreadyExists, initOpts.Name) } for idx, vol := range initOpts.Volumes { initOpts.Volumes[idx] = os.ExpandEnv(vol) } vm, err = provider.NewMachine(initOpts) if err != nil { return err } if finished, err := vm.Init(initOpts); err != nil || !finished { // Finished = true, err = nil - Success! Log a message with further instructions // Finished = false, err = nil - The installation is partially complete and podman should // exit gracefully with no error and no success message. // Examples: // - a user has chosen to perform their own reboot // - reexec for limited admin operations, returning to parent // Finished = *, err != nil - Exit with an error message return err } fmt.Println("Machine init complete") if now { err = vm.Start(initOpts.Name, machine.StartOptions{}) if err == nil { fmt.Printf("Machine %q started successfully\n", initOpts.Name) } } else { extra := "" if initOpts.Name != defaultMachineName { extra = " " + initOpts.Name } fmt.Printf("To start your machine run:\n\n\tpodman machine start%s\n\n", extra) } return err }