2018-01-09 23:26:53 +00:00
|
|
|
package structs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2018-02-13 22:54:27 +00:00
|
|
|
"fmt"
|
2019-10-04 14:08:03 +00:00
|
|
|
"strconv"
|
2018-01-09 23:26:53 +00:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2019-07-02 07:58:02 +00:00
|
|
|
errNoLeader = "No cluster leader"
|
|
|
|
errNotReadyForConsistentReads = "Not ready to serve consistent reads"
|
|
|
|
errNoRegionPath = "No path to region"
|
|
|
|
errTokenNotFound = "ACL token not found"
|
|
|
|
errPermissionDenied = "Permission denied"
|
|
|
|
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"
|
2019-10-01 20:06:24 +00:00
|
|
|
errMissingAllocID = "Missing allocation ID"
|
2018-02-13 22:54:27 +00:00
|
|
|
|
|
|
|
// 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"
|
2019-10-04 14:08:03 +00:00
|
|
|
|
2019-11-19 21:03:55 +00:00
|
|
|
errRPCCodedErrorPrefix = "RPC Error:: "
|
2020-06-16 12:10:41 +00:00
|
|
|
|
|
|
|
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"
|
2020-06-17 14:25:37 +00:00
|
|
|
errDeploymentTerminalNoRun = "can't run terminal deployment"
|
2020-06-16 12:10:41 +00:00
|
|
|
errDeploymentTerminalNoSetHealth = "can't set health of allocations for a terminal deployment"
|
|
|
|
errDeploymentRunningNoUnblock = "can't unblock running deployment"
|
2018-01-09 23:26:53 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2019-07-02 07:58:02 +00:00
|
|
|
ErrNoLeader = errors.New(errNoLeader)
|
|
|
|
ErrNotReadyForConsistentReads = errors.New(errNotReadyForConsistentReads)
|
|
|
|
ErrNoRegionPath = errors.New(errNoRegionPath)
|
|
|
|
ErrTokenNotFound = errors.New(errTokenNotFound)
|
|
|
|
ErrPermissionDenied = errors.New(errPermissionDenied)
|
|
|
|
ErrNoNodeConn = errors.New(errNoNodeConn)
|
|
|
|
ErrUnknownMethod = errors.New(errUnknownMethod)
|
|
|
|
ErrUnknownNomadVersion = errors.New(errUnknownNomadVersion)
|
|
|
|
ErrNodeLacksRpc = errors.New(errNodeLacksRpc)
|
2019-10-01 20:06:24 +00:00
|
|
|
ErrMissingAllocID = errors.New(errMissingAllocID)
|
2020-06-16 12:10:41 +00:00
|
|
|
|
|
|
|
ErrDeploymentTerminalNoCancel = errors.New(errDeploymentTerminalNoCancel)
|
|
|
|
ErrDeploymentTerminalNoFail = errors.New(errDeploymentTerminalNoFail)
|
|
|
|
ErrDeploymentTerminalNoPause = errors.New(errDeploymentTerminalNoPause)
|
|
|
|
ErrDeploymentTerminalNoPromote = errors.New(errDeploymentTerminalNoPromote)
|
|
|
|
ErrDeploymentTerminalNoResume = errors.New(errDeploymentTerminalNoResume)
|
|
|
|
ErrDeploymentTerminalNoUnblock = errors.New(errDeploymentTerminalNoUnblock)
|
2020-06-17 14:25:37 +00:00
|
|
|
ErrDeploymentTerminalNoRun = errors.New(errDeploymentTerminalNoRun)
|
2020-06-16 12:10:41 +00:00
|
|
|
ErrDeploymentTerminalNoSetHealth = errors.New(errDeploymentTerminalNoSetHealth)
|
|
|
|
ErrDeploymentRunningNoUnblock = errors.New(errDeploymentRunningNoUnblock)
|
2018-01-09 23:26:53 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
2018-01-27 01:05:38 +00:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
2018-02-05 21:15:06 +00:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
2018-02-13 22:54:27 +00:00
|
|
|
|
2019-12-13 19:44:02 +00:00
|
|
|
func IsErrRPCCoded(err error) bool {
|
2019-12-19 18:32:23 +00:00
|
|
|
return err != nil && strings.HasPrefix(err.Error(), errRPCCodedErrorPrefix)
|
2019-12-13 19:44:02 +00:00
|
|
|
}
|
|
|
|
|
2018-02-13 22:54:27 +00:00
|
|
|
// 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)
|
|
|
|
}
|
2018-04-06 18:33:08 +00:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
2019-10-04 14:08:03 +00:00
|
|
|
|
2019-11-19 21:03:55 +00:00
|
|
|
// NewErrRPCCoded wraps an RPC error with a code to be converted to HTTP status
|
|
|
|
// code
|
2019-10-04 14:08:03 +00:00
|
|
|
func NewErrRPCCoded(code int, msg string) error {
|
|
|
|
return fmt.Errorf("%s%d,%s", errRPCCodedErrorPrefix, code, msg)
|
|
|
|
}
|
|
|
|
|
2019-11-19 21:03:55 +00:00
|
|
|
// NewErrRPCCoded wraps an RPC error with a code to be converted to HTTP status
|
|
|
|
// code
|
2019-10-04 14:08:03 +00:00
|
|
|
func NewErrRPCCodedf(code int, format string, args ...interface{}) error {
|
|
|
|
msg := fmt.Sprintf(format, args...)
|
|
|
|
return fmt.Errorf("%s%d,%s", errRPCCodedErrorPrefix, code, msg)
|
|
|
|
}
|
|
|
|
|
2019-11-19 21:03:55 +00:00
|
|
|
// 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
|
2019-10-04 14:08:03 +00:00
|
|
|
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
|
|
|
|
}
|