go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/luciexe/build/errors.go (about) 1 // Copyright 2020 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 build 16 17 import ( 18 "context" 19 "fmt" 20 21 "google.golang.org/protobuf/proto" 22 23 bbpb "go.chromium.org/luci/buildbucket/proto" 24 "go.chromium.org/luci/buildbucket/protoutil" 25 "go.chromium.org/luci/common/errors" 26 "go.chromium.org/luci/common/logging" 27 ) 28 29 type buildStatus struct { 30 status bbpb.Status 31 details *bbpb.StatusDetails 32 } 33 34 var statusTag = errors.NewTagKey("build Status") 35 36 // AttachStatus attaches a buildbucket status (and details) to a given error. 37 // 38 // If such an error is handled by Step.End or State.End, that step/build will 39 // have its status updated to match. 40 // 41 // AttachStatus allows overriding the attached status if the error already has 42 // one. 43 // 44 // This panics if the status is a non-terminal status like SCHEDULED or STARTED. 45 // 46 // This is a no-op if the error is nil. 47 func AttachStatus(err error, status bbpb.Status, details *bbpb.StatusDetails) error { 48 if !protoutil.IsEnded(status) { 49 panic(errors.Reason("AttachStatus cannot be used with non-terminal status %q", status).Err()) 50 } 51 if err == nil { 52 return nil 53 } 54 if details != nil { 55 details = proto.Clone(details).(*bbpb.StatusDetails) 56 } 57 return errors.TagValue{Key: statusTag, Value: &buildStatus{status, details}}.Apply(err) 58 } 59 60 // ExtractStatus retrieves the Buildbucket status (and details) from a given 61 // error. 62 // 63 // This returns: 64 // - (SUCCESS, nil) on nil error 65 // - Any values attached with AttachStatus. 66 // - (CANCELED, nil) on context.Canceled 67 // - (INFRA_FAILURE, &bbpb.StatusDetails{Timeout: {}}) on 68 // context.DeadlineExceeded 69 // - (FAILURE, nil) otherwise 70 // 71 // This function is used internally by Step.End and State.End, but is provided 72 // publically for completeness. 73 func ExtractStatus(err error) (bbpb.Status, *bbpb.StatusDetails) { 74 if err == nil { 75 return bbpb.Status_SUCCESS, nil 76 } 77 if value, ok := errors.TagValueIn(statusTag, err); ok { 78 bs := value.(*buildStatus) 79 details := bs.details 80 if details != nil { 81 details = proto.Clone(details).(*bbpb.StatusDetails) 82 } 83 return bs.status, details 84 } 85 switch err { 86 case context.Canceled: 87 return bbpb.Status_CANCELED, nil 88 case context.DeadlineExceeded: 89 return bbpb.Status_INFRA_FAILURE, &bbpb.StatusDetails{ 90 Timeout: &bbpb.StatusDetails_Timeout{}, 91 } 92 } 93 return bbpb.Status_FAILURE, nil 94 } 95 96 func computePanicStatus(err error) (status bbpb.Status, message string) { 97 if errors.IsPanicking(2) { 98 message = "PANIC" 99 // TODO(iannucci): include details of panic in SummaryMarkdown or log? 100 // How to prevent panic dump from showing up at every single step on the 101 // stack? 102 status = bbpb.Status_INFRA_FAILURE 103 } else { 104 status, _ = ExtractStatus(err) 105 if err != nil { 106 message = err.Error() 107 } 108 } 109 return 110 } 111 112 func logStatus(ctx context.Context, status bbpb.Status, message, markdown string) { 113 logf := logging.Errorf 114 switch status { 115 case bbpb.Status_SUCCESS: 116 logf = logging.Infof 117 case bbpb.Status_CANCELED: 118 logf = logging.Warningf 119 } 120 121 logMsg := fmt.Sprintf("set status: %s", status) 122 if len(message) > 0 { 123 logMsg += ": " + message 124 } 125 if len(markdown) > 0 { 126 logMsg += "\n with SummaryMarkdown:\n" + markdown 127 } 128 logf(ctx, "%s", logMsg) 129 }