@marcel91200 / caddy Mirror · read-only / commits / 984d384d
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

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")