Fix tests, add PanicOnError option

- Helper method for internal server errors with consistent logging.
- Add PanicOnError option to panic on internal server errors. This
  makes it easier to traces where the condition was hit in testing.
This commit is contained in:
Konrad Wojas 2020-05-31 21:39:27 +08:00 committed by Alexander Neumann
parent d4cd47e503
commit 63c8797ba3
3 changed files with 55 additions and 46 deletions

View file

@ -27,6 +27,7 @@ type Server struct {
Prometheus bool Prometheus bool
Debug bool Debug bool
MaxRepoSize int64 MaxRepoSize int64
PanicOnError bool
htpasswdFile *HtpasswdFile htpasswdFile *HtpasswdFile
quotaManager *quota.Manager quotaManager *quota.Manager
@ -85,6 +86,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
AppendOnly: s.AppendOnly, AppendOnly: s.AppendOnly,
Debug: s.Debug, Debug: s.Debug,
QuotaManager: s.quotaManager, // may be nil QuotaManager: s.quotaManager, // may be nil
PanicOnError: s.PanicOnError,
} }
if s.Prometheus { if s.Prometheus {
opt.BlobMetricFunc = makeBlobMetricFunc(username, folderPath) opt.BlobMetricFunc = makeBlobMetricFunc(username, folderPath)

View file

@ -211,9 +211,15 @@ func TestResticHandler(t *testing.T) {
// set append-only mode and configure path // set append-only mode and configure path
mux, err := NewHandler(&Server{ mux, err := NewHandler(&Server{
AppendOnly: true, AppendOnly: true,
Path: tempdir, Path: tempdir,
NoAuth: true,
Debug: true,
PanicOnError: true,
}) })
if err != nil {
t.Fatalf("error from NewHandler: %v", err)
}
// create the repo // create the repo
checkRequest(t, mux.ServeHTTP, checkRequest(t, mux.ServeHTTP,

View file

@ -24,6 +24,10 @@ type Options struct {
DirMode os.FileMode DirMode os.FileMode
FileMode os.FileMode FileMode os.FileMode
// If set, we will panic when an internal server error happens. This
// makes it easier to debug such errors.
PanicOnError bool
BlobMetricFunc BlobMetricFunc BlobMetricFunc BlobMetricFunc
QuotaManager *quota.Manager QuotaManager *quota.Manager
} }
@ -285,16 +289,13 @@ func (h *Handler) saveConfig(w http.ResponseWriter, r *http.Request) {
_, err = io.Copy(f, r.Body) _, err = io.Copy(f, r.Body)
if err != nil { if err != nil {
if h.opt.Debug { h.internalServerError(w, err)
log.Print(err)
}
httpDefaultError(w, http.StatusInternalServerError)
return return
} }
err = f.Close() err = f.Close()
if err != nil { if err != nil {
httpDefaultError(w, http.StatusInternalServerError) h.internalServerError(w, err)
return return
} }
@ -321,7 +322,7 @@ func (h *Handler) deleteConfig(w http.ResponseWriter, r *http.Request) {
if os.IsNotExist(err) { if os.IsNotExist(err) {
httpDefaultError(w, http.StatusNotFound) httpDefaultError(w, http.StatusNotFound)
} else { } else {
httpDefaultError(w, http.StatusInternalServerError) h.internalServerError(w, err)
} }
return return
} }
@ -354,7 +355,8 @@ func (h *Handler) listBlobsV1(w http.ResponseWriter, r *http.Request) {
} }
objectType, _ := h.getObject(r.URL.Path) objectType, _ := h.getObject(r.URL.Path)
if objectType == "" { if objectType == "" {
httpDefaultError(w, http.StatusInternalServerError) h.internalServerError(w, fmt.Errorf(
"cannot determine object type: %s", r.URL.Path))
return return
} }
path := h.getSubPath(objectType) path := h.getSubPath(objectType)
@ -391,10 +393,8 @@ func (h *Handler) listBlobsV1(w http.ResponseWriter, r *http.Request) {
data, err := json.Marshal(names) data, err := json.Marshal(names)
if err != nil { if err != nil {
if h.opt.Debug { h.internalServerError(w, fmt.Errorf(
log.Print(err) "cannot determine object type: %s", r.URL.Path))
}
httpDefaultError(w, http.StatusInternalServerError)
return return
} }
@ -417,7 +417,8 @@ func (h *Handler) listBlobsV2(w http.ResponseWriter, r *http.Request) {
objectType, _ := h.getObject(r.URL.Path) objectType, _ := h.getObject(r.URL.Path)
if objectType == "" { if objectType == "" {
httpDefaultError(w, http.StatusInternalServerError) h.internalServerError(w, fmt.Errorf(
"cannot determine object type: %s", r.URL.Path))
return return
} }
path := h.getSubPath(objectType) path := h.getSubPath(objectType)
@ -454,10 +455,7 @@ func (h *Handler) listBlobsV2(w http.ResponseWriter, r *http.Request) {
data, err := json.Marshal(blobs) data, err := json.Marshal(blobs)
if err != nil { if err != nil {
if h.opt.Debug { h.internalServerError(w, err)
log.Print(err)
}
httpDefaultError(w, http.StatusInternalServerError)
return return
} }
@ -472,8 +470,9 @@ func (h *Handler) checkBlob(w http.ResponseWriter, r *http.Request) {
} }
objectType, objectID := h.getObject(r.URL.Path) objectType, objectID := h.getObject(r.URL.Path)
if objectType == "" || objectID != "" { if objectType == "" || objectID == "" {
httpDefaultError(w, http.StatusInternalServerError) h.internalServerError(w, fmt.Errorf(
"cannot determine object type or id: %s", r.URL.Path))
return return
} }
path := h.getObjectPath(objectType, objectID) path := h.getObjectPath(objectType, objectID)
@ -497,8 +496,9 @@ func (h *Handler) getBlob(w http.ResponseWriter, r *http.Request) {
} }
objectType, objectID := h.getObject(r.URL.Path) objectType, objectID := h.getObject(r.URL.Path)
if objectType == "" || objectID != "" { if objectType == "" || objectID == "" {
httpDefaultError(w, http.StatusInternalServerError) h.internalServerError(w, fmt.Errorf(
"cannot determine object type or id: %s", r.URL.Path))
return return
} }
path := h.getObjectPath(objectType, objectID) path := h.getObjectPath(objectType, objectID)
@ -516,7 +516,7 @@ func (h *Handler) getBlob(w http.ResponseWriter, r *http.Request) {
http.ServeContent(wc, r, "", time.Unix(0, 0), file) http.ServeContent(wc, r, "", time.Unix(0, 0), file)
if err = file.Close(); err != nil { if err = file.Close(); err != nil {
httpDefaultError(w, http.StatusInternalServerError) h.internalServerError(w, err)
return return
} }
@ -530,8 +530,9 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) {
} }
objectType, objectID := h.getObject(r.URL.Path) objectType, objectID := h.getObject(r.URL.Path)
if objectType == "" || objectID != "" { if objectType == "" || objectID == "" {
httpDefaultError(w, http.StatusInternalServerError) h.internalServerError(w, fmt.Errorf(
"cannot determine object type or id: %s", r.URL.Path))
return return
} }
path := h.getObjectPath(objectType, objectID) path := h.getObjectPath(objectType, objectID)
@ -552,10 +553,7 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) {
return return
} }
if err != nil { if err != nil {
if h.opt.Debug { h.internalServerError(w, err)
log.Print(err)
}
httpDefaultError(w, http.StatusInternalServerError)
return return
} }
@ -585,20 +583,14 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) {
_ = tf.Close() _ = tf.Close()
_ = os.Remove(path) _ = os.Remove(path)
h.incrementRepoSpaceUsage(-written) h.incrementRepoSpaceUsage(-written)
if h.opt.Debug { h.internalServerError(w, err)
log.Print(err)
}
httpDefaultError(w, http.StatusInternalServerError)
return return
} }
if err := tf.Close(); err != nil { if err := tf.Close(); err != nil {
_ = os.Remove(path) _ = os.Remove(path)
h.incrementRepoSpaceUsage(-written) h.incrementRepoSpaceUsage(-written)
if h.opt.Debug { h.internalServerError(w, err)
log.Print(err)
}
httpDefaultError(w, http.StatusInternalServerError)
return return
} }
@ -612,8 +604,9 @@ func (h *Handler) deleteBlob(w http.ResponseWriter, r *http.Request) {
} }
objectType, objectID := h.getObject(r.URL.Path) objectType, objectID := h.getObject(r.URL.Path)
if objectType == "" || objectID != "" { if objectType == "" || objectID == "" {
httpDefaultError(w, http.StatusInternalServerError) h.internalServerError(w, fmt.Errorf(
"cannot determine object type or id: %s", r.URL.Path))
return return
} }
if h.opt.AppendOnly && objectType != "locks" { if h.opt.AppendOnly && objectType != "locks" {
@ -638,7 +631,7 @@ func (h *Handler) deleteBlob(w http.ResponseWriter, r *http.Request) {
if os.IsNotExist(err) { if os.IsNotExist(err) {
httpDefaultError(w, http.StatusNotFound) httpDefaultError(w, http.StatusNotFound)
} else { } else {
httpDefaultError(w, http.StatusInternalServerError) h.internalServerError(w, err)
} }
return return
} }
@ -661,15 +654,13 @@ func (h *Handler) createRepo(w http.ResponseWriter, r *http.Request) {
log.Printf("Creating repository directories in %s\n", h.path) log.Printf("Creating repository directories in %s\n", h.path)
if err := os.MkdirAll(h.path, h.opt.DirMode); err != nil { if err := os.MkdirAll(h.path, h.opt.DirMode); err != nil {
log.Print(err) h.internalServerError(w, err)
httpDefaultError(w, http.StatusInternalServerError)
return return
} }
for _, d := range ObjectTypes { for _, d := range ObjectTypes {
if err := os.Mkdir(filepath.Join(h.path, d), h.opt.DirMode); err != nil && !os.IsExist(err) { if err := os.Mkdir(filepath.Join(h.path, d), h.opt.DirMode); err != nil && !os.IsExist(err) {
log.Print(err) h.internalServerError(w, err)
httpDefaultError(w, http.StatusInternalServerError)
return return
} }
} }
@ -677,9 +668,19 @@ func (h *Handler) createRepo(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 256; i++ { for i := 0; i < 256; i++ {
dirPath := filepath.Join(h.path, "data", fmt.Sprintf("%02x", i)) dirPath := filepath.Join(h.path, "data", fmt.Sprintf("%02x", i))
if err := os.Mkdir(dirPath, h.opt.DirMode); err != nil && !os.IsExist(err) { if err := os.Mkdir(dirPath, h.opt.DirMode); err != nil && !os.IsExist(err) {
log.Print(err) h.internalServerError(w, err)
httpDefaultError(w, http.StatusInternalServerError)
return return
} }
} }
} }
// internalServerError is called to repot an internal server error.
// The error message will be reported in the server logs. If PanicOnError
// is set, this will panic instead, which makes debugging easier.
func (h *Handler) internalServerError(w http.ResponseWriter, err error) {
log.Printf("ERROR: %v", err)
if h.opt.PanicOnError {
panic(fmt.Sprintf("internal server error: %v", err))
}
httpDefaultError(w, http.StatusInternalServerError)
}