|
|
@ -99,10 +99,10 @@ func handlePingbackConn(conn net.Conn, expect []byte) error { |
|
|
|
// there is no config available. It prints any warnings to stderr,
|
|
|
|
// and returns the resulting JSON config bytes along with
|
|
|
|
// whether a config file was loaded or not.
|
|
|
|
func loadConfig(configFile, adapterName string) ([]byte, bool, error) { |
|
|
|
func loadConfig(configFile, adapterName string) ([]byte, string, error) { |
|
|
|
// specifying an adapter without a config file is ambiguous
|
|
|
|
if adapterName != "" && configFile == "" { |
|
|
|
return nil, false, fmt.Errorf("cannot adapt config without config file (use --config)") |
|
|
|
return nil, "", fmt.Errorf("cannot adapt config without config file (use --config)") |
|
|
|
} |
|
|
|
|
|
|
|
// load initial config and adapter
|
|
|
@ -112,7 +112,7 @@ func loadConfig(configFile, adapterName string) ([]byte, bool, error) { |
|
|
|
if configFile != "" { |
|
|
|
config, err = ioutil.ReadFile(configFile) |
|
|
|
if err != nil { |
|
|
|
return nil, false, fmt.Errorf("reading config file: %v", err) |
|
|
|
return nil, "", fmt.Errorf("reading config file: %v", err) |
|
|
|
} |
|
|
|
caddy.Log().Info("using provided configuration", |
|
|
|
zap.String("config_file", configFile), |
|
|
@ -129,7 +129,7 @@ func loadConfig(configFile, adapterName string) ([]byte, bool, error) { |
|
|
|
cfgAdapter = nil |
|
|
|
} else if err != nil { |
|
|
|
// default Caddyfile exists, but error reading it
|
|
|
|
return nil, false, fmt.Errorf("reading default Caddyfile: %v", err) |
|
|
|
return nil, "", fmt.Errorf("reading default Caddyfile: %v", err) |
|
|
|
} else { |
|
|
|
// success reading default Caddyfile
|
|
|
|
configFile = "Caddyfile" |
|
|
@ -151,7 +151,7 @@ func loadConfig(configFile, adapterName string) ([]byte, bool, error) { |
|
|
|
if adapterName != "" { |
|
|
|
cfgAdapter = caddyconfig.GetAdapter(adapterName) |
|
|
|
if cfgAdapter == nil { |
|
|
|
return nil, false, fmt.Errorf("unrecognized config adapter: %s", adapterName) |
|
|
|
return nil, "", fmt.Errorf("unrecognized config adapter: %s", adapterName) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -161,7 +161,7 @@ func loadConfig(configFile, adapterName string) ([]byte, bool, error) { |
|
|
|
"filename": configFile, |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
return nil, false, fmt.Errorf("adapting config using %s: %v", adapterName, err) |
|
|
|
return nil, "", fmt.Errorf("adapting config using %s: %v", adapterName, err) |
|
|
|
} |
|
|
|
for _, warn := range warnings { |
|
|
|
msg := warn.Message |
|
|
@ -173,7 +173,85 @@ func loadConfig(configFile, adapterName string) ([]byte, bool, error) { |
|
|
|
config = adaptedConfig |
|
|
|
} |
|
|
|
|
|
|
|
return config, configFile != "", nil |
|
|
|
return config, configFile, nil |
|
|
|
} |
|
|
|
|
|
|
|
// watchConfigFile watches the config file at filename for changes
|
|
|
|
// and reloads the config if the file was updated. This function
|
|
|
|
// blocks indefinitely; it only quits if the poller has errors for
|
|
|
|
// long enough time. The filename passed in must be the actual
|
|
|
|
// config file used, not one to be discovered.
|
|
|
|
func watchConfigFile(filename, adapterName string) { |
|
|
|
// make our logger; since config reloads can change the
|
|
|
|
// default logger, we need to get it dynamically each time
|
|
|
|
logger := func() *zap.Logger { |
|
|
|
return caddy.Log(). |
|
|
|
Named("watcher"). |
|
|
|
With(zap.String("config_file", filename)) |
|
|
|
} |
|
|
|
|
|
|
|
// get the initial timestamp on the config file
|
|
|
|
info, err := os.Stat(filename) |
|
|
|
if err != nil { |
|
|
|
logger().Error("cannot watch config file", zap.Error(err)) |
|
|
|
return |
|
|
|
} |
|
|
|
lastModified := info.ModTime() |
|
|
|
|
|
|
|
logger().Info("watching config file for changes") |
|
|
|
|
|
|
|
// if the file disappears or something, we can
|
|
|
|
// stop polling if the error lasts long enough
|
|
|
|
var lastErr time.Time |
|
|
|
finalError := func(err error) bool { |
|
|
|
if lastErr.IsZero() { |
|
|
|
lastErr = time.Now() |
|
|
|
return false |
|
|
|
} |
|
|
|
if time.Since(lastErr) > 30*time.Second { |
|
|
|
logger().Error("giving up watching config file; too many errors", |
|
|
|
zap.Error(err)) |
|
|
|
return true |
|
|
|
} |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
// begin poller
|
|
|
|
for range time.Tick(1 * time.Second) { |
|
|
|
// get the file info
|
|
|
|
info, err := os.Stat(filename) |
|
|
|
if err != nil { |
|
|
|
if finalError(err) { |
|
|
|
return |
|
|
|
} |
|
|
|
continue |
|
|
|
} |
|
|
|
lastErr = time.Time{} // no error, so clear any memory of one
|
|
|
|
|
|
|
|
// if it hasn't changed, nothing to do
|
|
|
|
if !info.ModTime().After(lastModified) { |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
logger().Info("config file changed; reloading") |
|
|
|
|
|
|
|
// remember this timestamp
|
|
|
|
lastModified = info.ModTime() |
|
|
|
|
|
|
|
// load the contents of the file
|
|
|
|
config, _, err := loadConfig(filename, adapterName) |
|
|
|
if err != nil { |
|
|
|
logger().Error("unable to load latest config", zap.Error(err)) |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
// apply the updated config
|
|
|
|
err = caddy.Load(config, false) |
|
|
|
if err != nil { |
|
|
|
logger().Error("applying latest config", zap.Error(err)) |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Flags wraps a FlagSet so that typed values
|
|
|
|