diff --git a/caddyconfig/caddyfile/adapter.go b/caddyconfig/caddyfile/adapter.go index 8d624f1d..185816b8 100644 --- a/caddyconfig/caddyfile/adapter.go +++ b/caddyconfig/caddyfile/adapter.go @@ -94,5 +94,31 @@ type ServerType interface { Setup([]ServerBlock, map[string]interface{}) (*caddy.Config, []caddyconfig.Warning, error) } +// UnmarshalModule instantiates a module with the given ID and invokes +// UnmarshalCaddyfile on the new value using the immediate next segment +// of d as input. In other words, d's next token should be the first +// token of the module's Caddyfile input. +// +// This function is used when the next segment of Caddyfile tokens +// belongs to another Caddy module. The returned value is often +// type-asserted to the module's associated type for practical use +// when setting up a config. +func UnmarshalModule(d *Dispenser, moduleID string) (Unmarshaler, error) { + mod, err := caddy.GetModule(moduleID) + if err != nil { + return nil, d.Errf("getting module named '%s': %v", moduleID, err) + } + inst := mod.New() + unm, ok := inst.(Unmarshaler) + if !ok { + return nil, d.Errf("module %s is not a Caddyfile unmarshaler; is %T", mod.ID, inst) + } + err = unm.UnmarshalCaddyfile(d.NewFromNextSegment()) + if err != nil { + return nil, err + } + return unm, nil +} + // Interface guard var _ caddyconfig.Adapter = (*Adapter)(nil) diff --git a/caddyconfig/httpcaddyfile/builtins.go b/caddyconfig/httpcaddyfile/builtins.go index 4a0b335e..7d16da15 100644 --- a/caddyconfig/httpcaddyfile/builtins.go +++ b/caddyconfig/httpcaddyfile/builtins.go @@ -285,21 +285,14 @@ func parseTLS(h Helper) ([]ConfigValue, error) { return nil, h.ArgErr() } modName := h.Val() - mod, err := caddy.GetModule("tls.issuance." + modName) - if err != nil { - return nil, h.Errf("getting issuer module '%s': %v", modName, err) - } - unm, ok := mod.New().(caddyfile.Unmarshaler) - if !ok { - return nil, h.Errf("issuer module '%s' is not a Caddyfile unmarshaler", mod.ID) - } - err = unm.UnmarshalCaddyfile(h.NewFromNextSegment()) + modID := "tls.issuance." + modName + unm, err := caddyfile.UnmarshalModule(h.Dispenser, modID) if err != nil { return nil, err } issuer, ok := unm.(certmagic.Issuer) if !ok { - return nil, h.Errf("module %s is not a certmagic.Issuer", mod.ID) + return nil, h.Errf("module %s (%T) is not a certmagic.Issuer", modID, unm) } issuers = append(issuers, issuer) @@ -315,18 +308,12 @@ func parseTLS(h Helper) ([]ConfigValue, error) { acmeIssuer.Challenges = new(caddytls.ChallengesConfig) acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig) } - dnsProvModule, err := caddy.GetModule("dns.providers." + provName) + modID := "dns.providers." + provName + unm, err := caddyfile.UnmarshalModule(h.Dispenser, modID) if err != nil { - return nil, h.Errf("getting DNS provider module named '%s': %v", provName, err) + return nil, err } - dnsProvModuleInstance := dnsProvModule.New() - if unm, ok := dnsProvModuleInstance.(caddyfile.Unmarshaler); ok { - err = unm.UnmarshalCaddyfile(h.NewFromNextSegment()) - if err != nil { - return nil, err - } - } - acmeIssuer.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(dnsProvModuleInstance, "name", provName, h.warnings) + acmeIssuer.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(unm, "name", provName, h.warnings) case "ca_root": arg := h.RemainingArgs() @@ -583,21 +570,15 @@ func parseLog(h Helper) ([]ConfigValue, error) { case "discard": wo = caddy.DiscardWriter{} default: - mod, err := caddy.GetModule("caddy.logging.writers." + moduleName) - if err != nil { - return nil, h.Errf("getting log writer module named '%s': %v", moduleName, err) - } - unm, ok := mod.New().(caddyfile.Unmarshaler) - if !ok { - return nil, h.Errf("log writer module '%s' is not a Caddyfile unmarshaler", mod) - } - err = unm.UnmarshalCaddyfile(h.NewFromNextSegment()) + modID := "caddy.logging.writers." + moduleName + unm, err := caddyfile.UnmarshalModule(h.Dispenser, modID) if err != nil { return nil, err } + var ok bool wo, ok = unm.(caddy.WriterOpener) if !ok { - return nil, h.Errf("module %s is not a WriterOpener", mod) + return nil, h.Errf("module %s (%T) is not a WriterOpener", modID, unm) } } cl.WriterRaw = caddyconfig.JSONModuleObject(wo, "output", moduleName, h.warnings) @@ -607,21 +588,14 @@ func parseLog(h Helper) ([]ConfigValue, error) { return nil, h.ArgErr() } moduleName := h.Val() - mod, err := caddy.GetModule("caddy.logging.encoders." + moduleName) - if err != nil { - return nil, h.Errf("getting log encoder module named '%s': %v", moduleName, err) - } - unm, ok := mod.New().(caddyfile.Unmarshaler) - if !ok { - return nil, h.Errf("log encoder module '%s' is not a Caddyfile unmarshaler", mod) - } - err = unm.UnmarshalCaddyfile(h.NewFromNextSegment()) + moduleID := "caddy.logging.encoders." + moduleName + unm, err := caddyfile.UnmarshalModule(h.Dispenser, moduleID) if err != nil { return nil, err } enc, ok := unm.(zapcore.Encoder) if !ok { - return nil, h.Errf("module %s is not a zapcore.Encoder", mod) + return nil, h.Errf("module %s (%T) is not a zapcore.Encoder", moduleID, unm) } cl.EncoderRaw = caddyconfig.JSONModuleObject(enc, "format", moduleName, h.warnings) diff --git a/caddyconfig/httpcaddyfile/options.go b/caddyconfig/httpcaddyfile/options.go index 5001974b..3a3cdf68 100644 --- a/caddyconfig/httpcaddyfile/options.go +++ b/caddyconfig/httpcaddyfile/options.go @@ -34,7 +34,7 @@ func init() { RegisterGlobalOption("storage", parseOptStorage) RegisterGlobalOption("acme_ca", parseOptSingleString) RegisterGlobalOption("acme_ca_root", parseOptSingleString) - RegisterGlobalOption("acme_dns", parseOptSingleString) + RegisterGlobalOption("acme_dns", parseOptACMEDNS) RegisterGlobalOption("acme_eab", parseOptACMEEAB) RegisterGlobalOption("cert_issuer", parseOptCertIssuer) RegisterGlobalOption("email", parseOptSingleString) @@ -165,26 +165,37 @@ func parseOptStorage(d *caddyfile.Dispenser) (interface{}, error) { if !d.Next() { // get storage module name return nil, d.ArgErr() } - modName := d.Val() - mod, err := caddy.GetModule("caddy.storage." + modName) - if err != nil { - return nil, d.Errf("getting storage module '%s': %v", modName, err) - } - unm, ok := mod.New().(caddyfile.Unmarshaler) - if !ok { - return nil, d.Errf("storage module '%s' is not a Caddyfile unmarshaler", mod.ID) - } - err = unm.UnmarshalCaddyfile(d.NewFromNextSegment()) + modID := "caddy.storage." + d.Val() + unm, err := caddyfile.UnmarshalModule(d, modID) if err != nil { return nil, err } storage, ok := unm.(caddy.StorageConverter) if !ok { - return nil, d.Errf("module %s is not a StorageConverter", mod.ID) + return nil, d.Errf("module %s is not a caddy.StorageConverter", modID) } return storage, nil } +func parseOptACMEDNS(d *caddyfile.Dispenser) (interface{}, error) { + if !d.Next() { // consume option name + return nil, d.ArgErr() + } + if !d.Next() { // get DNS module name + return nil, d.ArgErr() + } + modID := "dns.providers." + d.Val() + unm, err := caddyfile.UnmarshalModule(d, modID) + if err != nil { + return nil, err + } + prov, ok := unm.(certmagic.ACMEDNSProvider) + if !ok { + return nil, d.Errf("module %s (%T) is not a certmagic.ACMEDNSProvider", modID, unm) + } + return prov, nil +} + func parseOptACMEEAB(d *caddyfile.Dispenser) (interface{}, error) { eab := new(acme.EAB) for d.Next() { @@ -220,22 +231,14 @@ func parseOptCertIssuer(d *caddyfile.Dispenser) (interface{}, error) { if !d.Next() { // get issuer module name return nil, d.ArgErr() } - modName := d.Val() - mod, err := caddy.GetModule("tls.issuance." + modName) - if err != nil { - return nil, d.Errf("getting issuer module '%s': %v", modName, err) - } - unm, ok := mod.New().(caddyfile.Unmarshaler) - if !ok { - return nil, d.Errf("issuer module '%s' is not a Caddyfile unmarshaler", mod.ID) - } - err = unm.UnmarshalCaddyfile(d.NewFromNextSegment()) + modID := "tls.issuance." + d.Val() + unm, err := caddyfile.UnmarshalModule(d, modID) if err != nil { return nil, err } iss, ok := unm.(certmagic.Issuer) if !ok { - return nil, d.Errf("module %s is not a certmagic.Issuer", mod.ID) + return nil, d.Errf("module %s (%T) is not a certmagic.Issuer", modID, unm) } return iss, nil } diff --git a/caddyconfig/httpcaddyfile/serveroptions.go b/caddyconfig/httpcaddyfile/serveroptions.go index 38fa0f15..9e94b863 100644 --- a/caddyconfig/httpcaddyfile/serveroptions.go +++ b/caddyconfig/httpcaddyfile/serveroptions.go @@ -57,21 +57,14 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (interface{}, error switch d.Val() { case "listener_wrappers": for nesting := d.Nesting(); d.NextBlock(nesting); { - mod, err := caddy.GetModule("caddy.listeners." + d.Val()) - if err != nil { - return nil, fmt.Errorf("finding listener module '%s': %v", d.Val(), err) - } - unm, ok := mod.New().(caddyfile.Unmarshaler) - if !ok { - return nil, fmt.Errorf("listener module '%s' is not a Caddyfile unmarshaler", mod) - } - err = unm.UnmarshalCaddyfile(d.NewFromNextSegment()) + modID := "caddy.listeners." + d.Val() + unm, err := caddyfile.UnmarshalModule(d, modID) if err != nil { return nil, err } listenerWrapper, ok := unm.(caddy.ListenerWrapper) if !ok { - return nil, fmt.Errorf("module %s is not a listener wrapper", mod) + return nil, fmt.Errorf("module %s (%T) is not a listener wrapper", modID, unm) } jsonListenerWrapper := caddyconfig.JSONModuleObject( listenerWrapper, diff --git a/caddyconfig/httpcaddyfile/tlsapp.go b/caddyconfig/httpcaddyfile/tlsapp.go index 1fabc455..440c447d 100644 --- a/caddyconfig/httpcaddyfile/tlsapp.go +++ b/caddyconfig/httpcaddyfile/tlsapp.go @@ -392,14 +392,9 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]interf acmeIssuer.TrustedRootsPEMFiles = append(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) } if globalACMEDNS != nil && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.DNS == nil) { - provName := globalACMEDNS.(string) - dnsProvModule, err := caddy.GetModule("dns.providers." + provName) - if err != nil { - return fmt.Errorf("getting DNS provider module named '%s': %v", provName, err) - } acmeIssuer.Challenges = &caddytls.ChallengesConfig{ DNS: &caddytls.DNSChallengeConfig{ - ProviderRaw: caddyconfig.JSONModuleObject(dnsProvModule.New(), "name", provName, nil), + ProviderRaw: caddyconfig.JSONModuleObject(globalACMEDNS, "name", globalACMEDNS.(caddy.Module).CaddyModule().ID.Name(), nil), }, } } diff --git a/modules/caddyhttp/encode/caddyfile.go b/modules/caddyhttp/encode/caddyfile.go index 9d9646c0..2f11ca06 100644 --- a/modules/caddyhttp/encode/caddyfile.go +++ b/modules/caddyhttp/encode/caddyfile.go @@ -64,21 +64,14 @@ func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { for d.NextBlock(0) { name := d.Val() - mod, err := caddy.GetModule("http.encoders." + name) - if err != nil { - return fmt.Errorf("getting encoder module '%s': %v", name, err) - } - unm, ok := mod.New().(caddyfile.Unmarshaler) - if !ok { - return fmt.Errorf("encoder module '%s' is not a Caddyfile unmarshaler", mod) - } - err = unm.UnmarshalCaddyfile(d.NewFromNextSegment()) + modID := "http.encoders." + name + unm, err := caddyfile.UnmarshalModule(d, modID) if err != nil { return err } encoding, ok := unm.(Encoding) if !ok { - return fmt.Errorf("module %s is not an HTTP encoding", mod) + return fmt.Errorf("module %s is not an HTTP encoding; is %T", modID, unm) } if enc.EncodingsRaw == nil { enc.EncodingsRaw = make(caddy.ModuleMap) diff --git a/modules/caddyhttp/reverseproxy/caddyfile.go b/modules/caddyhttp/reverseproxy/caddyfile.go index 57f425a4..895bcbb9 100644 --- a/modules/caddyhttp/reverseproxy/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/caddyfile.go @@ -241,21 +241,14 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return d.Err("load balancing selection policy already specified") } name := d.Val() - mod, err := caddy.GetModule("http.reverse_proxy.selection_policies." + name) - if err != nil { - return d.Errf("getting load balancing policy module '%s': %v", mod, err) - } - unm, ok := mod.New().(caddyfile.Unmarshaler) - if !ok { - return d.Errf("load balancing policy module '%s' is not a Caddyfile unmarshaler", mod) - } - err = unm.UnmarshalCaddyfile(d.NewFromNextSegment()) + modID := "http.reverse_proxy.selection_policies." + name + unm, err := caddyfile.UnmarshalModule(d, modID) if err != nil { return err } sel, ok := unm.(Selector) if !ok { - return d.Errf("module %s is not a Selector", mod) + return d.Errf("module %s (%T) is not a reverseproxy.Selector", modID, unm) } if h.LoadBalancing == nil { h.LoadBalancing = new(LoadBalancing) @@ -574,21 +567,14 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return d.Err("transport already specified") } transportModuleName = d.Val() - mod, err := caddy.GetModule("http.reverse_proxy.transport." + transportModuleName) - if err != nil { - return d.Errf("getting transport module '%s': %v", mod, err) - } - unm, ok := mod.New().(caddyfile.Unmarshaler) - if !ok { - return d.Errf("transport module '%s' is not a Caddyfile unmarshaler", mod) - } - err = unm.UnmarshalCaddyfile(d.NewFromNextSegment()) + modID := "http.reverse_proxy.transport." + transportModuleName + unm, err := caddyfile.UnmarshalModule(d, modID) if err != nil { return err } rt, ok := unm.(http.RoundTripper) if !ok { - return d.Errf("module %s is not a RoundTripper", mod) + return d.Errf("module %s (%T) is not a RoundTripper", modID, unm) } transport = rt diff --git a/modules/caddytls/acmeissuer.go b/modules/caddytls/acmeissuer.go index df071c4b..43e758fd 100644 --- a/modules/caddytls/acmeissuer.go +++ b/modules/caddytls/acmeissuer.go @@ -354,18 +354,11 @@ func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { if iss.Challenges.DNS == nil { iss.Challenges.DNS = new(DNSChallengeConfig) } - dnsProvModule, err := caddy.GetModule("dns.providers." + provName) + unm, err := caddyfile.UnmarshalModule(d, "dns.providers."+provName) if err != nil { - return d.Errf("getting DNS provider module named '%s': %v", provName, err) + return err } - dnsProvModuleInstance := dnsProvModule.New() - if unm, ok := dnsProvModuleInstance.(caddyfile.Unmarshaler); ok { - err = unm.UnmarshalCaddyfile(d.NewFromNextSegment()) - if err != nil { - return err - } - } - iss.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(dnsProvModuleInstance, "name", provName, nil) + iss.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(unm, "name", provName, nil) case "resolvers": if iss.Challenges == nil { diff --git a/modules/logging/filterencoder.go b/modules/logging/filterencoder.go index d1c335f4..cdb552d8 100644 --- a/modules/logging/filterencoder.go +++ b/modules/logging/filterencoder.go @@ -115,21 +115,14 @@ func (fe *FilterEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return d.ArgErr() } moduleName := d.Val() - mod, err := caddy.GetModule("caddy.logging.encoders." + moduleName) - if err != nil { - return d.Errf("getting log encoder module named '%s': %v", moduleName, err) - } - unm, ok := mod.New().(caddyfile.Unmarshaler) - if !ok { - return d.Errf("log encoder module '%s' is not a Caddyfile unmarshaler", mod) - } - err = unm.UnmarshalCaddyfile(d.NewFromNextSegment()) + moduleID := "caddy.logging.encoders." + moduleName + unm, err := caddyfile.UnmarshalModule(d, moduleID) if err != nil { return err } enc, ok := unm.(zapcore.Encoder) if !ok { - return d.Errf("module %s is not a zapcore.Encoder", mod) + return d.Errf("module %s (%T) is not a zapcore.Encoder", moduleID, unm) } fe.WrappedRaw = caddyconfig.JSONModuleObject(enc, "format", moduleName, nil) @@ -140,26 +133,19 @@ func (fe *FilterEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return d.ArgErr() } filterName := d.Val() - mod, err := caddy.GetModule("caddy.logging.encoders.filter." + filterName) - if err != nil { - return d.Errf("getting log filter module named '%s': %v", filterName, err) - } - unm, ok := mod.New().(caddyfile.Unmarshaler) - if !ok { - return d.Errf("log encoder module '%s' is not a Caddyfile unmarshaler", mod) - } - err = unm.UnmarshalCaddyfile(d.NewFromNextSegment()) + moduleID := "caddy.logging.encoders.filter." + filterName + unm, err := caddyfile.UnmarshalModule(d, moduleID) if err != nil { return err } - f, ok := unm.(LogFieldFilter) + filter, ok := unm.(LogFieldFilter) if !ok { - return d.Errf("module %s is not a LogFieldFilter", mod) + return d.Errf("module %s (%T) is not a logging.LogFieldFilter", moduleID, unm) } if fe.FieldsRaw == nil { fe.FieldsRaw = make(map[string]json.RawMessage) } - fe.FieldsRaw[field] = caddyconfig.JSONModuleObject(f, "filter", filterName, nil) + fe.FieldsRaw[field] = caddyconfig.JSONModuleObject(filter, "filter", filterName, nil) } default: