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
}