mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-07-03 20:12:59 +00:00
fix(vm): better handle of ctrl+c when qemu is not responding (#627)
* fix(vm): resolves issue where ctrl+c was not being responded when qemu agent lookups would fail Signed-off-by: TheNotary <799247+TheNotary@users.noreply.github.com> * add os.Interrupt signal handling Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> --------- Signed-off-by: TheNotary <799247+TheNotary@users.noreply.github.com> Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> Co-authored-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
2a56c23f52
commit
aec09e4ecd
@ -11,6 +11,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -435,57 +437,86 @@ func (c *Client) UpdateVMAsync(ctx context.Context, d *UpdateRequestBody) (*stri
|
|||||||
// WaitForNetworkInterfacesFromVMAgent waits for a virtual machine's QEMU agent to publish the network interfaces.
|
// WaitForNetworkInterfacesFromVMAgent waits for a virtual machine's QEMU agent to publish the network interfaces.
|
||||||
func (c *Client) WaitForNetworkInterfacesFromVMAgent(
|
func (c *Client) WaitForNetworkInterfacesFromVMAgent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
timeout int,
|
timeout int, // time in seconds to wait until giving up
|
||||||
delay int,
|
delay int, // the delay in seconds between requests to the agent
|
||||||
waitForIP bool,
|
waitForIP bool, // whether or not to block until an IP is found, or just block until the interfaces are published
|
||||||
) (*GetQEMUNetworkInterfacesResponseData, error) {
|
) (*GetQEMUNetworkInterfacesResponseData, error) {
|
||||||
timeDelay := int64(delay)
|
delaySeconds := int64(delay)
|
||||||
timeMax := float64(timeout)
|
timeMaxSeconds := float64(timeout)
|
||||||
timeStart := time.Now()
|
timeStart := time.Now()
|
||||||
timeElapsed := timeStart.Sub(timeStart)
|
timeElapsed := timeStart.Sub(timeStart)
|
||||||
|
|
||||||
for timeElapsed.Seconds() < timeMax {
|
ch := make(chan os.Signal, 1)
|
||||||
//nolint:nestif
|
signal.Notify(ch, os.Interrupt)
|
||||||
if int64(timeElapsed.Seconds())%timeDelay == 0 {
|
|
||||||
|
for timeElapsed.Seconds() < timeMaxSeconds {
|
||||||
|
timeElapsed = time.Since(timeStart)
|
||||||
|
|
||||||
|
// check if terraform wants to shut us down (we try to poll the ctx every 200ms)
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return nil, fmt.Errorf("error waiting for VM network interfaces: %w", ctx.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
{
|
||||||
|
// the returned error will be eaten by the terraform runtime, so we log it here as well
|
||||||
|
const msg = "interrupted by signal"
|
||||||
|
tflog.Warn(ctx, msg)
|
||||||
|
return nil, fmt.Errorf(msg)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// sleep another 200 milliseconds if we haven't delayed enough since our last call
|
||||||
|
if int64(timeElapsed.Seconds())%delaySeconds != 0 {
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// request the network interfaces from the agent
|
||||||
data, err := c.GetVMNetworkInterfacesFromAgent(ctx)
|
data, err := c.GetVMNetworkInterfacesFromAgent(ctx)
|
||||||
|
|
||||||
if err == nil && data != nil && data.Result != nil {
|
// tick ahead and continue if we got an error from the api
|
||||||
hasAnyGlobalUnicast := false
|
if err != nil || data == nil || data.Result == nil {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're waiting for an IP, check if we have one yet; if not then keep looping
|
||||||
if waitForIP {
|
if waitForIP {
|
||||||
for _, nic := range *data.Result {
|
for _, nic := range *data.Result {
|
||||||
|
// skip the loopback interface
|
||||||
if nic.Name == "lo" {
|
if nic.Name == "lo" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip the interface if it has no IP addresses
|
||||||
if nic.IPAddresses == nil ||
|
if nic.IPAddresses == nil ||
|
||||||
(nic.IPAddresses != nil && len(*nic.IPAddresses) == 0) {
|
(nic.IPAddresses != nil && len(*nic.IPAddresses) == 0) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return if the interface has any global unicast addresses
|
||||||
for _, addr := range *nic.IPAddresses {
|
for _, addr := range *nic.IPAddresses {
|
||||||
if ip := net.ParseIP(addr.Address); ip != nil && ip.IsGlobalUnicast() {
|
if ip := net.ParseIP(addr.Address); ip != nil && ip.IsGlobalUnicast() {
|
||||||
hasAnyGlobalUnicast = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasAnyGlobalUnicast {
|
|
||||||
return data, err
|
return data, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no IP address has come through the agent yet
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
continue //nolint
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(200 * time.Millisecond)
|
// if not waiting for an IP, and the agent sent us an interface, return
|
||||||
|
if data.Result != nil && len(*data.Result) > 0 {
|
||||||
timeElapsed = time.Since(timeStart)
|
return data, err
|
||||||
|
|
||||||
if ctx.Err() != nil {
|
|
||||||
return nil, fmt.Errorf("error waiting for VM network interfaces: %w", ctx.Err())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we didn't get any interfaces so tick ahead to keep looping
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
|
Loading…
Reference in New Issue
Block a user