go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/buildbucket/appengine/taskbackendlite/run_task_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 main 16 17 import ( 18 "context" 19 "encoding/json" 20 "fmt" 21 "testing" 22 23 "google.golang.org/grpc/codes" 24 "google.golang.org/protobuf/types/known/structpb" 25 26 "go.chromium.org/luci/auth/identity" 27 "go.chromium.org/luci/gae/impl/memory" 28 "go.chromium.org/luci/server/auth" 29 "go.chromium.org/luci/server/auth/authtest" 30 "go.chromium.org/luci/server/caching" 31 "go.chromium.org/luci/server/caching/cachingtest" 32 33 "go.chromium.org/luci/buildbucket/appengine/internal/clients" 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 TestRunTask(t *testing.T) { 41 t.Parallel() 42 43 Convey("RunTask", t, func() { 44 ctx := memory.UseWithAppID(context.Background(), "myApp-dev") 45 ctx = cachingtest.WithGlobalCache(ctx, map[string]caching.BlobCache{ 46 "taskbackendlite-run-task": cachingtest.NewBlobCache(), 47 }) 48 ctx = auth.WithState(ctx, &authtest.FakeState{ 49 Identity: identity.Identity("project:myProject"), 50 PeerIdentityOverride: identity.Identity("user:cr-buildbucket-dev@appspot.gserviceaccount.com"), 51 }) 52 ctx, psserver, psclient, err := clients.SetupTestPubsub(ctx, "myApp-dev") 53 So(err, ShouldBeNil) 54 defer func() { 55 psclient.Close() 56 psserver.Close() 57 }() 58 myTopic, err := psclient.CreateTopic(ctx, fmt.Sprintf(TopicIDFormat, "myProject")) 59 So(err, ShouldBeNil) 60 61 srv := &TaskBackendLite{} 62 req := &pb.RunTaskRequest{ 63 BuildId: "123", 64 RequestId: "request_id", 65 Target: "target", 66 Secrets: &pb.BuildSecrets{ 67 StartBuildToken: "token", 68 }, 69 BackendConfig: &structpb.Struct{ 70 Fields: map[string]*structpb.Value{ 71 "tags": { 72 Kind: &structpb.Value_ListValue{ 73 ListValue: &structpb.ListValue{ 74 Values: []*structpb.Value{ 75 {Kind: &structpb.Value_StringValue{StringValue: "buildbucket_bucket:infra/try"}}, 76 {Kind: &structpb.Value_StringValue{StringValue: "builder:foo"}}, 77 }, 78 }, 79 }, 80 }, 81 }, 82 }, 83 } 84 Convey("ok", func() { 85 res, err := srv.RunTask(ctx, req) 86 87 So(err, ShouldBeNil) 88 So(res, ShouldResembleProto, &pb.RunTaskResponse{ 89 Task: &pb.Task{ 90 Id: &pb.TaskID{ 91 Id: "123_request_id", 92 Target: "target", 93 }, 94 UpdateId: 1, 95 }, 96 }) 97 So(psserver.Messages(), ShouldHaveLength, 1) 98 publishedMsg := psserver.Messages()[0] 99 So(publishedMsg.Attributes["dummy_task_id"], ShouldEqual, "123_request_id") 100 So(publishedMsg.Attributes["project"], ShouldEqual, "infra") 101 So(publishedMsg.Attributes["bucket"], ShouldEqual, "try") 102 So(publishedMsg.Attributes["builder"], ShouldEqual, "foo") 103 data := &TaskNotification{} 104 err = json.Unmarshal(publishedMsg.Data, data) 105 So(err, ShouldBeNil) 106 So(data, ShouldResemble, &TaskNotification{ 107 BuildID: "123", 108 StartBuildToken: "token", 109 }) 110 }) 111 112 Convey("nil BackendConfig", func() { 113 req.BackendConfig = nil 114 115 res, err := srv.RunTask(ctx, req) 116 So(err, ShouldBeNil) 117 So(err, ShouldBeNil) 118 So(res, ShouldResembleProto, &pb.RunTaskResponse{ 119 Task: &pb.Task{ 120 Id: &pb.TaskID{ 121 Id: "123_request_id", 122 Target: "target", 123 }, 124 UpdateId: 1, 125 }, 126 }) 127 128 So(psserver.Messages(), ShouldHaveLength, 1) 129 publishedMsg := psserver.Messages()[0] 130 So(publishedMsg.Attributes["dummy_task_id"], ShouldEqual, "123_request_id") 131 So(publishedMsg.Attributes["project"], ShouldEqual, "") 132 So(publishedMsg.Attributes["bucket"], ShouldEqual, "") 133 So(publishedMsg.Attributes["builder"], ShouldEqual, "") 134 }) 135 136 Convey("no builder related tags in req", func() { 137 req.BackendConfig = &structpb.Struct{ 138 Fields: map[string]*structpb.Value{ 139 "tags": { 140 Kind: &structpb.Value_ListValue{ 141 ListValue: &structpb.ListValue{ 142 Values: []*structpb.Value{ 143 {Kind: &structpb.Value_NumberValue{NumberValue: 10}}, 144 {Kind: &structpb.Value_StringValue{StringValue: "any"}}, 145 }, 146 }, 147 }, 148 }, 149 }, 150 } 151 152 res, err := srv.RunTask(ctx, req) 153 So(err, ShouldBeNil) 154 So(res, ShouldResembleProto, &pb.RunTaskResponse{ 155 Task: &pb.Task{ 156 Id: &pb.TaskID{ 157 Id: "123_request_id", 158 Target: "target", 159 }, 160 UpdateId: 1, 161 }, 162 }) 163 164 So(psserver.Messages(), ShouldHaveLength, 1) 165 publishedMsg := psserver.Messages()[0] 166 So(publishedMsg.Attributes["dummy_task_id"], ShouldEqual, "123_request_id") 167 So(publishedMsg.Attributes["project"], ShouldEqual, "") 168 So(publishedMsg.Attributes["bucket"], ShouldEqual, "") 169 So(publishedMsg.Attributes["builder"], ShouldEqual, "") 170 }) 171 172 Convey("duplicate req", func() { 173 cache := caching.GlobalCache(ctx, "taskbackendlite-run-task") 174 err := cache.Set(ctx, "123_request_id", []byte{1}, DefaultTaskCreationTimeout) 175 So(err, ShouldBeNil) 176 177 res, err := srv.RunTask(ctx, req) 178 179 So(err, ShouldBeNil) 180 So(res, ShouldResembleProto, &pb.RunTaskResponse{ 181 Task: &pb.Task{ 182 Id: &pb.TaskID{ 183 Id: "123_request_id", 184 Target: "target", 185 }, 186 UpdateId: 1, 187 }, 188 }) 189 So(psserver.Messages(), ShouldHaveLength, 0) 190 }) 191 192 Convey("topic not exist", func() { 193 err := myTopic.Delete(ctx) 194 So(err, ShouldBeNil) 195 res, err := srv.RunTask(ctx, req) 196 So(res, ShouldBeNil) 197 So(err, ShouldHaveGRPCStatus, codes.InvalidArgument, "topic taskbackendlite-myProject does not exist on Cloud project myApp-dev") 198 }) 199 200 Convey("perm errors", func() { 201 Convey("no access", func() { 202 ctx = auth.WithState(ctx, &authtest.FakeState{ 203 Identity: identity.Identity("project:myProject"), 204 PeerIdentityOverride: identity.Identity("user:user1@example.com"), 205 }) 206 res, err := srv.RunTask(ctx, req) 207 So(res, ShouldBeNil) 208 So(err, ShouldHaveGRPCStatus, codes.PermissionDenied, `the peer "user:user1@example.com" is not allowed to access this task backend`) 209 }) 210 211 Convey("not a project identity", func() { 212 ctx = auth.WithState(ctx, &authtest.FakeState{ 213 Identity: identity.Identity("user:user1"), 214 PeerIdentityOverride: identity.Identity("user:cr-buildbucket-dev@appspot.gserviceaccount.com"), 215 }) 216 res, err := srv.RunTask(ctx, req) 217 So(res, ShouldBeNil) 218 So(err, ShouldHaveGRPCStatus, codes.PermissionDenied, `The caller's user identity "user:user1" is not a project identity`) 219 }) 220 }) 221 }) 222 }