@marcel91200 / caddy Mirror · read-only / commits / 749e55c7
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, 01:30 UTC

caddycmd: Add `--keep-backup` to upgrade commands (#4387)

* caddycmd: Add `--skip-cleanup` to upgrade commands This is a partial fix for https://github.com/caddyserver/caddy/issues/4057, making it possible to retain the old build of Caddy, in case something went wrong. * caddycmd: Fix duplicate error message The error message "download succeeded, but unable to execute" was repeated, because it was both in the `listModules`/`showVersion` functions and in the calling `upgradeBuild` function. Oversight when this was refactored. * caddycmd: Implement fix for performing cleanup on Windows Without this, the cleanup operation would fail with an error message like this: upgrade: download succeeded, but unable to clean up backup binary: remove C:\caddy\caddy.exe.tmp: Access is denied. * caddycmd: Rename to `--keep-backup`, simplify build constraints

Francis Lavoie committed Nov 8, 2021 at 13:35 UTC 749e55c738bf702b5f1265f85018c450a7b4e3b4
4 files changed +97 -19
cmd/commands.go
+15
@@ -296,6 +296,11 @@ is always printed to stdout.`,
296 Long: `
297 Downloads an updated Caddy binary with the same modules/plugins at the
298 latest versions. EXPERIMENTAL: May be changed or removed.`,
299 + Flags: func() *flag.FlagSet {
300 + fs := flag.NewFlagSet("upgrade", flag.ExitOnError)
301 + fs.Bool("keep-backup", false, "Keep the backed up binary, instead of deleting it")
302 + return fs
303 + }(),
304 })
305
306 RegisterCommand(Command{
@@ -308,6 +313,11 @@ Downloads an updated Caddy binary with the specified packages (module/plugin)
313 added. Retains existing packages. Returns an error if the any of packages are
314 already included. EXPERIMENTAL: May be changed or removed.
315 `,
316 + Flags: func() *flag.FlagSet {
317 + fs := flag.NewFlagSet("add-package", flag.ExitOnError)
318 + fs.Bool("keep-backup", false, "Keep the backed up binary, instead of deleting it")
319 + return fs
320 + }(),
321 })
322
323 RegisterCommand(Command{
@@ -320,6 +330,11 @@ Downloads an updated Caddy binaries without the specified packages (module/plugi
330 Returns an error if any of the packages are not included.
331 EXPERIMENTAL: May be changed or removed.
332 `,
333 + Flags: func() *flag.FlagSet {
334 + fs := flag.NewFlagSet("remove-package", flag.ExitOnError)
335 + fs.Bool("keep-backup", false, "Keep the backed up binary, instead of deleting it")
336 + return fs
337 + }(),
338 })
339
340 }
cmd/packagesfuncs.go
+16 -19
@@ -31,7 +31,7 @@ import (
31 "go.uber.org/zap"
32 )
33
34 -func cmdUpgrade(_ Flags) (int, error) {
34 +func cmdUpgrade(fl Flags) (int, error) {
35 _, nonstandard, _, err := getModules()
36 if err != nil {
37 return caddy.ExitCodeFailedStartup, fmt.Errorf("unable to enumerate installed plugins: %v", err)
@@ -41,7 +41,7 @@ func cmdUpgrade(_ Flags) (int, error) {
41 return caddy.ExitCodeFailedStartup, err
42 }
43
44 - return upgradeBuild(pluginPkgs)
44 + return upgradeBuild(pluginPkgs, fl)
45 }
46
47 func cmdAddPackage(fl Flags) (int, error) {
@@ -64,7 +64,7 @@ func cmdAddPackage(fl Flags) (int, error) {
64 pluginPkgs[arg] = struct{}{}
65 }
66
67 - return upgradeBuild(pluginPkgs)
67 + return upgradeBuild(pluginPkgs, fl)
68 }
69
70 func cmdRemovePackage(fl Flags) (int, error) {
@@ -88,10 +88,10 @@ func cmdRemovePackage(fl Flags) (int, error) {
88 delete(pluginPkgs, arg)
89 }
90
91 - return upgradeBuild(pluginPkgs)
91 + return upgradeBuild(pluginPkgs, fl)
92 }
93
94 -func upgradeBuild(pluginPkgs map[string]struct{}) (int, error) {
94 +func upgradeBuild(pluginPkgs map[string]struct{}, fl Flags) (int, error) {
95 l := caddy.Log()
96
97 thisExecPath, err := os.Executable()
@@ -152,18 +152,23 @@ func upgradeBuild(pluginPkgs map[string]struct{}) (int, error) {
152 // use the new binary to print out version and module info
153 fmt.Print("\nModule versions:\n\n")
154 if err = listModules(thisExecPath); err != nil {
155 - return caddy.ExitCodeFailedStartup, fmt.Errorf("download succeeded, but unable to execute: %v", err)
155 + return caddy.ExitCodeFailedStartup, fmt.Errorf("download succeeded, but unable to execute 'caddy list-modules': %v", err)
156 }
157 fmt.Println("\nVersion:")
158 if err = showVersion(thisExecPath); err != nil {
159 - return caddy.ExitCodeFailedStartup, fmt.Errorf("download succeeded, but unable to execute: %v", err)
159 + return caddy.ExitCodeFailedStartup, fmt.Errorf("download succeeded, but unable to execute 'caddy version': %v", err)
160 }
161 fmt.Println()
162
163 // clean up the backup file
164 - if err = os.Remove(backupExecPath); err != nil {
165 - return caddy.ExitCodeFailedStartup, fmt.Errorf("download succeeded, but unable to clean up backup binary: %v", err)
164 + if !fl.Bool("keep-backup") {
165 + if err = removeCaddyBinary(backupExecPath); err != nil {
166 + return caddy.ExitCodeFailedStartup, fmt.Errorf("download succeeded, but unable to clean up backup binary: %v", err)
167 + }
168 + } else {
169 + l.Info("skipped cleaning up the backup file", zap.String("backup_path", backupExecPath))
170 }
171 +
172 l.Info("upgrade successful; please restart any running Caddy instances", zap.String("executable", thisExecPath))
173
174 return caddy.ExitCodeSuccess, nil
@@ -223,22 +228,14 @@ func listModules(path string) error {
228 cmd := exec.Command(path, "list-modules", "--versions", "--skip-standard")
229 cmd.Stdout = os.Stdout
230 cmd.Stderr = os.Stderr
226 - err := cmd.Run()
227 - if err != nil {
228 - return fmt.Errorf("download succeeded, but unable to execute: %v", err)
229 - }
230 - return nil
231 + return cmd.Run()
232 }
233
234 func showVersion(path string) error {
235 cmd := exec.Command(path, "version")
236 cmd.Stdout = os.Stdout
237 cmd.Stderr = os.Stderr
237 - err := cmd.Run()
238 - if err != nil {
239 - return fmt.Errorf("download succeeded, but unable to execute: %v", err)
240 - }
241 - return nil
238 + return cmd.Run()
239 }
240
241 func downloadBuild(qs url.Values) (*http.Response, error) {
cmd/removebinary.go new
+30
@@ -0,0 +1,30 @@
1 +// Copyright 2015 Matthew Holt and The Caddy Authors
2 +//
3 +// Licensed under the Apache License, Version 2.0 (the "License");
4 +// you may not use this file except in compliance with the License.
5 +// You may obtain a copy of the License at
6 +//
7 +// http://www.apache.org/licenses/LICENSE-2.0
8 +//
9 +// Unless required by applicable law or agreed to in writing, software
10 +// distributed under the License is distributed on an "AS IS" BASIS,
11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 +// See the License for the specific language governing permissions and
13 +// limitations under the License.
14 +
15 +//go:build !windows
16 +// +build !windows
17 +
18 +package caddycmd
19 +
20 +import (
21 + "os"
22 +)
23 +
24 +// removeCaddyBinary removes the Caddy binary at the given path.
25 +//
26 +// On any non-Windows OS, this simply calls os.Remove, since they should
27 +// probably not exhibit any issue with processes deleting themselves.
28 +func removeCaddyBinary(path string) error {
29 + return os.Remove(path)
30 +}
cmd/removebinary_windows.go new
+36
@@ -0,0 +1,36 @@
1 +// Copyright 2015 Matthew Holt and The Caddy Authors
2 +//
3 +// Licensed under the Apache License, Version 2.0 (the "License");
4 +// you may not use this file except in compliance with the License.
5 +// You may obtain a copy of the License at
6 +//
7 +// http://www.apache.org/licenses/LICENSE-2.0
8 +//
9 +// Unless required by applicable law or agreed to in writing, software
10 +// distributed under the License is distributed on an "AS IS" BASIS,
11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 +// See the License for the specific language governing permissions and
13 +// limitations under the License.
14 +
15 +package caddycmd
16 +
17 +import (
18 + "os"
19 + "path/filepath"
20 + "syscall"
21 +)
22 +
23 +// removeCaddyBinary removes the Caddy binary at the given path.
24 +//
25 +// On Windows, this uses a syscall to indirectly remove the file,
26 +// because otherwise we get an "Access is denied." error when trying
27 +// to delete the binary while Caddy is still running and performing
28 +// the upgrade. "cmd.exe /C" executes a command specified by the
29 +// following arguments, i.e. "del" which will run as a separate process,
30 +// which avoids the "Access is denied." error.
31 +func removeCaddyBinary(path string) error {
32 + var sI syscall.StartupInfo
33 + var pI syscall.ProcessInformation
34 + argv := syscall.StringToUTF16Ptr(filepath.Join(os.Getenv("windir"), "system32", "cmd.exe") + " /C del " + path)
35 + return syscall.CreateProcess(nil, argv, nil, nil, true, 0, nil, nil, &sI, &pI)
36 +}