Implement machine inspect for WSL
Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
This commit is contained in:
parent
80315b9c86
commit
5b78f9576c
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/podman/v4/libpod/define"
|
|
||||||
"github.com/containers/podman/v4/pkg/machine"
|
"github.com/containers/podman/v4/pkg/machine"
|
||||||
"github.com/containers/podman/v4/utils"
|
"github.com/containers/podman/v4/utils"
|
||||||
"github.com/containers/storage/pkg/homedir"
|
"github.com/containers/storage/pkg/homedir"
|
||||||
|
|
@ -153,20 +152,22 @@ const (
|
||||||
type Provider struct{}
|
type Provider struct{}
|
||||||
|
|
||||||
type MachineVM struct {
|
type MachineVM struct {
|
||||||
// IdentityPath is the fq path to the ssh priv key
|
// ConfigPath is the path to the configuration file
|
||||||
IdentityPath string
|
ConfigPath string
|
||||||
|
// Created contains the original created time instead of querying the file mod time
|
||||||
|
Created time.Time
|
||||||
// ImageStream is the version of fcos being used
|
// ImageStream is the version of fcos being used
|
||||||
ImageStream string
|
ImageStream string
|
||||||
// ImagePath is the fq path to
|
// ImagePath is the fq path to
|
||||||
ImagePath string
|
ImagePath string
|
||||||
|
// LastUp contains the last recorded uptime
|
||||||
|
LastUp time.Time
|
||||||
// Name of the vm
|
// Name of the vm
|
||||||
Name string
|
Name string
|
||||||
// SSH port for user networking
|
|
||||||
Port int
|
|
||||||
// RemoteUsername of the vm user
|
|
||||||
RemoteUsername string
|
|
||||||
// Whether this machine should run in a rootful or rootless manner
|
// Whether this machine should run in a rootful or rootless manner
|
||||||
Rootful bool
|
Rootful bool
|
||||||
|
// SSH identity, username, etc
|
||||||
|
machine.SSHConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExitCodeError struct {
|
type ExitCodeError struct {
|
||||||
|
|
@ -181,16 +182,22 @@ func GetWSLProvider() machine.Provider {
|
||||||
return wslProvider
|
return wslProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMachine initializes an instance of a virtual machine based on the qemu
|
// NewMachine initializes an instance of a wsl machine
|
||||||
// virtualization.
|
|
||||||
func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
||||||
vm := new(MachineVM)
|
vm := new(MachineVM)
|
||||||
if len(opts.Name) > 0 {
|
if len(opts.Name) > 0 {
|
||||||
vm.Name = opts.Name
|
vm.Name = opts.Name
|
||||||
}
|
}
|
||||||
|
configPath, err := getConfigPath(opts.Name)
|
||||||
|
if err != nil {
|
||||||
|
return vm, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.ConfigPath = configPath
|
||||||
vm.ImagePath = opts.ImagePath
|
vm.ImagePath = opts.ImagePath
|
||||||
vm.RemoteUsername = opts.Username
|
vm.RemoteUsername = opts.Username
|
||||||
|
vm.Created = time.Now()
|
||||||
|
vm.LastUp = vm.Created
|
||||||
|
|
||||||
// Add a random port for ssh
|
// Add a random port for ssh
|
||||||
port, err := utils.GetRandomPort()
|
port, err := utils.GetRandomPort()
|
||||||
|
|
@ -202,25 +209,69 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
||||||
return vm, nil
|
return vm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getConfigPath(name string) (string, error) {
|
||||||
|
vmConfigDir, err := machine.GetConfDir(vmtype)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(vmConfigDir, name+".json"), nil
|
||||||
|
}
|
||||||
|
|
||||||
// LoadByName reads a json file that describes a known qemu vm
|
// LoadByName reads a json file that describes a known qemu vm
|
||||||
// and returns a vm instance
|
// and returns a vm instance
|
||||||
func (p *Provider) LoadVMByName(name string) (machine.VM, error) {
|
func (p *Provider) LoadVMByName(name string) (machine.VM, error) {
|
||||||
|
configPath, err := getConfigPath(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vm, err := readAndMigrate(configPath, name)
|
||||||
|
return vm, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// readAndMigrate returns the content of the VM's
|
||||||
|
// configuration file in json
|
||||||
|
func readAndMigrate(configPath string, name string) (*MachineVM, error) {
|
||||||
vm := new(MachineVM)
|
vm := new(MachineVM)
|
||||||
vmConfigDir, err := machine.GetConfDir(vmtype)
|
b, err := os.ReadFile(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
}
|
return nil, errors.Wrap(machine.ErrNoSuchVM, name)
|
||||||
b, err := ioutil.ReadFile(filepath.Join(vmConfigDir, name+".json"))
|
}
|
||||||
if os.IsNotExist(err) {
|
return vm, err
|
||||||
return nil, errors.Wrap(machine.ErrNoSuchVM, name)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(b, vm)
|
err = json.Unmarshal(b, vm)
|
||||||
|
if err == nil && vm.Created.IsZero() {
|
||||||
|
err = vm.migrate40(configPath)
|
||||||
|
}
|
||||||
return vm, err
|
return vm, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *MachineVM) migrate40(configPath string) error {
|
||||||
|
v.ConfigPath = configPath
|
||||||
|
fi, err := os.Stat(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.Created = fi.ModTime()
|
||||||
|
v.LastUp = getLegacyLastStart(v)
|
||||||
|
return v.writeConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLegacyLastStart(vm *MachineVM) time.Time {
|
||||||
|
vmDataDir, err := machine.GetDataDir(vmtype)
|
||||||
|
if err != nil {
|
||||||
|
return vm.Created
|
||||||
|
}
|
||||||
|
distDir := filepath.Join(vmDataDir, "wsldist")
|
||||||
|
start := filepath.Join(distDir, vm.Name, "laststart")
|
||||||
|
info, err := os.Stat(start)
|
||||||
|
if err != nil {
|
||||||
|
return vm.Created
|
||||||
|
}
|
||||||
|
return info.ModTime()
|
||||||
|
}
|
||||||
|
|
||||||
// Init writes the json configuration file to the filesystem for
|
// Init writes the json configuration file to the filesystem for
|
||||||
// other verbs (start, stop)
|
// other verbs (start, stop)
|
||||||
func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
|
func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
|
||||||
|
|
@ -289,12 +340,7 @@ func downloadDistro(v *MachineVM, opts machine.InitOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MachineVM) writeConfig() error {
|
func (v *MachineVM) writeConfig() error {
|
||||||
vmConfigDir, err := machine.GetConfDir(vmtype)
|
jsonFile := v.ConfigPath
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonFile := filepath.Join(vmConfigDir, v.Name) + ".json"
|
|
||||||
|
|
||||||
b, err := json.MarshalIndent(v, "", " ")
|
b, err := json.MarshalIndent(v, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -810,7 +856,8 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return markStart(name)
|
_, _, err = v.updateTimeStamps(true)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func launchWinProxy(v *MachineVM) (bool, string, error) {
|
func launchWinProxy(v *MachineVM) (bool, string, error) {
|
||||||
|
|
@ -1005,6 +1052,8 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
|
||||||
return errors.Errorf("%q is not running", v.Name)
|
return errors.Errorf("%q is not running", v.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, _, _ = v.updateTimeStamps(true)
|
||||||
|
|
||||||
if err := stopWinProxy(v); err != nil {
|
if err := stopWinProxy(v); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Could not stop API forwarding service (win-sshproxy.exe): %s\n", err.Error())
|
fmt.Fprintf(os.Stderr, "Could not stop API forwarding service (win-sshproxy.exe): %s\n", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -1032,10 +1081,12 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We need to rename isRunning to State(); I do not have a
|
|
||||||
// windows system to test this on.
|
|
||||||
func (v *MachineVM) State(bypass bool) (machine.Status, error) {
|
func (v *MachineVM) State(bypass bool) (machine.Status, error) {
|
||||||
return "", define.ErrNotImplemented
|
if v.isRunning() {
|
||||||
|
return machine.Running, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return machine.Stopped, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopWinProxy(v *MachineVM) error {
|
func stopWinProxy(v *MachineVM) error {
|
||||||
|
|
@ -1210,14 +1261,9 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
|
||||||
var listed []*machine.ListResponse
|
var listed []*machine.ListResponse
|
||||||
|
|
||||||
if err = filepath.WalkDir(vmConfigDir, func(path string, d fs.DirEntry, err error) error {
|
if err = filepath.WalkDir(vmConfigDir, func(path string, d fs.DirEntry, err error) error {
|
||||||
vm := new(MachineVM)
|
|
||||||
if strings.HasSuffix(d.Name(), ".json") {
|
if strings.HasSuffix(d.Name(), ".json") {
|
||||||
fullPath := filepath.Join(vmConfigDir, d.Name())
|
path := filepath.Join(vmConfigDir, d.Name())
|
||||||
b, err := ioutil.ReadFile(fullPath)
|
vm, err := readAndMigrate(path, strings.TrimSuffix(d.Name(), ".json"))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(b, vm)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -1229,15 +1275,13 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
|
||||||
listEntry.CPUs, _ = getCPUs(vm)
|
listEntry.CPUs, _ = getCPUs(vm)
|
||||||
listEntry.Memory, _ = getMem(vm)
|
listEntry.Memory, _ = getMem(vm)
|
||||||
listEntry.DiskSize = getDiskSize(vm)
|
listEntry.DiskSize = getDiskSize(vm)
|
||||||
fi, err := os.Stat(fullPath)
|
listEntry.RemoteUsername = vm.RemoteUsername
|
||||||
if err != nil {
|
listEntry.Port = vm.Port
|
||||||
return err
|
listEntry.IdentityPath = vm.IdentityPath
|
||||||
}
|
|
||||||
listEntry.CreatedAt = fi.ModTime()
|
running := vm.isRunning()
|
||||||
listEntry.LastUp = getLastStart(vm, fi.ModTime())
|
listEntry.CreatedAt, listEntry.LastUp, _ = vm.updateTimeStamps(running)
|
||||||
if vm.isRunning() {
|
listEntry.Running = running
|
||||||
listEntry.Running = true
|
|
||||||
}
|
|
||||||
|
|
||||||
listed = append(listed, listEntry)
|
listed = append(listed, listEntry)
|
||||||
}
|
}
|
||||||
|
|
@ -1248,6 +1292,16 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
|
||||||
return listed, err
|
return listed, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vm *MachineVM) updateTimeStamps(updateLast bool) (time.Time, time.Time, error) {
|
||||||
|
var err error
|
||||||
|
if updateLast {
|
||||||
|
vm.LastUp = time.Now()
|
||||||
|
err = vm.writeConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm.Created, vm.LastUp, err
|
||||||
|
}
|
||||||
|
|
||||||
func getDiskSize(vm *MachineVM) uint64 {
|
func getDiskSize(vm *MachineVM) uint64 {
|
||||||
vmDataDir, err := machine.GetDataDir(vmtype)
|
vmDataDir, err := machine.GetDataDir(vmtype)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1262,36 +1316,6 @@ func getDiskSize(vm *MachineVM) uint64 {
|
||||||
return uint64(info.Size())
|
return uint64(info.Size())
|
||||||
}
|
}
|
||||||
|
|
||||||
func markStart(name string) error {
|
|
||||||
vmDataDir, err := machine.GetDataDir(vmtype)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
distDir := filepath.Join(vmDataDir, "wsldist")
|
|
||||||
start := filepath.Join(distDir, name, "laststart")
|
|
||||||
file, err := os.Create(start)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
file.Close()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLastStart(vm *MachineVM, created time.Time) time.Time {
|
|
||||||
vmDataDir, err := machine.GetDataDir(vmtype)
|
|
||||||
if err != nil {
|
|
||||||
return created
|
|
||||||
}
|
|
||||||
distDir := filepath.Join(vmDataDir, "wsldist")
|
|
||||||
start := filepath.Join(distDir, vm.Name, "laststart")
|
|
||||||
info, err := os.Stat(start)
|
|
||||||
if err != nil {
|
|
||||||
return created
|
|
||||||
}
|
|
||||||
return info.ModTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCPUs(vm *MachineVM) (uint64, error) {
|
func getCPUs(vm *MachineVM) (uint64, error) {
|
||||||
dist := toDist(vm.Name)
|
dist := toDist(vm.Name)
|
||||||
if run, _ := isWSLRunning(dist); !run {
|
if run, _ := isWSLRunning(dist); !run {
|
||||||
|
|
@ -1388,6 +1412,33 @@ func (v *MachineVM) setRootful(rootful bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inspect returns verbose detail about the machine
|
||||||
func (v *MachineVM) Inspect() (*machine.InspectInfo, error) {
|
func (v *MachineVM) Inspect() (*machine.InspectInfo, error) {
|
||||||
return nil, define.ErrNotImplemented
|
state, err := v.State(false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
created, lastUp, _ := v.updateTimeStamps(state == machine.Running)
|
||||||
|
|
||||||
|
return &machine.InspectInfo{
|
||||||
|
ConfigPath: machine.VMFile{Path: v.ConfigPath},
|
||||||
|
Created: created,
|
||||||
|
Image: machine.ImageConfig{
|
||||||
|
ImagePath: machine.VMFile{Path: v.ImagePath},
|
||||||
|
ImageStream: v.ImageStream,
|
||||||
|
},
|
||||||
|
LastUp: lastUp,
|
||||||
|
Name: v.Name,
|
||||||
|
Resources: v.getResources(),
|
||||||
|
SSHConfig: v.SSHConfig,
|
||||||
|
State: state,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MachineVM) getResources() (resources machine.ResourceConfig) {
|
||||||
|
resources.CPUs, _ = getCPUs(v)
|
||||||
|
resources.Memory, _ = getMem(v)
|
||||||
|
resources.DiskSize = getDiskSize(v)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue