summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2019-05-17 08:48:12 -0600
committerMatthew Holt <mholt@users.noreply.github.com>2019-05-17 08:48:12 -0600
commit1a20fe330ecc39e8b98b5669b836f3b1b185f622 (patch)
treea728bbf310d112d7b54b14111717fea04b96d528
parent1f0c061ce30f218e63fcc17e0fdfc8b90d754ba5 (diff)
Improve godoc for contexts
-rw-r--r--context.go39
-rw-r--r--modules.go31
2 files changed, 58 insertions, 12 deletions
diff --git a/context.go b/context.go
index ca2f44c..cfb183a 100644
--- a/context.go
+++ b/context.go
@@ -10,12 +10,30 @@ import (
"github.com/mholt/certmagic"
)
+// Context is a type which defines the lifetime of modules that
+// are loaded and provides access to the parent configuration
+// that spawned the modules which are loaded. It should be used
+// with care and only wrapped with derivation functions from
+// the standard context package if you don't need the Caddy
+// specific features. These contexts are cancelled when the
+// lifetime of the modules loaded from it are over.
+//
+// Use NewContext() to get a valid value (but most modules will
+// not actually need to do this).
type Context struct {
context.Context
moduleInstances map[string][]interface{}
cfg *Config
}
+// NewContext provides a new context derived from the given
+// context ctx. Normally, you will not need to call this
+// function unless you are loading modules which have a
+// different lifespan than the ones for the context the
+// module was provisioned with. Be sure to call the cancel
+// func when the context is to be cleaned up so that
+// modules which are loaded will be properly unloaded.
+// See standard library context package's documentation.
func NewContext(ctx Context) (Context, context.CancelFunc) {
newCtx := Context{moduleInstances: make(map[string][]interface{}), cfg: ctx.cfg}
c, cancel := context.WithCancel(ctx.Context)
@@ -36,6 +54,14 @@ func NewContext(ctx Context) (Context, context.CancelFunc) {
return newCtx, wrappedCancel
}
+// LoadModule decodes rawMsg into a new instance of mod and
+// returns the value. If mod.New() does not return a pointer
+// value, it is converted to one so that it is unmarshaled
+// into the underlying concrete type. If mod.New is nil, an
+// error is returned. If the module implements Validator or
+// Provisioner interfaces, those methods are invoked to
+// ensure the module is fully configured and valid before
+// being used.
func (ctx Context) LoadModule(name string, rawMsg json.RawMessage) (interface{}, error) {
modulesMu.Lock()
mod, ok := modules[name]
@@ -74,7 +100,7 @@ func (ctx Context) LoadModule(name string, rawMsg json.RawMessage) (interface{},
}
if validator, ok := val.(Validator); ok {
- err := validator.Validate(ctx)
+ err := validator.Validate()
if err != nil {
if cleanerUpper, ok := val.(CleanerUpper); ok {
err2 := cleanerUpper.Cleanup()
@@ -91,6 +117,17 @@ func (ctx Context) LoadModule(name string, rawMsg json.RawMessage) (interface{},
return val, nil
}
+// LoadModuleInline loads a module from a JSON raw message which decodes
+// to a map[string]interface{}, where one of the keys is moduleNameKey
+// and the corresponding value is the module name as a string, which
+// can be found in the given scope.
+//
+// This allows modules to be decoded into their concrete types and
+// used when their names cannot be the unique key in a map, such as
+// when there are multiple instances in the map or it appears in an
+// array (where there are no custom keys). In other words, the key
+// containing the module name is treated special/separate from all
+// the other keys.
func (ctx Context) LoadModuleInline(moduleNameKey, moduleScope string, raw json.RawMessage) (interface{}, error) {
moduleName, err := getModuleNameInline(moduleNameKey, raw)
if err != nil {
diff --git a/modules.go b/modules.go
index bba6b93..afe4b38 100644
--- a/modules.go
+++ b/modules.go
@@ -136,23 +136,32 @@ func getModuleNameInline(moduleNameKey string, raw json.RawMessage) (string, err
return moduleName, nil
}
-// Validator is implemented by modules which can verify that their
-// configurations are valid. This method will be called after New()
-// instantiations of modules (if implemented). Validation should
-// always be fast (imperceptible running time) and an error should
-// be returned only if the value's configuration is invalid.
-type Validator interface {
- Validate(Context) error
-}
-
// Provisioner is implemented by modules which may need to perform
// some additional "setup" steps immediately after being loaded.
-// This method will be called after Validate() (if implemented).
+// Provisioning should be fast (imperceptible running time). If
+// any side-effects result in the execution of this function (e.g.
+// creating global state, any other allocations which require
+// garbage collection, opening files, starting goroutines etc.),
+// be sure to clean up properly by implementing the CleanerUpper
+// interface to avoid leaking resources.
type Provisioner interface {
Provision(Context) error
}
-// TODO: different name...
+// Validator is implemented by modules which can verify that their
+// configurations are valid. This method will be called after
+// Provision() (if implemented). Validation should always be fast
+// (imperceptible running time) and an error should be returned only
+// if the value's configuration is invalid.
+type Validator interface {
+ Validate() error
+}
+
+// CleanerUpper is implemented by modules which may have side-effects
+// such as opened files, spawned goroutines, or allocated some sort
+// of non-local state when they were provisioned. This method should
+// deallocate/cleanup those resources to prevent memory leaks. Cleanup
+// should be fast and efficient.
type CleanerUpper interface {
Cleanup() error
}