1package structs 2 3import ( 4 "errors" 5 "fmt" 6 "strconv" 7 "strings" 8) 9 10const ( 11 errNoLeader = "No cluster leader" 12 errNotReadyForConsistentReads = "Not ready to serve consistent reads" 13 errNoRegionPath = "No path to region" 14 errTokenNotFound = "ACL token not found" 15 errPermissionDenied = "Permission denied" 16 errNoNodeConn = "No path to node" 17 errUnknownMethod = "Unknown rpc method" 18 errUnknownNomadVersion = "Unable to determine Nomad version" 19 errNodeLacksRpc = "Node does not support RPC; requires 0.8 or later" 20 errMissingAllocID = "Missing allocation ID" 21 22 // Prefix based errors that are used to check if the error is of a given 23 // type. These errors should be created with the associated constructor. 24 ErrUnknownAllocationPrefix = "Unknown allocation" 25 ErrUnknownNodePrefix = "Unknown node" 26 ErrUnknownJobPrefix = "Unknown job" 27 ErrUnknownEvaluationPrefix = "Unknown evaluation" 28 ErrUnknownDeploymentPrefix = "Unknown deployment" 29 30 errRPCCodedErrorPrefix = "RPC Error:: " 31 32 errDeploymentTerminalNoCancel = "can't cancel terminal deployment" 33 errDeploymentTerminalNoFail = "can't fail terminal deployment" 34 errDeploymentTerminalNoPause = "can't pause terminal deployment" 35 errDeploymentTerminalNoPromote = "can't promote terminal deployment" 36 errDeploymentTerminalNoResume = "can't resume terminal deployment" 37 errDeploymentTerminalNoUnblock = "can't unblock terminal deployment" 38 errDeploymentTerminalNoRun = "can't run terminal deployment" 39 errDeploymentTerminalNoSetHealth = "can't set health of allocations for a terminal deployment" 40 errDeploymentRunningNoUnblock = "can't unblock running deployment" 41) 42 43var ( 44 ErrNoLeader = errors.New(errNoLeader) 45 ErrNotReadyForConsistentReads = errors.New(errNotReadyForConsistentReads) 46 ErrNoRegionPath = errors.New(errNoRegionPath) 47 ErrTokenNotFound = errors.New(errTokenNotFound) 48 ErrPermissionDenied = errors.New(errPermissionDenied) 49 ErrNoNodeConn = errors.New(errNoNodeConn) 50 ErrUnknownMethod = errors.New(errUnknownMethod) 51 ErrUnknownNomadVersion = errors.New(errUnknownNomadVersion) 52 ErrNodeLacksRpc = errors.New(errNodeLacksRpc) 53 ErrMissingAllocID = errors.New(errMissingAllocID) 54 55 ErrUnknownNode = errors.New(ErrUnknownNodePrefix) 56 57 ErrDeploymentTerminalNoCancel = errors.New(errDeploymentTerminalNoCancel) 58 ErrDeploymentTerminalNoFail = errors.New(errDeploymentTerminalNoFail) 59 ErrDeploymentTerminalNoPause = errors.New(errDeploymentTerminalNoPause) 60 ErrDeploymentTerminalNoPromote = errors.New(errDeploymentTerminalNoPromote) 61 ErrDeploymentTerminalNoResume = errors.New(errDeploymentTerminalNoResume) 62 ErrDeploymentTerminalNoUnblock = errors.New(errDeploymentTerminalNoUnblock) 63 ErrDeploymentTerminalNoRun = errors.New(errDeploymentTerminalNoRun) 64 ErrDeploymentTerminalNoSetHealth = errors.New(errDeploymentTerminalNoSetHealth) 65 ErrDeploymentRunningNoUnblock = errors.New(errDeploymentRunningNoUnblock) 66 67 ErrCSIClientRPCIgnorable = errors.New("CSI client error (ignorable)") 68 ErrCSIClientRPCRetryable = errors.New("CSI client error (retryable)") 69) 70 71// IsErrNoLeader returns whether the error is due to there being no leader. 72func IsErrNoLeader(err error) bool { 73 return err != nil && strings.Contains(err.Error(), errNoLeader) 74} 75 76// IsErrNoRegionPath returns whether the error is due to there being no path to 77// the given region. 78func IsErrNoRegionPath(err error) bool { 79 return err != nil && strings.Contains(err.Error(), errNoRegionPath) 80} 81 82// IsErrTokenNotFound returns whether the error is due to the passed token not 83// being resolvable. 84func IsErrTokenNotFound(err error) bool { 85 return err != nil && strings.Contains(err.Error(), errTokenNotFound) 86} 87 88// IsErrPermissionDenied returns whether the error is due to the operation not 89// being allowed due to lack of permissions. 90func IsErrPermissionDenied(err error) bool { 91 return err != nil && strings.Contains(err.Error(), errPermissionDenied) 92} 93 94// IsErrNoNodeConn returns whether the error is due to there being no path to 95// the given node. 96func IsErrNoNodeConn(err error) bool { 97 return err != nil && strings.Contains(err.Error(), errNoNodeConn) 98} 99 100// IsErrUnknownMethod returns whether the error is due to the operation not 101// being allowed due to lack of permissions. 102func IsErrUnknownMethod(err error) bool { 103 return err != nil && strings.Contains(err.Error(), errUnknownMethod) 104} 105 106func IsErrRPCCoded(err error) bool { 107 return err != nil && strings.HasPrefix(err.Error(), errRPCCodedErrorPrefix) 108} 109 110// NewErrUnknownAllocation returns a new error caused by the allocation being 111// unknown. 112func NewErrUnknownAllocation(allocID string) error { 113 return fmt.Errorf("%s %q", ErrUnknownAllocationPrefix, allocID) 114} 115 116// NewErrUnknownNode returns a new error caused by the node being unknown. 117func NewErrUnknownNode(nodeID string) error { 118 return fmt.Errorf("%s %q", ErrUnknownNodePrefix, nodeID) 119} 120 121// NewErrUnknownJob returns a new error caused by the job being unknown. 122func NewErrUnknownJob(jobID string) error { 123 return fmt.Errorf("%s %q", ErrUnknownJobPrefix, jobID) 124} 125 126// NewErrUnknownEvaluation returns a new error caused by the evaluation being 127// unknown. 128func NewErrUnknownEvaluation(evaluationID string) error { 129 return fmt.Errorf("%s %q", ErrUnknownEvaluationPrefix, evaluationID) 130} 131 132// NewErrUnknownDeployment returns a new error caused by the deployment being 133// unknown. 134func NewErrUnknownDeployment(deploymentID string) error { 135 return fmt.Errorf("%s %q", ErrUnknownDeploymentPrefix, deploymentID) 136} 137 138// IsErrUnknownAllocation returns whether the error is due to an unknown 139// allocation. 140func IsErrUnknownAllocation(err error) bool { 141 return err != nil && strings.Contains(err.Error(), ErrUnknownAllocationPrefix) 142} 143 144// IsErrUnknownNode returns whether the error is due to an unknown 145// node. 146func IsErrUnknownNode(err error) bool { 147 return err != nil && strings.Contains(err.Error(), ErrUnknownNodePrefix) 148} 149 150// IsErrUnknownJob returns whether the error is due to an unknown 151// job. 152func IsErrUnknownJob(err error) bool { 153 return err != nil && strings.Contains(err.Error(), ErrUnknownJobPrefix) 154} 155 156// IsErrUnknownEvaluation returns whether the error is due to an unknown 157// evaluation. 158func IsErrUnknownEvaluation(err error) bool { 159 return err != nil && strings.Contains(err.Error(), ErrUnknownEvaluationPrefix) 160} 161 162// IsErrUnknownDeployment returns whether the error is due to an unknown 163// deployment. 164func IsErrUnknownDeployment(err error) bool { 165 return err != nil && strings.Contains(err.Error(), ErrUnknownDeploymentPrefix) 166} 167 168// IsErrUnknownNomadVersion returns whether the error is due to Nomad being 169// unable to determine the version of a node. 170func IsErrUnknownNomadVersion(err error) bool { 171 return err != nil && strings.Contains(err.Error(), errUnknownNomadVersion) 172} 173 174// IsErrNodeLacksRpc returns whether error is due to a Nomad server being 175// unable to connect to a client node because the client is too old (pre-v0.8). 176func IsErrNodeLacksRpc(err error) bool { 177 return err != nil && strings.Contains(err.Error(), errNodeLacksRpc) 178} 179 180// NewErrRPCCoded wraps an RPC error with a code to be converted to HTTP status 181// code 182func NewErrRPCCoded(code int, msg string) error { 183 return fmt.Errorf("%s%d,%s", errRPCCodedErrorPrefix, code, msg) 184} 185 186// NewErrRPCCoded wraps an RPC error with a code to be converted to HTTP status 187// code 188func NewErrRPCCodedf(code int, format string, args ...interface{}) error { 189 msg := fmt.Sprintf(format, args...) 190 return fmt.Errorf("%s%d,%s", errRPCCodedErrorPrefix, code, msg) 191} 192 193// CodeFromRPCCodedErr returns the code and message of error if it's an RPC error 194// created through NewErrRPCCoded function. Returns `ok` false if error is not 195// an rpc error 196func CodeFromRPCCodedErr(err error) (code int, msg string, ok bool) { 197 if err == nil || !strings.HasPrefix(err.Error(), errRPCCodedErrorPrefix) { 198 return 0, "", false 199 } 200 201 headerLen := len(errRPCCodedErrorPrefix) 202 parts := strings.SplitN(err.Error()[headerLen:], ",", 2) 203 if len(parts) != 2 { 204 return 0, "", false 205 } 206 207 code, err = strconv.Atoi(parts[0]) 208 if err != nil { 209 return 0, "", false 210 } 211 212 return code, parts[1], true 213} 214