@@ -25,10 +25,8 @@ import (
2525 "log"
2626 "net"
2727 "net/http"
28- "net/url"
2928 "os"
3029 "os/exec"
31- "reflect"
3230 "runtime"
3331 "runtime/debug"
3432 "sort"
@@ -570,151 +568,6 @@ func cmdFmt(fl Flags) (int, error) {
570568 return caddy .ExitCodeSuccess , nil
571569}
572570
573- func cmdUpgrade (_ Flags ) (int , error ) {
574- l := caddy .Log ()
575-
576- thisExecPath , err := os .Executable ()
577- if err != nil {
578- return caddy .ExitCodeFailedStartup , fmt .Errorf ("determining current executable path: %v" , err )
579- }
580- thisExecStat , err := os .Stat (thisExecPath )
581- if err != nil {
582- return caddy .ExitCodeFailedStartup , fmt .Errorf ("retrieving current executable permission bits: %v" , err )
583- }
584- l .Info ("this executable will be replaced" , zap .String ("path" , thisExecPath ))
585-
586- // get the list of nonstandard plugins
587- _ , nonstandard , _ , err := getModules ()
588- if err != nil {
589- return caddy .ExitCodeFailedStartup , fmt .Errorf ("unable to enumerate installed plugins: %v" , err )
590- }
591- pluginPkgs := make (map [string ]struct {})
592- for _ , mod := range nonstandard {
593- if mod .goModule .Replace != nil {
594- return caddy .ExitCodeFailedStartup , fmt .Errorf ("cannot auto-upgrade when Go module has been replaced: %s => %s" ,
595- mod .goModule .Path , mod .goModule .Replace .Path )
596- }
597- l .Info ("found non-standard module" ,
598- zap .String ("id" , mod .caddyModuleID ),
599- zap .String ("package" , mod .goModule .Path ))
600- pluginPkgs [mod .goModule .Path ] = struct {}{}
601- }
602-
603- // build the request URL to download this custom build
604- qs := url.Values {
605- "os" : {runtime .GOOS },
606- "arch" : {runtime .GOARCH },
607- }
608- for pkg := range pluginPkgs {
609- qs .Add ("p" , pkg )
610- }
611- urlStr := fmt .Sprintf ("https://caddyserver.com/api/download?%s" , qs .Encode ())
612-
613- // initiate the build
614- l .Info ("requesting build" ,
615- zap .String ("os" , qs .Get ("os" )),
616- zap .String ("arch" , qs .Get ("arch" )),
617- zap .Strings ("packages" , qs ["p" ]))
618- resp , err := http .Get (urlStr )
619- if err != nil {
620- return caddy .ExitCodeFailedStartup , fmt .Errorf ("secure request failed: %v" , err )
621- }
622- defer resp .Body .Close ()
623- if resp .StatusCode >= 400 {
624- var details struct {
625- StatusCode int `json:"status_code"`
626- Error struct {
627- Message string `json:"message"`
628- ID string `json:"id"`
629- } `json:"error"`
630- }
631- err2 := json .NewDecoder (resp .Body ).Decode (& details )
632- if err2 != nil {
633- return caddy .ExitCodeFailedStartup , fmt .Errorf ("download and error decoding failed: HTTP %d: %v" , resp .StatusCode , err2 )
634- }
635- return caddy .ExitCodeFailedStartup , fmt .Errorf ("download failed: HTTP %d: %s (id=%s)" , resp .StatusCode , details .Error .Message , details .Error .ID )
636- }
637-
638- // back up the current binary, in case something goes wrong we can replace it
639- backupExecPath := thisExecPath + ".tmp"
640- l .Info ("build acquired; backing up current executable" ,
641- zap .String ("current_path" , thisExecPath ),
642- zap .String ("backup_path" , backupExecPath ))
643- err = os .Rename (thisExecPath , backupExecPath )
644- if err != nil {
645- return caddy .ExitCodeFailedStartup , fmt .Errorf ("backing up current binary: %v" , err )
646- }
647- defer func () {
648- if err != nil {
649- err2 := os .Rename (backupExecPath , thisExecPath )
650- if err2 != nil {
651- l .Error ("restoring original executable failed; will need to be restored manually" ,
652- zap .String ("backup_path" , backupExecPath ),
653- zap .String ("original_path" , thisExecPath ),
654- zap .Error (err2 ))
655- }
656- }
657- }()
658-
659- // download the file; do this in a closure to close reliably before we execute it
660- writeFile := func () error {
661- destFile , err := os .OpenFile (thisExecPath , os .O_RDWR | os .O_CREATE | os .O_TRUNC , thisExecStat .Mode ())
662- if err != nil {
663- return fmt .Errorf ("unable to open destination file: %v" , err )
664- }
665- defer destFile .Close ()
666-
667- l .Info ("downloading binary" , zap .String ("source" , urlStr ), zap .String ("destination" , thisExecPath ))
668-
669- _ , err = io .Copy (destFile , resp .Body )
670- if err != nil {
671- return fmt .Errorf ("unable to download file: %v" , err )
672- }
673-
674- err = destFile .Sync ()
675- if err != nil {
676- return fmt .Errorf ("syncing downloaded file to device: %v" , err )
677- }
678-
679- return nil
680- }
681- err = writeFile ()
682- if err != nil {
683- return caddy .ExitCodeFailedStartup , err
684- }
685-
686- l .Info ("download successful; displaying new binary details" , zap .String ("location" , thisExecPath ))
687-
688- // use the new binary to print out version and module info
689- fmt .Print ("\n Module versions:\n \n " )
690- cmd := exec .Command (thisExecPath , "list-modules" , "--versions" )
691- cmd .Stdout = os .Stdout
692- cmd .Stderr = os .Stderr
693- err = cmd .Run ()
694- if err != nil {
695- return caddy .ExitCodeFailedStartup , fmt .Errorf ("download succeeded, but unable to execute: %v" , err )
696- }
697- fmt .Println ("\n Version:" )
698- cmd = exec .Command (thisExecPath , "version" )
699- cmd .Stdout = os .Stdout
700- cmd .Stderr = os .Stderr
701- err = cmd .Run ()
702- if err != nil {
703- return caddy .ExitCodeFailedStartup , fmt .Errorf ("download succeeded, but unable to execute: %v" , err )
704- }
705- fmt .Println ()
706-
707- // clean up the backup file
708- err = os .Remove (backupExecPath )
709- if err != nil {
710- return caddy .ExitCodeFailedStartup , fmt .Errorf ("download succeeded, but unable to clean up backup binary: %v" , err )
711- }
712-
713- l .Info ("upgrade successful; please restart any running Caddy instances" , zap .String ("executable" , thisExecPath ))
714-
715- return caddy .ExitCodeSuccess , nil
716- }
717-
718571func cmdHelp (fl Flags ) (int , error ) {
719572 const fullDocs = `Full documentation is available at:
720573https://caddyserver.com/docs/command-line`
@@ -779,56 +632,6 @@ commands:
779632 return caddy .ExitCodeSuccess , nil
780633}
781634
782- func getModules () (standard , nonstandard , unknown []moduleInfo , err error ) {
783- bi , ok := debug .ReadBuildInfo ()
784- if ! ok {
785- err = fmt .Errorf ("no build info" )
786- return
787- }
788-
789- for _ , modID := range caddy .Modules () {
790- modInfo , err := caddy .GetModule (modID )
791- if err != nil {
792- // that's weird, shouldn't happen
793- unknown = append (unknown , moduleInfo {caddyModuleID : modID , err : err })
794- continue
795- }
796-
797- // to get the Caddy plugin's version info, we need to know
798- // the package that the Caddy module's value comes from; we
799- // can use reflection but we need a non-pointer value (I'm
800- // not sure why), and since New() should return a pointer
801- // value, we need to dereference it first
802- iface := interface {}(modInfo .New ())
803- if rv := reflect .ValueOf (iface ); rv .Kind () == reflect .Ptr {
804- iface = reflect .New (reflect .TypeOf (iface ).Elem ()).Elem ().Interface ()
805- }
806- modPkgPath := reflect .TypeOf (iface ).PkgPath ()
807-
808- // now we find the Go module that the Caddy module's package
809- // belongs to; we assume the Caddy module package path will
810- // be prefixed by its Go module path, and we will choose the
811- // longest matching prefix in case there are nested modules
812- var matched * debug.Module
813- for _ , dep := range bi .Deps {
814- if strings .HasPrefix (modPkgPath , dep .Path ) {
815- if matched == nil || len (dep .Path ) > len (matched .Path ) {
816- matched = dep
817- }
818- }
819- }
820-
821- caddyModGoMod := moduleInfo {caddyModuleID : modID , goModule : matched }
822-
823- if strings .HasPrefix (modPkgPath , caddy .ImportPath ) {
824- standard = append (standard , caddyModGoMod )
825- } else {
826- nonstandard = append (nonstandard , caddyModGoMod )
827- }
828- }
829- return
830- }
831-
832635// apiRequest makes an API request to the endpoint adminAddr with the
833636// given HTTP method and request URI. If body is non-nil, it will be
834637// assumed to be Content-Type application/json.
0 commit comments