diff --git a/admin.go b/admin.go index 11be4f23..f3337b0c 100644 --- a/admin.go +++ b/admin.go @@ -44,7 +44,7 @@ type AdminConfig struct { // DefaultAdminConfig is the default configuration // for the administration endpoint. var DefaultAdminConfig = &AdminConfig{ - Listen: "localhost:2019", + Listen: DefaultAdminListen, } // StartAdmin starts Caddy's administration endpoint, @@ -192,6 +192,10 @@ func Load(r io.Reader) error { return nil } +// DefaultAdminListen is the address for the admin +// listener, if none is specified at startup. +var DefaultAdminListen = "localhost:2019" + var bufPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) diff --git a/cmd/commands.go b/cmd/commands.go index f98b4019..5e0217a8 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -15,12 +15,16 @@ package caddycmd import ( + "bytes" "crypto/rand" + "encoding/json" "flag" "fmt" + "io" "io/ioutil" "log" "net" + "net/http" "os" "os/exec" "path/filepath" @@ -200,6 +204,59 @@ func cmdStop() (int, error) { return 0, nil } +func cmdReload() (int, error) { + reloadCmd := flag.NewFlagSet("load", flag.ExitOnError) + reloadCmdConfigFlag := reloadCmd.String("config", "", "Configuration file") + reloadCmdAddrFlag := reloadCmd.String("address", "", "Address of the administration listener, if different from config") + reloadCmd.Parse(os.Args[2:]) + + // a configuration is required + if *reloadCmdConfigFlag == "" { + return 1, fmt.Errorf("no configuration to load (use --config)") + } + + // load the configuration file + config, err := ioutil.ReadFile(*reloadCmdConfigFlag) + if err != nil { + return 1, fmt.Errorf("reading config file: %v", err) + } + + // get the address of the admin listener and craft endpoint URL + adminAddr := *reloadCmdAddrFlag + if adminAddr == "" { + var tmpStruct struct { + Admin caddy.AdminConfig `json:"admin"` + } + err = json.Unmarshal(config, &tmpStruct) + if err != nil { + return 1, fmt.Errorf("unmarshaling admin listener address from config: %v", err) + } + adminAddr = tmpStruct.Admin.Listen + } + if adminAddr == "" { + adminAddr = caddy.DefaultAdminListen + } + adminEndpoint := fmt.Sprintf("http://%s/load", adminAddr) + + // send the configuration to the instance + resp, err := http.Post(adminEndpoint, "application/json", bytes.NewReader(config)) + if err != nil { + return 1, fmt.Errorf("sending configuration to instance: %v", err) + } + defer resp.Body.Close() + + // if it didn't work, let the user know + if resp.StatusCode >= 400 { + respBody, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1024*10)) + if err != nil { + return 1, fmt.Errorf("HTTP %d: reading error message: %v", resp.StatusCode, err) + } + return 1, fmt.Errorf("caddy responded with error: HTTP %d: %s", resp.StatusCode, respBody) + } + + return 0, nil +} + func cmdVersion() (int, error) { goModule := caddy.GoModule() if goModule.Sum != "" { diff --git a/cmd/main.go b/cmd/main.go index 217bd743..4691b37a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -51,8 +51,9 @@ type commandFunc func() (int, error) var commands = map[string]commandFunc{ "start": cmdStart, - "stop": cmdStop, "run": cmdRun, + "stop": cmdStop, + "reload": cmdReload, "version": cmdVersion, "list-modules": cmdListModules, "environ": cmdEnviron,