machine starting status

podman machine was using the file modification time to get the running status
add three new config entries Starting (bool) Created (time) LastUp (time) to actually
keep track of when these events happened. This means we can use the config file
to actually store this data and not mess up the created/last-up time.

This fixes the issues where the machine would report running 15 seconds before it was up.
Also fixes the issue of modifying the file manually and saying the machine is "up"

[NO NEW TESTS NEEDED]

resolves #13711

Signed-off-by: cdoern <cbdoer23@g.holycross.edu>
This commit is contained in:
cdoern
2022-04-12 22:21:33 -04:00
parent ba6356280a
commit d441a711e5
6 changed files with 65 additions and 24 deletions

BIN
capture.pcap Normal file

Binary file not shown.

View File

@ -59,7 +59,7 @@ func inspect(cmd *cobra.Command, args []string) error {
errs = append(errs, err)
continue
}
state, err := vm.State()
state, err := vm.State(false)
if err != nil {
errs = append(errs, err)
continue

View File

@ -121,7 +121,7 @@ type VM interface {
Set(name string, opts SetOptions) error
SSH(name string, opts SSHOptions) error
Start(name string, opts StartOptions) error
State() (Status, error)
State(bypass bool) (Status, error)
Stop(name string, opts StopOptions) error
}

View File

@ -86,6 +86,12 @@ type MachineVM struct {
ResourceConfig
// SSHConfig for accessing the remote vm
SSHConfig
// Starting tells us whether the machine is running or if we have just dialed it to start it
Starting bool
// Created contains the original created time instead of querying the file mod time
Created time.Time
// LastUp contains the last recorded uptime
LastUp time.Time
}
// ImageConfig describes the bootable image for the VM

View File

@ -95,6 +95,8 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
vm.Memory = opts.Memory
vm.DiskSize = opts.DiskSize
vm.Created = time.Now()
// Find the qemu executable
cfg, err := config.Default()
if err != nil {
@ -439,7 +441,7 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) error {
return nil
}
state, err := v.State()
state, err := v.State(false)
if err != nil {
return err
}
@ -480,6 +482,17 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
wait = time.Millisecond * 500
)
v.Starting = true
if err := v.writeConfig(); err != nil {
return fmt.Errorf("writing JSON file: %w", err)
}
defer func() error {
v.Starting = false
if err := v.writeConfig(); err != nil {
return fmt.Errorf("writing JSON file: %w", err)
}
return nil
}()
if v.isIncompatible() {
logrus.Errorf("machine %q is incompatible with this release of podman and needs to be recreated, starting for recovery only", v.Name)
}
@ -501,6 +514,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
return err
}
}
// If the qemusocketpath exists and the vm is off/down, we should rm
// it before the dial as to avoid a segv
if err := v.QMPMonitor.Address.Delete(); err != nil {
@ -581,14 +595,14 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
}
if len(v.Mounts) > 0 {
state, err := v.State()
state, err := v.State(true)
if err != nil {
return err
}
listening := v.isListening()
for state != machine.Running || !listening {
time.Sleep(100 * time.Millisecond)
state, err = v.State()
state, err = v.State(true)
if err != nil {
return err
}
@ -630,7 +644,6 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
}
v.waitAPIAndPrintInfo(forwardState, forwardSock)
return nil
}
@ -639,9 +652,10 @@ func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (machine.Status, err
// {"return": {"status": "running", "singlestep": false, "running": true}}
type statusDetails struct {
Status string `json:"status"`
Step bool `json:"singlestep"`
Running bool `json:"running"`
Status string `json:"status"`
Step bool `json:"singlestep"`
Running bool `json:"running"`
Starting bool `json:"starting"`
}
type statusResponse struct {
Response statusDetails `json:"return"`
@ -727,6 +741,11 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
if p == nil && err != nil {
return err
}
v.LastUp = time.Now()
if err := v.writeConfig(); err != nil { // keep track of last up
return err
}
// Kill the process
if err := p.Kill(); err != nil {
return err
@ -748,7 +767,7 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
disconnected = true
waitInternal := 250 * time.Millisecond
for i := 0; i < 5; i++ {
state, err := v.State()
state, err := v.State(false)
if err != nil {
return err
}
@ -800,7 +819,7 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func()
)
// cannot remove a running vm unless --force is used
state, err := v.State()
state, err := v.State(false)
if err != nil {
return "", nil, err
}
@ -866,12 +885,19 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func()
}, nil
}
func (v *MachineVM) State() (machine.Status, error) {
func (v *MachineVM) State(bypass bool) (machine.Status, error) {
// Check if qmp socket path exists
if _, err := os.Stat(v.QMPMonitor.Address.GetPath()); os.IsNotExist(err) {
return "", nil
}
err := v.update()
if err != nil {
return "", err
}
// Check if we can dial it
if v.Starting && !bypass {
return "", nil
}
monitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address.GetPath(), v.QMPMonitor.Timeout)
if err != nil {
// FIXME: this error should probably be returned
@ -902,7 +928,7 @@ func (v *MachineVM) isListening() bool {
// SSH opens an interactive SSH session to the vm specified.
// Added ssh function to VM interface: pkg/machine/config/go : line 58
func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error {
state, err := v.State()
state, err := v.State(true)
if err != nil {
return err
}
@ -1016,20 +1042,29 @@ func getVMInfos() ([]*machine.ListResponse, error) {
listEntry.Port = vm.Port
listEntry.RemoteUsername = vm.RemoteUsername
listEntry.IdentityPath = vm.IdentityPath
fi, err := os.Stat(fullPath)
if err != nil {
return err
}
listEntry.CreatedAt = fi.ModTime()
listEntry.CreatedAt = vm.Created
fi, err = os.Stat(vm.getImageFile())
if listEntry.CreatedAt.IsZero() {
listEntry.CreatedAt = time.Now()
vm.Created = time.Now()
if err := vm.writeConfig(); err != nil {
return err
}
}
state, err := vm.State(false)
if err != nil {
return err
}
listEntry.LastUp = fi.ModTime()
state, err := vm.State()
if err != nil {
return err
if !vm.LastUp.IsZero() {
listEntry.LastUp = vm.LastUp
} else {
listEntry.LastUp = vm.Created
vm.Created = time.Now()
if err := vm.writeConfig(); err != nil {
return err
}
}
if state == machine.Running {
listEntry.Running = true

View File

@ -1024,7 +1024,7 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
// TODO: We need to rename isRunning to State(); I do not have a
// windows system to test this on.
func (v *MachineVM) State() (machine.Status, error) {
func (v *MachineVM) State(bypass bool) (machine.Status, error) {
return "", define.ErrNotImplemented
}