go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/deploy/service/model/expiry_test.go (about) 1 // Copyright 2022 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 model 16 17 import ( 18 "context" 19 "testing" 20 "time" 21 22 "google.golang.org/protobuf/types/known/durationpb" 23 24 "go.chromium.org/luci/common/clock/testclock" 25 "go.chromium.org/luci/deploy/api/modelpb" 26 "go.chromium.org/luci/deploy/api/rpcpb" 27 "go.chromium.org/luci/gae/impl/memory" 28 "go.chromium.org/luci/gae/service/datastore" 29 30 . "github.com/smartystreets/goconvey/convey" 31 ) 32 33 func TestExpireActuations(t *testing.T) { 34 t.Parallel() 35 36 Convey("With datastore", t, func() { 37 now := testclock.TestRecentTimeUTC.Round(time.Millisecond) 38 ctx, tc := testclock.UseTime(context.Background(), now) 39 ctx = memory.Use(ctx) 40 41 datastore.GetTestable(ctx).AutoIndex(true) 42 datastore.GetTestable(ctx).Consistent(true) 43 44 intendedState := func(payload string, traffic int32) *modelpb.AssetState { 45 return &modelpb.AssetState{ 46 State: &modelpb.AssetState_Appengine{ 47 Appengine: mockedIntendedState(payload, traffic), 48 }, 49 } 50 } 51 52 reportedState := func(payload string, traffic int32) *modelpb.AssetState { 53 return &modelpb.AssetState{ 54 State: &modelpb.AssetState_Appengine{ 55 Appengine: mockedReportedState(payload, traffic), 56 }, 57 } 58 } 59 60 actuation := func(actuationID string) *modelpb.Actuation { 61 ent := &Actuation{ID: actuationID} 62 So(datastore.Get(ctx, ent), ShouldBeNil) 63 return ent.Actuation 64 } 65 66 assetEntity := func(assetID string) *Asset { 67 ent := &Asset{ID: assetID} 68 So(datastore.Get(ctx, ent), ShouldBeNil) 69 return ent 70 } 71 72 history := func(assetID string, historyID int64) *modelpb.AssetHistory { 73 ent := &AssetHistory{ 74 ID: historyID, 75 Parent: datastore.NewKey(ctx, "Asset", assetID, 0, nil), 76 } 77 if datastore.Get(ctx, ent) == datastore.ErrNoSuchEntity { 78 return nil 79 } 80 return ent.Entry 81 } 82 83 Convey("Expiry works", func() { 84 So(datastore.Put(ctx, &Asset{ 85 ID: "apps/app-1", 86 Asset: &modelpb.Asset{Id: "apps/app-1"}, 87 ConsecutiveFailures: 111, 88 }), ShouldBeNil) 89 90 // Start the new actuation. 91 op, err := NewActuationBeginOp(ctx, []string{"apps/app-1"}, &modelpb.Actuation{ 92 Id: "new-actuation", 93 Deployment: &modelpb.Deployment{ 94 Config: &modelpb.DeploymentConfig{ 95 ActuationTimeout: durationpb.New(5 * time.Minute), 96 }, 97 }, 98 }) 99 So(err, ShouldBeNil) 100 op.MakeDecision(ctx, "apps/app-1", &rpcpb.AssetToActuate{ 101 Config: &modelpb.AssetConfig{EnableAutomation: true}, 102 IntendedState: intendedState("app-1", 0), 103 ReportedState: reportedState("app-1", 0), 104 }) 105 _, err = op.Apply(ctx) 106 So(err, ShouldBeNil) 107 108 // It is executing now. 109 So(actuation("new-actuation").State, ShouldEqual, modelpb.Actuation_EXECUTING) 110 111 // Run the expiration cron a bit later (but before the expiry). 112 tc.Add(4 * time.Minute) 113 So(ExpireActuations(ctx), ShouldBeNil) 114 115 // Still executing. 116 So(actuation("new-actuation").State, ShouldEqual, modelpb.Actuation_EXECUTING) 117 118 // Run the expiration cron after the expiry. 119 tc.Add(2 * time.Minute) 120 So(ExpireActuations(ctx), ShouldBeNil) 121 122 // The actuation has expired. 123 So(actuation("new-actuation").State, ShouldEqual, modelpb.Actuation_EXPIRED) 124 125 // There's a history record for the asset being actuated. 126 So(history("apps/app-1", 1).Actuation.State, ShouldEqual, modelpb.Actuation_EXPIRED) 127 128 // Failure counter incremented. 129 So(assetEntity("apps/app-1").ConsecutiveFailures, ShouldEqual, 112) 130 }) 131 }) 132 }