blob: 95ba0d96ec8a048fc8de23d8cfc1ff0a1e5e00f1 [file] [log] [blame]
// Copyright 2021 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 rpc
import (
"context"
"google.golang.org/grpc/codes"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/common/logging"
"go.chromium.org/luci/grpc/appstatus"
"go.chromium.org/luci/server/auth"
apiv0pb "go.chromium.org/luci/cv/api/v0"
"go.chromium.org/luci/cv/internal/acls"
"go.chromium.org/luci/cv/internal/common"
"go.chromium.org/luci/cv/internal/rpc/versioning"
"go.chromium.org/luci/cv/internal/run"
)
// RunsServer implements the v0 API.
type RunsServer struct {
apiv0pb.UnimplementedRunsServer
}
// checkCanUseAPI ensures that calling user is granted permission to use
// unstable v0 API.
func checkCanUseAPI(ctx context.Context, name string) error {
switch yes, err := auth.IsMember(ctx, acls.V0APIAllowGroup); {
case err != nil:
return appstatus.Errorf(codes.Internal, "failed to check ACL")
case !yes:
return appstatus.Errorf(codes.PermissionDenied, "not a member of %s", acls.V0APIAllowGroup)
default:
logging.Debugf(ctx, "%s is calling %s", auth.CurrentIdentity(ctx), name)
return nil
}
}
// populateRunResponse constructs and populates a apiv0pb.Run to use in a response.
func populateRunResponse(ctx context.Context, r *run.Run) (resp *apiv0pb.Run, err error) {
rcls, err := run.LoadRunCLs(ctx, r.ID, r.CLs)
if err != nil {
return nil, err
}
gcls := make([]*apiv0pb.GerritChange, len(rcls))
sCLSet := common.MakeCLIDsSet(r.Submission.GetSubmittedCls()...)
fCLSet := common.MakeCLIDsSet(r.Submission.GetFailedCls()...)
sCLIndexes := make([]int32, 0, len(fCLSet))
fCLIndexes := make([]int32, 0, len(sCLSet))
for i, rcl := range rcls {
host, change, err := rcl.ExternalID.ParseGobID()
switch {
case err != nil:
// As of Sep 2, 2021, CV works only with Gerrit (GoB) CL.
panic(errors.Annotate(err, "ParseGobID").Err())
case sCLSet.Has(rcl.ID):
sCLIndexes = append(sCLIndexes, int32(i))
case fCLSet.Has(rcl.ID):
fCLIndexes = append(fCLIndexes, int32(i))
}
gcls[i] = &apiv0pb.GerritChange{
Host: host,
Change: change,
Patchset: rcl.Detail.GetPatchset(),
}
}
tryjobs := make([]*apiv0pb.Tryjob, len(r.Tryjobs.GetTryjobs()))
for i, tj := range r.Tryjobs.GetTryjobs() {
tryjobs[i] = &apiv0pb.Tryjob{
Status: versioning.TryjobStatusV0(tj.Status),
}
if tj.Critical {
tryjobs[i].Critical = true
}
if tj.Reused {
tryjobs[i].Reuse = true
}
if result := tj.GetResult(); result != nil {
tryjobs[i].Result = &apiv0pb.Tryjob_Result{
Status: versioning.TryjobResultStatusV0(result.Status),
}
if bb := result.GetBuildbucket(); bb != nil {
tryjobs[i].Result.Backend = &apiv0pb.Tryjob_Result_Buildbucket_{
Buildbucket: &apiv0pb.Tryjob_Result_Buildbucket{Id: bb.Id},
}
}
}
}
var submission *apiv0pb.Run_Submission
if len(sCLIndexes) > 0 || len(fCLIndexes) > 0 {
submission = &apiv0pb.Run_Submission{
SubmittedClIndexes: sCLIndexes,
FailedClIndexes: fCLIndexes,
}
}
return &apiv0pb.Run{
Id: r.ID.PublicID(),
Eversion: int64(r.EVersion),
Status: versioning.RunStatusV0(r.Status),
Mode: string(r.Mode),
CreateTime: common.Time2PBNillable(r.CreateTime),
StartTime: common.Time2PBNillable(r.StartTime),
UpdateTime: common.Time2PBNillable(r.UpdateTime),
EndTime: common.Time2PBNillable(r.EndTime),
Owner: string(r.Owner),
Cls: gcls,
Tryjobs: tryjobs,
Submission: submission,
}, nil
}
func min(i, j int) int {
if i < j {
return i
}
return j
}