go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/compilefailureanalysis/cancelanalysis/cancel_analysis.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 cancelanalysis handles cancelation of existing analyses. 16 package cancelanalysis 17 18 import ( 19 "context" 20 21 "google.golang.org/protobuf/proto" 22 23 bbpb "go.chromium.org/luci/buildbucket/proto" 24 "go.chromium.org/luci/common/clock" 25 "go.chromium.org/luci/common/errors" 26 "go.chromium.org/luci/common/logging" 27 "go.chromium.org/luci/common/retry/transient" 28 "go.chromium.org/luci/gae/service/datastore" 29 "go.chromium.org/luci/server/tq" 30 31 "go.chromium.org/luci/bisection/compilefailureanalysis/statusupdater" 32 "go.chromium.org/luci/bisection/internal/buildbucket" 33 "go.chromium.org/luci/bisection/model" 34 pb "go.chromium.org/luci/bisection/proto/v1" 35 tpb "go.chromium.org/luci/bisection/task/proto" 36 "go.chromium.org/luci/bisection/util/datastoreutil" 37 "go.chromium.org/luci/bisection/util/loggingutil" 38 ) 39 40 const ( 41 taskClass = "cancel-analysis" 42 queue = "cancel-analysis" 43 ) 44 45 // RegisterTaskClass registers the task class for tq dispatcher. 46 func RegisterTaskClass() { 47 tq.RegisterTaskClass(tq.TaskClass{ 48 ID: taskClass, 49 Prototype: (*tpb.CancelAnalysisTask)(nil), 50 Queue: queue, 51 Kind: tq.NonTransactional, 52 Handler: func(c context.Context, payload proto.Message) error { 53 task := payload.(*tpb.CancelAnalysisTask) 54 logging.Infof(c, "Process CancelAnalysisTask with id = %d", task.GetAnalysisId()) 55 err := CancelAnalysis(c, task.GetAnalysisId()) 56 if err != nil { 57 err := errors.Annotate(err, "cancelAnalysis id=%d", task.GetAnalysisId()).Err() 58 logging.Errorf(c, err.Error()) 59 // If the error is transient, return err to retry 60 if transient.Tag.In(err) { 61 return err 62 } 63 return nil 64 } 65 return nil 66 }, 67 }) 68 } 69 70 // CancelAnalysis cancels all pending and running reruns for an analysis. 71 func CancelAnalysis(c context.Context, analysisID int64) error { 72 c, err := loggingutil.UpdateLoggingWithAnalysisID(c, analysisID) 73 if err != nil { 74 // not critical, just log 75 err := errors.Annotate(err, "failed UpdateLoggingWithAnalysisID %d", analysisID) 76 logging.Errorf(c, "%v", err) 77 } 78 logging.Infof(c, "Cancel analysis %d", analysisID) 79 80 cfa, err := datastoreutil.GetCompileFailureAnalysis(c, analysisID) 81 if err != nil { 82 return errors.Annotate(err, "couldn't get analysis %d", analysisID).Err() 83 } 84 reruns, err := datastoreutil.GetRerunsForAnalysis(c, cfa) 85 if err != nil { 86 return errors.Annotate(err, "couldn't get reruns for analysis %d", analysisID).Err() 87 } 88 89 var errs []error 90 for _, rerun := range reruns { 91 if rerun.Status == pb.RerunStatus_RERUN_STATUS_IN_PROGRESS { 92 bbid := rerun.RerunBuild.IntID() 93 _, err := buildbucket.CancelBuild(c, bbid, "analysis was canceled") 94 if err != nil { 95 errs = append(errs, errors.Annotate(err, "couldn't cancel build %d", bbid).Err()) 96 } else { 97 err = updateCancelStatusForRerun(c, rerun) 98 if err != nil { 99 errs = append(errs, errors.Annotate(err, "couldn't update rerun status %d", rerun.RerunBuild.IntID()).Err()) 100 } 101 } 102 } 103 } 104 105 if len(errs) > 0 { 106 return errors.NewMultiError(errs...) 107 } 108 109 // Update status of analysis and nthsection analysis 110 newStatus := cfa.Status 111 // Only updates status if is was running 112 if cfa.Status == pb.AnalysisStatus_RUNNING { 113 newStatus = pb.AnalysisStatus_NOTFOUND 114 } 115 err = statusupdater.UpdateStatus(c, cfa, newStatus, pb.AnalysisRunStatus_CANCELED) 116 117 if err != nil { 118 return errors.Annotate(err, "couldn't update status for analysis %d", cfa.Id).Err() 119 } 120 121 // Update status of nthsection analysis 122 nsa, err := datastoreutil.GetNthSectionAnalysis(c, cfa) 123 if err != nil { 124 return err 125 } 126 if nsa == nil { 127 return nil 128 } 129 newStatus = nsa.Status 130 if nsa.Status == pb.AnalysisStatus_RUNNING { 131 newStatus = pb.AnalysisStatus_NOTFOUND 132 } 133 134 err = statusupdater.UpdateNthSectionStatus(c, nsa, newStatus, pb.AnalysisRunStatus_CANCELED) 135 if err != nil { 136 return errors.Annotate(err, "couldn't update status for nthsection for analysis %d", cfa.Id).Err() 137 } 138 139 return nil 140 } 141 142 func updateCancelStatusForRerun(c context.Context, rerun *model.SingleRerun) error { 143 return datastore.RunInTransaction(c, func(c context.Context) error { 144 // Update rerun 145 err := datastore.Get(c, rerun) 146 if err != nil { 147 return err 148 } 149 rerun.EndTime = clock.Now(c) 150 rerun.Status = pb.RerunStatus_RERUN_STATUS_CANCELED 151 152 err = datastore.Put(c, rerun) 153 if err != nil { 154 return err 155 } 156 157 // Update rerun build model 158 rerunBuild := &model.CompileRerunBuild{ 159 Id: rerun.RerunBuild.IntID(), 160 } 161 err = datastore.Get(c, rerunBuild) 162 if err != nil { 163 return err 164 } 165 rerunBuild.EndTime = clock.Now(c) 166 rerunBuild.Status = bbpb.Status_CANCELED 167 err = datastore.Put(c, rerunBuild) 168 if err != nil { 169 return err 170 } 171 172 // Also if the rerun is for culprit verification, set the status of the suspect 173 if rerun.Suspect != nil { 174 suspect := &model.Suspect{ 175 Id: rerun.Suspect.IntID(), 176 ParentAnalysis: rerun.Suspect.Parent(), 177 } 178 err = datastore.Get(c, suspect) 179 if err != nil { 180 return errors.Annotate(err, "couldn't get suspect for rerun").Err() 181 } 182 183 suspect.VerificationStatus = model.SuspectVerificationStatus_Canceled 184 err = datastore.Put(c, suspect) 185 186 if err != nil { 187 return errors.Annotate(err, "couldn't update suspect status").Err() 188 } 189 } 190 return nil 191 }, nil) 192 }