mirror of https://github.com/caddyserver/caddy.git
127 lines
3.2 KiB
Go
127 lines
3.2 KiB
Go
|
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
|
||
|
}
|