go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/frontend/actions.go (about) 1 // Copyright 2019 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 frontend 16 17 import ( 18 "fmt" 19 "net/http" 20 "strings" 21 22 "go.chromium.org/luci/common/errors" 23 "go.chromium.org/luci/grpc/grpcutil" 24 "go.chromium.org/luci/milo/frontend/ui" 25 "go.chromium.org/luci/milo/internal/buildsource/buildbucket" 26 "go.chromium.org/luci/milo/internal/utils" 27 "go.chromium.org/luci/server/router" 28 ) 29 30 // cancelBuildHandler parses inputs from HTTP form, cancels the build with the given buildbucket build ID 31 // then redirects the users back to the original page. 32 func cancelBuildHandler(ctx *router.Context) error { 33 id, reason, err := parseCancelBuildInput(ctx) 34 if err != nil { 35 return errors.Annotate(err, "error while parsing cancel build request input fields").Tag(grpcutil.InvalidArgumentTag).Err() 36 } 37 38 if _, err := buildbucket.CancelBuild(ctx.Request.Context(), id, reason); err != nil { 39 return err 40 } 41 42 http.Redirect(ctx.Writer, ctx.Request, ctx.Request.Referer(), http.StatusSeeOther) 43 return nil 44 } 45 46 func parseCancelBuildInput(ctx *router.Context) (buildbucketID int64, reason string, err error) { 47 if err := ctx.Request.ParseForm(); err != nil { 48 return 0, "", errors.Annotate(err, "unable to parse cancel build form").Err() 49 } 50 51 buildbucketID, err = utils.ParseIntFromForm(ctx.Request.Form, "buildbucket-id", 10, 64) 52 if err != nil { 53 return 0, "", errors.Annotate(err, "invalid buildbucket-id").Err() 54 } 55 56 reason, err = utils.ReadExactOneFromForm(ctx.Request.Form, "reason") 57 if err != nil { 58 return 0, "", err 59 } 60 reason = strings.TrimSpace(reason) 61 if reason == "" { 62 return 0, "", fmt.Errorf("reason cannot be empty") 63 } 64 65 return buildbucketID, reason, nil 66 } 67 68 // retryBuildHandler parses inputs from HTTP form, retries the build with the given buildbucket build ID 69 // then redirects the users to the retry build page. 70 func retryBuildHandler(ctx *router.Context) error { 71 buildbucketID, requestID, err := parseRetryBuildInput(ctx) 72 if err != nil { 73 return errors.Annotate(err, "error while parsing retry build request input fields").Tag(grpcutil.InvalidArgumentTag).Err() 74 } 75 76 build, err := buildbucket.RetryBuild(ctx.Request.Context(), buildbucketID, requestID) 77 if err != nil { 78 return err 79 } 80 uiBuild := ui.Build{ 81 Build: build, 82 } 83 http.Redirect(ctx.Writer, ctx.Request, uiBuild.Link().URL, http.StatusSeeOther) 84 return nil 85 } 86 87 func parseRetryBuildInput(ctx *router.Context) (buildbucketID int64, retryRequestID string, err error) { 88 if err := ctx.Request.ParseForm(); err != nil { 89 return 0, "", errors.Annotate(err, "unable to parse retry build form").Err() 90 } 91 92 buildbucketID, err = utils.ParseIntFromForm(ctx.Request.Form, "buildbucket-id", 10, 64) 93 if err != nil { 94 return 0, "", errors.Annotate(err, "invalid buildbucket-id").Err() 95 } 96 97 retryRequestID, err = utils.ReadExactOneFromForm(ctx.Request.Form, "retry-request-id") 98 if err != nil { 99 return 0, "", err 100 } 101 retryRequestID = strings.TrimSpace(retryRequestID) 102 if retryRequestID == "" { 103 return 0, "", fmt.Errorf("retry request ID cannot be empty") 104 } 105 106 return buildbucketID, retryRequestID, nil 107 }