go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/module/module.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 module defines a framework for extending server.Server with optional 16 // reusable bundles of functionality (called "modules", naturally). 17 package module 18 19 import ( 20 "context" 21 "net" 22 23 "google.golang.org/grpc" 24 25 "go.chromium.org/luci/server/auth" 26 "go.chromium.org/luci/server/router" 27 ) 28 29 // Serverless is an enumeration of recognized serverless runtimes. 30 // 31 // It exists to allow modules to recognize they run in specific serverless 32 // environments in case modules explicitly depend on features provided by these 33 // environment. 34 // 35 // For example, on GAE (and only there!) headers `X-Appengine-*` can be trusted 36 // and some modules take advantage of that. 37 type Serverless string 38 39 const ( 40 // Unknown means the server didn't detect any serverless environment. 41 Unknown Serverless = "" 42 // GAE means the server is running on Google Cloud AppEngine. 43 GAE Serverless = "GAE" 44 // CloudRun means the server is running on Google Serverless Cloud Run. 45 CloudRun Serverless = "Cloud Run" 46 ) 47 48 // IsGCP is true for a serverless Google platform. 49 func (s Serverless) IsGCP() bool { 50 return s == GAE || s == CloudRun 51 } 52 53 // Module represents some optional part of a server. 54 // 55 // It is generally a stateful object constructed from some configuration. It can 56 // be hooked to the server in server.New(...) or server.Main(...). 57 type Module interface { 58 // Name is a name of this module for logs and dependency relations. 59 // 60 // Usually a package that implements the module registers module's name as 61 // a public global variable (so other packages can refer to it) and this 62 // method just returns it. 63 Name() Name 64 65 // Dependencies returns dependencies (required and optional) of this module. 66 Dependencies() []Dependency 67 68 // Initialize is called during the server startup after the core functionality 69 // is initialized, but before the server enters the serving loop. 70 // 71 // The method receives the global server context and can return a derived 72 // context which will become the new global server context. That way the 73 // module may inject additional state into the context inherited by all 74 // requests. If it returns nil, the server context won't be changed. 75 // 76 // The module may use the context (with all values available there) and the 77 // given Host interface to register itself in various server systems. It must 78 // not retain 'host': the object it points to becomes invalid after Initialize 79 // finishes and using it causes panics. This is intentional. 80 // 81 // Modules are initialized sequentially in order they are registered. A module 82 // observes effects of initialization of all prior modules, e.g. via 83 // context.Context. 84 // 85 // An error causes the process to crash with the corresponding message. 86 Initialize(ctx context.Context, host Host, opts HostOptions) (context.Context, error) 87 } 88 89 // HostOptions are options the server was started with. 90 // 91 // It is a subset of global server.Options that modules are allowed to use to 92 // tweak their behavior, if necessary. 93 type HostOptions struct { 94 Prod bool // set when running in production (not on a dev workstation) 95 Serverless Serverless // set when running in a serverless environment, implies Prod 96 CloudProject string // name of hosting Google Cloud Project if running in GCP 97 CloudRegion string // name of a hosting region (e.g. 'us-central1') if known 98 } 99 100 // Host is part of server.Server API that modules are allowed to use during 101 // their initialization to inject useful functionality into the server. 102 // 103 // TODO(vadimsh): 104 // - Allow adding a middleware to the default middleware chain. 105 type Host interface { 106 // ServiceRegistrar is a registrar that can be used to register gRPC services. 107 // 108 // The services registered here will be exposed through both gRPC and pRPC 109 // protocols on corresponding ports. 110 grpc.ServiceRegistrar 111 112 // HTTPAddr is the address the main HTTP port is bound to. 113 // 114 // May be nil if the server doesn't expose the main port. 115 HTTPAddr() net.Addr 116 117 // GRPCAddr is the address the gRPC port is bound to. 118 // 119 // May be nil if the server doesn't expose the gRPC port. 120 GRPCAddr() net.Addr 121 122 // Routes returns a router that servers HTTP requests hitting the main port. 123 // 124 // The module can use it to register additional request handlers. 125 // 126 // Note that users of server.Server can register more routers through 127 // server.AddPort(...) and server.VirtualHost(...). Such application-specific 128 // routers are not accessible to modules. 129 Routes() *router.Router 130 131 // RunInBackground launches the given callback in a separate goroutine right 132 // before starting the serving loop. 133 // 134 // Should be used for background asynchronous activities like reloading 135 // configs. 136 // 137 // All logs lines emitted by the callback are annotated with "activity" field 138 // which can be arbitrary, but by convention has format "<namespace>.<name>", 139 // where "luci" namespace is reserved for internal activities. 140 // 141 // The context passed to the callback is derived from the global server 142 // context and it is canceled when the server is shutting down. It is expected 143 // the goroutine will exit soon after the context is canceled. 144 RunInBackground(activity string, f func(context.Context)) 145 146 // RegisterWarmup registers a callback that is run in server's Serve right 147 // before the serving loop. 148 // 149 // It receives the global server context (including all customizations made 150 // by the user code in server.Main). Intended for best-effort warmups: there's 151 // no way to gracefully abort the server startup from a warmup callback. 152 // 153 // All module's essential initialization should happen in its Initialize 154 // method instead. The downside is that Initialize runs before the user code 155 // in server.Main and it can't depend on modifications done there. 156 RegisterWarmup(cb func(context.Context)) 157 158 // RegisterCleanup registers a callback that is run in server's Serve after 159 // the server exits the serving loop. 160 // 161 // It receives the global server context. 162 RegisterCleanup(cb func(context.Context)) 163 164 // RegisterUnaryServerInterceptors registers grpc.UnaryServerInterceptor's 165 // applied to all unary RPCs that hit the server. 166 // 167 // Interceptors are chained in order they are registered, which matches 168 // the order of modules in the list of modules. The first registered 169 // interceptor becomes the outermost. 170 RegisterUnaryServerInterceptors(intr ...grpc.UnaryServerInterceptor) 171 172 // RegisterStreamServerInterceptors registers grpc.StreamServerInterceptor's 173 // applied to all streaming RPCs that hit the server. 174 // 175 // Interceptors are chained in order they are registered, which matches 176 // the order of modules in the list of modules. The first registered 177 // interceptor becomes the outermost. 178 RegisterStreamServerInterceptors(intr ...grpc.StreamServerInterceptor) 179 180 // RegisterCookieAuth registers an implementation of a cookie-based 181 // authentication scheme. 182 // 183 // If there are multiple such schemes registered, the server will refuse to 184 // start. 185 RegisterCookieAuth(method auth.Method) 186 }