go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/util/bqutil/bq_util.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 bqutil contains utility functions for BigQuery.
    16  package bqutil
    17  
    18  import (
    19  	"context"
    20  
    21  	"google.golang.org/protobuf/types/known/fieldmaskpb"
    22  	"google.golang.org/protobuf/types/known/timestamppb"
    23  
    24  	"go.chromium.org/luci/bisection/model"
    25  	bqpb "go.chromium.org/luci/bisection/proto/bq"
    26  	pb "go.chromium.org/luci/bisection/proto/v1"
    27  	"go.chromium.org/luci/bisection/util/datastoreutil"
    28  	"go.chromium.org/luci/bisection/util/protoutil"
    29  	buildbucketpb "go.chromium.org/luci/buildbucket/proto"
    30  	"go.chromium.org/luci/common/errors"
    31  	"go.chromium.org/luci/common/proto/mask"
    32  )
    33  
    34  // TestFailureAnalysisToBqRow returns a TestAnalysisRow for a TestFailureAnalysis.
    35  // This is very similar to TestFailureAnalysisToPb in protoutil, but we intentionally
    36  // keep them separate because they are for 2 different purposes.
    37  // It gives us the flexibility to change one without affecting the others.
    38  func TestFailureAnalysisToBqRow(ctx context.Context, tfa *model.TestFailureAnalysis) (*bqpb.TestAnalysisRow, error) {
    39  	// Make sure that the analysis has ended.
    40  	if !tfa.HasEnded() {
    41  		return nil, errors.Reason("analysis %d has not ended", tfa.ID).Err()
    42  	}
    43  	result := &bqpb.TestAnalysisRow{
    44  		Project:     tfa.Project,
    45  		AnalysisId:  tfa.ID,
    46  		CreatedTime: timestamppb.New(tfa.CreateTime),
    47  		StartTime:   timestamppb.New(tfa.StartTime),
    48  		EndTime:     timestamppb.New(tfa.EndTime),
    49  		Status:      tfa.Status,
    50  		RunStatus:   tfa.RunStatus,
    51  		Builder: &buildbucketpb.BuilderID{
    52  			Project: tfa.Project,
    53  			Bucket:  tfa.Bucket,
    54  			Builder: tfa.Builder,
    55  		},
    56  		SampleBbid: tfa.FailedBuildID,
    57  	}
    58  
    59  	// Get test bundle.
    60  	bundle, err := datastoreutil.GetTestFailureBundle(ctx, tfa)
    61  	if err != nil {
    62  		return nil, errors.Annotate(err, "get test failure bundle").Err()
    63  	}
    64  	tfFieldMask := fieldmaskpb.FieldMask{
    65  		Paths: []string{"*"},
    66  	}
    67  	tfMask, err := mask.FromFieldMask(&tfFieldMask, &pb.TestFailure{}, false, false)
    68  	if err != nil {
    69  		return nil, errors.Annotate(err, "from field mask").Err()
    70  	}
    71  
    72  	result.TestFailures = protoutil.TestFailureBundleToPb(ctx, bundle, tfMask)
    73  	primary := bundle.Primary()
    74  	result.StartFailureRate = float32(primary.StartPositionFailureRate)
    75  	result.EndFailureRate = float32(primary.EndPositionFailureRate)
    76  	result.StartGitilesCommit = &buildbucketpb.GitilesCommit{
    77  		Host:     primary.Ref.GetGitiles().GetHost(),
    78  		Project:  primary.Ref.GetGitiles().GetProject(),
    79  		Ref:      primary.Ref.GetGitiles().GetRef(),
    80  		Id:       tfa.StartCommitHash,
    81  		Position: uint32(primary.RegressionStartPosition),
    82  	}
    83  	result.EndGitilesCommit = &buildbucketpb.GitilesCommit{
    84  		Host:     primary.Ref.GetGitiles().GetHost(),
    85  		Project:  primary.Ref.GetGitiles().GetProject(),
    86  		Ref:      primary.Ref.GetGitiles().GetRef(),
    87  		Id:       tfa.EndCommitHash,
    88  		Position: uint32(primary.RegressionEndPosition),
    89  	}
    90  
    91  	nsa, err := datastoreutil.GetTestNthSectionForAnalysis(ctx, tfa)
    92  	if err != nil {
    93  		return nil, errors.Annotate(err, "get test nthsection for analysis").Err()
    94  	}
    95  	if nsa != nil {
    96  		nsaFieldMask := fieldmaskpb.FieldMask{
    97  			Paths: []string{"*"},
    98  		}
    99  		nsaMask, err := mask.FromFieldMask(&nsaFieldMask, &pb.TestNthSectionAnalysisResult{}, false, false)
   100  		if err != nil {
   101  			return nil, errors.Annotate(err, "from field mask").Err()
   102  		}
   103  
   104  		nsaResult, err := protoutil.NthSectionAnalysisToPb(ctx, tfa, nsa, primary.Ref, nsaMask)
   105  		if err != nil {
   106  			return nil, errors.Annotate(err, "nthsection analysis to pb").Err()
   107  		}
   108  		result.NthSectionResult = nsaResult
   109  		culprit, err := datastoreutil.GetVerifiedCulpritForTestAnalysis(ctx, tfa)
   110  		if err != nil {
   111  			return nil, errors.Annotate(err, "get verified culprit").Err()
   112  		}
   113  		if culprit != nil {
   114  			culpritFieldMask := fieldmaskpb.FieldMask{
   115  				Paths: []string{"*"},
   116  			}
   117  			culpritMask, err := mask.FromFieldMask(&culpritFieldMask, &pb.TestCulprit{}, false, false)
   118  			if err != nil {
   119  				return nil, errors.Annotate(err, "from field mask").Err()
   120  			}
   121  
   122  			culpritPb, err := protoutil.CulpritToPb(ctx, culprit, nsa, culpritMask)
   123  			if err != nil {
   124  				return nil, errors.Annotate(err, "culprit to pb").Err()
   125  			}
   126  			result.Culprit = culpritPb
   127  		}
   128  	}
   129  	return result, nil
   130  }