go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/resultdb/sink/validation.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 sink
    16  
    17  import (
    18  	"mime"
    19  	"time"
    20  
    21  	"go.chromium.org/luci/common/errors"
    22  
    23  	"go.chromium.org/luci/resultdb/pbutil"
    24  	sinkpb "go.chromium.org/luci/resultdb/sink/proto/v1"
    25  )
    26  
    27  // validateTestResult returns a non-nil error if msg is invalid.
    28  func validateTestResult(now time.Time, msg *sinkpb.TestResult) (err error) {
    29  	ec := checker{&err}
    30  	switch {
    31  	case msg == nil:
    32  		return unspecified()
    33  	case ec.isErr(pbutil.ValidateTestID(msg.TestId), "test_id"):
    34  	case ec.isErr(pbutil.ValidateResultID(msg.ResultId), "result_id"):
    35  	// skip `Expected`
    36  	case ec.isErr(pbutil.ValidateTestResultStatus(msg.Status), "status"):
    37  	case ec.isErr(pbutil.ValidateSummaryHTML(msg.SummaryHtml), "summary_html"):
    38  	case ec.isErr(pbutil.ValidateStartTimeWithDuration(now, msg.StartTime, msg.Duration), ""):
    39  	case ec.isErr(pbutil.ValidateStringPairs(msg.Tags), "tags"):
    40  	case ec.isErr(validateArtifacts(msg.Artifacts), "artifacts"):
    41  	case msg.TestMetadata != nil && ec.isErr(pbutil.ValidateTestMetadata(msg.TestMetadata), "test_metadata"):
    42  	case msg.FailureReason != nil && ec.isErr(pbutil.ValidateFailureReason(msg.FailureReason), "failure_reason"):
    43  	case msg.Properties != nil && ec.isErr(pbutil.ValidateProperties(msg.Properties), "properties"):
    44  	}
    45  	return err
    46  }
    47  
    48  // validateArtifact returns a non-nil error if art is invalid.
    49  func validateArtifact(art *sinkpb.Artifact) error {
    50  	if art.GetFilePath() == "" && art.GetContents() == nil && art.GetGcsUri() == "" {
    51  		return errors.Reason("body: one of file_path or contents or gcs_uri must be provided").Err()
    52  	}
    53  	if art.GetContentType() != "" {
    54  		_, _, err := mime.ParseMediaType(art.ContentType)
    55  		if err != nil {
    56  			return err
    57  		}
    58  	}
    59  	return nil
    60  }
    61  
    62  // validateArtifacts returns a non-nil error if any element of arts is invalid.
    63  func validateArtifacts(arts map[string]*sinkpb.Artifact) error {
    64  	for id, art := range arts {
    65  		if art == nil {
    66  			return errors.Reason("%s: %s", id, unspecified()).Err()
    67  		}
    68  		if err := pbutil.ValidateArtifactID(id); err != nil {
    69  			return errors.Annotate(err, "%s", id).Err()
    70  		}
    71  		if err := validateArtifact(art); err != nil {
    72  			return errors.Annotate(err, "%s", id).Err()
    73  		}
    74  	}
    75  	return nil
    76  }
    77  
    78  type checker struct {
    79  	lastCheckedErr *error
    80  }
    81  
    82  // isErr returns true if err is nil. False, otherwise.
    83  //
    84  // It also stores err into lastCheckedErr. If err was not nil, it wraps err with
    85  // errors.Annotate before storing it in lastErr.
    86  func (c *checker) isErr(err error, format string, args ...any) bool {
    87  	if err == nil {
    88  		return false
    89  	}
    90  	*c.lastCheckedErr = errors.Annotate(err, format, args...).Err()
    91  	return true
    92  }
    93  
    94  func unspecified() error {
    95  	return errors.Reason("unspecified").Err()
    96  }