go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/luci_notify/frontend/main.go (about) 1 // Copyright 2017 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 "net/http" 20 21 "github.com/golang/protobuf/proto" 22 "google.golang.org/grpc/codes" 23 "google.golang.org/grpc/status" 24 25 "go.chromium.org/luci/common/logging" 26 "go.chromium.org/luci/common/retry/transient" 27 "go.chromium.org/luci/common/tsmon/field" 28 "go.chromium.org/luci/common/tsmon/metric" 29 "go.chromium.org/luci/config/server/cfgmodule" 30 "go.chromium.org/luci/grpc/prpc" 31 luciserver "go.chromium.org/luci/server" 32 "go.chromium.org/luci/server/auth" 33 "go.chromium.org/luci/server/cron" 34 "go.chromium.org/luci/server/gaeemulation" 35 "go.chromium.org/luci/server/mailer" 36 "go.chromium.org/luci/server/module" 37 "go.chromium.org/luci/server/router" 38 spanmodule "go.chromium.org/luci/server/span" 39 "go.chromium.org/luci/server/tq" 40 41 pb "go.chromium.org/luci/luci_notify/api/service/v1" 42 "go.chromium.org/luci/luci_notify/config" 43 "go.chromium.org/luci/luci_notify/internal/span" 44 "go.chromium.org/luci/luci_notify/notify" 45 "go.chromium.org/luci/luci_notify/rpc" 46 "go.chromium.org/luci/luci_notify/server" 47 ) 48 49 const ( 50 AccessGroup = "luci-notify-api-access" 51 ) 52 53 var buildbucketPubSub = metric.NewCounter( 54 "luci/notify/buildbucket-pubsub", 55 "Number of received Buildbucket PubSub messages", 56 nil, 57 // "success", "transient-failure" or "permanent-failure" 58 field.String("status"), 59 ) 60 61 func checkAPIAccess(ctx context.Context, methodName string, req proto.Message) (context.Context, error) { 62 switch yes, err := auth.IsMember(ctx, AccessGroup); { 63 case err != nil: 64 return nil, status.Errorf(codes.Internal, "error when checking group membership") 65 case !yes: 66 return nil, status.Errorf(codes.PermissionDenied, "%s does not have access to method %s of Luci Notify", auth.CurrentIdentity(ctx), methodName) 67 default: 68 return ctx, nil 69 } 70 } 71 72 func main() { 73 modules := []module.Module{ 74 cfgmodule.NewModuleFromFlags(), 75 cron.NewModuleFromFlags(), 76 gaeemulation.NewModuleFromFlags(), 77 mailer.NewModuleFromFlags(), 78 spanmodule.NewModuleFromFlags(nil), 79 tq.NewModuleFromFlags(), 80 } 81 82 notify.InitDispatcher(&tq.Default) 83 84 luciserver.Main(nil, modules, func(srv *luciserver.Server) error { 85 srv.ConfigurePRPC(func(s *prpc.Server) { 86 s.AccessControl = prpc.AllowOriginAll 87 // TODO(crbug/1082369): Remove this workaround once field masks can be decoded. 88 s.HackFixFieldMasksForJSON = true 89 }) 90 srv.RegisterUnaryServerInterceptors(span.SpannerDefaultsInterceptor()) 91 92 // Cron endpoints. 93 cron.RegisterHandler("update-config", config.UpdateHandler) 94 cron.RegisterHandler("update-tree-status", notify.UpdateTreeStatus) 95 96 // Buildbucket Pub/Sub endpoint. 97 srv.Routes.POST("/_ah/push-handlers/buildbucket", nil, 98 func(c *router.Context) { 99 ctx, cancel := context.WithTimeout(c.Request.Context(), notify.PUBSUB_POST_REQUEST_TIMEOUT) 100 defer cancel() 101 c.Request = c.Request.WithContext(ctx) 102 103 status := "" 104 switch err := notify.BuildbucketPubSubHandler(c); { 105 case transient.Tag.In(err): 106 status = "transient-failure" 107 logging.Errorf(ctx, "transient failure: %s", err) 108 // Retry the message. 109 c.Writer.WriteHeader(http.StatusInternalServerError) 110 111 case err != nil: 112 status = "permanent-failure" 113 logging.Errorf(ctx, "permanent failure: %s", err) 114 115 default: 116 status = "success" 117 } 118 119 buildbucketPubSub.Add(ctx, 1, status) 120 }) 121 122 // Installs RPC service. 123 pb.RegisterTreeCloserServer(srv, &pb.DecoratedTreeCloser{ 124 Service: &server.TreeCloserServer{}, 125 Prelude: checkAPIAccess, 126 }) 127 pb.RegisterAlertsServer(srv, rpc.NewAlertsServer()) 128 129 return nil 130 }) 131 }