summaryrefslogtreecommitdiff
path: root/pkg/api/handlers
diff options
context:
space:
mode:
authorJhon Honce <jhonce@redhat.com>2021-01-19 09:16:01 -0700
committerJhon Honce <jhonce@redhat.com>2021-02-02 12:44:08 -0700
commit7e4d696d949f4132c17a9c66c9f24e65b184be4c (patch)
tree4601ecb29e0184944a1928ee55ce9a1f541a71dc /pkg/api/handlers
parentd66a18cb11688060a3ef737dd05758398279f053 (diff)
downloadpodman-7e4d696d949f4132c17a9c66c9f24e65b184be4c.tar.gz
podman-7e4d696d949f4132c17a9c66c9f24e65b184be4c.tar.bz2
podman-7e4d696d949f4132c17a9c66c9f24e65b184be4c.zip
Report StatusConflict on Pod opt partial failures
- When one or more containers in the Pod reports an error on an operation report StatusConflict and report the error(s) - jsoniter type encoding used to marshal error as string using error.Error() - Update test framework to allow setting any flag when creating pods - Fix test_resize() result check Fixes #8865 Signed-off-by: Jhon Honce <jhonce@redhat.com>
Diffstat (limited to 'pkg/api/handlers')
-rw-r--r--pkg/api/handlers/compat/resize.go2
-rw-r--r--pkg/api/handlers/libpod/pods.go90
-rw-r--r--pkg/api/handlers/utils/handler.go47
3 files changed, 97 insertions, 42 deletions
diff --git a/pkg/api/handlers/compat/resize.go b/pkg/api/handlers/compat/resize.go
index cc8c6ef0a..a769ae1b5 100644
--- a/pkg/api/handlers/compat/resize.go
+++ b/pkg/api/handlers/compat/resize.go
@@ -84,5 +84,5 @@ func ResizeTTY(w http.ResponseWriter, r *http.Request) {
// reasons.
status = http.StatusCreated
}
- utils.WriteResponse(w, status, "")
+ w.WriteHeader(status)
}
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index 2409d3a20..2c35dd191 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -139,19 +139,20 @@ func PodStop(w http.ResponseWriter, r *http.Request) {
logrus.Errorf("Error cleaning up pod %s container %s: %v", pod.ID(), id, err)
}
}
- var errs []error //nolint
+
+ report := entities.PodStopReport{Id: pod.ID()}
for id, err := range responses {
- errs = append(errs, errors.Wrapf(err, "error stopping container %s", id))
+ report.Errs = append(report.Errs, errors.Wrapf(err, "error stopping container %s", id))
}
- report := entities.PodStopReport{
- Errs: errs,
- Id: pod.ID(),
+
+ code := http.StatusOK
+ if len(report.Errs) > 0 {
+ code = http.StatusConflict
}
- utils.WriteResponse(w, http.StatusOK, report)
+ utils.WriteResponse(w, code, report)
}
func PodStart(w http.ResponseWriter, r *http.Request) {
- var errs []error //nolint
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
@@ -168,19 +169,23 @@ func PodStart(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusNotModified, "")
return
}
+
responses, err := pod.Start(r.Context())
if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ utils.Error(w, "Something went wrong", http.StatusConflict, err)
return
}
+
+ report := entities.PodStartReport{Id: pod.ID()}
for id, err := range responses {
- errs = append(errs, errors.Wrapf(err, "error starting container %s", id))
+ report.Errs = append(report.Errs, errors.Wrapf(err, "error starting container "+id))
}
- report := entities.PodStartReport{
- Errs: errs,
- Id: pod.ID(),
+
+ code := http.StatusOK
+ if len(report.Errs) > 0 {
+ code = http.StatusConflict
}
- utils.WriteResponse(w, http.StatusOK, report)
+ utils.WriteResponse(w, code, report)
}
func PodDelete(w http.ResponseWriter, r *http.Request) {
@@ -209,14 +214,11 @@ func PodDelete(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- report := entities.PodRmReport{
- Id: pod.ID(),
- }
+ report := entities.PodRmReport{Id: pod.ID()}
utils.WriteResponse(w, http.StatusOK, report)
}
func PodRestart(w http.ResponseWriter, r *http.Request) {
- var errs []error //nolint
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
@@ -229,14 +231,17 @@ func PodRestart(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
+
+ report := entities.PodRestartReport{Id: pod.ID()}
for id, err := range responses {
- errs = append(errs, errors.Wrapf(err, "error restarting container %s", id))
+ report.Errs = append(report.Errs, errors.Wrapf(err, "error restarting container %s", id))
}
- report := entities.PodRestartReport{
- Errs: errs,
- Id: pod.ID(),
+
+ code := http.StatusOK
+ if len(report.Errs) > 0 {
+ code = http.StatusConflict
}
- utils.WriteResponse(w, http.StatusOK, report)
+ utils.WriteResponse(w, code, report)
}
func PodPrune(w http.ResponseWriter, r *http.Request) {
@@ -267,7 +272,6 @@ func PodPruneHelper(r *http.Request) ([]*entities.PodPruneReport, error) {
}
func PodPause(w http.ResponseWriter, r *http.Request) {
- var errs []error //nolint
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
@@ -280,18 +284,20 @@ func PodPause(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
+
+ report := entities.PodPauseReport{Id: pod.ID()}
for id, v := range responses {
- errs = append(errs, errors.Wrapf(v, "error pausing container %s", id))
+ report.Errs = append(report.Errs, errors.Wrapf(v, "error pausing container %s", id))
}
- report := entities.PodPauseReport{
- Errs: errs,
- Id: pod.ID(),
+
+ code := http.StatusOK
+ if len(report.Errs) > 0 {
+ code = http.StatusConflict
}
- utils.WriteResponse(w, http.StatusOK, report)
+ utils.WriteResponse(w, code, report)
}
func PodUnpause(w http.ResponseWriter, r *http.Request) {
- var errs []error //nolint
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
@@ -304,14 +310,17 @@ func PodUnpause(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "failed to pause pod", http.StatusInternalServerError, err)
return
}
+
+ report := entities.PodUnpauseReport{Id: pod.ID()}
for id, v := range responses {
- errs = append(errs, errors.Wrapf(v, "error unpausing container %s", id))
+ report.Errs = append(report.Errs, errors.Wrapf(v, "error unpausing container %s", id))
}
- report := entities.PodUnpauseReport{
- Errs: errs,
- Id: pod.ID(),
+
+ code := http.StatusOK
+ if len(report.Errs) > 0 {
+ code = http.StatusConflict
}
- utils.WriteResponse(w, http.StatusOK, &report)
+ utils.WriteResponse(w, code, &report)
}
func PodTop(w http.ResponseWriter, r *http.Request) {
@@ -361,7 +370,6 @@ func PodKill(w http.ResponseWriter, r *http.Request) {
runtime = r.Context().Value("runtime").(*libpod.Runtime)
decoder = r.Context().Value("decoder").(*schema.Decoder)
signal = "SIGKILL"
- errs []error //nolint
)
query := struct {
Signal string `schema:"signal"`
@@ -413,16 +421,18 @@ func PodKill(w http.ResponseWriter, r *http.Request) {
return
}
+ report := &entities.PodKillReport{Id: pod.ID()}
for _, v := range responses {
if v != nil {
- errs = append(errs, v)
+ report.Errs = append(report.Errs, v)
}
}
- report := &entities.PodKillReport{
- Errs: errs,
- Id: pod.ID(),
+
+ code := http.StatusOK
+ if len(report.Errs) > 0 {
+ code = http.StatusConflict
}
- utils.WriteResponse(w, http.StatusOK, report)
+ utils.WriteResponse(w, code, report)
}
func PodExists(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go
index 517dccad0..ebbe7f24f 100644
--- a/pkg/api/handlers/utils/handler.go
+++ b/pkg/api/handlers/utils/handler.go
@@ -1,16 +1,17 @@
package utils
import (
- "encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
+ "unsafe"
"github.com/blang/semver"
"github.com/gorilla/mux"
+ jsoniter "github.com/json-iterator/go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -144,6 +145,50 @@ func WriteResponse(w http.ResponseWriter, code int, value interface{}) {
}
}
+func init() {
+ jsoniter.RegisterTypeEncoderFunc("error", MarshalErrorJSON, MarshalErrorJSONIsEmpty)
+ jsoniter.RegisterTypeEncoderFunc("[]error", MarshalErrorSliceJSON, MarshalErrorSliceJSONIsEmpty)
+}
+
+var json = jsoniter.ConfigCompatibleWithStandardLibrary
+
+// MarshalErrorJSON writes error to stream as string
+func MarshalErrorJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
+ p := *((*error)(ptr))
+ if p == nil {
+ stream.WriteNil()
+ } else {
+ stream.WriteString(p.Error())
+ }
+}
+
+// MarshalErrorSliceJSON writes []error to stream as []string JSON blob
+func MarshalErrorSliceJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
+ a := *((*[]error)(ptr))
+ switch {
+ case len(a) == 0:
+ stream.WriteNil()
+ default:
+ stream.WriteArrayStart()
+ for i, e := range a {
+ if i > 0 {
+ stream.WriteMore()
+ }
+ stream.WriteString(e.Error())
+ }
+ stream.WriteArrayEnd()
+ }
+}
+
+func MarshalErrorJSONIsEmpty(_ unsafe.Pointer) bool {
+ return false
+}
+
+func MarshalErrorSliceJSONIsEmpty(_ unsafe.Pointer) bool {
+ return false
+}
+
+// WriteJSON writes an interface value encoded as JSON to w
func WriteJSON(w http.ResponseWriter, code int, value interface{}) {
// FIXME: we don't need to write the header in all/some circumstances.
w.Header().Set("Content-Type", "application/json")