admin: Support config adapters at /load endpoint

Based on Content-Type
This commit is contained in:
Matthew Holt 2019-08-22 14:52:39 -06:00
parent e34ff21a71
commit afd154119a
No known key found for this signature in database
GPG key ID: 2A349DD577D586A5
2 changed files with 43 additions and 4 deletions

View file

@ -20,6 +20,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"net" "net"
"net/http" "net/http"
@ -29,6 +30,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/caddyserver/caddy/caddyconfig"
"github.com/mholt/certmagic" "github.com/mholt/certmagic"
"github.com/rs/cors" "github.com/rs/cors"
) )
@ -164,12 +166,45 @@ func handleLoadConfig(w http.ResponseWriter, r *http.Request) {
return return
} }
if !strings.Contains(r.Header.Get("Content-Type"), "/json") { var payload io.Reader = r.Body
http.Error(w, "unacceptable Content-Type", http.StatusBadRequest)
return // if the config is formatted other than Caddy's native
// JSON, we need to adapt it before loading it
ct := r.Header.Get("Content-Type")
if !strings.Contains(ct, "/json") {
slashIdx := strings.Index(ct, "/")
if slashIdx < 0 {
http.Error(w, "Malformed Content-Type", http.StatusBadRequest)
return
}
adapterName := ct[slashIdx+1:]
cfgAdapter := caddyconfig.GetAdapter(adapterName)
if cfgAdapter == nil {
http.Error(w, "Unrecognized config adapter: "+adapterName, http.StatusBadRequest)
return
}
body, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, 1024*1024))
if err != nil {
http.Error(w, "Error reading request body: "+err.Error(), http.StatusBadRequest)
return
}
result, warnings, err := cfgAdapter.Adapt(body, nil)
if err != nil {
log.Printf("[ADMIN][ERROR] adapting config from %s: %v", adapterName, err)
http.Error(w, fmt.Sprintf("Adapting config from %s: %v", adapterName, err), http.StatusBadRequest)
return
}
if len(warnings) > 0 {
respBody, err := json.Marshal(warnings)
if err != nil {
log.Printf("[ADMIN][ERROR] marshaling warnings: %v", err)
}
w.Write(respBody)
}
payload = bytes.NewReader(result)
} }
err := Load(r.Body) err := Load(payload)
if err != nil { if err != nil {
log.Printf("[ADMIN][ERROR] loading config: %v", err) log.Printf("[ADMIN][ERROR] loading config: %v", err)
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)

View file

@ -98,6 +98,8 @@ func JSONIndent(val interface{}) ([]byte, error) {
return json.MarshalIndent(val, "", "\t") return json.MarshalIndent(val, "", "\t")
} }
// RegisterAdapter registers a config adapter with the given name.
// This should usually be done at init-time.
func RegisterAdapter(name string, adapter Adapter) error { func RegisterAdapter(name string, adapter Adapter) error {
if _, ok := configAdapters[name]; ok { if _, ok := configAdapters[name]; ok {
return fmt.Errorf("%s: already registered", name) return fmt.Errorf("%s: already registered", name)
@ -106,6 +108,8 @@ func RegisterAdapter(name string, adapter Adapter) error {
return nil return nil
} }
// GetAdapter returns the adapter with the given name,
// or nil if one with that name is not registered.
func GetAdapter(name string) Adapter { func GetAdapter(name string) Adapter {
return configAdapters[name] return configAdapters[name]
} }