go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cipd/common/cipderr/cipderr.go (about) 1 // Copyright 2022 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package cipderr contains an enumeration with possible CIPD error categories. 16 package cipderr 17 18 import ( 19 "context" 20 21 "go.chromium.org/luci/common/errors" 22 ) 23 24 // Code is returned as part of JSON output by CIPD CLI. 25 // 26 // It as an enumeration with broad categories of possible errors. 27 type Code string 28 29 const ( 30 // Authentication or authorization error when contacting the backend. 31 Auth Code = "auth_error" 32 // An error doing local I/O (i.e. writing or reading files). 33 IO Code = "io_error" 34 // An IO error reading or writing from CIPD CAS. 35 CAS Code = "cas_error" 36 // An incorrectly formatted version name, instance ID, etc. 37 BadArgument Code = "bad_argument_error" 38 // A requested package is missing or its version can't be resolved. 39 InvalidVersion Code = "invalid_version_error" 40 // An error getting a response from the backend. 41 BackendUnavailable Code = "backend_unavailable_error" 42 // A generic fatal RPC error, e.g. violation of some precodition. 43 RPC Code = "rpc_error" 44 // Something (e.g. a resolved pins file) needs to be regenerated. 45 Stale Code = "stale_error" 46 // A hash of downloaded file doesn't match the expected value. 47 HashMismatch Code = "hash_mismatch_error" 48 // The admission plugin forbid installation of a package. 49 NotAdmitted Code = "not_admitted_error" 50 // A timeout of some sort. 51 Timeout Code = "timeout_error" 52 // Unrecognized (possibly transient) error. 53 Unknown Code = "unknown_error" 54 ) 55 56 // Details can be optionally attached to an error. 57 type Details struct { 58 Package string `json:"package,omitempty"` 59 Version string `json:"version,omitempty"` 60 Subdir string `json:"subdir,omitempty"` 61 } 62 63 var tagKey = errors.NewTagKey("cipderr.Code") 64 65 type tagValue struct { 66 code Code 67 details *Details 68 } 69 70 // GenerateErrorTagValue is part of errors.TagValueGenerator, allowing this 71 // pair to be used as en error tag. 72 func (v tagValue) GenerateErrorTagValue() errors.TagValue { 73 return errors.TagValue{Key: tagKey, Value: v} 74 } 75 76 // GenerateErrorTagValue is part of errors.TagValueGenerator, allowing this 77 // code to be used as en error tag. 78 func (c Code) GenerateErrorTagValue() errors.TagValue { 79 return errors.TagValue{Key: tagKey, Value: tagValue{code: c}} 80 } 81 82 // WithDetails returns a error tag that attaches this code together with some 83 // details. 84 func (c Code) WithDetails(d Details) errors.TagValueGenerator { 85 return tagValue{code: c, details: &d} 86 } 87 88 // ToCode examines a CIPD error to get a representative error code. 89 func ToCode(err error) Code { 90 if val, ok := errors.TagValueIn(tagKey, err); ok { 91 return val.(tagValue).code 92 } 93 deadline := errors.Any(err, func(err error) bool { 94 return err == context.DeadlineExceeded || err == context.Canceled 95 }) 96 if deadline { 97 return Timeout 98 } 99 return Unknown 100 } 101 102 // ToDetails extracts error details, if available. 103 func ToDetails(err error) *Details { 104 if val, ok := errors.TagValueIn(tagKey, err); ok { 105 return val.(tagValue).details 106 } 107 return nil 108 } 109 110 // AttachDetails attaches details to an error, preserving its code. 111 // 112 // Overrides any previous details. Does nothing if `err` is nil. 113 func AttachDetails(err *error, d Details) { 114 if *err != nil { 115 *err = errors.Annotate(*err, "").Tag(ToCode(*err).WithDetails(d)).Err() 116 } 117 }