mirror of
https://github.com/containers/podman.git
synced 2025-06-20 17:13:43 +08:00
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:
BIN
capture.pcap
Normal file
BIN
capture.pcap
Normal file
Binary file not shown.
@ -59,7 +59,7 @@ func inspect(cmd *cobra.Command, args []string) error {
|
|||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
state, err := vm.State()
|
state, err := vm.State(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
continue
|
continue
|
||||||
|
@ -121,7 +121,7 @@ type VM interface {
|
|||||||
Set(name string, opts SetOptions) error
|
Set(name string, opts SetOptions) error
|
||||||
SSH(name string, opts SSHOptions) error
|
SSH(name string, opts SSHOptions) error
|
||||||
Start(name string, opts StartOptions) error
|
Start(name string, opts StartOptions) error
|
||||||
State() (Status, error)
|
State(bypass bool) (Status, error)
|
||||||
Stop(name string, opts StopOptions) error
|
Stop(name string, opts StopOptions) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +86,12 @@ type MachineVM struct {
|
|||||||
ResourceConfig
|
ResourceConfig
|
||||||
// SSHConfig for accessing the remote vm
|
// SSHConfig for accessing the remote vm
|
||||||
SSHConfig
|
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
|
// ImageConfig describes the bootable image for the VM
|
||||||
|
@ -95,6 +95,8 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
|||||||
vm.Memory = opts.Memory
|
vm.Memory = opts.Memory
|
||||||
vm.DiskSize = opts.DiskSize
|
vm.DiskSize = opts.DiskSize
|
||||||
|
|
||||||
|
vm.Created = time.Now()
|
||||||
|
|
||||||
// Find the qemu executable
|
// Find the qemu executable
|
||||||
cfg, err := config.Default()
|
cfg, err := config.Default()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -439,7 +441,7 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
state, err := v.State()
|
state, err := v.State(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -480,6 +482,17 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
|
|||||||
wait = time.Millisecond * 500
|
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() {
|
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)
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the qemusocketpath exists and the vm is off/down, we should rm
|
// If the qemusocketpath exists and the vm is off/down, we should rm
|
||||||
// it before the dial as to avoid a segv
|
// it before the dial as to avoid a segv
|
||||||
if err := v.QMPMonitor.Address.Delete(); err != nil {
|
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 {
|
if len(v.Mounts) > 0 {
|
||||||
state, err := v.State()
|
state, err := v.State(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
listening := v.isListening()
|
listening := v.isListening()
|
||||||
for state != machine.Running || !listening {
|
for state != machine.Running || !listening {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
state, err = v.State()
|
state, err = v.State(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -630,7 +644,6 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
v.waitAPIAndPrintInfo(forwardState, forwardSock)
|
v.waitAPIAndPrintInfo(forwardState, forwardSock)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -639,9 +652,10 @@ func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (machine.Status, err
|
|||||||
// {"return": {"status": "running", "singlestep": false, "running": true}}
|
// {"return": {"status": "running", "singlestep": false, "running": true}}
|
||||||
|
|
||||||
type statusDetails struct {
|
type statusDetails struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Step bool `json:"singlestep"`
|
Step bool `json:"singlestep"`
|
||||||
Running bool `json:"running"`
|
Running bool `json:"running"`
|
||||||
|
Starting bool `json:"starting"`
|
||||||
}
|
}
|
||||||
type statusResponse struct {
|
type statusResponse struct {
|
||||||
Response statusDetails `json:"return"`
|
Response statusDetails `json:"return"`
|
||||||
@ -727,6 +741,11 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
|
|||||||
if p == nil && err != nil {
|
if p == nil && err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v.LastUp = time.Now()
|
||||||
|
if err := v.writeConfig(); err != nil { // keep track of last up
|
||||||
|
return err
|
||||||
|
}
|
||||||
// Kill the process
|
// Kill the process
|
||||||
if err := p.Kill(); err != nil {
|
if err := p.Kill(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -748,7 +767,7 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
|
|||||||
disconnected = true
|
disconnected = true
|
||||||
waitInternal := 250 * time.Millisecond
|
waitInternal := 250 * time.Millisecond
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
state, err := v.State()
|
state, err := v.State(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// cannot remove a running vm unless --force is used
|
||||||
state, err := v.State()
|
state, err := v.State(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
@ -866,12 +885,19 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func()
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MachineVM) State() (machine.Status, error) {
|
func (v *MachineVM) State(bypass bool) (machine.Status, error) {
|
||||||
// Check if qmp socket path exists
|
// Check if qmp socket path exists
|
||||||
if _, err := os.Stat(v.QMPMonitor.Address.GetPath()); os.IsNotExist(err) {
|
if _, err := os.Stat(v.QMPMonitor.Address.GetPath()); os.IsNotExist(err) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
err := v.update()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
// Check if we can dial it
|
// 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)
|
monitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address.GetPath(), v.QMPMonitor.Timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// FIXME: this error should probably be returned
|
// 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.
|
// SSH opens an interactive SSH session to the vm specified.
|
||||||
// Added ssh function to VM interface: pkg/machine/config/go : line 58
|
// Added ssh function to VM interface: pkg/machine/config/go : line 58
|
||||||
func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error {
|
func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error {
|
||||||
state, err := v.State()
|
state, err := v.State(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1016,20 +1042,29 @@ func getVMInfos() ([]*machine.ListResponse, error) {
|
|||||||
listEntry.Port = vm.Port
|
listEntry.Port = vm.Port
|
||||||
listEntry.RemoteUsername = vm.RemoteUsername
|
listEntry.RemoteUsername = vm.RemoteUsername
|
||||||
listEntry.IdentityPath = vm.IdentityPath
|
listEntry.IdentityPath = vm.IdentityPath
|
||||||
fi, err := os.Stat(fullPath)
|
listEntry.CreatedAt = vm.Created
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
listEntry.CreatedAt = fi.ModTime()
|
|
||||||
|
|
||||||
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
listEntry.LastUp = fi.ModTime()
|
|
||||||
state, err := vm.State()
|
if !vm.LastUp.IsZero() {
|
||||||
if err != nil {
|
listEntry.LastUp = vm.LastUp
|
||||||
return err
|
} else {
|
||||||
|
listEntry.LastUp = vm.Created
|
||||||
|
vm.Created = time.Now()
|
||||||
|
if err := vm.writeConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if state == machine.Running {
|
if state == machine.Running {
|
||||||
listEntry.Running = true
|
listEntry.Running = true
|
||||||
|
@ -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
|
// TODO: We need to rename isRunning to State(); I do not have a
|
||||||
// windows system to test this on.
|
// 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
|
return "", define.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user