@marcel91200 / caddy Mirror · read-only / commits / 5a0603ed
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

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.

Matthew Holt committed Dec 31, 2019 at 16:56 UTC 5a0603ed72cead424a34b3bc5af3a5b1629ac187
5 files changed +94 -24
admin.go
+15 -1
@@ -66,6 +66,16 @@ type AdminConfig struct {
66 // will be the default value. If set but empty, no origins will
67 // be allowed.
68 Origins []string `json:"origins,omitempty"`
69 +
70 + // Options related to configuration management.
71 + Config *ConfigSettings `json:"config,omitempty"`
72 +}
73 +
74 +// ConfigSettings configures the, uh, configuration... and
75 +// management thereof.
76 +type ConfigSettings struct {
77 + // Whether to keep a copy of the active config on disk. Default is true.
78 + Persist *bool `json:"persist,omitempty"`
79 }
80
81 // listenAddr extracts a singular listen address from ac.Listen,
@@ -775,7 +785,11 @@ traverseLoop:
785 return nil
786 }
787
778 -// RemoveMetaFields removes meta fields like "@id" from a JSON message.
788 +// RemoveMetaFields removes meta fields like "@id" from a JSON message
789 +// by using a simple regular expression. (An alternate way to do this
790 +// would be to delete them from the raw, map[string]interface{}
791 +// representation as they are indexed, then iterate the index we made
792 +// and add them back after encoding as JSON, but this is simpler.)
793 func RemoveMetaFields(rawJSON []byte) []byte {
794 return idRegexp.ReplaceAllFunc(rawJSON, func(in []byte) []byte {
795 // matches with a comma on both sides (when "@id" property is
caddy.go
+29 -14
@@ -20,6 +20,7 @@ import (
20 "encoding/json"
21 "fmt"
22 "io"
23 + "io/ioutil"
24 "log"
25 "net/http"
26 "path"
@@ -30,6 +31,7 @@ import (
31 "time"
32
33 "github.com/mholt/certmagic"
34 + "go.uber.org/zap"
35 )
36
37 // 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 {
150 }
151 }
152
151 - // remove any @id fields from the JSON, which would cause
152 - // loading to break since the field wouldn't be recognized
153 - // (an alternate way to do this would be to delete them from
154 - // rawCfg as they are indexed, then iterate the index we made
155 - // and add them back after encoding as JSON)
156 - newCfg = RemoveMetaFields(newCfg)
157 -
153 // load this new config; if it fails, we need to revert to
154 // our old representation of caddy's actual config
155 err = unsyncedDecodeAndRun(newCfg)
@@ -232,15 +227,19 @@ func indexConfigObjects(ptr interface{}, configPath string, index map[string]str
227 return nil
228 }
229
235 -// unsyncedDecodeAndRun decodes cfgJSON and runs
236 -// it as the new config, replacing any other
237 -// current config. It does not update the raw
238 -// config state, as this is a lower-level function;
239 -// most callers will want to use Load instead.
240 -// A write lock on currentCfgMu is required!
230 +// unsyncedDecodeAndRun removes any meta fields (like @id tags)
231 +// from cfgJSON, decodes the result into a *Config, and runs
232 +// it as the new config, replacing any other current config.
233 +// It does NOT update the raw config state, as this is a
234 +// lower-level function; most callers will want to use Load
235 +// instead. A write lock on currentCfgMu is required!
236 func unsyncedDecodeAndRun(cfgJSON []byte) error {
237 + // remove any @id fields from the JSON, which would cause
238 + // loading to break since the field wouldn't be recognized
239 + strippedCfgJSON := RemoveMetaFields(cfgJSON)
240 +
241 var newCfg *Config
243 - err := strictUnmarshalJSON(cfgJSON, &newCfg)
242 + err := strictUnmarshalJSON(strippedCfgJSON, &newCfg)
243 if err != nil {
244 return err
245 }
@@ -258,6 +257,22 @@ func unsyncedDecodeAndRun(cfgJSON []byte) error {
257 // Stop, Cleanup each old app
258 unsyncedStop(oldCfg)
259
260 + // autosave a non-nil config, if not disabled
261 + if newCfg != nil &&
262 + (newCfg.Admin == nil ||
263 + newCfg.Admin.Config == nil ||
264 + newCfg.Admin.Config.Persist == nil ||
265 + *newCfg.Admin.Config.Persist) {
266 + err := ioutil.WriteFile(ConfigAutosavePath, cfgJSON, 0600)
267 + if err == nil {
268 + Log().Info("autosaved config", zap.String("file", ConfigAutosavePath))
269 + } else {
270 + Log().Error("unable to autosave config",
271 + zap.String("file", ConfigAutosavePath),
272 + zap.Error(err))
273 + }
274 + }
275 +
276 return nil
277 }
278
cmd/commandfuncs.go
+26 -8
@@ -27,6 +27,7 @@ import (
27 "os"
28 "os/exec"
29 "reflect"
30 + "runtime"
31 "runtime/debug"
32 "sort"
33 "strings"
@@ -141,6 +142,7 @@ func cmdStart(fl Flags) (int, error) {
142 func cmdRun(fl Flags) (int, error) {
143 runCmdConfigFlag := fl.String("config")
144 runCmdConfigAdapterFlag := fl.String("adapter")
145 + runCmdResumeFlag := fl.Bool("resume")
146 runCmdPrintEnvFlag := fl.Bool("environ")
147 runCmdPingbackFlag := fl.String("pingback")
148
@@ -149,14 +151,32 @@ func cmdRun(fl Flags) (int, error) {
151 printEnvironment()
152 }
153
152 - // get the config in caddy's native format
153 - config, err := loadConfig(runCmdConfigFlag, runCmdConfigAdapterFlag)
154 - if err != nil {
155 - return caddy.ExitCodeFailedStartup, err
156 - }
154 // TODO: This is TEMPORARY, until the RCs
155 moveStorage()
156
157 + // load the config, depending on flags
158 + var config []byte
159 + var err error
160 + if runCmdResumeFlag {
161 + config, err = ioutil.ReadFile(caddy.ConfigAutosavePath)
162 + if os.IsNotExist(err) {
163 + // not a bad error; just can't resume if autosave file doesn't exist
164 + caddy.Log().Info("no autosave file exists", zap.String("autosave_file", caddy.ConfigAutosavePath))
165 + runCmdResumeFlag = false
166 + } else if err != nil {
167 + return caddy.ExitCodeFailedStartup, err
168 + } else {
169 + caddy.Log().Info("resuming from last configuration", zap.String("autosave_file", caddy.ConfigAutosavePath))
170 + }
171 + }
172 + // we don't use 'else' here since this value might have been changed in 'if' block; i.e. not mutually exclusive
173 + if !runCmdResumeFlag {
174 + config, err = loadConfig(runCmdConfigFlag, runCmdConfigAdapterFlag)
175 + if err != nil {
176 + return caddy.ExitCodeFailedStartup, err
177 + }
178 + }
179 +
180 // set a fitting User-Agent for ACME requests
181 goModule := caddy.GoModule()
182 cleanModVersion := strings.TrimPrefix(goModule.Version, "v")
@@ -167,9 +187,7 @@ func cmdRun(fl Flags) (int, error) {
187 if err != nil {
188 return caddy.ExitCodeFailedStartup, fmt.Errorf("loading initial config: %v", err)
189 }
170 - if len(config) > 0 {
171 - caddy.Log().Named("admin").Info("Caddy 2 serving initial configuration")
172 - }
190 + caddy.Log().Info("serving initial configuration")
191
192 // if we are to report to another process the successful start
193 // of the server, do so now by echoing back contents of stdin
cmd/commands.go
+5 -1
@@ -116,11 +116,15 @@ line flags.
116
117 If --environ is specified, the environment as seen by the Caddy process will
118 be printed before starting. This is the same as the environ command but does
119 -not quit after printing, and can be useful for troubleshooting.`,
119 +not quit after printing, and can be useful for troubleshooting.
120 +
121 +The --resume flag will override the --config flag if there is a config auto-
122 +save file. It is not an error if --resume is used and no autosave file exists.`,
123 Flags: func() *flag.FlagSet {
124 fs := flag.NewFlagSet("run", flag.ExitOnError)
125 fs.String("config", "", "Configuration file")
126 fs.String("adapter", "", "Name of config adapter to apply")
127 + fs.Bool("resume", false, "Use saved config, if any (and prefer over --config file)")
128 fs.Bool("environ", false, "Print environment")
129 fs.String("pingback", "", "Echo confirmation bytes to this address on success")
130 return fs
cmd/main.go
+19
@@ -110,6 +110,9 @@ func loadConfig(configFile, adapterName string) ([]byte, error) {
110 if err != nil {
111 return nil, fmt.Errorf("reading config file: %v", err)
112 }
113 + caddy.Log().Info("using provided configuration",
114 + zap.String("config_file", configFile),
115 + zap.String("config_adapter", adapterName))
116 } else if adapterName == "" {
117 // as a special case when no config file or adapter
118 // is specified, see if the Caddyfile adapter is
@@ -126,6 +129,7 @@ func loadConfig(configFile, adapterName string) ([]byte, error) {
129 } else {
130 // success reading default Caddyfile
131 configFile = "Caddyfile"
132 + caddy.Log().Info("using adjacent Caddyfile")
133 }
134 }
135 }
@@ -225,6 +229,21 @@ func flagHelp(fs *flag.FlagSet) string {
229 }
230
231 func printEnvironment() {
232 + fmt.Printf("caddy.HomeDir=%s\n", caddy.HomeDir())
233 + fmt.Printf("caddy.AppDataDir=%s\n", caddy.AppDataDir())
234 + fmt.Printf("caddy.AppConfigDir=%s\n", caddy.AppConfigDir())
235 + fmt.Printf("caddy.ConfigAutosavePath=%s\n", caddy.ConfigAutosavePath)
236 + fmt.Printf("runtime.GOOS=%s\n", runtime.GOOS)
237 + fmt.Printf("runtime.GOARCH=%s\n", runtime.GOARCH)
238 + fmt.Printf("runtime.Compiler=%s\n", runtime.Compiler)
239 + fmt.Printf("runtime.NumCPU=%d\n", runtime.NumCPU())
240 + fmt.Printf("runtime.GOMAXPROCS=%d\n", runtime.GOMAXPROCS(0))
241 + fmt.Printf("runtime.Version=%s\n", runtime.Version())
242 + cwd, err := os.Getwd()
243 + if err != nil {
244 + cwd = fmt.Sprintf("<error: %v>", err)
245 + }
246 + fmt.Printf("os.Getwd=%s\n\n", cwd)
247 for _, v := range os.Environ() {
248 fmt.Println(v)
249 }