go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/testfailureanalysis/status_updater.go (about)

     1  // Copyright 2023 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 testfailureanalysis handles test failure analysis.
    16  package testfailureanalysis
    17  
    18  import (
    19  	"context"
    20  
    21  	"go.chromium.org/luci/bisection/model"
    22  	pb "go.chromium.org/luci/bisection/proto/v1"
    23  	"go.chromium.org/luci/bisection/util/datastoreutil"
    24  	"go.chromium.org/luci/common/clock"
    25  	"go.chromium.org/luci/common/errors"
    26  	"go.chromium.org/luci/gae/service/datastore"
    27  )
    28  
    29  // UpdateAnalysisStatus updates status of a test failure analysis.
    30  func UpdateAnalysisStatus(ctx context.Context, tfa *model.TestFailureAnalysis, status pb.AnalysisStatus, runStatus pb.AnalysisRunStatus) error {
    31  	return datastore.RunInTransaction(ctx, func(ctx context.Context) error {
    32  		e := datastore.Get(ctx, tfa)
    33  		if e != nil {
    34  			return e
    35  		}
    36  
    37  		// If the run has ended or canceled, we don't want to do anything.
    38  		if tfa.RunStatus == pb.AnalysisRunStatus_ENDED || tfa.RunStatus == pb.AnalysisRunStatus_CANCELED {
    39  			return nil
    40  		}
    41  
    42  		// All the same, no need to update.
    43  		if tfa.RunStatus == runStatus && tfa.Status == status {
    44  			return nil
    45  		}
    46  
    47  		if runStatus == pb.AnalysisRunStatus_ENDED || runStatus == pb.AnalysisRunStatus_CANCELED {
    48  			tfa.EndTime = clock.Now(ctx)
    49  		}
    50  		// Do not update start time again if it has started.
    51  		if runStatus == pb.AnalysisRunStatus_STARTED && tfa.RunStatus != pb.AnalysisRunStatus_STARTED {
    52  			tfa.StartTime = clock.Now(ctx)
    53  		}
    54  
    55  		tfa.Status = status
    56  		tfa.RunStatus = runStatus
    57  		return datastore.Put(ctx, tfa)
    58  	}, nil)
    59  }
    60  
    61  // UpdateNthSectionAnalysisStatus updates status of a test failure analysis.
    62  func UpdateNthSectionAnalysisStatus(ctx context.Context, nsa *model.TestNthSectionAnalysis, status pb.AnalysisStatus, runStatus pb.AnalysisRunStatus) error {
    63  	return datastore.RunInTransaction(ctx, func(ctx context.Context) error {
    64  		e := datastore.Get(ctx, nsa)
    65  		if e != nil {
    66  			return e
    67  		}
    68  
    69  		// If the run has ended or canceled, we don't want to do anything.
    70  		if nsa.RunStatus == pb.AnalysisRunStatus_ENDED || nsa.RunStatus == pb.AnalysisRunStatus_CANCELED {
    71  			return nil
    72  		}
    73  
    74  		// All the same, no need to update.
    75  		if nsa.RunStatus == runStatus && nsa.Status == status {
    76  			return nil
    77  		}
    78  
    79  		nsa.Status = status
    80  		nsa.RunStatus = runStatus
    81  		if runStatus == pb.AnalysisRunStatus_ENDED || runStatus == pb.AnalysisRunStatus_CANCELED {
    82  			nsa.EndTime = clock.Now(ctx)
    83  		}
    84  		return datastore.Put(ctx, nsa)
    85  	}, nil)
    86  }
    87  
    88  // UpdateAnalysisStatusWhenError updates analysis and nthsection analysis
    89  // when an error occured.
    90  // As there still maybe reruns in progress, we should check if there are still
    91  // running reruns.
    92  func UpdateAnalysisStatusWhenError(ctx context.Context, tfa *model.TestFailureAnalysis) error {
    93  	reruns, err := datastoreutil.GetInProgressReruns(ctx, tfa)
    94  	if err != nil {
    95  		return errors.Annotate(err, "get in progress rerun").Err()
    96  	}
    97  	// Analysis still in progress, we don't need to do anything.
    98  	if len(reruns) > 0 {
    99  		return nil
   100  	}
   101  	// Otherwise, update status to error.
   102  	nsa, err := datastoreutil.GetTestNthSectionForAnalysis(ctx, tfa)
   103  	if err != nil {
   104  		return errors.Annotate(err, "get test nthsection for analysis").Err()
   105  	}
   106  	if nsa == nil {
   107  		return errors.New("no nthsection analysis")
   108  	}
   109  	// This will be a no-op if the nthsection analysis already ended or canceled.
   110  	err = UpdateNthSectionAnalysisStatus(ctx, nsa, pb.AnalysisStatus_ERROR, pb.AnalysisRunStatus_ENDED)
   111  	if err != nil {
   112  		return errors.Annotate(err, "update nthsection analysis status").Err()
   113  	}
   114  
   115  	// This will be a no-op if the analysis already ended or canceled.
   116  	err = UpdateAnalysisStatus(ctx, tfa, pb.AnalysisStatus_ERROR, pb.AnalysisRunStatus_ENDED)
   117  	if err != nil {
   118  		return errors.Annotate(err, "update analysis status").Err()
   119  	}
   120  	return nil
   121  }