go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/swarming/server/testing/fakesubmit/main.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  // Command fakesubmit submits an RBE reservation for testing fakebot.
    16  package main
    17  
    18  import (
    19  	"context"
    20  	"flag"
    21  	"fmt"
    22  	"os"
    23  	"time"
    24  
    25  	"github.com/google/uuid"
    26  	"google.golang.org/grpc"
    27  	"google.golang.org/grpc/codes"
    28  	"google.golang.org/grpc/credentials"
    29  	"google.golang.org/grpc/status"
    30  	"google.golang.org/protobuf/types/known/anypb"
    31  	"google.golang.org/protobuf/types/known/timestamppb"
    32  
    33  	"go.chromium.org/luci/auth"
    34  	"go.chromium.org/luci/common/clock"
    35  	"go.chromium.org/luci/common/errors"
    36  	"go.chromium.org/luci/common/logging"
    37  	"go.chromium.org/luci/common/logging/gologger"
    38  	"go.chromium.org/luci/common/system/signals"
    39  	"go.chromium.org/luci/hardcoded/chromeinfra"
    40  
    41  	"go.chromium.org/luci/swarming/internal/remoteworkers"
    42  	internalspb "go.chromium.org/luci/swarming/proto/internals"
    43  )
    44  
    45  var (
    46  	pool         = flag.String("pool", "local-test", "Value for `pool` dimension")
    47  	rbeInstance  = flag.String("rbe-instance", "projects/chromium-swarm-dev/instances/default_instance", "Full RBE instance name to use for tests")
    48  	expiration   = flag.Duration("expiration", 10*time.Minute, "Task expiration time")
    49  	tasks        = flag.Int("tasks", 1, "How many tasks to submit in parallel")
    50  	noop         = flag.Bool("noop", false, "If set, mark tasks as noop")
    51  	taskIDPrefix = flag.String("task-id-prefix", "", "Prefix for reservation IDs")
    52  )
    53  
    54  func main() {
    55  	flag.Parse()
    56  	ctx := gologger.StdConfig.Use(context.Background())
    57  	if err := run(ctx); err != nil {
    58  		errors.Log(ctx, err)
    59  		os.Exit(1)
    60  	}
    61  }
    62  
    63  func run(ctx context.Context) error {
    64  	creds, err := auth.NewAuthenticator(ctx, auth.SilentLogin, chromeinfra.SetDefaultAuthOptions(auth.Options{
    65  		Scopes: []string{
    66  			"https://www.googleapis.com/auth/cloud-platform",
    67  			"https://www.googleapis.com/auth/userinfo.email",
    68  		},
    69  	})).PerRPCCredentials()
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	cc, err := grpc.DialContext(ctx, "remotebuildexecution.googleapis.com:443",
    75  		grpc.WithTransportCredentials(credentials.NewTLS(nil)),
    76  		grpc.WithPerRPCCredentials(creds),
    77  		grpc.WithBlock(),
    78  	)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	rbe := remoteworkers.NewReservationsClient(cc)
    83  
    84  	loopCtx, cancel := context.WithCancel(ctx)
    85  	defer cancel()
    86  	signals.HandleInterrupt(func() {
    87  		logging.Infof(ctx, "Got termination signal")
    88  		cancel()
    89  	})
    90  
    91  	if *taskIDPrefix == "" {
    92  		*taskIDPrefix = uuid.New().String()
    93  	}
    94  
    95  	type execResult struct {
    96  		taskID      string
    97  		start       time.Time
    98  		end         time.Time
    99  		reservation *remoteworkers.Reservation
   100  		err         error
   101  	}
   102  	results := make(chan execResult, *tasks)
   103  
   104  	for i := 0; i < *tasks; i++ {
   105  		i := i
   106  		go func() {
   107  			taskID := fmt.Sprintf("%s-%04d", *taskIDPrefix, i)
   108  			start := clock.Now(ctx)
   109  			reservation, err := execTask(ctx, loopCtx, rbe, taskID)
   110  			results <- execResult{
   111  				taskID:      taskID,
   112  				start:       start,
   113  				end:         clock.Now(ctx),
   114  				reservation: reservation,
   115  				err:         err,
   116  			}
   117  		}()
   118  	}
   119  
   120  	for i := 0; i < *tasks; i++ {
   121  		res := <-results
   122  		switch {
   123  		case res.err != nil:
   124  			logging.Errorf(ctx, "%s: %s", res.taskID, res.err)
   125  		case res.reservation == nil:
   126  			logging.Warningf(ctx, "%s: canceled", res.taskID)
   127  		default:
   128  			reservation := res.reservation
   129  			if reservation.Status.GetCode() != 0 {
   130  				logging.Infof(ctx, "%s: %s %s", res.taskID, reservation.AssignedBotId, status.FromProto(reservation.Status))
   131  			} else {
   132  				logging.Infof(ctx, "%s: %s", res.taskID, reservation.AssignedBotId)
   133  			}
   134  		}
   135  	}
   136  
   137  	return nil
   138  }
   139  
   140  func execTask(ctx, loopCtx context.Context, rbe remoteworkers.ReservationsClient, taskID string) (*remoteworkers.Reservation, error) {
   141  	reservationName := fmt.Sprintf("%s/reservations/%s", *rbeInstance, taskID)
   142  
   143  	payload, err := anypb.New(&internalspb.TaskPayload{
   144  		TaskId: taskID,
   145  		Noop:   *noop,
   146  	})
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	reservation, err := rbe.CreateReservation(ctx, &remoteworkers.CreateReservationRequest{
   152  		Parent: *rbeInstance,
   153  		Reservation: &remoteworkers.Reservation{
   154  			Name:       reservationName,
   155  			Payload:    payload,
   156  			ExpireTime: timestamppb.New(time.Now().Add(*expiration)),
   157  			Constraints: []*remoteworkers.Constraint{
   158  				{Key: "label:pool", AllowedValues: []string{*pool}},
   159  			},
   160  		},
   161  	})
   162  	if status.Code(err) == codes.AlreadyExists {
   163  		reservation, err = rbe.GetReservation(ctx, &remoteworkers.GetReservationRequest{
   164  			Name: reservationName,
   165  		})
   166  		if err != nil {
   167  			return nil, errors.Annotate(err, "GetReservation").Err()
   168  		}
   169  	} else if err != nil {
   170  		return nil, errors.Annotate(err, "CreateReservation").Err()
   171  	}
   172  
   173  	for loopCtx.Err() == nil {
   174  		if reservation.State == remoteworkers.ReservationState_RESERVATION_COMPLETED || reservation.State == remoteworkers.ReservationState_RESERVATION_CANCELLED {
   175  			return reservation, nil
   176  		}
   177  		reservation, err = rbe.GetReservation(loopCtx, &remoteworkers.GetReservationRequest{
   178  			Name:        reservationName,
   179  			WaitSeconds: 60,
   180  		})
   181  		if err != nil {
   182  			if loopCtx.Err() != nil {
   183  				break
   184  			}
   185  			return nil, err
   186  		}
   187  	}
   188  
   189  	_, err = rbe.CancelReservation(ctx, &remoteworkers.CancelReservationRequest{
   190  		Name:   reservationName,
   191  		Intent: remoteworkers.CancelReservationIntent_ANY,
   192  	})
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  	return nil, nil
   197  }