go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/buildbucket/appengine/tasks/check_build_liveness_test.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 tasks
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  	"time"
    21  
    22  	"google.golang.org/protobuf/types/known/durationpb"
    23  	"google.golang.org/protobuf/types/known/timestamppb"
    24  
    25  	"go.chromium.org/luci/common/clock/testclock"
    26  	"go.chromium.org/luci/gae/filter/txndefer"
    27  	"go.chromium.org/luci/gae/impl/memory"
    28  	"go.chromium.org/luci/gae/service/datastore"
    29  	"go.chromium.org/luci/server/tq"
    30  
    31  	"go.chromium.org/luci/buildbucket/appengine/internal/metrics"
    32  	"go.chromium.org/luci/buildbucket/appengine/model"
    33  	taskdefs "go.chromium.org/luci/buildbucket/appengine/tasks/defs"
    34  	pb "go.chromium.org/luci/buildbucket/proto"
    35  
    36  	. "github.com/smartystreets/goconvey/convey"
    37  	. "go.chromium.org/luci/common/testing/assertions"
    38  )
    39  
    40  func TestCheckLiveness(t *testing.T) {
    41  	t.Parallel()
    42  
    43  	Convey("CheckLiveness", t, func() {
    44  		ctx := txndefer.FilterRDS(memory.Use(context.Background()))
    45  		ctx = metrics.WithServiceInfo(ctx, "svc", "job", "ins")
    46  		datastore.GetTestable(ctx).AutoIndex(true)
    47  		datastore.GetTestable(ctx).Consistent(true)
    48  		ctx, sch := tq.TestingContext(ctx, nil)
    49  		baseTime := testclock.TestRecentTimeLocal
    50  		ctx, _ = testclock.UseTime(ctx, baseTime)
    51  
    52  		bld := &model.Build{
    53  			Proto: &pb.Build{
    54  				Id: 1,
    55  				Builder: &pb.BuilderID{
    56  					Project: "project",
    57  					Bucket:  "bucket",
    58  					Builder: "builder",
    59  				},
    60  				Status:     pb.Status_SCHEDULED,
    61  				CreateTime: timestamppb.New(baseTime),
    62  				UpdateTime: timestamppb.New(baseTime),
    63  				SchedulingTimeout: &durationpb.Duration{
    64  					Seconds: 60,
    65  				},
    66  				ExecutionTimeout: &durationpb.Duration{
    67  					Seconds: 120,
    68  				},
    69  			},
    70  		}
    71  		bs := &model.BuildStatus{
    72  			Build:  datastore.MakeKey(ctx, "Build", 1),
    73  			Status: pb.Status_SCHEDULED,
    74  		}
    75  
    76  		So(datastore.Put(ctx, bld, bs), ShouldBeNil)
    77  
    78  		Convey("build not found", func() {
    79  			err := CheckLiveness(ctx, 999, 10)
    80  			So(err, ShouldErrLike, "failed to get build")
    81  			So(sch.Tasks(), ShouldBeEmpty)
    82  		})
    83  
    84  		Convey("build is ended", func() {
    85  			bld.Proto.Status = pb.Status_SUCCESS
    86  			bs.Status = pb.Status_SUCCESS
    87  			So(datastore.Put(ctx, bld), ShouldBeNil)
    88  
    89  			err := CheckLiveness(ctx, 1, 10)
    90  			So(err, ShouldBeNil)
    91  			So(sch.Tasks(), ShouldBeEmpty)
    92  		})
    93  
    94  		Convey("exceeds scheduling timeout", func() {
    95  			now := baseTime.Add(61 * time.Second)
    96  			ctx, _ = testclock.UseTime(ctx, now)
    97  
    98  			err := CheckLiveness(ctx, 1, 10)
    99  			So(err, ShouldBeNil)
   100  			So(datastore.Get(ctx, bld, bs), ShouldBeNil)
   101  			So(bld.Status, ShouldEqual, pb.Status_INFRA_FAILURE)
   102  			So(bs.Status, ShouldEqual, pb.Status_INFRA_FAILURE)
   103  			// FinalizeResultDB, ExportBigQuery, NotifyPubSub, NotifyPubSubGoProxy tasks.
   104  			So(sch.Tasks(), ShouldHaveLength, 4)
   105  		})
   106  
   107  		Convey("exceeds heartbeat timeout", func() {
   108  			bld.Proto.Status = pb.Status_STARTED
   109  			bld.Proto.StartTime = timestamppb.New(baseTime)
   110  			bs.Status = pb.Status_STARTED
   111  			steps := &model.BuildSteps{
   112  				ID:    1,
   113  				Build: datastore.KeyForObj(ctx, bld),
   114  			}
   115  			So(steps.FromProto([]*pb.Step{
   116  				{Name: "step1", Status: pb.Status_STARTED},
   117  			}), ShouldBeNil)
   118  			So(datastore.Put(ctx, bld, steps), ShouldBeNil)
   119  			now := baseTime.Add(11 * time.Second)
   120  			ctx, _ = testclock.UseTime(ctx, now)
   121  
   122  			err := CheckLiveness(ctx, 1, 10)
   123  			So(err, ShouldBeNil)
   124  			So(datastore.Get(ctx, bld, bs, steps), ShouldBeNil)
   125  			So(bld.Status, ShouldEqual, pb.Status_INFRA_FAILURE)
   126  			So(bs.Status, ShouldEqual, pb.Status_INFRA_FAILURE)
   127  			mSteps, err := steps.ToProto(ctx)
   128  			So(err, ShouldBeNil)
   129  			So(mSteps, ShouldResembleProto, []*pb.Step{
   130  				{
   131  					Name:    "step1",
   132  					Status:  pb.Status_CANCELED,
   133  					EndTime: timestamppb.New(now),
   134  				},
   135  			})
   136  			So(sch.Tasks(), ShouldHaveLength, 4)
   137  		})
   138  
   139  		Convey("exceeds execution timeout", func() {
   140  			now := baseTime.Add(121 * time.Second)
   141  			bld.Proto.Status = pb.Status_STARTED
   142  			bld.Proto.StartTime = timestamppb.New(baseTime)
   143  			// not exceed heartbeat timeout
   144  			bld.Proto.UpdateTime = timestamppb.New(now.Add(-9 * time.Second))
   145  			bs.Status = pb.Status_STARTED
   146  
   147  			So(datastore.Put(ctx, bld), ShouldBeNil)
   148  			ctx, _ = testclock.UseTime(ctx, now)
   149  
   150  			err := CheckLiveness(ctx, 1, 10)
   151  			So(err, ShouldBeNil)
   152  			So(datastore.Get(ctx, bld, bs), ShouldBeNil)
   153  			So(bld.Status, ShouldEqual, pb.Status_INFRA_FAILURE)
   154  			So(bs.Status, ShouldEqual, pb.Status_INFRA_FAILURE)
   155  			So(sch.Tasks(), ShouldHaveLength, 4)
   156  		})
   157  
   158  		Convey("not exceed scheduling timeout", func() {
   159  			now := baseTime.Add(59 * time.Second)
   160  			ctx, _ = testclock.UseTime(ctx, now)
   161  
   162  			err := CheckLiveness(ctx, 1, 10)
   163  			So(err, ShouldBeNil)
   164  			// continuation task
   165  			So(sch.Tasks(), ShouldHaveLength, 1)
   166  			So(sch.Tasks().Payloads()[0], ShouldResembleProto, &taskdefs.CheckBuildLiveness{
   167  				BuildId:          1,
   168  				HeartbeatTimeout: 10,
   169  			})
   170  		})
   171  
   172  		Convey("not exceed heartbeat timeout", func() {
   173  			bld.Proto.Status = pb.Status_STARTED
   174  			bld.Proto.StartTime = timestamppb.New(baseTime)
   175  			bs.Status = pb.Status_STARTED
   176  			So(datastore.Put(ctx, bld), ShouldBeNil)
   177  			now := baseTime.Add(9 * time.Second)
   178  			ctx, _ = testclock.UseTime(ctx, now)
   179  
   180  			err := CheckLiveness(ctx, 1, 10)
   181  			So(err, ShouldBeNil)
   182  			// continuation task
   183  			So(sch.Tasks(), ShouldHaveLength, 1)
   184  			So(sch.Tasks().Payloads()[0], ShouldResembleProto, &taskdefs.CheckBuildLiveness{
   185  				BuildId:          1,
   186  				HeartbeatTimeout: 10,
   187  			})
   188  			So(sch.Tasks()[0].ETA, ShouldEqual, now.Add(10*time.Second))
   189  		})
   190  
   191  		Convey("not exceed any timout && heartbeat timeout not set", func() {
   192  			bld.Proto.Status = pb.Status_STARTED
   193  			bld.Proto.StartTime = timestamppb.New(baseTime)
   194  			bs.Status = pb.Status_STARTED
   195  			So(datastore.Put(ctx, bld), ShouldBeNil)
   196  			now := baseTime.Add(9 * time.Second)
   197  			ctx, _ = testclock.UseTime(ctx, now)
   198  
   199  			err := CheckLiveness(ctx, 1, 0)
   200  			So(err, ShouldBeNil)
   201  			So(sch.Tasks(), ShouldHaveLength, 1)
   202  			So(sch.Tasks().Payloads()[0], ShouldResembleProto, &taskdefs.CheckBuildLiveness{
   203  				BuildId:          1,
   204  				HeartbeatTimeout: 0,
   205  			})
   206  			So(sch.Tasks()[0].ETA, ShouldEqual, baseTime.Add(120*time.Second))
   207  		})
   208  	})
   209  }