blob: 0238d8758f1772028fb832487a525c8501dd46c8 [file] [log] [blame]
// Copyright 2020 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package run
import (
"context"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/common/retry/transient"
"go.chromium.org/luci/gae/service/datastore"
"go.chromium.org/luci/cv/internal/common"
)
// IsEnded returns true if the given status is final.
func IsEnded(status Status) bool {
return status&Status_ENDED_MASK == Status_ENDED_MASK
}
// LoadRunCLs loads `RunCL` entities of the provided cls in the Run.
func LoadRunCLs(ctx context.Context, runID common.RunID, clids common.CLIDs) ([]*RunCL, error) {
runCLs := make([]*RunCL, len(clids))
runKey := datastore.MakeKey(ctx, RunKind, string(runID))
for i, clID := range clids {
runCLs[i] = &RunCL{
ID: clID,
Run: runKey,
}
}
err := datastore.Get(ctx, runCLs)
switch merr, ok := err.(errors.MultiError); {
case ok:
for i, err := range merr {
if err == datastore.ErrNoSuchEntity {
return nil, errors.Reason("RunCL %d not found in Datastore", runCLs[i].ID).Err()
}
}
count, err := merr.Summary()
return nil, errors.Annotate(err, "failed to load %d out of %d RunCLs", count, len(runCLs)).Tag(transient.Tag).Err()
case err != nil:
return nil, errors.Annotate(err, "failed to load %d RunCLs", len(runCLs)).Tag(transient.Tag).Err()
}
return runCLs, nil
}
// LoadRunLogEntries loads all log entries of a given Run.
//
// Ordered from logically oldest to newest.
func LoadRunLogEntries(ctx context.Context, runID common.RunID) ([]*LogEntry, error) {
// Since RunLog entities are immutable, it's cheapest to load them from
// DS cache. So, perform KeysOnly query first, which is cheap & fast, and then
// additional multi-Get which will go via DS cache.
var keys []*datastore.Key
runKey := datastore.MakeKey(ctx, RunKind, string(runID))
q := datastore.NewQuery(RunLogKind).KeysOnly(true).Ancestor(runKey)
if err := datastore.GetAll(ctx, q, &keys); err != nil {
return nil, errors.Annotate(err, "failed to fetch keys of RunLog entities").Tag(transient.Tag).Err()
}
entities := make([]*RunLog, len(keys))
for i, key := range keys {
entities[i] = &RunLog{
Run: runKey,
ID: key.IntID(),
}
}
if err := datastore.Get(ctx, entities); err != nil {
// It's possible to get EntityNotExists, it may only happen if data
// retention enforcement is deleting old entities at the same time.
// Thus, treat all errors as transient.
return nil, errors.Annotate(common.MostSevereError(err), "failed to fetch RunLog entities").Tag(transient.Tag).Err()
}
// Each RunLog entity contains at least 1 LogEntry.
out := make([]*LogEntry, 0, len(entities))
for _, e := range entities {
out = append(out, e.Entries.GetEntries()...)
}
return out, nil
}