Files
scaleset/errors.go
T
Nikola Jokic 7e0eae99a6 Modify error API and add Listener tests (#36)
* Modify error API to not include exception, but to test on type

* reformat

* address renames and documentations from the review

* test is errors

* document
2025-12-02 18:23:40 +01:00

146 lines
3.3 KiB
Go

package scaleset
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strings"
)
// Header names for request IDs
const (
headerActionsActivityID = "ActivityId"
headerGitHubRequestID = "X-GitHub-Request-Id"
)
type GitHubAPIError struct {
StatusCode int
RequestID string
Err error
}
func (e *GitHubAPIError) Error() string {
return fmt.Sprintf("github api error: StatusCode %d, RequestID %q: %v", e.StatusCode, e.RequestID, e.Err)
}
func (e *GitHubAPIError) Unwrap() error {
return e.Err
}
type ActionsError struct {
ActivityID string
StatusCode int
Err error
}
func (e *ActionsError) Error() string {
return fmt.Sprintf("actions error: StatusCode %d, ActivityId %q: %v", e.StatusCode, e.ActivityID, e.Err)
}
func (e *ActionsError) Unwrap() error {
return e.Err
}
func (e *ActionsError) IsAgentNotFound() bool {
return e.isException("AgentNotFoundException")
}
func (e *ActionsError) IsJobStillRunning() bool {
return e.isException("JobStillRunningException")
}
func (e *ActionsError) IsMessageQueueTokenExpired() bool {
if e == nil {
return false
}
var err *messageQueueTokenExpiredError
return errors.As(e.Err, &err)
}
func (e *ActionsError) IsAgentExists() bool {
return e.isException("AgentExistsException")
}
func (e *ActionsError) isException(target string) bool {
if e == nil {
return false
}
if ex, ok := e.Err.(*actionsExceptionError); ok {
return strings.Contains(ex.ExceptionName, target)
}
return false
}
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)
}
func ParseActionsErrorFromResponse(response *http.Response) error {
if response.ContentLength == 0 {
return &ActionsError{
ActivityID: response.Header.Get(headerActionsActivityID),
StatusCode: response.StatusCode,
Err: errors.New("unknown exception"),
}
}
body, err := io.ReadAll(response.Body)
if err != nil {
return &ActionsError{
ActivityID: response.Header.Get(headerActionsActivityID),
StatusCode: response.StatusCode,
Err: err,
}
}
body = trimByteOrderMark(body)
contentType := response.Header.Get("Content-Type")
if len(contentType) > 0 && strings.Contains(contentType, "text/plain") {
message := string(body)
return &ActionsError{
ActivityID: response.Header.Get(headerActionsActivityID),
StatusCode: response.StatusCode,
Err: errors.New(message),
}
}
var exception actionsExceptionError
if err := json.Unmarshal(body, &exception); err != nil {
return &ActionsError{
ActivityID: response.Header.Get(headerActionsActivityID),
StatusCode: response.StatusCode,
Err: err,
}
}
return &ActionsError{
ActivityID: response.Header.Get(headerActionsActivityID),
StatusCode: response.StatusCode,
Err: &exception,
}
}
type messageQueueTokenExpiredError struct {
message string
}
func (e *messageQueueTokenExpiredError) Error() string {
return fmt.Sprintf("message queue token expired: %s", e.message)
}
// NewMessageQueueTokenExpiredError creates a new MessageQueueTokenExpiredError.
//
// This function is mostly used by tests.
func NewMessageQueueTokenExpiredError(message string) error {
return &messageQueueTokenExpiredError{
message: message,
}
}