mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-26 13:43:47 +03:00
Config auto-save; run --resume flag; update environ output (close #2903)
Config auto-saving is on by default and can be disabled. The --environ flag (or environ subcommand) now print more useful information from Caddy and the runtime, including some nifty paths.
This commit is contained in:
parent
984d384d14
commit
5a0603ed72
5 changed files with 94 additions and 24 deletions
16
admin.go
16
admin.go
|
@ -66,6 +66,16 @@ type AdminConfig struct {
|
||||||
// will be the default value. If set but empty, no origins will
|
// will be the default value. If set but empty, no origins will
|
||||||
// be allowed.
|
// be allowed.
|
||||||
Origins []string `json:"origins,omitempty"`
|
Origins []string `json:"origins,omitempty"`
|
||||||
|
|
||||||
|
// Options related to configuration management.
|
||||||
|
Config *ConfigSettings `json:"config,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigSettings configures the, uh, configuration... and
|
||||||
|
// management thereof.
|
||||||
|
type ConfigSettings struct {
|
||||||
|
// Whether to keep a copy of the active config on disk. Default is true.
|
||||||
|
Persist *bool `json:"persist,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// listenAddr extracts a singular listen address from ac.Listen,
|
// listenAddr extracts a singular listen address from ac.Listen,
|
||||||
|
@ -775,7 +785,11 @@ traverseLoop:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveMetaFields removes meta fields like "@id" from a JSON message.
|
// RemoveMetaFields removes meta fields like "@id" from a JSON message
|
||||||
|
// by using a simple regular expression. (An alternate way to do this
|
||||||
|
// would be to delete them from the raw, map[string]interface{}
|
||||||
|
// representation as they are indexed, then iterate the index we made
|
||||||
|
// and add them back after encoding as JSON, but this is simpler.)
|
||||||
func RemoveMetaFields(rawJSON []byte) []byte {
|
func RemoveMetaFields(rawJSON []byte) []byte {
|
||||||
return idRegexp.ReplaceAllFunc(rawJSON, func(in []byte) []byte {
|
return idRegexp.ReplaceAllFunc(rawJSON, func(in []byte) []byte {
|
||||||
// matches with a comma on both sides (when "@id" property is
|
// matches with a comma on both sides (when "@id" property is
|
||||||
|
|
43
caddy.go
43
caddy.go
|
@ -20,6 +20,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
|
@ -30,6 +31,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mholt/certmagic"
|
"github.com/mholt/certmagic"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config is the top (or beginning) of the Caddy configuration structure.
|
// Config is the top (or beginning) of the Caddy configuration structure.
|
||||||
|
@ -148,13 +150,6 @@ func changeConfig(method, path string, input []byte, forceReload bool) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove any @id fields from the JSON, which would cause
|
|
||||||
// loading to break since the field wouldn't be recognized
|
|
||||||
// (an alternate way to do this would be to delete them from
|
|
||||||
// rawCfg as they are indexed, then iterate the index we made
|
|
||||||
// and add them back after encoding as JSON)
|
|
||||||
newCfg = RemoveMetaFields(newCfg)
|
|
||||||
|
|
||||||
// load this new config; if it fails, we need to revert to
|
// load this new config; if it fails, we need to revert to
|
||||||
// our old representation of caddy's actual config
|
// our old representation of caddy's actual config
|
||||||
err = unsyncedDecodeAndRun(newCfg)
|
err = unsyncedDecodeAndRun(newCfg)
|
||||||
|
@ -232,15 +227,19 @@ func indexConfigObjects(ptr interface{}, configPath string, index map[string]str
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// unsyncedDecodeAndRun decodes cfgJSON and runs
|
// unsyncedDecodeAndRun removes any meta fields (like @id tags)
|
||||||
// it as the new config, replacing any other
|
// from cfgJSON, decodes the result into a *Config, and runs
|
||||||
// current config. It does not update the raw
|
// it as the new config, replacing any other current config.
|
||||||
// config state, as this is a lower-level function;
|
// It does NOT update the raw config state, as this is a
|
||||||
// most callers will want to use Load instead.
|
// lower-level function; most callers will want to use Load
|
||||||
// A write lock on currentCfgMu is required!
|
// instead. A write lock on currentCfgMu is required!
|
||||||
func unsyncedDecodeAndRun(cfgJSON []byte) error {
|
func unsyncedDecodeAndRun(cfgJSON []byte) error {
|
||||||
|
// remove any @id fields from the JSON, which would cause
|
||||||
|
// loading to break since the field wouldn't be recognized
|
||||||
|
strippedCfgJSON := RemoveMetaFields(cfgJSON)
|
||||||
|
|
||||||
var newCfg *Config
|
var newCfg *Config
|
||||||
err := strictUnmarshalJSON(cfgJSON, &newCfg)
|
err := strictUnmarshalJSON(strippedCfgJSON, &newCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -258,6 +257,22 @@ func unsyncedDecodeAndRun(cfgJSON []byte) error {
|
||||||
// Stop, Cleanup each old app
|
// Stop, Cleanup each old app
|
||||||
unsyncedStop(oldCfg)
|
unsyncedStop(oldCfg)
|
||||||
|
|
||||||
|
// autosave a non-nil config, if not disabled
|
||||||
|
if newCfg != nil &&
|
||||||
|
(newCfg.Admin == nil ||
|
||||||
|
newCfg.Admin.Config == nil ||
|
||||||
|
newCfg.Admin.Config.Persist == nil ||
|
||||||
|
*newCfg.Admin.Config.Persist) {
|
||||||
|
err := ioutil.WriteFile(ConfigAutosavePath, cfgJSON, 0600)
|
||||||
|
if err == nil {
|
||||||
|
Log().Info("autosaved config", zap.String("file", ConfigAutosavePath))
|
||||||
|
} else {
|
||||||
|
Log().Error("unable to autosave config",
|
||||||
|
zap.String("file", ConfigAutosavePath),
|
||||||
|
zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -141,6 +142,7 @@ func cmdStart(fl Flags) (int, error) {
|
||||||
func cmdRun(fl Flags) (int, error) {
|
func cmdRun(fl Flags) (int, error) {
|
||||||
runCmdConfigFlag := fl.String("config")
|
runCmdConfigFlag := fl.String("config")
|
||||||
runCmdConfigAdapterFlag := fl.String("adapter")
|
runCmdConfigAdapterFlag := fl.String("adapter")
|
||||||
|
runCmdResumeFlag := fl.Bool("resume")
|
||||||
runCmdPrintEnvFlag := fl.Bool("environ")
|
runCmdPrintEnvFlag := fl.Bool("environ")
|
||||||
runCmdPingbackFlag := fl.String("pingback")
|
runCmdPingbackFlag := fl.String("pingback")
|
||||||
|
|
||||||
|
@ -149,14 +151,32 @@ func cmdRun(fl Flags) (int, error) {
|
||||||
printEnvironment()
|
printEnvironment()
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the config in caddy's native format
|
|
||||||
config, err := loadConfig(runCmdConfigFlag, runCmdConfigAdapterFlag)
|
|
||||||
if err != nil {
|
|
||||||
return caddy.ExitCodeFailedStartup, err
|
|
||||||
}
|
|
||||||
// TODO: This is TEMPORARY, until the RCs
|
// TODO: This is TEMPORARY, until the RCs
|
||||||
moveStorage()
|
moveStorage()
|
||||||
|
|
||||||
|
// load the config, depending on flags
|
||||||
|
var config []byte
|
||||||
|
var err error
|
||||||
|
if runCmdResumeFlag {
|
||||||
|
config, err = ioutil.ReadFile(caddy.ConfigAutosavePath)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// not a bad error; just can't resume if autosave file doesn't exist
|
||||||
|
caddy.Log().Info("no autosave file exists", zap.String("autosave_file", caddy.ConfigAutosavePath))
|
||||||
|
runCmdResumeFlag = false
|
||||||
|
} else if err != nil {
|
||||||
|
return caddy.ExitCodeFailedStartup, err
|
||||||
|
} else {
|
||||||
|
caddy.Log().Info("resuming from last configuration", zap.String("autosave_file", caddy.ConfigAutosavePath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// we don't use 'else' here since this value might have been changed in 'if' block; i.e. not mutually exclusive
|
||||||
|
if !runCmdResumeFlag {
|
||||||
|
config, err = loadConfig(runCmdConfigFlag, runCmdConfigAdapterFlag)
|
||||||
|
if err != nil {
|
||||||
|
return caddy.ExitCodeFailedStartup, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set a fitting User-Agent for ACME requests
|
// set a fitting User-Agent for ACME requests
|
||||||
goModule := caddy.GoModule()
|
goModule := caddy.GoModule()
|
||||||
cleanModVersion := strings.TrimPrefix(goModule.Version, "v")
|
cleanModVersion := strings.TrimPrefix(goModule.Version, "v")
|
||||||
|
@ -167,9 +187,7 @@ func cmdRun(fl Flags) (int, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, fmt.Errorf("loading initial config: %v", err)
|
return caddy.ExitCodeFailedStartup, fmt.Errorf("loading initial config: %v", err)
|
||||||
}
|
}
|
||||||
if len(config) > 0 {
|
caddy.Log().Info("serving initial configuration")
|
||||||
caddy.Log().Named("admin").Info("Caddy 2 serving initial configuration")
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we are to report to another process the successful start
|
// if we are to report to another process the successful start
|
||||||
// of the server, do so now by echoing back contents of stdin
|
// of the server, do so now by echoing back contents of stdin
|
||||||
|
|
|
@ -116,11 +116,15 @@ line flags.
|
||||||
|
|
||||||
If --environ is specified, the environment as seen by the Caddy process will
|
If --environ is specified, the environment as seen by the Caddy process will
|
||||||
be printed before starting. This is the same as the environ command but does
|
be printed before starting. This is the same as the environ command but does
|
||||||
not quit after printing, and can be useful for troubleshooting.`,
|
not quit after printing, and can be useful for troubleshooting.
|
||||||
|
|
||||||
|
The --resume flag will override the --config flag if there is a config auto-
|
||||||
|
save file. It is not an error if --resume is used and no autosave file exists.`,
|
||||||
Flags: func() *flag.FlagSet {
|
Flags: func() *flag.FlagSet {
|
||||||
fs := flag.NewFlagSet("run", flag.ExitOnError)
|
fs := flag.NewFlagSet("run", flag.ExitOnError)
|
||||||
fs.String("config", "", "Configuration file")
|
fs.String("config", "", "Configuration file")
|
||||||
fs.String("adapter", "", "Name of config adapter to apply")
|
fs.String("adapter", "", "Name of config adapter to apply")
|
||||||
|
fs.Bool("resume", false, "Use saved config, if any (and prefer over --config file)")
|
||||||
fs.Bool("environ", false, "Print environment")
|
fs.Bool("environ", false, "Print environment")
|
||||||
fs.String("pingback", "", "Echo confirmation bytes to this address on success")
|
fs.String("pingback", "", "Echo confirmation bytes to this address on success")
|
||||||
return fs
|
return fs
|
||||||
|
|
19
cmd/main.go
19
cmd/main.go
|
@ -110,6 +110,9 @@ func loadConfig(configFile, adapterName string) ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 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),
|
||||||
|
zap.String("config_adapter", adapterName))
|
||||||
} else if adapterName == "" {
|
} else if adapterName == "" {
|
||||||
// as a special case when no config file or adapter
|
// as a special case when no config file or adapter
|
||||||
// is specified, see if the Caddyfile adapter is
|
// is specified, see if the Caddyfile adapter is
|
||||||
|
@ -126,6 +129,7 @@ func loadConfig(configFile, adapterName string) ([]byte, error) {
|
||||||
} else {
|
} else {
|
||||||
// success reading default Caddyfile
|
// success reading default Caddyfile
|
||||||
configFile = "Caddyfile"
|
configFile = "Caddyfile"
|
||||||
|
caddy.Log().Info("using adjacent Caddyfile")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,6 +229,21 @@ func flagHelp(fs *flag.FlagSet) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func printEnvironment() {
|
func printEnvironment() {
|
||||||
|
fmt.Printf("caddy.HomeDir=%s\n", caddy.HomeDir())
|
||||||
|
fmt.Printf("caddy.AppDataDir=%s\n", caddy.AppDataDir())
|
||||||
|
fmt.Printf("caddy.AppConfigDir=%s\n", caddy.AppConfigDir())
|
||||||
|
fmt.Printf("caddy.ConfigAutosavePath=%s\n", caddy.ConfigAutosavePath)
|
||||||
|
fmt.Printf("runtime.GOOS=%s\n", runtime.GOOS)
|
||||||
|
fmt.Printf("runtime.GOARCH=%s\n", runtime.GOARCH)
|
||||||
|
fmt.Printf("runtime.Compiler=%s\n", runtime.Compiler)
|
||||||
|
fmt.Printf("runtime.NumCPU=%d\n", runtime.NumCPU())
|
||||||
|
fmt.Printf("runtime.GOMAXPROCS=%d\n", runtime.GOMAXPROCS(0))
|
||||||
|
fmt.Printf("runtime.Version=%s\n", runtime.Version())
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
cwd = fmt.Sprintf("<error: %v>", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("os.Getwd=%s\n\n", cwd)
|
||||||
for _, v := range os.Environ() {
|
for _, v := range os.Environ() {
|
||||||
fmt.Println(v)
|
fmt.Println(v)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue