| // Copyright 2015 The etcd Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Package error describes errors in etcd project. When any change happens, |
| // Documentation/v2/errorcode.md needs to be updated correspondingly. |
| package error |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "net/http" |
| ) |
| |
| var errors = map[int]string{ |
| // command related errors |
| EcodeKeyNotFound: "Key not found", |
| EcodeTestFailed: "Compare failed", //test and set |
| EcodeNotFile: "Not a file", |
| ecodeNoMorePeer: "Reached the max number of peers in the cluster", |
| EcodeNotDir: "Not a directory", |
| EcodeNodeExist: "Key already exists", // create |
| ecodeKeyIsPreserved: "The prefix of given key is a keyword in etcd", |
| EcodeRootROnly: "Root is read only", |
| EcodeDirNotEmpty: "Directory not empty", |
| ecodeExistingPeerAddr: "Peer address has existed", |
| EcodeUnauthorized: "The request requires user authentication", |
| |
| // Post form related errors |
| ecodeValueRequired: "Value is Required in POST form", |
| EcodePrevValueRequired: "PrevValue is Required in POST form", |
| EcodeTTLNaN: "The given TTL in POST form is not a number", |
| EcodeIndexNaN: "The given index in POST form is not a number", |
| ecodeValueOrTTLRequired: "Value or TTL is required in POST form", |
| ecodeTimeoutNaN: "The given timeout in POST form is not a number", |
| ecodeNameRequired: "Name is required in POST form", |
| ecodeIndexOrValueRequired: "Index or value is required", |
| ecodeIndexValueMutex: "Index and value cannot both be specified", |
| EcodeInvalidField: "Invalid field", |
| EcodeInvalidForm: "Invalid POST form", |
| EcodeRefreshValue: "Value provided on refresh", |
| EcodeRefreshTTLRequired: "A TTL must be provided on refresh", |
| |
| // raft related errors |
| EcodeRaftInternal: "Raft Internal Error", |
| EcodeLeaderElect: "During Leader Election", |
| |
| // etcd related errors |
| EcodeWatcherCleared: "watcher is cleared due to etcd recovery", |
| EcodeEventIndexCleared: "The event in requested index is outdated and cleared", |
| ecodeStandbyInternal: "Standby Internal Error", |
| ecodeInvalidActiveSize: "Invalid active size", |
| ecodeInvalidRemoveDelay: "Standby remove delay", |
| |
| // client related errors |
| ecodeClientInternal: "Client Internal Error", |
| } |
| |
| var errorStatus = map[int]int{ |
| EcodeKeyNotFound: http.StatusNotFound, |
| EcodeNotFile: http.StatusForbidden, |
| EcodeDirNotEmpty: http.StatusForbidden, |
| EcodeUnauthorized: http.StatusUnauthorized, |
| EcodeTestFailed: http.StatusPreconditionFailed, |
| EcodeNodeExist: http.StatusPreconditionFailed, |
| EcodeRaftInternal: http.StatusInternalServerError, |
| EcodeLeaderElect: http.StatusInternalServerError, |
| } |
| |
| const ( |
| EcodeKeyNotFound = 100 |
| EcodeTestFailed = 101 |
| EcodeNotFile = 102 |
| ecodeNoMorePeer = 103 |
| EcodeNotDir = 104 |
| EcodeNodeExist = 105 |
| ecodeKeyIsPreserved = 106 |
| EcodeRootROnly = 107 |
| EcodeDirNotEmpty = 108 |
| ecodeExistingPeerAddr = 109 |
| EcodeUnauthorized = 110 |
| |
| ecodeValueRequired = 200 |
| EcodePrevValueRequired = 201 |
| EcodeTTLNaN = 202 |
| EcodeIndexNaN = 203 |
| ecodeValueOrTTLRequired = 204 |
| ecodeTimeoutNaN = 205 |
| ecodeNameRequired = 206 |
| ecodeIndexOrValueRequired = 207 |
| ecodeIndexValueMutex = 208 |
| EcodeInvalidField = 209 |
| EcodeInvalidForm = 210 |
| EcodeRefreshValue = 211 |
| EcodeRefreshTTLRequired = 212 |
| |
| EcodeRaftInternal = 300 |
| EcodeLeaderElect = 301 |
| |
| EcodeWatcherCleared = 400 |
| EcodeEventIndexCleared = 401 |
| ecodeStandbyInternal = 402 |
| ecodeInvalidActiveSize = 403 |
| ecodeInvalidRemoveDelay = 404 |
| |
| ecodeClientInternal = 500 |
| ) |
| |
| type Error struct { |
| ErrorCode int `json:"errorCode"` |
| Message string `json:"message"` |
| Cause string `json:"cause,omitempty"` |
| Index uint64 `json:"index"` |
| } |
| |
| func NewRequestError(errorCode int, cause string) *Error { |
| return NewError(errorCode, cause, 0) |
| } |
| |
| func NewError(errorCode int, cause string, index uint64) *Error { |
| return &Error{ |
| ErrorCode: errorCode, |
| Message: errors[errorCode], |
| Cause: cause, |
| Index: index, |
| } |
| } |
| |
| // Error is for the error interface |
| func (e Error) Error() string { |
| return e.Message + " (" + e.Cause + ")" |
| } |
| |
| func (e Error) toJsonString() string { |
| b, _ := json.Marshal(e) |
| return string(b) |
| } |
| |
| func (e Error) StatusCode() int { |
| status, ok := errorStatus[e.ErrorCode] |
| if !ok { |
| status = http.StatusBadRequest |
| } |
| return status |
| } |
| |
| func (e Error) WriteTo(w http.ResponseWriter) error { |
| w.Header().Add("X-Etcd-Index", fmt.Sprint(e.Index)) |
| w.Header().Set("Content-Type", "application/json") |
| w.WriteHeader(e.StatusCode()) |
| _, err := w.Write([]byte(e.toJsonString() + "\n")) |
| return err |
| } |