diff --git a/caddy.go b/caddy.go index b29a0280a..1363a1b1c 100644 --- a/caddy.go +++ b/caddy.go @@ -41,12 +41,10 @@ import ( "strconv" "strings" "sync" - "sync/atomic" "time" "github.com/mholt/caddy/caddyfile" "github.com/mholt/caddy/telemetry" - "github.com/mholt/certmagic" ) // Configurable application parameters @@ -472,26 +470,6 @@ func (i *Instance) Caddyfile() Input { // // This function blocks until all the servers are listening. func Start(cdyfile Input) (*Instance, error) { - // set up the clustering plugin, if there is one (and there should - // always be one) -- this should be done exactly once, but we can't - // do it during init while plugins are still registering, so do it - // when starting the first instance) - if atomic.CompareAndSwapInt32(&clusterPluginSetup, 0, 1) { - clusterPluginName := os.Getenv("CADDY_CLUSTERING") - if clusterPluginName == "" { - clusterPluginName = "file" // name of default storage plugin as registered in caddytls package - } - clusterFn, ok := clusterProviders[clusterPluginName] - if !ok { - return nil, fmt.Errorf("unrecognized cluster plugin (was it included in the Caddy build?): %s", clusterPluginName) - } - storage, err := clusterFn() - if err != nil { - return nil, fmt.Errorf("constructing cluster plugin %s: %v", clusterPluginName, err) - } - certmagic.DefaultStorage = storage - } - inst := &Instance{serverType: cdyfile.ServerType(), wg: new(sync.WaitGroup), Storage: make(map[interface{}]interface{})} err := startWithListenerFds(cdyfile, inst, nil) if err != nil { @@ -1021,7 +999,5 @@ var ( DefaultConfigFile = "Caddyfile" ) -var clusterPluginSetup int32 // access atomically - // CtxKey is a value type for use with context.WithValue. type CtxKey string diff --git a/caddyhttp/caddyhttp_test.go b/caddyhttp/caddyhttp_test.go index 4e759b54b..47b084a7b 100644 --- a/caddyhttp/caddyhttp_test.go +++ b/caddyhttp/caddyhttp_test.go @@ -25,9 +25,9 @@ import ( // ensure that the standard plugins are in fact plugged in // and registered properly; this is a quick/naive way to do it. func TestStandardPlugins(t *testing.T) { - numStandardPlugins := 31 // importing caddyhttp plugs in this many plugins + numStandardPlugins := 32 // importing caddyhttp plugs in this many plugins s := caddy.DescribePlugins() - if got, want := strings.Count(s, "\n"), numStandardPlugins+7; got != want { + if got, want := strings.Count(s, "\n"), numStandardPlugins+4; got != want { t.Errorf("Expected all standard plugins to be plugged in, got:\n%s", s) } } diff --git a/caddytls/setup.go b/caddytls/setup.go index 9ac89a07f..e76732385 100644 --- a/caddytls/setup.go +++ b/caddytls/setup.go @@ -26,6 +26,7 @@ import ( "path/filepath" "strconv" "strings" + "sync/atomic" "github.com/mholt/caddy" "github.com/mholt/caddy/telemetry" @@ -36,13 +37,34 @@ func init() { caddy.RegisterPlugin("tls", caddy.Plugin{Action: setupTLS}) // ensure the default Storage implementation is plugged in - caddy.RegisterClusterPlugin("file", constructDefaultClusterPlugin) + RegisterClusterPlugin("file", constructDefaultClusterPlugin) } // setupTLS sets up the TLS configuration and installs certificates that // are specified by the user in the config file. All the automatic HTTPS // stuff comes later outside of this function. func setupTLS(c *caddy.Controller) error { + // set up the clustering plugin, if there is one (and there should always + // be one since this tls plugin requires it) -- this should be done exactly + // once, but we can't do it during init while plugins are still registering, + // so do it as soon as we run a setup) + if atomic.CompareAndSwapInt32(&clusterPluginSetup, 0, 1) { + clusterPluginName := os.Getenv("CADDY_CLUSTERING") + if clusterPluginName == "" { + clusterPluginName = "file" // name of default storage plugin + } + clusterFn, ok := clusterProviders[clusterPluginName] + if ok { + storage, err := clusterFn() + if err != nil { + return fmt.Errorf("constructing cluster plugin %s: %v", clusterPluginName, err) + } + certmagic.DefaultStorage = storage + } else { + return fmt.Errorf("unrecognized cluster plugin (was it included in the Caddy build?): %s", clusterPluginName) + } + } + configGetter, ok := configGetters[c.ServerType()] if !ok { return fmt.Errorf("no caddytls.ConfigGetter for %s server type; must call RegisterConfigGetter", c.ServerType()) @@ -423,3 +445,5 @@ func loadCertsInDir(cfg *Config, c *caddy.Controller, dir string) error { func constructDefaultClusterPlugin() (certmagic.Storage, error) { return &certmagic.FileStorage{Path: caddy.AssetsPath()}, nil } + +var clusterPluginSetup int32 // access atomically diff --git a/caddytls/tls.go b/caddytls/tls.go index 9a9d5c639..f6a375b79 100644 --- a/caddytls/tls.go +++ b/caddytls/tls.go @@ -108,3 +108,19 @@ func RegisterDNSProvider(name string, provider DNSProviderConstructor) { dnsProviders[name] = provider caddy.RegisterPlugin("tls.dns."+name, caddy.Plugin{}) } + +// ClusterPluginConstructor is a function type that is used to +// instantiate a new implementation of both certmagic.Storage +// and certmagic.Locker, which are required for successful +// use in cluster environments. +type ClusterPluginConstructor func() (certmagic.Storage, error) + +// clusterProviders is the list of storage providers +var clusterProviders = make(map[string]ClusterPluginConstructor) + +// RegisterClusterPlugin registers provider by name for facilitating +// cluster-wide operations like storage and synchronization. +func RegisterClusterPlugin(name string, provider ClusterPluginConstructor) { + clusterProviders[name] = provider + caddy.RegisterPlugin("tls.cluster."+name, caddy.Plugin{}) +} diff --git a/plugins.go b/plugins.go index 56f3679af..cd4497fa9 100644 --- a/plugins.go +++ b/plugins.go @@ -22,7 +22,6 @@ import ( "sync" "github.com/mholt/caddy/caddyfile" - "github.com/mholt/certmagic" ) // These are all the registered plugins. @@ -107,11 +106,6 @@ func ListPlugins() map[string][]string { p["caddyfile_loaders"] = append(p["caddyfile_loaders"], defaultCaddyfileLoader.name) } - // cluster plugins in registration order - for name := range clusterProviders { - p["clustering"] = append(p["clustering"], name) - } - // List the event hook plugins eventHooks.Range(func(k, _ interface{}) bool { p["event_hooks"] = append(p["event_hooks"], k.(string)) @@ -456,21 +450,6 @@ func loadCaddyfileInput(serverType string) (Input, error) { return caddyfileToUse, nil } -// ClusterPluginConstructor is a function type that is used to -// instantiate a new implementation of both certmagic.Storage -// and certmagic.Locker, which are required for successful -// use in cluster environments. -type ClusterPluginConstructor func() (certmagic.Storage, error) - -// clusterProviders is the list of storage providers -var clusterProviders = make(map[string]ClusterPluginConstructor) - -// RegisterClusterPlugin registers provider by name for facilitating -// cluster-wide operations like storage and synchronization. -func RegisterClusterPlugin(name string, provider ClusterPluginConstructor) { - clusterProviders[name] = provider -} - // OnProcessExit is a list of functions to run when the process // exits -- they are ONLY for cleanup and should not block, // return errors, or do anything fancy. They will be run with