diff options
Diffstat (limited to 'pkg/api/handlers/utils')
-rw-r--r-- | pkg/api/handlers/utils/containers.go | 118 | ||||
-rw-r--r-- | pkg/api/handlers/utils/errors.go | 11 | ||||
-rw-r--r-- | pkg/api/handlers/utils/handler.go | 26 | ||||
-rw-r--r-- | pkg/api/handlers/utils/images.go | 29 | ||||
-rw-r--r-- | pkg/api/handlers/utils/pods.go | 84 |
5 files changed, 191 insertions, 77 deletions
diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go index 2c986db3a..bbe4cee3c 100644 --- a/pkg/api/handlers/utils/containers.go +++ b/pkg/api/handlers/utils/containers.go @@ -1,77 +1,33 @@ package utils import ( - "fmt" + "context" "net/http" - "syscall" "time" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" - "github.com/gorilla/mux" + createconfig "github.com/containers/libpod/pkg/spec" "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, "") +// ContainerCreateResponse is the response struct for creating a container +type ContainerCreateResponse struct { + // ID of the container created + ID string `json:"Id"` + // Warnings during container creation + Warnings []string `json:"Warnings"` } func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) { + var ( + err error + interval time.Duration + ) 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"` @@ -82,25 +38,34 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) { 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 { - UnSupportedParameter("condition") + if _, found := r.URL.Query()["interval"]; found { + interval, err = time.ParseDuration(query.Interval) + if err != nil { + InternalServerError(w, err) + return 0, err + } + } else { + interval, err = time.ParseDuration("250ms") + if err != nil { + InternalServerError(w, err) + return 0, err + } } - - name := mux.Vars(r)["name"] + condition := define.ContainerStateStopped + if _, found := r.URL.Query()["condition"]; found { + condition, err = define.StringToContainerStatus(query.Condition) + if err != nil { + InternalServerError(w, err) + return 0, err + } + } + name := GetName(r) 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() + return con.WaitForConditionWithInterval(interval, condition) } // GenerateFilterFuncsFromMap is used to generate un-executed functions that can be used to filter @@ -120,3 +85,18 @@ func GenerateFilterFuncsFromMap(r *libpod.Runtime, filters map[string][]string) } return filterFuncs, nil } + +func CreateContainer(ctx context.Context, w http.ResponseWriter, runtime *libpod.Runtime, cc *createconfig.CreateConfig) { + var pod *libpod.Pod + ctr, err := shared.CreateContainerFromCreateConfig(runtime, cc, ctx, pod) + if err != nil { + Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "CreateContainerFromCreateConfig()")) + return + } + + response := ContainerCreateResponse{ + ID: ctr.ID(), + Warnings: []string{}} + + WriteResponse(w, http.StatusCreated, response) +} diff --git a/pkg/api/handlers/utils/errors.go b/pkg/api/handlers/utils/errors.go index 9d2081cd8..8d499f40b 100644 --- a/pkg/api/handlers/utils/errors.go +++ b/pkg/api/handlers/utils/errors.go @@ -21,8 +21,9 @@ 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(), + Because: (errors.Cause(err)).Error(), + Message: err.Error(), + ResponseCode: code, } WriteJSON(w, code, em) } @@ -79,6 +80,8 @@ type ErrorModel struct { // human error message, formatted for a human to read // example: human error message Message string `json:"message"` + // http response code + ResponseCode int `json:"response"` } func (e ErrorModel) Error() string { @@ -89,6 +92,10 @@ func (e ErrorModel) Cause() error { return errors.New(e.Because) } +func (e ErrorModel) Code() int { + return e.ResponseCode +} + // UnsupportedParameter logs a given param by its string name as not supported. func UnSupportedParameter(param string) { log.Infof("API parameter %q: not supported", param) diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go index f2ce26f1a..32b8c5b0a 100644 --- a/pkg/api/handlers/utils/handler.go +++ b/pkg/api/handlers/utils/handler.go @@ -5,9 +5,12 @@ import ( "fmt" "io" "net/http" + "net/url" "os" "strings" + "github.com/gorilla/mux" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -20,6 +23,14 @@ func IsLibpodRequest(r *http.Request) bool { // WriteResponse encodes the given value as JSON or string and renders it for http client func WriteResponse(w http.ResponseWriter, code int, value interface{}) { + // RFC2616 explicitly states that the following status codes "MUST NOT + // include a message-body": + switch code { + case http.StatusNoContent, http.StatusNotModified: // 204, 304 + w.WriteHeader(code) + return + } + switch v := value.(type) { case string: w.Header().Set("Content-Type", "text/plain; charset=us-ascii") @@ -59,3 +70,18 @@ func FilterMapToString(filters map[string][]string) (string, error) { } return string(f), nil } + +func getVar(r *http.Request, k string) string { + val := mux.Vars(r)[k] + safeVal, err := url.PathUnescape(val) + if err != nil { + logrus.Error(errors.Wrapf(err, "failed to unescape mux key %s, value %s", k, val)) + return val + } + return safeVal +} + +// GetName extracts the name from the mux +func GetName(r *http.Request) string { + return getVar(r, "name") +} diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go index a0d340471..696d5f745 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -15,19 +15,36 @@ 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 + All bool Filters map[string][]string `schema:"filters"` - // digests bool # digests is currently unused + Digests bool }{ // This is where you can override the golang default value for one of fields } + // TODO I think all is implemented with a filter? + if err := decoder.Decode(&query, r.URL.Query()); err != nil { return nil, err } - var filters = []string{} - if _, found := r.URL.Query()["filters"]; found { - filters = append(filters, fmt.Sprintf("reference=%s", "")) + if _, found := r.URL.Query()["digests"]; found && query.Digests { + UnSupportedParameter("digests") + } + + if len(query.Filters) > 0 { + for k, v := range query.Filters { + for _, val := range v { + filters = append(filters, fmt.Sprintf("%s=%s", k, val)) + } + } + return runtime.ImageRuntime().GetImagesWithFilters(filters) + } else { + return runtime.ImageRuntime().GetImages() } - return runtime.ImageRuntime().GetImagesWithFilters(filters) + +} + +func GetImage(r *http.Request, name string) (*image.Image, error) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + return runtime.ImageRuntime().NewFromLocal(name) } diff --git a/pkg/api/handlers/utils/pods.go b/pkg/api/handlers/utils/pods.go new file mode 100644 index 000000000..79d1a5090 --- /dev/null +++ b/pkg/api/handlers/utils/pods.go @@ -0,0 +1,84 @@ +package utils + +import ( + "fmt" + "net/http" + + "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/gorilla/schema" +) + +func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport, error) { + var ( + lps []*entities.ListPodsReport + pods []*libpod.Pod + podErr error + ) + runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value("decoder").(*schema.Decoder) + + query := struct { + All bool + Filters map[string][]string `schema:"filters"` + Digests bool + }{} + + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + return nil, err + } + var filters = []string{} + if _, found := r.URL.Query()["digests"]; found && query.Digests { + UnSupportedParameter("digests") + } + + if len(query.Filters) > 0 { + for k, v := range query.Filters { + for _, val := range v { + filters = append(filters, fmt.Sprintf("%s=%s", k, val)) + } + } + filterFuncs, err := shared.GenerateFilterFunction(runtime, filters) + if err != nil { + return nil, err + } + pods, podErr = shared.FilterAllPodsWithFilterFunc(runtime, filterFuncs...) + } else { + pods, podErr = runtime.GetAllPods() + } + if podErr != nil { + return nil, podErr + } + for _, pod := range pods { + status, err := pod.GetPodStatus() + if err != nil { + return nil, err + } + ctrs, err := pod.AllContainers() + if err != nil { + return nil, err + } + lp := entities.ListPodsReport{ + Cgroup: pod.CgroupParent(), + Created: pod.CreatedTime(), + Id: pod.ID(), + Name: pod.Name(), + Namespace: pod.Namespace(), + Status: status, + } + for _, ctr := range ctrs { + state, err := ctr.State() + if err != nil { + return nil, err + } + lp.Containers = append(lp.Containers, &entities.ListPodContainer{ + Id: ctr.ID(), + Names: ctr.Name(), + Status: state.String(), + }) + } + lps = append(lps, &lp) + } + return lps, nil +} |