99 lines
3.3 KiB
Go
99 lines
3.3 KiB
Go
|
|
package scaleset
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"io"
|
||
|
|
"net/http"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
type scalesetError string
|
||
|
|
|
||
|
|
func (e scalesetError) Error() string {
|
||
|
|
return string(e)
|
||
|
|
}
|
||
|
|
|
||
|
|
var (
|
||
|
|
RunnerNotFoundError = scalesetError("runner not found")
|
||
|
|
RunnerExistsError = scalesetError("runner exists")
|
||
|
|
JobStillRunningError = scalesetError("job still running")
|
||
|
|
MessageQueueTokenExpiredError = scalesetError("message queue token expired")
|
||
|
|
)
|
||
|
|
|
||
|
|
type actionsExceptionError struct {
|
||
|
|
ExceptionName string `json:"typeName,omitempty"`
|
||
|
|
Message string `json:"message,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e actionsExceptionError) Error() string {
|
||
|
|
return fmt.Sprintf("%s: %s", e.ExceptionName, e.Message)
|
||
|
|
}
|
||
|
|
|
||
|
|
// newRequestResponseError creates a detailed error message based on the HTTP request and response,
|
||
|
|
// including parsing the response body for known error formats.
|
||
|
|
//
|
||
|
|
// The sendRequest already parses errors using this method, so use this error if the client doesn't
|
||
|
|
// return an error, but the error is happening on the application logic level.
|
||
|
|
//
|
||
|
|
// Prefer creating errors using this function instead of manually constructing error messages since it automatically
|
||
|
|
// includes useful metadata like activity IDs and request IDs, and handles well-known error cases.
|
||
|
|
func newRequestResponseError(req *http.Request, resp *http.Response, err error) error {
|
||
|
|
var sb strings.Builder
|
||
|
|
fmt.Fprintf(&sb, "request %s %s failed", req.Method, req.URL.String())
|
||
|
|
|
||
|
|
if resp == nil {
|
||
|
|
return fmt.Errorf("%s: %w", sb.String(), err)
|
||
|
|
}
|
||
|
|
|
||
|
|
sb.WriteRune('(')
|
||
|
|
fmt.Fprintf(&sb, "status=%q", resp.Status)
|
||
|
|
if resp.Header.Get(headerActionsActivityID) != "" {
|
||
|
|
fmt.Fprintf(&sb, ", activity_id=%q", resp.Header.Get(headerActionsActivityID))
|
||
|
|
}
|
||
|
|
|
||
|
|
if resp.Header.Get(headerGitHubRequestID) != "" {
|
||
|
|
fmt.Fprintf(&sb, ", github_request_id=%q", resp.Header.Get(headerGitHubRequestID))
|
||
|
|
}
|
||
|
|
sb.WriteRune(')')
|
||
|
|
|
||
|
|
if resp.Body == nil || resp.ContentLength == 0 {
|
||
|
|
return fmt.Errorf("%s: %w: unknown error", sb.String(), err)
|
||
|
|
}
|
||
|
|
|
||
|
|
body, bodyErr := io.ReadAll(resp.Body)
|
||
|
|
if bodyErr != nil {
|
||
|
|
return fmt.Errorf("%s: %w: failed to read error response body: %w", sb.String(), err, bodyErr)
|
||
|
|
}
|
||
|
|
if len(body) == 0 {
|
||
|
|
return fmt.Errorf("%s: %w: unknown error", sb.String(), err)
|
||
|
|
}
|
||
|
|
|
||
|
|
var scalesetErr scalesetError
|
||
|
|
if errors.As(err, &scalesetErr) {
|
||
|
|
return fmt.Errorf("%s: %w: %s", sb.String(), err, string(body))
|
||
|
|
}
|
||
|
|
|
||
|
|
contentType := resp.Header.Get("Content-Type")
|
||
|
|
if len(contentType) > 0 && strings.Contains(contentType, "text/plain") {
|
||
|
|
return fmt.Errorf("%s: %w: %s", sb.String(), err, string(body))
|
||
|
|
}
|
||
|
|
|
||
|
|
var exception actionsExceptionError
|
||
|
|
if err := json.Unmarshal(body, &exception); err != nil {
|
||
|
|
return fmt.Errorf("%s: %w: failed to unmarshal error response body: %q", sb.String(), err, string(body))
|
||
|
|
}
|
||
|
|
|
||
|
|
switch {
|
||
|
|
case strings.Contains(exception.ExceptionName, "AgentExistsException"):
|
||
|
|
return fmt.Errorf("%s: %w: %s", sb.String(), RunnerExistsError, exception.Message)
|
||
|
|
case strings.Contains(exception.ExceptionName, "AgentNotFoundException"):
|
||
|
|
return fmt.Errorf("%s: %w: %s", sb.String(), RunnerNotFoundError, exception.Message)
|
||
|
|
case strings.Contains(exception.ExceptionName, "JobStillRunningException"):
|
||
|
|
return fmt.Errorf("%s: %w: %s", sb.String(), JobStillRunningError, exception.Message)
|
||
|
|
default:
|
||
|
|
return fmt.Errorf("%s: %w: %w", sb.String(), err, exception)
|
||
|
|
}
|
||
|
|
}
|