github.com/arthur-befumo/witchcraft-go-server@v1.12.0/README.md (about)

     1  <p align="right">
     2  <a href="https://autorelease.general.dmz.palantir.tech/palantir/witchcraft-go-server"><img src="https://img.shields.io/badge/Perform%20an-Autorelease-success.svg" alt="Autorelease"></a>
     3  </p>
     4  
     5  witchcraft-go-server
     6  ====================
     7  [![](https://godoc.org/github.com/palantir/witchcraft-go-server?status.svg)](http://godoc.org/github.com/palantir/witchcraft-go-server)
     8  
     9  `witchcraft-go-server` is a Go implementation of a Witchcraft server. It provides a way to quickly and easily create
    10  servers that work in the Witchcraft ecosystem.
    11  
    12  Implementation
    13  --------------
    14  
    15  ### Configuration
    16  A witchcraft server is provided with install configuration and runtime configuration. Install configuration specifies
    17  configuration values that are static -- they are read in once at server startup and are known to never change. Runtime
    18  configuration values are considered *refreshable*. When file-based configuration is used, whenever the runtime 
    19  configuration file is updated its contents are loaded and the corresponding values are refreshed. See the section on 
    20  refreshable configuration for more information on this.
    21  
    22  The default configuration uses file-based configuration with the install configuration at `var/conf/install.yml` and
    23  runtime configuration at `var/conf/runtime.yml`. It is possible to use code to specify different sources of 
    24  configuration (for example, in-memory providers).
    25  
    26  `witchcraft-server` also supports using `encrypted-config-value` to automatically decrypt encrypted configuration
    27  values. The default configuration expects a key file to be at `var/conf/encrypted-config-value.key`. It is possible to 
    28  use code to specify a different source for the key (or to specify that no key should be used). If the configuration
    29  does not contain encrypted values, any specified ECV key will not be read. If the install configuration contains
    30  encrypted values but the encryption key is missing or malformed, the server will fail to start. If the runtime config
    31  contains encrypted values but fails to decrypt them, a warning will be logged and the encrypted values passed to the
    32  server.
    33  
    34  `witchcraft-server` defines base configuration for its install and runtime configuration. Servers that want to provide
    35  their own install and/or runtime configuration should embed the base configuration structs within the definition of 
    36  their configuration structs. 
    37  
    38  ### Route registration
    39  A witchcraft server is backed by a `wrouter.Router` and allows authors to register route handlers on the server. The 
    40  router uses a specific format for path templates to specify path parameters and has rules around the kinds of paths that
    41  can be matched. witchcraft is opinionated about the path formats and does not support registering paths that cannot be
    42  expressed using its template rules. All witchcraft routes are configured to emit request logs and trace logs and update
    43  metrics for the requests using built-in middleware. The `context.Context` for the `http.Request` provided to the 
    44  handlers  is configured with all of the standard loggers (service logger, event logger, trace logger, etc.).
    45  
    46  When registering routes on the router, it is also possible to specify path/header/query param keys that should be
    47  considered "safe" or "forbidden" when used as parameters in logging. These are combined with the default set of safe and
    48  forbidden header parameters defined by the `req2log` package in `witchcraft-go-logging`.
    49  
    50  ### Liveness, readiness, and health
    51  `witchcraft-server` registers the endpoints `/status/liveness`, `/status/readiness` and `/status/health` to report the
    52  server's liveness, readiness and health. By default, these endpoints use a built-in provider that reports liveness,
    53  readiness and health based on the state of the server. It is possible to configure the liveness and readiness providers
    54  in code, and health status providers can also be added via code (health supports specifying multiple sources to report
    55  health, and the server's built-in health status provider will always be one of them).
    56  
    57  The default behavior serves both the user-registered endpoints and the status endpoints from the same server. However,
    58  if a "management port" is specified in the server's install configuration and its value differs from the "port" value in
    59  configuration, then `witchcraft-server` starts a second management server on the specified port and serves the status
    60  endpoints on that port. This can be useful in scenarios where all of the traffic to the main endpoints require client
    61  certificates for TLS but the status endpoints need to be served without requiring client TLS certificates.
    62  
    63  ### Debug Routes
    64  The following routes are registered on the management server (if enabled, otherwise the main server) to aid in debugging
    65  and telemetry collection:
    66  * `/debug/pprof`: Provides an HTML index of the other endpoints at this route.
    67  * `/debug/pprof/profile`: Returns the pprof-formatted cpu profile. See [pprof.Profile](https://golang.org/pkg/net/http/pprof/#Profile).
    68  * `/debug/pprof/heap`: Returns the pprof-formatted heap profile as of the last GC. See [pprof.Profile](https://golang.org/pkg/runtime/pprof/#Profile).
    69  * `/debug/pprof/cmdline`: Returns the process's command line invocation as `text/plain`. See [pprof.Cmdline](https://golang.org/pkg/net/http/pprof/#Cmdline).
    70  * `/debug/pprof/symbol`: Looks up the program counters listed in the request, responding with a table mapping program counters to function names See [pprof.Symbol](https://golang.org/pkg/net/http/pprof/#Symbol).
    71  * `/debug/pprof/trace`: Returns the execution trace in binary form. See [pprof.Trace](https://golang.org/pkg/net/http/pprof/#Trace).
    72  
    73  ### Context path
    74  If `context-path` is specified in the install configuration, all of the routes registered on the server will be prefixed
    75  with the specified `context-path`.
    76  
    77  ### Security
    78  `witchcraft-server` only supports HTTPS. The TLS client authentication type is configurable in code. The base install 
    79  configuration has fields to specify the location of server key and certificate material for TLS connections.
    80  
    81  Although it is not possible to run `witchcraft-server` using HTTP, it is possible to configure the server in code to use
    82  a generated self-signed certificate on start-up. Running the server in this mode and connecting to it using TLS without
    83  server certificate verification (equivalent of `curl -k` or an `http.Transport` with 
    84  `TLSClientConfig: &tls.Config{InsecureSkipVerify: true}`) provides an analog to using HTTP, with the benefit that the
    85  traffic itself is still encrypted.
    86  
    87  ### Logging
    88  `witchcraft-server` is configured with service, event, metric, request and trace loggers from the 
    89  `witchcraft-go-logging` project and emits structured JSON logs using [`zap`](https://github.com/uber-go/zap) as the
    90  logger implementation. The default behavior emits logs to the `var/log` directory (`var/log/service.log`, 
    91  `var/log/request.log`, etc.) unless the server is run in a Docker container, in which case the logs are always emitted 
    92  to `stdout`. The `use-console-log` property in the install configuration can also be set to "true" to always output logs 
    93  to `stdout`. The runtime configuration supports configuring the log output level for service logs.
    94  
    95  The `context.Context` provided to request handlers is configured with all of the standard loggers (service logger, event
    96  logger, trace logger, etc.). All of the handlers are also configured to emit request logs and trace logs.
    97  
    98  ### Service logger origin
    99  By default, the `origin` field of the service logger is set to be the package path of the package in which the
   100  `witchcraft-server` is started. For example, if the server is started in the file 
   101  `github.com/palantir/project/server/server.go`, the origin for all service log lines will be
   102  `github.com/palantir/project/server`.
   103  
   104  It is possible to configure the origin to be a different value using code. The origin can be specified to be a string
   105  constant or a function can be used that returns a specific package path based on supplied parameters (for example, the
   106  function can specify that the caller package's parent package should be used as the origin). The origin can also be set
   107  to empty, in which case it is omitted from the log output.
   108  
   109  ### Trace IDs and instrumentation
   110  `witchcraft-server` supports zipkin-compatible tracing and ensures that every request is instrumented for tracing.
   111  `witchcraft-server` also recognizes that some code will use trace IDs without necessarily using full zipkin-compatible
   112  spans, so some allowances are made to support this scenario.
   113  
   114  The built-in `witchcraft-server` middleware that registers loggers on the context also ensures that a zipkin span is
   115  started. If the incoming request header has valid zipkin span information (that is, it specifies both a `X-B3-TraceId`
   116  and `X-B3-SpanId` in the header), then the span created by the middleware is a child span of the incoming span. If the
   117  incoming request does not have a trace ID header, a new root span is created. If the header specifies a trace ID but not
   118  a span ID, the middleware creates a new root zipkin span, but ensures that the trace ID of the created span matches what 
   119  is specified in the header. If an incoming request is routed to a registered endpoint, the built-in router middleware
   120  will create another span (which is a child span of the one created by the request middleware) whose span name is the
   121  HTTP method and template for the endpoint.
   122  
   123  The trace information generated by the middleware is set on the header and will be visible to subsequent handlers. If
   124  the request specifies a `X-B3-Sampled` header, the value specified in that header is used to determine sampling. If this
   125  header is not present, whether or not the trace is sampled is determined by the sampling source configured for the 
   126  `witchcraft-server` (by default, all traces are sampled). If a trace is not sampled, `witchcraft-server` will not 
   127  generate any trace log output for it. However, the infrastructure will still perform all of the trace-related operations
   128  (such as creating child spans and setting span information on headers). The install configuration field
   129  `trace-sample-rate` represents a float between 0 and 1 (inclusive) to control the proportion of traces sampled by
   130  default. If the `WithTraceSampler` server option is provided, it overrides this configuration.
   131  
   132  `witchcraft-server` also ensures that the context for every request has a trace ID. After the logging middleware 
   133  executes, the request is guaranteed to have a trace ID (either from the incoming request or from the newly generated 
   134  root span), and that trace ID is registered on the context. The `witchcraft.TraceIDFromContext(context.Context) string`
   135  function can be used to retrieve the trace ID from the context.
   136  
   137  ### Creating new spans/trace log entries
   138  Use the `wtracing.StartSpanFromContext` function to start a new span.
   139  This function will create a new span that is a child span of the span in the provided context.
   140  Defer the `Finish()` function of the returned span to ensure that the span is properly marked as finished (the "finish"
   141  operation will also generate a trace log entry if the span is sampled).
   142  
   143  ### Middleware
   144  `witchcraft-server` supports registering middleware to perform custom handling/augmenting of incoming requests. There
   145  are 2 different kinds of middleware: *request* and *route* middleware.
   146  
   147  Request middleware is executed on every request received by the server. The function signature for request middleware is
   148  `func(rw http.ResponseWriter, r *http.Request, next http.Handler)`. Request middleware is the most common kind of 
   149  middleware. The server has built-in request middleware that adds a panic handler, sets the loggers and trade ID on the 
   150  request context and updates request-related metrics. Any user-supplied request middleware is run after the built-in 
   151  request middleware in the order in which they were added (which means that the context has all of the loggers 
   152  configured). Request middleware is run before the request is handled by the router, which means that it is possible to 
   153  rewrite the URL and other properties of the request and the router will route the _modified_ request. However, note that 
   154  the built-in logging middleware extracts the UID, SID, TokenID and TraceID from the request and sets them on the loggers 
   155  before user-provided middleware is invoked, so if the user-defined middleware modifies the header in a manner that would 
   156  change any of these values, the middleware should also update the request context to have loggers that use the updated 
   157  values.  
   158  
   159  Route middleware is only executed on the routes that are registered on the router -- they wrap the handler registered on
   160  the route, so they are executed after the path has been matched and the handler for the router has been located and the
   161  path parameters have been extracted and set on the context. The function signature for route middleware is 
   162  `func(rw http.ResponseWriter, r *http.Request, reqVals RequestVals, next RouteRequestHandler)`. The `RequestVals` struct
   163  stores the path template for the route along with the path parameters and their values. The server has built-in route
   164  middleware that records a request log entry after the request has completed and creates a trace log span and logs a 
   165  trace log entry after the request has completed. Route middleware is run after all of the request middleware has run, 
   166  and any user-supplied route middleware is run after the built-in route middleware in the order in which they were added.
   167  Because router middleware is executed after the routing has been determined, changing the URL of the request will not
   168  change the handler that is invoked or the path parameter values that have been extracted/stored (although it may still
   169  impact behavior based on the content of the actual handler that is registered). In general, most users will likely use
   170  request middleware rather than route middleware. However, if users want to only execute middleware on matched routes and
   171  want route-specific information such as the unrendered path template and the path parameter values, then route
   172  middleware should be used.
   173  
   174  ### Long-running execution not associated with a route
   175  In some instances, a server may want a long-running task not associated with an endpoint. For example, the server may
   176  want a long-running goroutine that performs an operation at some interval for the lifetime of the server.
   177  
   178  It is recommended that such goroutines be launched in the initialization function provided to `witchcraft.With` and use
   179  the `ctx Context` as its context. This context has the same lifecycle as the server and has all of the configured 
   180  loggers (service loggers, metric loggers, etc.) already configured on it.
   181  
   182  The provided context does not have a span or trace ID associated with it. If a trace ID is desired,
   183  [create a new span](#creating-new-spanstrace-log-entries) with `wtracing.StartSpanFromContext` and the provided context to derive a new context that has a
   184  new root span associated with it. This function also updates any loggers in the context to use the new trace ID (for 
   185  example, service loggers will include the trace ID).
   186  
   187  ### Metrics
   188  `witchcraft-server` initializes a metrics registry that uses the `github.com/palantir/pkg/metrics` package (which uses 
   189  `github.com/rcrowley/go-metrics` internally) to track metrics for the server. All of the tracked metrics are emitted as
   190  metric log entries once every metric emit interval, which is 60 seconds by default (and can be configured to be a custom
   191  interval in the install configuration).
   192  
   193  By default, `witchcraft-server` captures various Go runtime metrics (such as allocations, number of running goroutines, 
   194  etc.) at the same frequency as the metric emit frequency. The collection of Go runtime statistics can be disabled with
   195  the `WithDisableGoRuntimeMetrics` server method.
   196  
   197  ### SIGQUIT handling
   198  `witchcraft-server` sets up a SIGQUIT handler such that, if the program is terminated using a SIGQUIT signal
   199  (`kill -3`), a goroutine dump is written as a `diagnostic.1` log. This behavior can be disabled using
   200  `server.WithDisableSigQuitHandler`.  If `server.WithSigQuitHandlerWriter` is used, the stacks will also be written in
   201  their unparsed form to the provided writer.
   202  
   203  ### Shutdown signal handling
   204  `witchcraft-server` attempts to drain active connections and gracefully shut down by calling `server.Shutdown` upon receiving a SIGTERM or SIGINT signal. This behavior can be disabled using `server.WithDisableShutdownSignalHandler`.
   205  
   206  Example server initialization
   207  -----------------------------
   208  
   209  ### Basic production server
   210  The following is an example program that launches a `witchcraft-server` that registers a `GET /myNum` endpoint that
   211  returns a randomly generated number encoded as JSON: 
   212  
   213  ```go
   214  package main
   215  
   216  import (
   217  	"context"
   218  	"math/rand"
   219  	"net/http"
   220  
   221  	"github.com/palantir/witchcraft-go-server/rest"
   222  	"github.com/palantir/witchcraft-go-server/witchcraft"
   223  	"github.com/palantir/witchcraft-go-server/witchcraft/refreshable"
   224  	"github.com/palantir/witchcraft-go-server/wrouter"
   225  )
   226  
   227  func main() {
   228  	if err := witchcraft.NewServer().
   229  		WithInitFunc(func(ctx context.Context, info witchcraft.InitInfo) (func(), error) {
   230  			if err := registerMyNumEndpoint(info.Router); err != nil {
   231  				return nil, err
   232  			}
   233  			return nil, nil
   234  		}).
   235  		Start(); err != nil {
   236  		panic(err)
   237  	}
   238  }
   239  
   240  func registerMyNumEndpoint(router wrouter.Router) error {
   241  	return router.Get("/myNum", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   242  		rest.WriteJSONResponse(rw, rand.Intn(100), http.StatusOK)
   243  	}))
   244  }
   245  ```
   246  
   247  Creating a `witchcraft-server` starts with the `witchcraft.NewServer` function, which returns a new witchcraft server
   248  with default configuration. The `*witchcraft.Server` struct has various `With*` functions that can be used to configure
   249  the server, and the `Start()` function starts the server using the specified configuration.
   250  
   251  The `WithInitFunc(InitFunc)` function is used to register routes on the server. The initialization function provided to 
   252  `WithInitFunc` is of the type `witchcraft.InitFunc`, which has the following definition:
   253  `type InitFunc func(ctx context.Context, info InitInfo) (cleanup func(), rErr error)`.
   254  
   255  The `ctx` provided to the function is valid for the duration of the server and has loggers configured on it. The `info`
   256  struct contains fields that can be used to initialize various state and configuration for the server -- refer to the
   257  `InitInfo` documentation for more information. 
   258  
   259  In this example, a "GET" endpoint is registered on the router using the "/myNum" path, and `rest` package is used to
   260  write a JSON response.
   261  
   262  This example server uses all of the `witchcraft` defaults -- it looks for install configuration in 
   263  `var/conf/install.yml` and uses `config.Install` as its type, looks for runtime configuration in `var/conf/runtime.yml`
   264  and uses `config.Runtime` as its type, and looks for an encrypted-config-value key in 
   265  `var/conf/encrypted-config-value.key`. The install configuration must also specify paths to key and certificate files to
   266  use for TLS.
   267  
   268  ### Basic local/test server
   269  The defaults for the server make sense for a production environment, but can make running the server locally (or in 
   270  tests) cumbersome. We can modify the `main` function as follows to configure the witchcraft server to use in-memory 
   271  defaults:
   272  
   273  ```go
   274  func main() {
   275  	if err := witchcraft.NewServer().
   276  		WithInitFunc(func(ctx context.Context, info witchcraft.InitInfo) (func(), error) {
   277  			if err := registerMyNumEndpoint(info.Router); err != nil {
   278  				return nil, err
   279  			}
   280  			return nil, nil
   281  		}).
   282  		WithSelfSignedCertificate().
   283  		WithECVKeyProvider(witchcraft.ECVKeyNoOp()).
   284  		WithRuntimeConfig(config.Runtime{}).
   285  		WithInstallConfig(config.Install{
   286  			ProductName: "example-app",
   287  			Server: config.Server{
   288  				Port: 8100,
   289  			},
   290  			UseConsoleLog: true,
   291  		}).
   292  		Start(); err != nil {
   293  		panic(err)
   294  	}
   295  }
   296  ```
   297  
   298  The `WithSelfSignedCertificate()` function configures the server to start using a generated self-signed certificate, 
   299  which removes the need to specify server TLS material. The `WithECVKeyProvider(witchcraft.ECVKeyNoOp())` function 
   300  configures the server to use an empty ECV key source. The `WithRuntimeConfig(config.Runtime{})` function configures the 
   301  server to use the  provided runtime configuration (in this case, it is empty), and the `WithInstallConfig` function 
   302  specifies the install configuration that should be used (it specifies that port 8100 should be used and that log output 
   303  should go to STDOUT).
   304  
   305  With this configuration, the program can be run using `go run`:
   306  
   307  ```
   308  ➜ go run main.go
   309  {"level":"INFO","time":"2018-11-27T05:47:02.013456Z","message":"Listening to https","type":"service.1","origin":"github.com/palantir/witchcraft-go-server/app_example","params":{"address":":8100","server":"example-app"}}
   310  ```
   311  
   312  Issuing a request to this server using `curl` produces the expected response (note that the `-k` option is used to skip
   313  certificate verification because the server is using a self-signed certificate):
   314  
   315  ```go
   316  ➜ curl -k https://localhost:8100/myNum
   317  81
   318  ```
   319  
   320  You can also observe that the server emits trace and request logs based on receiving this request:
   321  
   322  ```
   323  {"time":"2018-11-27T05:47:28.313585Z","type":"trace.1","span":{"traceId":"7e43bde2647413fc","id":"01228e628b3b3d22","name":"GET /myNum","parentId":"7e43bde2647413fc","timestamp":1543297648313551,"duration":29000}}
   324  {"time":"2018-11-27T05:47:28.313719Z","type":"request.2","method":"GET","protocol":"HTTP/2.0","path":"/myNum","status":200,"requestSize":0,"responseSize":3,"duration":146,"traceId":"7e43bde2647413fc","params":{"Accept":"*/*","User-Agent":"curl/7.54.0","X-B3-Parentspanid":"7e43bde2647413fc","X-B3-Sampled":"1","X-B3-Spanid":"01228e628b3b3d22","X-B3-Traceid":"7e43bde2647413fc"}}
   325  {"time":"2018-11-27T05:47:28.313802Z","type":"trace.1","span":{"traceId":"7e43bde2647413fc","id":"7e43bde2647413fc","name":"witchcraft-go-server request middleware","timestamp":1543297648313496,"duration":304000}}
   326  ```
   327  
   328  ### Server using install configuration
   329  The previous examples used the built-in install configuration. Most real servers will use custom install configuration 
   330  that specifies configuration for the server. Any struct can be used as install configuration, but it must support being
   331  unmarshaled as YAML and must embed the `config.Install` struct. The install configuration is loaded once when the server
   332  starts (it is never reloaded), so only values that are static for the lifetime of the server should be specified in this
   333  configuration.
   334  
   335  The following example modifies the previous example so that the endpoint returns the number defined in the install
   336  configuration instead of a random number:
   337  
   338  ```go
   339  package main
   340  
   341  import (
   342  	"context"
   343  	"net/http"
   344  
   345  	"github.com/palantir/witchcraft-go-server/config"
   346  	"github.com/palantir/witchcraft-go-server/rest"
   347  	"github.com/palantir/witchcraft-go-server/witchcraft"
   348  	"github.com/palantir/witchcraft-go-server/witchcraft/refreshable"
   349  	"github.com/palantir/witchcraft-go-server/wrouter"
   350  )
   351  
   352  type AppInstallConfig struct {
   353  	config.Install `yaml:",inline"`
   354  
   355  	MyNum int `yaml:"my-num"`
   356  }
   357  
   358  func main() {
   359  	if err := witchcraft.NewServer().
   360  		WithInitFunc(func(ctx context.Context, info witchcraft.InitInfo) (func(), error) {
   361  			if err := registerMyNumEndpoint(info.Router, info.InstallConfig.(AppInstallConfig).MyNum); err != nil {
   362  				return nil, err
   363  			}
   364  			return nil, nil
   365  		},
   366  		).
   367  		WithSelfSignedCertificate().
   368  		WithECVKeyProvider(witchcraft.ECVKeyNoOp()).
   369  		WithRuntimeConfig(config.Runtime{}).
   370  		WithInstallConfigType(AppInstallConfig{}).
   371  		WithInstallConfig(AppInstallConfig{
   372  			Install: config.Install{
   373  				ProductName: "example-app",
   374  				Server: config.Server{
   375  					Port: 8100,
   376  				},
   377  				UseConsoleLog: true,
   378  			},
   379  			MyNum: 13,
   380  		}).
   381  		Start(); err != nil {
   382  		panic(err)
   383  	}
   384  }
   385  
   386  func registerMyNumEndpoint(router wrouter.Router, num int) error {
   387  	return router.Get("/myNum", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   388  		rest.WriteJSONResponse(rw, num, http.StatusOK)
   389  	}))
   390  }
   391  ```
   392  
   393  This example defines the `AppInstallConfig` struct, which embeds `config.Install` and also defines a `MyNum` field. The 
   394  `WithInstallConfigType(AppInstallConfig{})` function call is added to specify `AppInstallConfig{}` as the install struct 
   395  and the initialization function logic is modified to convert the provided `installConfig interface{}` into an 
   396  `AppInstallConfig` and uses the `MyNum` value as the value that is returned by the endpoint. The `WithInstallConfig` 
   397  function is also updated to use configuration that specifies a value for `MyNum`.
   398  
   399  Running the updated program using `go run main.go` and issuing `curl -k https://localhost:8100/myNum` returns `13`.
   400  
   401  A real program will generally read runtime configuration from disk rather than specifying it directly in code. We can
   402  modify the example above to do this by simply removing the `WithInstallConfig` call:
   403  
   404  ```go
   405  package main
   406  
   407  import (
   408  	"context"
   409  	"net/http"
   410  
   411  	"github.com/palantir/witchcraft-go-server/config"
   412  	"github.com/palantir/witchcraft-go-server/rest"
   413  	"github.com/palantir/witchcraft-go-server/witchcraft"
   414  	"github.com/palantir/witchcraft-go-server/witchcraft/refreshable"
   415  	"github.com/palantir/witchcraft-go-server/wrouter"
   416  )
   417  
   418  type AppInstallConfig struct {
   419  	config.Install `yaml:",inline"`
   420  
   421  	MyNum int `yaml:"my-num"`
   422  }
   423  
   424  func main() {
   425  	if err := witchcraft.NewServer().
   426  		WithInitFunc(func(ctx context.Context, info witchcraft.InitInfo) (func(), error) {
   427  			if err := registerMyNumEndpoint(info.Router, info.InstallConfig.(AppInstallConfig).MyNum); err != nil {
   428  				return nil, err
   429  			}
   430  			return nil, nil
   431  		},
   432  		).
   433  		WithSelfSignedCertificate().
   434  		WithECVKeyProvider(witchcraft.ECVKeyNoOp()).
   435  		WithInstallConfigType(AppInstallConfig{}).
   436  		Start(); err != nil {
   437  		panic(err)
   438  	}
   439  }
   440  
   441  func registerMyNumEndpoint(router wrouter.Router, num int) error {
   442  	return router.Get("/myNum", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   443  		rest.WriteJSONResponse(rw, num, http.StatusOK)
   444  	}))
   445  }
   446  ```
   447  
   448  By default, the install configuration is read from `var/conf/install.yml`. Create a file at that path relative to the
   449  Go file and provide it with the YAML content for the configuration:
   450  
   451  ```yaml
   452  product-name: "example-app"
   453  use-console-log: true
   454  server:
   455    port: 8100
   456  my-num: 77
   457  ```
   458  
   459  Running the updated program using `go run main.go` and issuing `curl -k https://localhost:8100/myNum` returns `77`.
   460  
   461  ### Server using runtime configuration
   462  Runtime configuration is similar to install configuration. The main difference is that runtime configuration supports
   463  reloading configuration. When file-based runtime configuration is used, whenever the configuration file is updated, the
   464  associated values are updated as well.
   465  
   466  The following example defines a custom runtime configuration struct and returns the refreshable int value in the runtime
   467  from its endpoint (the example uses a basic in-memory install configuration for simplicity):
   468  
   469  ```go
   470  package main
   471  
   472  import (
   473  	"context"
   474  	"net/http"
   475  
   476  	"github.com/palantir/witchcraft-go-server/config"
   477  	"github.com/palantir/witchcraft-go-server/rest"
   478  	"github.com/palantir/witchcraft-go-server/witchcraft"
   479  	"github.com/palantir/witchcraft-go-server/witchcraft/refreshable"
   480  	"github.com/palantir/witchcraft-go-server/wrouter"
   481  )
   482  
   483  type AppRuntimeConfig struct {
   484  	config.Runtime `yaml:",inline"`
   485  
   486  	MyNum int `yaml:"my-num"`
   487  }
   488  
   489  func main() {
   490  	if err := witchcraft.NewServer().
   491  		WithInitFunc(func(ctx context.Context, info witchcraft.InitInfo) (func(), error) {
   492  			myNumRefreshable := refreshable.NewInt(info.RuntimeConfig.Map(func(in interface{}) interface{} {
   493  				return in.(AppRuntimeConfig).MyNum
   494  			}))
   495  			if err := registerMyNumEndpoint(info.Router, myNumRefreshable); err != nil {
   496  				return nil, err
   497  			}
   498  			return nil, nil
   499  		},
   500  		).
   501  		WithSelfSignedCertificate().
   502  		WithECVKeyProvider(witchcraft.ECVKeyNoOp()).
   503  		WithInstallConfig(config.Install{
   504  			ProductName: "example-app",
   505  			Server: config.Server{
   506  				Port: 8100,
   507  			},
   508  			UseConsoleLog: true,
   509  		}).
   510  		WithRuntimeConfigType(AppRuntimeConfig{}).
   511  		Start(); err != nil {
   512  		panic(err)
   513  	}
   514  }
   515  
   516  func registerMyNumEndpoint(router wrouter.Router, numProvider refreshable.Int) error {
   517  	return router.Get("/myNum", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   518  		rest.WriteJSONResponse(rw, numProvider.CurrentInt(), http.StatusOK)
   519  	}))
   520  }
   521  ``` 
   522  
   523  The refreshable configuration warrants some closer examination. Note that the `registerMyNumEndpoint` takes a
   524  `numProvider refreshable.Int` as an argument rather than an `int` and returns the result of `CurrentInt()`. 
   525  Conceptually, the `numProvider` is guaranteed to always return the current value of the number specified in the runtime
   526  configuration. Using this pattern removes the need for writing code that listens for updates -- the code can simply
   527  assume that the provider always returns the most recent value. `refreshable.Int` and `refreshable.String` are helper
   528  types that provide functions that return the current value of the correct type. For types without helper functions, the
   529  general `refreshable.Refreshable` should be used, and the `interface{}` returned by `Current()` must be explicitly
   530  converted to the proper target type (this is required because Go does not support generics/templatization).
   531  
   532  The `numProvider` provided to `registerMyNumEndpoint` is derived by applying a mapping function to the
   533  `runtimeConfig refreshable.Refreshable` parameter. `runtimeConfig.Map` is provided with a function that, given an
   534  updated runtime configuration, returns the portion of the configuration that is required. The input to the mapping 
   535  function must be explicitly cast to the runtime configuration type (in this case, `in.(AppRuntimeConfig)`), and then the
   536  relevant section can be accessed (or derived) and returned. The result of the `Map` function is a `Refreshable` that
   537  returns the mapped portion. In this case, because we know the result will always be an `int`, we wrap the returned
   538  `Refreshable` in a `refreshable.NewInt` call, which provides the convenience function `CurrentInt()` that performs the
   539  type conversion of the result to an `int`.
   540  
   541  By default, the runtime configuration is read from `var/conf/runtime.yml`. Create a file at that path relative to the
   542  Go file and provide it with the YAML content for the configuration:
   543  
   544  ```yaml
   545  my-num: 99
   546  ```
   547  
   548  Running the updated program using `go run main.go` and issuing `curl -k https://localhost:8100/myNum` returns `99`.
   549  While the program is still running, update the content of the file to be `my-num: 88`, save it, then run the `curl`
   550  command again. The output is `88`. 
   551  
   552  ### Full server example
   553  The following is an example of a server that defines and uses both custom install and runtime configuration:
   554  
   555  ```go
   556  package main
   557  
   558  import (
   559  	"context"
   560  	"net/http"
   561  
   562  	"github.com/palantir/witchcraft-go-server/config"
   563  	"github.com/palantir/witchcraft-go-server/rest"
   564  	"github.com/palantir/witchcraft-go-server/witchcraft"
   565  	"github.com/palantir/witchcraft-go-server/witchcraft/refreshable"
   566  	"github.com/palantir/witchcraft-go-server/wrouter"
   567  )
   568  
   569  type AppInstallConfig struct {
   570  	config.Install `yaml:",inline"`
   571  
   572  	MyNum int `yaml:"my-num"`
   573  }
   574  
   575  type AppRuntimeConfig struct {
   576  	config.Runtime `yaml:",inline"`
   577  
   578  	MyNum int `yaml:"my-num"`
   579  }
   580  
   581  func main() {
   582  	if err := witchcraft.NewServer().
   583  		WithInitFunc(func(ctx context.Context, info witchcraft.InitInfo) (func(), error) {
   584  			if err := registerInstallNumEndpoint(info.Router, info.InstallConfig.(AppInstallConfig).MyNum); err != nil {
   585  				return nil, err
   586  			}
   587  
   588  			myNumRefreshable := refreshable.NewInt(info.RuntimeConfig.Map(func(in interface{}) interface{} {
   589  				return in.(AppRuntimeConfig).MyNum
   590  			}))
   591  			if err := registerRuntimeNumEndpoint(info.Router, myNumRefreshable); err != nil {
   592  				return nil, err
   593  			}
   594  			return nil, nil
   595  		},
   596  		).
   597  		WithInstallConfigType(AppInstallConfig{}).
   598  		WithRuntimeConfigType(AppRuntimeConfig{}).
   599  		WithSelfSignedCertificate().
   600  		WithECVKeyProvider(witchcraft.ECVKeyNoOp()).
   601  		Start(); err != nil {
   602  		panic(err)
   603  	}
   604  }
   605  
   606  func registerInstallNumEndpoint(router wrouter.Router, num int) error {
   607  	return router.Get("/installNum", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   608  		rest.WriteJSONResponse(rw, num, http.StatusOK)
   609  	}))
   610  }
   611  
   612  func registerRuntimeNumEndpoint(router wrouter.Router, numProvider refreshable.Int) error {
   613  	return router.Get("/runtimeNum", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   614  		rest.WriteJSONResponse(rw, numProvider.CurrentInt(), http.StatusOK)
   615  	}))
   616  }
   617  ```
   618  
   619  With `var/conf/install.yml`:
   620  
   621  ```yaml
   622  product-name: "example-app"
   623  use-console-log: true
   624  server:
   625    port: 8100
   626  my-num: 7
   627  ```
   628  
   629  And `var/conf/runtime.yml`:
   630  
   631  ```yaml
   632  my-num: 13
   633  ```
   634  
   635  Querying `installNum` returns `7`, while querying `runtimeNum` returns `13`:
   636  
   637  ```
   638  ➜ curl -k https://localhost:8100/installNum
   639  7
   640  ➜ curl -k https://localhost:8100/runtimeNum
   641  13
   642  ```
   643  
   644  In a production server, `WithSelfSignedCertificate()` and `WithECVKeyProvider(witchcraft.ECVKeyNoOp())` would not be
   645  called and the proper security and key material would exist in their expected locations.
   646  
   647  Refreshable configuration
   648  -------------------------
   649  The runtime configuration for `witchcraft-server` uses the `refreshable.Refreshable` interface. Conceptually, a
   650  `Refreshable` is a container that holds a value of a specific type that may be updated/refreshed. The following is the
   651  interface definition for `Refreshable`:
   652  
   653  ```go
   654  type Refreshable interface {
   655  	// Current returns the most recent value of this Refreshable.
   656  	Current() interface{}
   657  
   658  	// Subscribe subscribes to changes of this Refreshable. The provided function is called with the value of Current()
   659  	// whenever the value changes.
   660  	Subscribe(consumer func(interface{})) (unsubscribe func())
   661  
   662  	// Map returns a new Refreshable based on the current one that handles updates based on the current Refreshable.
   663  	Map(func(interface{}) interface{}) Refreshable
   664  }
   665  ```
   666  
   667  The `runtimeConfig refreshable.Refreshable` parameter provided to the initialization function specified using 
   668  `WithInitFunc` stores the latest unmarshaled runtime configuration as its current value, and the type of the value is
   669  specified using the `WithRuntimeConfigType` function (if this function is not called, `config.Runtime` is used as the
   670  default type).
   671  
   672  For example, for the call: 
   673  
   674  ```go
   675  witchcraft.NewServer().
   676      WithInitFunc(func(ctx context.Context, info witchcraft.InitInfo) (func(), error) {
   677          return nil, nil
   678      }).
   679      WithRuntimeConfigType(AppRuntimeConfig{})
   680  ```
   681  
   682  The `WithRuntimeConfigType(AppRuntimeConfig{})` function specifies that the type of the runtime configuration is 
   683  `AppRuntimeConfig`, so the value returned by `runtimeConfig.Current()` in `WithInitFunc` will have the type 
   684  `AppRuntimeConfig`. Because Go does not have a notion of generics, the author must make this association manually and 
   685  perform the conversion of the current value into the desired type when using it (for example, 
   686  `runtimeConfig.Current().(AppRuntimeConfig)`).
   687  
   688  The `Refreshable` interface supports using the `Map` function to derive a new refreshable based on the value of the
   689  current refreshable. This allows downstream functions that are only interested in a subset of the refreshable to observe
   690  just the relevant portion.
   691  
   692  For example, consider the `AppRuntimeConfig` definition:
   693  
   694  ```go
   695  type AppRuntimeConfig struct {
   696  	config.Runtime `yaml:",inline"`
   697  
   698  	MyNum int `yaml:"my-num"`
   699  }
   700  ```
   701  
   702  A downstream function may only be interested in updates to the `MyNum` variable -- if updates to `config.Runtime` are 
   703  not relevant to the function, there is no need to subscribe to it. The following code derives a new `Refreshable` from
   704  the `runtimeConfig` refreshable: 
   705  
   706  ```go
   707  myNumRefreshable := runtimeConfig.Map(func(in interface{}) interface{} {
   708      return in.(AppRuntimeConfig).MyNum
   709  })
   710  ```
   711  
   712  The `Current()` function for `myNumRefreshable` returns the `MyNum` field of `in.(AppRuntimeConfig)`, and the derived
   713  `Refreshable` is only updated when the derived value changes. Accessing a field is the most common usage of `Map`, but
   714  any arbitrary logic can be performed in the mapping function. Just note that the mapping will be performed whenever the
   715  parent refreshable is updated and the result will be compared using `reflect.DeepEqual`. 
   716  
   717  The general `Refreshable` interface returns an `interface{}` and its result must always be converted to the actual
   718  underlying type. However, if a `Refreshable` is known to return an `int`, `string` or `bool`, convenience wrapper types
   719  are provided to return typed values. For example, `refreshable.NewInt(in Refreshable)` returns a `refreshable.Int`,
   720  which is defined as:
   721  
   722  ```go
   723  type Int interface {
   724  	Refreshable
   725  	CurrentInt() int
   726  }
   727  ```
   728  
   729  The `CurrentInt()` function returns the current value converted to an `int`, which makes it easier to use in code and
   730  alleviates the need for clients to manually remember the type stored in the `Refreshable`.
   731  
   732  If a `Refreshable` with a particular value/type is used widely throughout a code base, it may make sense to define a 
   733  similar interface so that clients do not have to manually track the type information. For example, a typed `Refreshable`
   734  for `AppRuntimeConfig` can be defined as follows:
   735  
   736  ```go
   737  type RefreshableAppRuntimeConfig interface {
   738  	Refreshable
   739  	CurrentAppRuntimeConfig() AppRuntimeConfig
   740  }
   741  
   742  type refreshableAppRuntimeConfig struct {
   743  	Refreshable
   744  }
   745  
   746  func (r refreshableAppRuntimeConfig) CurrentAppRuntimeConfig() AppRuntimeConfig {
   747  	return rt.Current().(AppRuntimeConfig)
   748  } 
   749  ```
   750  
   751  ### Updating refreshable configuration: provider-based vs. push-based
   752  The "provider" model of configuration updates takes the philosophy that executing code simply needs the most up-to-date
   753  value of a `Refreshable` when it executes. This model makes the most sense when the value is read whenever an endpoint
   754  is executed or when a long-running or periodically executed background task executes. In these scenarios, the latest 
   755  value of the `Refreshable` is only needed when the logic executes. This update model is typically the most common, and
   756  is achieved by passing down specific `Refreshable` providers for the required values to the handlers/routines.
   757  
   758  However, in some cases, an application may want to be notified of every update to a field and react to that update
   759  immediately -- for example, if updating a specific configuration field triggers an expensive computation that should
   760  happen immediately, the logic wants to be notified as soon as the update is made.
   761  
   762  In this scenario, the `Subscribe` function should be used for the `Refreshable` that has the value for which updates are
   763  needed. For example, consider the following configuration:
   764  
   765  ```go
   766  type AppRuntimeConfig struct {
   767  	config.Runtime `yaml:",inline"`
   768  
   769  	AssetURLs []string `yaml:"asset-urls"`
   770  }
   771  ```
   772  
   773  The `AssetURLs` field specifies URLs that should be downloaded by the program whenever the value is updated. This
   774  can be handled as follows:
   775  
   776  ```go
   777  unsubscribe := runtimeConfig.Map(func(in interface{}) interface{} {
   778      return in.(AppRuntimeConfig).AssetURLs
   779  }).Subscribe(func(in interface{}) {
   780  	assetURLs := in.([]string)
   781  	// perform work
   782  })
   783  // unsubscribe should be deferred or stored and run at shutdown 
   784  ```
   785  
   786  The `Map` function returns a new `Refreshable` that updates only when the `AssetURLs` field is updated, and the
   787  `Subscribe` function subscribes a listener that performs work as soon as the value is updated. This ensures that the 
   788  logic is run as soon as the value is refreshed every time the value is updated.
   789  
   790  License
   791  -------
   792  This project is made available under the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0).