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  }