diff options
Diffstat (limited to 'pkg/api/handlers/utils')
-rw-r--r-- | pkg/api/handlers/utils/containers.go | 103 | ||||
-rw-r--r-- | pkg/api/handlers/utils/errors.go | 88 | ||||
-rw-r--r-- | pkg/api/handlers/utils/handler.go | 44 | ||||
-rw-r--r-- | pkg/api/handlers/utils/images.go | 32 |
4 files changed, 267 insertions, 0 deletions
diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go new file mode 100644 index 000000000..64d3d378a --- /dev/null +++ b/pkg/api/handlers/utils/containers.go @@ -0,0 +1,103 @@ +package utils + +import ( + "fmt" + "net/http" + "syscall" + "time" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" + "github.com/gorilla/mux" + "github.com/gorilla/schema" + "github.com/pkg/errors" +) + +func KillContainer(w http.ResponseWriter, r *http.Request) (*libpod.Container, error) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value("decorder").(*schema.Decoder) + query := struct { + Signal syscall.Signal `schema:"signal"` + }{ + Signal: syscall.SIGKILL, + } + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) + return nil, err + } + name := mux.Vars(r)["name"] + con, err := runtime.LookupContainer(name) + if err != nil { + ContainerNotFound(w, name, err) + return nil, err + } + + state, err := con.State() + if err != nil { + InternalServerError(w, err) + return con, err + } + + // If the Container is stopped already, send a 409 + if state == define.ContainerStateStopped || state == define.ContainerStateExited { + Error(w, fmt.Sprintf("Container %s is not running", name), http.StatusConflict, errors.New(fmt.Sprintf("Cannot kill Container %s, it is not running", name))) + return con, err + } + + err = con.Kill(uint(query.Signal)) + if err != nil { + Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "unable to kill Container %s", name)) + } + return con, err +} + +func RemoveContainer(w http.ResponseWriter, r *http.Request, force, vols bool) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + name := mux.Vars(r)["name"] + con, err := runtime.LookupContainer(name) + if err != nil { + ContainerNotFound(w, name, err) + return + } + + if err := runtime.RemoveContainer(r.Context(), con, force, vols); err != nil { + InternalServerError(w, err) + return + } + WriteResponse(w, http.StatusNoContent, "") +} + +func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value("decoder").(*schema.Decoder) + // /{version}/containers/(name)/restart + query := struct { + Interval string `schema:"interval"` + Condition string `schema:"condition"` + }{ + // Override golang default values for types + } + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) + return 0, err + } + + if len(query.Condition) > 0 { + return 0, errors.Errorf("the condition parameter is not supported") + } + + name := mux.Vars(r)["name"] + con, err := runtime.LookupContainer(name) + if err != nil { + ContainerNotFound(w, name, err) + return 0, err + } + if len(query.Interval) > 0 { + d, err := time.ParseDuration(query.Interval) + if err != nil { + Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %s for interval", query.Interval)) + } + return con.WaitWithInterval(d) + } + return con.Wait() +} diff --git a/pkg/api/handlers/utils/errors.go b/pkg/api/handlers/utils/errors.go new file mode 100644 index 000000000..3ec0742bd --- /dev/null +++ b/pkg/api/handlers/utils/errors.go @@ -0,0 +1,88 @@ +package utils + +import ( + "fmt" + "net/http" + + "github.com/containers/libpod/libpod/define" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" +) + +var ( + ErrLinkNotSupport = errors.New("Link is not supported") +) + +// Error formats an API response to an error +// +// apiMessage and code must match the container API, and are sent to client +// err is logged on the system running the podman service +func Error(w http.ResponseWriter, apiMessage string, code int, err error) { + // Log detailed message of what happened to machine running podman service + log.Infof("Request Failed(%s): %s", http.StatusText(code), err.Error()) + em := ErrorModel{ + Because: (errors.Cause(err)).Error(), + Message: err.Error(), + } + WriteJSON(w, code, em) +} + +func VolumeNotFound(w http.ResponseWriter, nameOrId string, err error) { + if errors.Cause(err) != define.ErrNoSuchVolume { + InternalServerError(w, err) + } + msg := fmt.Sprintf("No such volume: %s", nameOrId) + Error(w, msg, http.StatusNotFound, err) +} +func ContainerNotFound(w http.ResponseWriter, nameOrId string, err error) { + if errors.Cause(err) != define.ErrNoSuchCtr { + InternalServerError(w, err) + } + msg := fmt.Sprintf("No such container: %s", nameOrId) + Error(w, msg, http.StatusNotFound, err) +} + +func ImageNotFound(w http.ResponseWriter, nameOrId string, err error) { + if errors.Cause(err) != define.ErrNoSuchImage { + InternalServerError(w, err) + } + msg := fmt.Sprintf("No such image: %s", nameOrId) + Error(w, msg, http.StatusNotFound, err) +} + +func PodNotFound(w http.ResponseWriter, nameOrId string, err error) { + if errors.Cause(err) != define.ErrNoSuchPod { + InternalServerError(w, err) + } + msg := fmt.Sprintf("No such pod: %s", nameOrId) + Error(w, msg, http.StatusNotFound, err) +} + +func ContainerNotRunning(w http.ResponseWriter, containerID string, err error) { + msg := fmt.Sprintf("Container %s is not running", containerID) + Error(w, msg, http.StatusConflict, err) +} + +func InternalServerError(w http.ResponseWriter, err error) { + Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError, err) +} + +func BadRequest(w http.ResponseWriter, key string, value string, err error) { + e := errors.Wrapf(err, "Failed to parse query parameter '%s': %q", key, value) + Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, e) +} + +type ErrorModel struct { + // root cause + Because string `json:"cause"` + // error message + Message string `json:"message"` +} + +func (e ErrorModel) Error() string { + return e.Message +} + +func (e ErrorModel) Cause() error { + return errors.New(e.Because) +} diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go new file mode 100644 index 000000000..0815e6eca --- /dev/null +++ b/pkg/api/handlers/utils/handler.go @@ -0,0 +1,44 @@ +package utils + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os" + + log "github.com/sirupsen/logrus" +) + +// WriteResponse encodes the given value as JSON or string and renders it for http client +func WriteResponse(w http.ResponseWriter, code int, value interface{}) { + switch value.(type) { + case string: + w.Header().Set("Content-Type", "text/plain; charset=us-ascii") + w.WriteHeader(code) + + if _, err := fmt.Fprintln(w, value); err != nil { + log.Errorf("unable to send string response: %q", err) + } + case *os.File: + w.Header().Set("Content-Type", "application/octet; charset=us-ascii") + w.WriteHeader(code) + + if _, err := io.Copy(w, value.(*os.File)); err != nil { + log.Errorf("unable to copy to response: %q", err) + } + default: + WriteJSON(w, code, value) + } +} + +func WriteJSON(w http.ResponseWriter, code int, value interface{}) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + + coder := json.NewEncoder(w) + coder.SetEscapeHTML(true) + if err := coder.Encode(value); err != nil { + log.Errorf("unable to write json: %q", err) + } +} diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go new file mode 100644 index 000000000..9445298ca --- /dev/null +++ b/pkg/api/handlers/utils/images.go @@ -0,0 +1,32 @@ +package utils + +import ( + "fmt" + "net/http" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/image" + "github.com/gorilla/schema" +) + +// GetImages is a common function used to get images for libpod and other compatibility +// mechanisms +func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) { + decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value("runtime").(*libpod.Runtime) + query := struct { + //all bool # all is currently unused + filters []string + //digests bool # digests is currently unused + }{ + // This is where you can override the golang default value for one of fields + } + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + return nil, err + } + filters := query.filters + if len(filters) < 1 { + filters = append(filters, fmt.Sprintf("reference=%s", "")) + } + return runtime.ImageRuntime().GetImagesWithFilters(filters) +} |