open-nomad/nomad/structs/errors.go
2023-04-10 15:36:59 +00:00

233 lines
9.3 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package structs
import (
"errors"
"fmt"
"strconv"
"strings"
)
const (
errNoLeader = "No cluster leader"
errNotReadyForConsistentReads = "Not ready to serve consistent reads"
errNoRegionPath = "No path to region"
errTokenNotFound = "ACL token not found"
errTokenExpired = "ACL token expired"
errTokenInvalid = "ACL token is invalid" // not a UUID
errPermissionDenied = "Permission denied"
errJobRegistrationDisabled = "Job registration, dispatch, and scale are disabled by the scheduler configuration"
errNoNodeConn = "No path to node"
errUnknownMethod = "Unknown rpc method"
errUnknownNomadVersion = "Unable to determine Nomad version"
errNodeLacksRpc = "Node does not support RPC; requires 0.8 or later"
errMissingAllocID = "Missing allocation ID"
errIncompatibleFiltering = "Filter expression cannot be used with other filter parameters"
errMalformedChooseParameter = "Parameter for choose must be in form '<number>|<key>'"
// Prefix based errors that are used to check if the error is of a given
// type. These errors should be created with the associated constructor.
ErrUnknownAllocationPrefix = "Unknown allocation"
ErrUnknownNodePrefix = "Unknown node"
ErrUnknownJobPrefix = "Unknown job"
ErrUnknownEvaluationPrefix = "Unknown evaluation"
ErrUnknownDeploymentPrefix = "Unknown deployment"
errRPCCodedErrorPrefix = "RPC Error:: "
errDeploymentTerminalNoCancel = "can't cancel terminal deployment"
errDeploymentTerminalNoFail = "can't fail terminal deployment"
errDeploymentTerminalNoPause = "can't pause terminal deployment"
errDeploymentTerminalNoPromote = "can't promote terminal deployment"
errDeploymentTerminalNoResume = "can't resume terminal deployment"
errDeploymentTerminalNoUnblock = "can't unblock terminal deployment"
errDeploymentTerminalNoRun = "can't run terminal deployment"
errDeploymentTerminalNoSetHealth = "can't set health of allocations for a terminal deployment"
errDeploymentRunningNoUnblock = "can't unblock running deployment"
)
var (
ErrNoLeader = errors.New(errNoLeader)
ErrNotReadyForConsistentReads = errors.New(errNotReadyForConsistentReads)
ErrNoRegionPath = errors.New(errNoRegionPath)
ErrTokenNotFound = errors.New(errTokenNotFound)
ErrTokenExpired = errors.New(errTokenExpired)
ErrTokenInvalid = errors.New(errTokenInvalid)
ErrPermissionDenied = errors.New(errPermissionDenied)
ErrJobRegistrationDisabled = errors.New(errJobRegistrationDisabled)
ErrNoNodeConn = errors.New(errNoNodeConn)
ErrUnknownMethod = errors.New(errUnknownMethod)
ErrUnknownNomadVersion = errors.New(errUnknownNomadVersion)
ErrNodeLacksRpc = errors.New(errNodeLacksRpc)
ErrMissingAllocID = errors.New(errMissingAllocID)
ErrIncompatibleFiltering = errors.New(errIncompatibleFiltering)
ErrMalformedChooseParameter = errors.New(errMalformedChooseParameter)
ErrUnknownNode = errors.New(ErrUnknownNodePrefix)
ErrDeploymentTerminalNoCancel = errors.New(errDeploymentTerminalNoCancel)
ErrDeploymentTerminalNoFail = errors.New(errDeploymentTerminalNoFail)
ErrDeploymentTerminalNoPause = errors.New(errDeploymentTerminalNoPause)
ErrDeploymentTerminalNoPromote = errors.New(errDeploymentTerminalNoPromote)
ErrDeploymentTerminalNoResume = errors.New(errDeploymentTerminalNoResume)
ErrDeploymentTerminalNoUnblock = errors.New(errDeploymentTerminalNoUnblock)
ErrDeploymentTerminalNoRun = errors.New(errDeploymentTerminalNoRun)
ErrDeploymentTerminalNoSetHealth = errors.New(errDeploymentTerminalNoSetHealth)
ErrDeploymentRunningNoUnblock = errors.New(errDeploymentRunningNoUnblock)
ErrCSIClientRPCIgnorable = errors.New("CSI client error (ignorable)")
ErrCSIClientRPCRetryable = errors.New("CSI client error (retryable)")
ErrCSIVolumeMaxClaims = errors.New("volume max claims reached")
ErrCSIVolumeUnschedulable = errors.New("volume is currently unschedulable")
)
// IsErrNoLeader returns whether the error is due to there being no leader.
func IsErrNoLeader(err error) bool {
return err != nil && strings.Contains(err.Error(), errNoLeader)
}
// IsErrNoRegionPath returns whether the error is due to there being no path to
// the given region.
func IsErrNoRegionPath(err error) bool {
return err != nil && strings.Contains(err.Error(), errNoRegionPath)
}
// IsErrTokenNotFound returns whether the error is due to the passed token not
// being resolvable.
func IsErrTokenNotFound(err error) bool {
return err != nil && strings.Contains(err.Error(), errTokenNotFound)
}
// IsErrPermissionDenied returns whether the error is due to the operation not
// being allowed due to lack of permissions.
func IsErrPermissionDenied(err error) bool {
return err != nil && strings.Contains(err.Error(), errPermissionDenied)
}
// IsErrNoNodeConn returns whether the error is due to there being no path to
// the given node.
func IsErrNoNodeConn(err error) bool {
return err != nil && strings.Contains(err.Error(), errNoNodeConn)
}
// IsErrUnknownMethod returns whether the error is due to the operation not
// being allowed due to lack of permissions.
func IsErrUnknownMethod(err error) bool {
return err != nil && strings.Contains(err.Error(), errUnknownMethod)
}
func IsErrRPCCoded(err error) bool {
return err != nil && strings.HasPrefix(err.Error(), errRPCCodedErrorPrefix)
}
// NewErrUnknownAllocation returns a new error caused by the allocation being
// unknown.
func NewErrUnknownAllocation(allocID string) error {
return fmt.Errorf("%s %q", ErrUnknownAllocationPrefix, allocID)
}
// NewErrUnknownNode returns a new error caused by the node being unknown.
func NewErrUnknownNode(nodeID string) error {
return fmt.Errorf("%s %q", ErrUnknownNodePrefix, nodeID)
}
// NewErrUnknownJob returns a new error caused by the job being unknown.
func NewErrUnknownJob(jobID string) error {
return fmt.Errorf("%s %q", ErrUnknownJobPrefix, jobID)
}
// NewErrUnknownEvaluation returns a new error caused by the evaluation being
// unknown.
func NewErrUnknownEvaluation(evaluationID string) error {
return fmt.Errorf("%s %q", ErrUnknownEvaluationPrefix, evaluationID)
}
// NewErrUnknownDeployment returns a new error caused by the deployment being
// unknown.
func NewErrUnknownDeployment(deploymentID string) error {
return fmt.Errorf("%s %q", ErrUnknownDeploymentPrefix, deploymentID)
}
// IsErrUnknownAllocation returns whether the error is due to an unknown
// allocation.
func IsErrUnknownAllocation(err error) bool {
return err != nil && strings.Contains(err.Error(), ErrUnknownAllocationPrefix)
}
// IsErrUnknownNode returns whether the error is due to an unknown
// node.
func IsErrUnknownNode(err error) bool {
return err != nil && strings.Contains(err.Error(), ErrUnknownNodePrefix)
}
// IsErrUnknownJob returns whether the error is due to an unknown
// job.
func IsErrUnknownJob(err error) bool {
return err != nil && strings.Contains(err.Error(), ErrUnknownJobPrefix)
}
// IsErrUnknownEvaluation returns whether the error is due to an unknown
// evaluation.
func IsErrUnknownEvaluation(err error) bool {
return err != nil && strings.Contains(err.Error(), ErrUnknownEvaluationPrefix)
}
// IsErrUnknownDeployment returns whether the error is due to an unknown
// deployment.
func IsErrUnknownDeployment(err error) bool {
return err != nil && strings.Contains(err.Error(), ErrUnknownDeploymentPrefix)
}
// IsErrUnknownNomadVersion returns whether the error is due to Nomad being
// unable to determine the version of a node.
func IsErrUnknownNomadVersion(err error) bool {
return err != nil && strings.Contains(err.Error(), errUnknownNomadVersion)
}
// IsErrNodeLacksRpc returns whether error is due to a Nomad server being
// unable to connect to a client node because the client is too old (pre-v0.8).
func IsErrNodeLacksRpc(err error) bool {
return err != nil && strings.Contains(err.Error(), errNodeLacksRpc)
}
func IsErrNoSuchFileOrDirectory(err error) bool {
return err != nil && strings.Contains(err.Error(), "no such file or directory")
}
// NewErrRPCCoded wraps an RPC error with a code to be converted to HTTP status
// code
func NewErrRPCCoded(code int, msg string) error {
return fmt.Errorf("%s%d,%s", errRPCCodedErrorPrefix, code, msg)
}
// NewErrRPCCodedf wraps an RPC error with a code to be converted to HTTP
// status code.
func NewErrRPCCodedf(code int, format string, args ...interface{}) error {
msg := fmt.Sprintf(format, args...)
return fmt.Errorf("%s%d,%s", errRPCCodedErrorPrefix, code, msg)
}
// CodeFromRPCCodedErr returns the code and message of error if it's an RPC error
// created through NewErrRPCCoded function. Returns `ok` false if error is not
// an rpc error
func CodeFromRPCCodedErr(err error) (code int, msg string, ok bool) {
if err == nil || !strings.HasPrefix(err.Error(), errRPCCodedErrorPrefix) {
return 0, "", false
}
headerLen := len(errRPCCodedErrorPrefix)
parts := strings.SplitN(err.Error()[headerLen:], ",", 2)
if len(parts) != 2 {
return 0, "", false
}
code, err = strconv.Atoi(parts[0])
if err != nil {
return 0, "", false
}
return code, parts[1], true
}