diff --git a/internal/worker/worker.go b/internal/worker/worker.go index 8173480..ea264c5 100644 --- a/internal/worker/worker.go +++ b/internal/worker/worker.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net/http" "os" "slices" "time" @@ -206,10 +207,21 @@ func (worker *Worker) runNewSession(ctx context.Context) error { } // Backward compatibility with for older Orchard Controllers - updateFunc := worker.client.VMs().UpdateState + updateFuncInner := worker.client.VMs().UpdateState if !info.Capabilities.Has(v1.ControllerCapabilityVMStateEndpoint) { - updateFunc = worker.client.VMs().Update + updateFuncInner = worker.client.VMs().Update + } + + // Ignore HTTP 404, because the VM might no longer exist while we're still processing it + updateFunc := func(ctx context.Context, vm v1.VM) error { + _, err = updateFuncInner(ctx, vm) + var apiError *client.APIError + if errors.As(err, &apiError) && apiError.StatusCode == http.StatusNotFound { + return nil + } + + return err } for { @@ -281,7 +293,7 @@ func (worker *Worker) updateWorker(ctx context.Context) error { } //nolint:nestif,gocognit // nested "if" and cognitive complexity is tolerable for now -func (worker *Worker) syncVMs(ctx context.Context, updateVM func(context.Context, v1.VM) (*v1.VM, error)) error { +func (worker *Worker) syncVMs(ctx context.Context, updateVM func(context.Context, v1.VM) error) error { allKeys := mapset.NewSet[ondiskname.OnDiskName]() remoteVMs, err := worker.client.VMs().FindForWorker(ctx, worker.name) @@ -350,7 +362,7 @@ func (worker *Worker) syncVMs(ctx context.Context, updateVM func(context.Context if vmResource.StatusMessage != vm.StatusMessage() { vmResource.StatusMessage = vm.StatusMessage() - if _, err := updateVM(ctx, *vmResource); err != nil { + if err := updateVM(ctx, *vmResource); err != nil { return err } } @@ -368,7 +380,7 @@ func (worker *Worker) syncVMs(ctx context.Context, updateVM func(context.Context vmResource.Status = v1.VMStatusRunning vmResource.StatusMessage = vm.StatusMessage() - if _, err := updateVM(ctx, *vmResource); err != nil { + if err := updateVM(ctx, *vmResource); err != nil { return err } case ActionMonitorRunning: @@ -422,7 +434,7 @@ func (worker *Worker) syncVMs(ctx context.Context, updateVM func(context.Context } if updateNeeded { - if _, err := updateVM(ctx, *vmResource); err != nil { + if err := updateVM(ctx, *vmResource); err != nil { return err } } @@ -450,7 +462,7 @@ func (worker *Worker) syncVMs(ctx context.Context, updateVM func(context.Context vmResource.Status = v1.VMStatusFailed vmResource.StatusMessage = statusMessage - if _, err := updateVM(ctx, *vmResource); err != nil { + if err := updateVM(ctx, *vmResource); err != nil { return err } case ActionDelete: diff --git a/pkg/client/client.go b/pkg/client/client.go index ff8d6e5..4385b6e 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -25,9 +25,22 @@ import ( "google.golang.org/grpc/metadata" ) +type APIError struct { + StatusCode int +} + +func (apiError *APIError) Error() string { + return "API client encountered an error while attempting" +} + +func (apiError *APIError) Is(target error) bool { + _, ok := target.(*APIError) + return ok +} + var ( ErrFailed = errors.New("API client failed") - ErrAPI = errors.New("API client encountered an API error") + ErrAPI = &APIError{} ErrInvalidState = errors.New("invalid state") ) @@ -212,8 +225,12 @@ func (client *Client) requestWithHeaders( }() if response.StatusCode != http.StatusOK { + apiError := &APIError{ + StatusCode: response.StatusCode, + } + return nil, fmt.Errorf("%w to make a request: %d %s%s", - ErrAPI, response.StatusCode, http.StatusText(response.StatusCode), + apiError, response.StatusCode, http.StatusText(response.StatusCode), detailsFromErrorResponseBody(response.Body)) }