mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-07-04 04:22:59 +00:00
fix(core): improve error handling while waiting for PVE tasks to complete (#526)
This commit is contained in:
parent
5556b17a1e
commit
6f02df4440
@ -25,9 +25,6 @@ import (
|
||||
"github.com/bpg/terraform-provider-proxmox/utils"
|
||||
)
|
||||
|
||||
// ErrNoDataObjectInResponse is returned when the server does not include a data object in the response.
|
||||
var ErrNoDataObjectInResponse = errors.New("the server did not include a data object in the response")
|
||||
|
||||
const (
|
||||
basePathJSONAPI = "api2/json"
|
||||
)
|
||||
@ -292,7 +289,7 @@ func (c *client) IsRootTicket() bool {
|
||||
// validateResponseCode ensures that a response is valid.
|
||||
func validateResponseCode(res *http.Response) error {
|
||||
if res.StatusCode < 200 || res.StatusCode >= 300 {
|
||||
status := strings.TrimPrefix(res.Status, fmt.Sprintf("%d ", res.StatusCode))
|
||||
msg := strings.TrimPrefix(res.Status, fmt.Sprintf("%d ", res.StatusCode))
|
||||
|
||||
errRes := &ErrorResponseBody{}
|
||||
err := json.NewDecoder(res.Body).Decode(errRes)
|
||||
@ -304,10 +301,13 @@ func validateResponseCode(res *http.Response) error {
|
||||
errList = append(errList, fmt.Sprintf("%s: %s", k, strings.TrimRight(v, "\n\r")))
|
||||
}
|
||||
|
||||
status = fmt.Sprintf("%s (%s)", status, strings.Join(errList, " - "))
|
||||
msg = fmt.Sprintf("%s (%s)", msg, strings.Join(errList, " - "))
|
||||
}
|
||||
|
||||
return fmt.Errorf("received an HTTP %d response - Reason: %s", res.StatusCode, status)
|
||||
return &HTTPError{
|
||||
Code: res.StatusCode,
|
||||
Message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
29
proxmox/api/errors.go
Normal file
29
proxmox/api/errors.go
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package api
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Error is a sentinel error type for API errors.
|
||||
type Error string
|
||||
|
||||
func (err Error) Error() string {
|
||||
return string(err)
|
||||
}
|
||||
|
||||
// ErrNoDataObjectInResponse is returned when the server does not include a data object in the response.
|
||||
const ErrNoDataObjectInResponse Error = "the server did not include a data object in the response"
|
||||
|
||||
// HTTPError is a generic error type for HTTP errors.
|
||||
type HTTPError struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
|
||||
func (err *HTTPError) Error() string {
|
||||
return fmt.Sprintf("received an HTTP %d response - Reason: %s", err.Code, err.Message)
|
||||
}
|
@ -282,7 +282,6 @@ func (c *Client) APIUpload(
|
||||
}
|
||||
|
||||
err = c.Tasks().WaitForTask(ctx, *resBody.UploadID, uploadTimeout, 5)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error uploading file to datastore %s: failed waiting for upload - %w", datastoreID, err)
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -28,7 +29,7 @@ func (c *Client) GetTaskStatus(ctx context.Context, upid string) (*GetTaskStatus
|
||||
resBody,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error retrievinf task status: %w", err)
|
||||
return nil, fmt.Errorf("error retrieving task status: %w", err)
|
||||
}
|
||||
|
||||
if resBody.Data == nil {
|
||||
@ -45,10 +46,24 @@ func (c *Client) WaitForTask(ctx context.Context, upid string, timeoutSec, delay
|
||||
timeStart := time.Now()
|
||||
timeElapsed := timeStart.Sub(timeStart)
|
||||
|
||||
isCriticalError := func(err error) bool {
|
||||
var target *api.HTTPError
|
||||
if errors.As(err, &target) {
|
||||
if target.Code != http.StatusBadRequest {
|
||||
// this is a special case to account for eventual consistency
|
||||
// when creating a task -- the task may not be available via status API
|
||||
// immediately after creation
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return err != nil
|
||||
}
|
||||
|
||||
for timeElapsed.Seconds() < timeMax {
|
||||
if int64(timeElapsed.Seconds())%timeDelay == 0 {
|
||||
status, err := c.GetTaskStatus(ctx, upid)
|
||||
if err != nil {
|
||||
if isCriticalError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user