go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/buildbucket/appengine/rpc/get_builder_test.go (about)

     1  // Copyright 2020 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 rpc
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  
    21  	"google.golang.org/grpc/codes"
    22  
    23  	"go.chromium.org/luci/auth/identity"
    24  	"go.chromium.org/luci/gae/impl/memory"
    25  	"go.chromium.org/luci/gae/service/datastore"
    26  	"go.chromium.org/luci/server/auth"
    27  	"go.chromium.org/luci/server/auth/authtest"
    28  
    29  	"go.chromium.org/luci/buildbucket/appengine/model"
    30  	"go.chromium.org/luci/buildbucket/bbperms"
    31  	pb "go.chromium.org/luci/buildbucket/proto"
    32  
    33  	. "github.com/smartystreets/goconvey/convey"
    34  	. "go.chromium.org/luci/common/testing/assertions"
    35  )
    36  
    37  func TestGetBuilder(t *testing.T) {
    38  	t.Parallel()
    39  
    40  	const userID = identity.Identity("user:user@example.com")
    41  
    42  	Convey("GetBuilder", t, func() {
    43  		srv := &Builders{}
    44  		ctx := memory.Use(context.Background())
    45  		datastore.GetTestable(ctx).AutoIndex(true)
    46  		datastore.GetTestable(ctx).Consistent(true)
    47  
    48  		bid := &pb.BuilderID{
    49  			Project: "project",
    50  			Bucket:  "bucket",
    51  			Builder: "builder",
    52  		}
    53  
    54  		Convey(`Request validation`, func() {
    55  			Convey(`Invalid ID`, func() {
    56  				_, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{})
    57  				So(err, ShouldHaveAppStatus, codes.InvalidArgument, "id: project must match")
    58  			})
    59  		})
    60  
    61  		Convey(`No permissions`, func() {
    62  			ctx = auth.WithState(ctx, &authtest.FakeState{
    63  				Identity: userID,
    64  			})
    65  			So(datastore.Put(
    66  				ctx,
    67  				&model.Bucket{
    68  					Parent: model.ProjectKey(ctx, "project"),
    69  					ID:     "bucket",
    70  				},
    71  				&model.Builder{
    72  					Parent: model.BucketKey(ctx, "project", "bucket"),
    73  					ID:     "builder",
    74  					Config: &pb.BuilderConfig{Name: "builder"},
    75  				},
    76  			), ShouldBeNil)
    77  
    78  			_, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{Id: bid})
    79  			So(err, ShouldHaveAppStatus, codes.NotFound, "not found")
    80  		})
    81  
    82  		Convey(`End to end`, func() {
    83  			ctx = auth.WithState(ctx, &authtest.FakeState{
    84  				Identity: userID,
    85  				FakeDB: authtest.NewFakeDB(
    86  					authtest.MockPermission(userID, "project:bucket", bbperms.BuildersGet),
    87  				),
    88  			})
    89  			So(datastore.Put(
    90  				ctx,
    91  				&model.Bucket{
    92  					Parent: model.ProjectKey(ctx, "project"),
    93  					ID:     "bucket",
    94  					Proto:  &pb.Bucket{},
    95  				},
    96  				&model.Builder{
    97  					Parent: model.BucketKey(ctx, "project", "bucket"),
    98  					ID:     "builder",
    99  					Config: &pb.BuilderConfig{Name: "builder"},
   100  				},
   101  			), ShouldBeNil)
   102  
   103  			res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{Id: bid})
   104  			So(err, ShouldBeNil)
   105  			So(res, ShouldResembleProto, &pb.BuilderItem{
   106  				Id:     bid,
   107  				Config: &pb.BuilderConfig{Name: "builder"},
   108  			})
   109  		})
   110  
   111  		Convey(`metadata`, func() {
   112  			ctx = auth.WithState(ctx, &authtest.FakeState{
   113  				Identity: userID,
   114  				FakeDB: authtest.NewFakeDB(
   115  					authtest.MockPermission(userID, "project:bucket", bbperms.BuildersGet),
   116  				),
   117  			})
   118  			So(datastore.Put(
   119  				ctx,
   120  				&model.Bucket{
   121  					Parent: model.ProjectKey(ctx, "project"),
   122  					ID:     "bucket",
   123  					Proto:  &pb.Bucket{},
   124  				},
   125  				&model.Builder{
   126  					Parent: model.BucketKey(ctx, "project", "bucket"),
   127  					ID:     "builder",
   128  					Config: &pb.BuilderConfig{Name: "builder"},
   129  					Metadata: &pb.BuilderMetadata{
   130  						Owner: "owner",
   131  						Health: &pb.HealthStatus{
   132  							HealthScore: 9,
   133  						},
   134  					},
   135  				},
   136  				&model.Builder{
   137  					Parent: model.BucketKey(ctx, "project", "bucket"),
   138  					ID:     "builder2",
   139  					Config: &pb.BuilderConfig{Name: "builder2"},
   140  				},
   141  			), ShouldBeNil)
   142  			res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{
   143  				Id: bid,
   144  				Mask: &pb.BuilderMask{
   145  					Type: pb.BuilderMask_ALL,
   146  				},
   147  			})
   148  			So(err, ShouldBeNil)
   149  			So(res, ShouldResembleProto, &pb.BuilderItem{
   150  				Id:     bid,
   151  				Config: &pb.BuilderConfig{Name: "builder"},
   152  				Metadata: &pb.BuilderMetadata{
   153  					Owner: "owner",
   154  					Health: &pb.HealthStatus{
   155  						HealthScore: 9,
   156  					},
   157  				},
   158  			})
   159  			bid.Builder = "builder2"
   160  			res, err = srv.GetBuilder(ctx, &pb.GetBuilderRequest{
   161  				Id: bid,
   162  				Mask: &pb.BuilderMask{
   163  					Type: pb.BuilderMask_ALL,
   164  				},
   165  			})
   166  			So(err, ShouldBeNil)
   167  			So(res, ShouldResembleProto, &pb.BuilderItem{
   168  				Id:     bid,
   169  				Config: &pb.BuilderConfig{Name: "builder2"},
   170  			})
   171  		})
   172  
   173  		Convey(`mask`, func() {
   174  			ctx = auth.WithState(ctx, &authtest.FakeState{
   175  				Identity: userID,
   176  				FakeDB: authtest.NewFakeDB(
   177  					authtest.MockPermission(userID, "project:bucket", bbperms.BuildersGet),
   178  				),
   179  			})
   180  			So(datastore.Put(
   181  				ctx,
   182  				&model.Bucket{
   183  					Parent: model.ProjectKey(ctx, "project"),
   184  					ID:     "bucket",
   185  					Proto:  &pb.Bucket{},
   186  				},
   187  				&model.Builder{
   188  					Parent: model.BucketKey(ctx, "project", "bucket"),
   189  					ID:     "builder",
   190  					Config: &pb.BuilderConfig{Name: "builder"},
   191  					Metadata: &pb.BuilderMetadata{
   192  						Owner: "owner",
   193  						Health: &pb.HealthStatus{
   194  							HealthScore: 9,
   195  						},
   196  					},
   197  				},
   198  			), ShouldBeNil)
   199  
   200  			Convey(`default mask`, func() {
   201  				res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{Id: bid})
   202  				So(err, ShouldBeNil)
   203  				So(res, ShouldResembleProto, &pb.BuilderItem{
   204  					Id:     bid,
   205  					Config: &pb.BuilderConfig{Name: "builder"},
   206  				})
   207  			})
   208  
   209  			Convey(`config only`, func() {
   210  				res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{
   211  					Id: bid,
   212  					Mask: &pb.BuilderMask{
   213  						Type: pb.BuilderMask_CONFIG_ONLY,
   214  					},
   215  				})
   216  				So(err, ShouldBeNil)
   217  				So(res, ShouldResembleProto, &pb.BuilderItem{
   218  					Id:     bid,
   219  					Config: &pb.BuilderConfig{Name: "builder"},
   220  				})
   221  			})
   222  
   223  			Convey(`metadata only`, func() {
   224  				res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{
   225  					Id: bid,
   226  					Mask: &pb.BuilderMask{
   227  						Type: pb.BuilderMask_METADATA_ONLY,
   228  					},
   229  				})
   230  				So(err, ShouldBeNil)
   231  				So(res, ShouldResembleProto, &pb.BuilderItem{
   232  					Id: bid,
   233  					Metadata: &pb.BuilderMetadata{
   234  						Owner: "owner",
   235  						Health: &pb.HealthStatus{
   236  							HealthScore: 9,
   237  						},
   238  					},
   239  				})
   240  			})
   241  
   242  			Convey(`all`, func() {
   243  				res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{
   244  					Id: bid,
   245  					Mask: &pb.BuilderMask{
   246  						Type: pb.BuilderMask_ALL,
   247  					},
   248  				})
   249  				So(err, ShouldBeNil)
   250  				So(res, ShouldResembleProto, &pb.BuilderItem{
   251  					Id:     bid,
   252  					Config: &pb.BuilderConfig{Name: "builder"},
   253  					Metadata: &pb.BuilderMetadata{
   254  						Owner: "owner",
   255  						Health: &pb.HealthStatus{
   256  							HealthScore: 9,
   257  						},
   258  					},
   259  				})
   260  			})
   261  		})
   262  
   263  		Convey(`shadow`, func() {
   264  			ctx = auth.WithState(ctx, &authtest.FakeState{
   265  				Identity: userID,
   266  				FakeDB: authtest.NewFakeDB(
   267  					authtest.MockPermission(userID, "project:bucket.shadow", bbperms.BuildersGet),
   268  				),
   269  			})
   270  			So(datastore.Put(
   271  				ctx,
   272  				&model.Bucket{
   273  					Parent: model.ProjectKey(ctx, "project"),
   274  					ID:     "bucket",
   275  					Proto: &pb.Bucket{
   276  						Shadow: "bucket.shadow",
   277  					},
   278  				},
   279  				&model.Bucket{
   280  					Parent:  model.ProjectKey(ctx, "project"),
   281  					ID:      "bucket.shadow",
   282  					Proto:   &pb.Bucket{},
   283  					Shadows: []string{"bucket"},
   284  				},
   285  				&model.Builder{
   286  					Parent: model.BucketKey(ctx, "project", "bucket"),
   287  					ID:     "builder",
   288  					Config: &pb.BuilderConfig{
   289  						Name:           "builder",
   290  						ServiceAccount: "example@example.com",
   291  						Dimensions:     []string{"os:Linux", "pool:luci.chromium.try", "todelete:random"},
   292  						Properties:     `{"a":"b"}`,
   293  						ShadowBuilderAdjustments: &pb.BuilderConfig_ShadowBuilderAdjustments{
   294  							ServiceAccount: "shadow@example.com",
   295  							Pool:           "shadow.pool",
   296  							Properties:     `{"a":"b2"}`,
   297  							Dimensions: []string{
   298  								"pool:shadow.pool",
   299  								"todelete:",
   300  								"120:toadd:v",
   301  							},
   302  						},
   303  					},
   304  				},
   305  			), ShouldBeNil)
   306  
   307  			shadowBID := &pb.BuilderID{
   308  				Project: "project",
   309  				Bucket:  "bucket.shadow",
   310  				Builder: "builder",
   311  			}
   312  			res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{Id: shadowBID})
   313  			So(err, ShouldBeNil)
   314  			So(res, ShouldResembleProto, &pb.BuilderItem{
   315  				Id: shadowBID,
   316  				Config: &pb.BuilderConfig{
   317  					Name:           "builder",
   318  					ServiceAccount: "shadow@example.com",
   319  					Dimensions:     []string{"120:toadd:v", "os:Linux", "pool:shadow.pool"},
   320  					Properties:     `{"a":"b2"}`,
   321  				},
   322  			})
   323  		})
   324  	})
   325  }