summaryrefslogtreecommitdiff
path: root/context.go
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2019-05-16 16:05:38 -0600
committerMatthew Holt <mholt@users.noreply.github.com>2019-05-16 16:05:38 -0600
commit1f0c061ce30f218e63fcc17e0fdfc8b90d754ba5 (patch)
treeef148ac39ec520d14342ce48cec07f5efe0379f3 /context.go
parentff5b4639d597203f8aec43e5eae8fe3774976d32 (diff)
Architectural shift to using context for config and module state
Diffstat (limited to 'context.go')
-rw-r--r--context.go126
1 files changed, 126 insertions, 0 deletions
diff --git a/context.go b/context.go
new file mode 100644
index 0000000..ca2f44c
--- /dev/null
+++ b/context.go
@@ -0,0 +1,126 @@
+package caddy2
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "log"
+ "reflect"
+
+ "github.com/mholt/certmagic"
+)
+
+type Context struct {
+ context.Context
+ moduleInstances map[string][]interface{}
+ cfg *Config
+}
+
+func NewContext(ctx Context) (Context, context.CancelFunc) {
+ newCtx := Context{moduleInstances: make(map[string][]interface{}), cfg: ctx.cfg}
+ c, cancel := context.WithCancel(ctx.Context)
+ wrappedCancel := func() {
+ cancel()
+ for modName, modInstances := range newCtx.moduleInstances {
+ for _, inst := range modInstances {
+ if cu, ok := inst.(CleanerUpper); ok {
+ err := cu.Cleanup()
+ if err != nil {
+ log.Printf("[ERROR] %s (%p): cleanup: %v", modName, inst, err)
+ }
+ }
+ }
+ }
+ }
+ newCtx.Context = c
+ return newCtx, wrappedCancel
+}
+
+func (ctx Context) LoadModule(name string, rawMsg json.RawMessage) (interface{}, error) {
+ modulesMu.Lock()
+ mod, ok := modules[name]
+ modulesMu.Unlock()
+ if !ok {
+ return nil, fmt.Errorf("unknown module: %s", name)
+ }
+
+ if mod.New == nil {
+ return nil, fmt.Errorf("module '%s' has no constructor", mod.Name)
+ }
+
+ val, err := mod.New()
+ if err != nil {
+ return nil, fmt.Errorf("initializing module '%s': %v", mod.Name, err)
+ }
+
+ // value must be a pointer for unmarshaling into concrete type
+ if rv := reflect.ValueOf(val); rv.Kind() != reflect.Ptr {
+ val = reflect.New(rv.Type()).Elem().Addr().Interface()
+ }
+
+ // fill in its config only if there is a config to fill in
+ if len(rawMsg) > 0 {
+ err = json.Unmarshal(rawMsg, &val)
+ if err != nil {
+ return nil, fmt.Errorf("decoding module config: %s: %v", mod.Name, err)
+ }
+ }
+
+ if prov, ok := val.(Provisioner); ok {
+ err := prov.Provision(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("provision %s: %v", mod.Name, err)
+ }
+ }
+
+ if validator, ok := val.(Validator); ok {
+ err := validator.Validate(ctx)
+ if err != nil {
+ if cleanerUpper, ok := val.(CleanerUpper); ok {
+ err2 := cleanerUpper.Cleanup()
+ if err2 != nil {
+ err = fmt.Errorf("%v; additionally, cleanup: %v", err, err2)
+ }
+ return nil, fmt.Errorf("%s: invalid configuration: %v", mod.Name, err)
+ }
+ }
+ }
+
+ ctx.moduleInstances[name] = append(ctx.moduleInstances[name], val)
+
+ return val, nil
+}
+
+func (ctx Context) LoadModuleInline(moduleNameKey, moduleScope string, raw json.RawMessage) (interface{}, error) {
+ moduleName, err := getModuleNameInline(moduleNameKey, raw)
+ if err != nil {
+ return nil, err
+ }
+
+ val, err := ctx.LoadModule(moduleScope+"."+moduleName, raw)
+ if err != nil {
+ return nil, fmt.Errorf("loading module '%s': %v", moduleName, err)
+ }
+
+ return val, nil
+}
+
+// App returns the configured app named name. If no app with
+// that name is currently configured, a new empty one will be
+// instantiated. (The app module must still be registered.)
+func (ctx Context) App(name string) (interface{}, error) {
+ if app, ok := ctx.cfg.apps[name]; ok {
+ return app, nil
+ }
+ modVal, err := ctx.LoadModule(name, nil)
+ if err != nil {
+ return nil, fmt.Errorf("instantiating new module %s: %v", name, err)
+ }
+ ctx.cfg.apps[name] = modVal.(App)
+ return modVal, nil
+}
+
+// Storage returns the configured Caddy storage implementation.
+func (ctx Context) Storage() certmagic.Storage {
+ return ctx.cfg.storage
+}