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
Change storage paths to follow OS conventions; migrate folder (#2955)
Matthew Holt committed
Dec 31, 2019 at 16:47 UTC
984d384d148090cdb0f6aa2f234a8b946c3b9ee3
5 files changed
+202
-15
caddy.go
+1
-1
@@ -348,7 +348,7 @@ func run(newCfg *Config, start bool) error {
348
}
349
350
if newCfg.storage == nil {
351
- newCfg.storage = &certmagic.FileStorage{Path: dataDir()}
351
+ newCfg.storage = &certmagic.FileStorage{Path: AppDataDir()}
352
}
353
certmagic.Default.Storage = newCfg.storage
354
cmd/commandfuncs.go
+21
@@ -154,6 +154,8 @@ func cmdRun(fl Flags) (int, error) {
154
if err != nil {
155
return caddy.ExitCodeFailedStartup, err
156
}
157
+ // TODO: This is TEMPORARY, until the RCs
158
+ moveStorage()
159
160
// set a fitting User-Agent for ACME requests
161
goModule := caddy.GoModule()
@@ -190,6 +192,25 @@ func cmdRun(fl Flags) (int, error) {
192
}
193
}
194
195
+ // warn if the environment does not provide enough information about the disk
196
+ hasXDG := os.Getenv("XDG_DATA_HOME") != "" &&
197
+ os.Getenv("XDG_CONFIG_HOME") != "" &&
198
+ os.Getenv("XDG_CACHE_HOME") != ""
199
+ switch runtime.GOOS {
200
+ case "windows":
201
+ if os.Getenv("HOME") == "" && os.Getenv("USERPROFILE") == "" && !hasXDG {
202
+ caddy.Log().Warn("neither HOME nor USERPROFILE environment variables are set - please fix; some assets might be stored in ./caddy")
203
+ }
204
+ case "plan9":
205
+ if os.Getenv("home") == "" && !hasXDG {
206
+ caddy.Log().Warn("$home environment variable is empty - please fix; some assets might be stored in ./caddy")
207
+ }
208
+ default:
209
+ if os.Getenv("HOME") == "" && !hasXDG {
210
+ caddy.Log().Warn("$HOME environment variable is empty - please fix; some assets might be stored in ./caddy")
211
+ }
212
+ }
213
+
214
select {}
215
}
216
cmd/main.go
+71
@@ -22,12 +22,15 @@ import (
22
"io/ioutil"
23
"net"
24
"os"
25
+ "path/filepath"
26
+ "runtime"
27
"strconv"
28
"strings"
29
"time"
30
31
"github.com/caddyserver/caddy/v2"
32
"github.com/caddyserver/caddy/v2/caddyconfig"
33
+ "go.uber.org/zap"
34
)
35
36
// Main implements the main function of the caddy command.
@@ -226,3 +229,71 @@ func printEnvironment() {
229
fmt.Println(v)
230
}
231
}
232
+
233
+// moveStorage moves the old default dataDir to the new default dataDir.
234
+// TODO: This is TEMPORARY until the release candidates.
235
+func moveStorage() {
236
+ // get the home directory (the old way)
237
+ oldHome := os.Getenv("HOME")
238
+ if oldHome == "" && runtime.GOOS == "windows" {
239
+ drive := os.Getenv("HOMEDRIVE")
240
+ path := os.Getenv("HOMEPATH")
241
+ oldHome = drive + path
242
+ if drive == "" || path == "" {
243
+ oldHome = os.Getenv("USERPROFILE")
244
+ }
245
+ }
246
+ if oldHome == "" {
247
+ oldHome = "."
248
+ }
249
+ oldDataDir := filepath.Join(oldHome, ".local", "share", "caddy")
250
+
251
+ // nothing to do if old data dir doesn't exist
252
+ _, err := os.Stat(oldDataDir)
253
+ if os.IsNotExist(err) {
254
+ return
255
+ }
256
+
257
+ // nothing to do if the new data dir is the same as the old one
258
+ newDataDir := caddy.AppDataDir()
259
+ if oldDataDir == newDataDir {
260
+ return
261
+ }
262
+
263
+ logger := caddy.Log().Named("automigrate").With(
264
+ zap.String("old_dir", oldDataDir),
265
+ zap.String("new_dir", newDataDir))
266
+
267
+ logger.Info("beginning one-time data directory migration",
268
+ zap.String("details", "https://github.com/caddyserver/caddy/issues/2955"))
269
+
270
+ // if new data directory exists, avoid auto-migration as a conservative safety measure
271
+ _, err = os.Stat(newDataDir)
272
+ if !os.IsNotExist(err) {
273
+ logger.Error("new data directory already exists; skipping auto-migration as conservative safety measure",
274
+ zap.Error(err),
275
+ zap.String("instructions", "https://github.com/caddyserver/caddy/issues/2955#issuecomment-570000333"))
276
+ return
277
+ }
278
+
279
+ // construct the new data directory's parent folder
280
+ err = os.MkdirAll(filepath.Dir(newDataDir), 0700)
281
+ if err != nil {
282
+ logger.Error("unable to make new datadirectory - follow link for instructions",
283
+ zap.String("instructions", "https://github.com/caddyserver/caddy/issues/2955#issuecomment-570000333"),
284
+ zap.Error(err))
285
+ return
286
+ }
287
+
288
+ // folder structure is same, so just try to rename (move) it;
289
+ // this fails if the new path is on a separate device
290
+ err = os.Rename(oldDataDir, newDataDir)
291
+ if err != nil {
292
+ logger.Error("new data directory already exists; skipping auto-migration as conservative safety measure - follow link for instructions",
293
+ zap.String("instructions", "https://github.com/caddyserver/caddy/issues/2955#issuecomment-570000333"),
294
+ zap.Error(err))
295
+ }
296
+
297
+ logger.Info("successfully completed one-time migration of data directory",
298
+ zap.String("details", "https://github.com/caddyserver/caddy/issues/2955"))
299
+}
go.sum
+1
@@ -63,6 +63,7 @@ github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA
63
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
64
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
65
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
66
+github.com/cloudflare/cloudflare-go v0.10.2 h1:VBodKICVPnwmDxstcW3biKcDSpFIfS/RELUXsZSBYK4=
67
github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY=
68
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
69
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
storage.go
+108
-14
@@ -20,6 +20,7 @@ import (
20
"runtime"
21
22
"github.com/mholt/certmagic"
23
+ "go.uber.org/zap"
24
)
25
26
// StorageConverter is a type that can convert itself
@@ -31,10 +32,28 @@ type StorageConverter interface {
32
CertMagicStorage() (certmagic.Storage, error)
33
}
34
34
-// homeDir returns the best guess of the current user's home
35
+// HomeDir returns the best guess of the current user's home
36
// directory from environment variables. If unknown, "." (the
36
-// current directory) is returned instead.
37
-func homeDir() string {
37
+// current directory) is returned instead, except GOOS=android,
38
+// which returns "/sdcard".
39
+func HomeDir() string {
40
+ home := homeDirUnsafe()
41
+ if home == "" && runtime.GOOS == "android" {
42
+ home = "/sdcard"
43
+ }
44
+ if home == "" {
45
+ home = "."
46
+ }
47
+ return home
48
+}
49
+
50
+// homeDirUnsafe is a low-level function that returns
51
+// the user's home directory from environment
52
+// variables. Careful: if it cannot be determined, an
53
+// empty string is returned. If not accounting for
54
+// that case, use HomeDir() instead; otherwise you
55
+// may end up using the root of the file system.
56
+func homeDirUnsafe() string {
57
home := os.Getenv("HOME")
58
if home == "" && runtime.GOOS == "windows" {
59
drive := os.Getenv("HOMEDRIVE")
@@ -44,21 +63,96 @@ func homeDir() string {
63
home = os.Getenv("USERPROFILE")
64
}
65
}
47
- if home == "" {
48
- home = "."
66
+ if home == "" && runtime.GOOS == "plan9" {
67
+ home = os.Getenv("home")
68
}
69
return home
70
}
71
53
-// dataDir returns a directory path that is suitable for storage.
54
-// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
55
-func dataDir() string {
56
- baseDir := filepath.Join(homeDir(), ".local", "share")
57
- if xdgData := os.Getenv("XDG_DATA_HOME"); xdgData != "" {
58
- baseDir = xdgData
72
+// AppConfigDir returns the directory where to store user's config.
73
+//
74
+// If XDG_CONFIG_HOME is set, it returns: $XDG_CONFIG_HOME/caddy.
75
+// Otherwise, os.UserConfigDir() is used; if successful, it appends
76
+// "Caddy" (Windows & Mac) or "caddy" (every other OS) to the path.
77
+// If it returns an error, the fallback path "./caddy" is returned.
78
+//
79
+// The config directory is not guaranteed to be different from
80
+// AppDataDir().
81
+//
82
+// Unlike os.UserConfigDir(), this function prefers the
83
+// XDG_CONFIG_HOME env var on all platforms, not just Unix.
84
+//
85
+// Ref: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
86
+func AppConfigDir() string {
87
+ basedir := os.Getenv("XDG_CONFIG_HOME")
88
+ if basedir == "" {
89
+ var err error
90
+ basedir, err = os.UserConfigDir()
91
+ if err != nil {
92
+ Log().Warn("unable to determine directory for user configuration; falling back to current directory", zap.Error(err))
93
+ return "./caddy"
94
+ }
95
+ }
96
+ subdir := "caddy"
97
+ switch runtime.GOOS {
98
+ case "windows", "darwin":
99
+ subdir = "Caddy"
100
+ }
101
+ return filepath.Join(basedir, subdir)
102
+}
103
+
104
+// AppDataDir returns a directory path that is suitable for storing
105
+// application data on disk. It uses the environment for finding the
106
+// best place to store data, and appends a "caddy" or "Caddy" (depending
107
+// on OS and environment) subdirectory.
108
+//
109
+// For a base directory path:
110
+// If XDG_DATA_HOME is set, it returns: $XDG_DATA_HOME/caddy; otherwise,
111
+// on Windows it returns: %AppData%/Caddy,
112
+// on Mac: $HOME/Library/Application Support/Caddy,
113
+// on Plan9: $home/lib/caddy,
114
+// on Android: $HOME/caddy,
115
+// and on everything else: $HOME/.local/share/caddy.
116
+//
117
+// If a data directory cannot be determined, it returns "./caddy"
118
+// (this is not ideal, and the environment should be fixed).
119
+//
120
+// The data directory is not guaranteed to be different from AppConfigDir().
121
+//
122
+// Ref: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
123
+func AppDataDir() string {
124
+ if basedir := os.Getenv("XDG_DATA_HOME"); basedir != "" {
125
+ return filepath.Join(basedir, "caddy")
126
+ }
127
+ switch runtime.GOOS {
128
+ case "windows":
129
+ appData := os.Getenv("AppData")
130
+ if appData != "" {
131
+ return filepath.Join(appData, "Caddy")
132
+ }
133
+ case "darwin":
134
+ home := homeDirUnsafe()
135
+ if home != "" {
136
+ return filepath.Join(home, "Library", "Application Support", "Caddy")
137
+ }
138
+ case "plan9":
139
+ home := homeDirUnsafe()
140
+ if home != "" {
141
+ return filepath.Join(home, "lib", "caddy")
142
+ }
143
+ case "android":
144
+ home := homeDirUnsafe()
145
+ if home != "" {
146
+ return filepath.Join(home, "caddy")
147
+ }
148
+ default:
149
+ home := homeDirUnsafe()
150
+ if home != "" {
151
+ return filepath.Join(home, ".local", "share", "caddy")
152
+ }
153
}
60
- return filepath.Join(baseDir, "caddy")
154
+ return "./caddy"
155
}
156
63
-// TODO: Consider using Go 1.13's os.UserConfigDir() (https://golang.org/pkg/os/#UserConfigDir)
64
-// if we are going to store the last-loaded config anywhere
157
+// ConfigAutosavePath is the default path to which the last config will be persisted.
158
+var ConfigAutosavePath = filepath.Join(AppConfigDir(), "autosave.json")