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  }