mirror of
https://github.com/containers/podman.git
synced 2025-05-31 15:42:48 +08:00
Add query parameter converters for complex types
* Add converter for URL query parameters of type map[string][]string * Add converter for URL query parameters of type time.Time * Added function to allocate and configure schema.Decoder for API use * Updated API handlers to leverage new converters, and correct handler code for filter type An encoding example for a client using filters: v := map[string][]string{ "dangling": {"true"}, } payload, err := jsoniter.MarshalToString(v) if err != nil { panic(err) } payload = "?filters=" + url.QueryEscape(payload) Signed-off-by: Jhon Honce <jhonce@redhat.com>
This commit is contained in:
50
pkg/api/handlers/decoder.go
Normal file
50
pkg/api/handlers/decoder.go
Normal file
@ -0,0 +1,50 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// NewAPIDecoder returns a configured schema.Decoder
|
||||
func NewAPIDecoder() *schema.Decoder {
|
||||
d := schema.NewDecoder()
|
||||
d.IgnoreUnknownKeys(true)
|
||||
d.RegisterConverter(map[string][]string{}, convertUrlValuesString)
|
||||
d.RegisterConverter(time.Time{}, convertTimeString)
|
||||
return d
|
||||
}
|
||||
|
||||
// On client:
|
||||
// v := map[string][]string{
|
||||
// "dangling": {"true"},
|
||||
// }
|
||||
//
|
||||
// payload, err := jsoniter.MarshalToString(v)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// payload = url.QueryEscape(payload)
|
||||
func convertUrlValuesString(query string) reflect.Value {
|
||||
f := map[string][]string{}
|
||||
|
||||
err := json.Unmarshal([]byte(query), &f)
|
||||
if err != nil {
|
||||
logrus.Infof("convertUrlValuesString: Failed to Unmarshal %s: %s", query, err.Error())
|
||||
}
|
||||
|
||||
return reflect.ValueOf(f)
|
||||
}
|
||||
|
||||
func convertTimeString(query string) reflect.Value {
|
||||
t, err := time.Parse(time.RFC3339, query)
|
||||
if err != nil {
|
||||
logrus.Infof("convertTimeString: Failed to Unmarshal %s: %s", query, err.Error())
|
||||
|
||||
return reflect.ValueOf(time.Time{})
|
||||
}
|
||||
return reflect.ValueOf(t)
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||
"github.com/pkg/errors"
|
||||
@ -11,30 +12,24 @@ import (
|
||||
|
||||
func GetEvents(w http.ResponseWriter, r *http.Request) {
|
||||
query := struct {
|
||||
Since string `json:"since"`
|
||||
Until string `json:"until"`
|
||||
Filters string `json:"filters"`
|
||||
Since time.Time `schema:"since"`
|
||||
Until time.Time `schema:"until"`
|
||||
Filters map[string][]string `schema:"filters"`
|
||||
}{}
|
||||
if err := decodeQuery(r, &query); err != nil {
|
||||
utils.Error(w, "Failed to parse parameters", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
|
||||
}
|
||||
|
||||
var filters = map[string][]string{}
|
||||
if found := hasVar(r, "filters"); found {
|
||||
if err := json.Unmarshal([]byte(query.Filters), &filters); err != nil {
|
||||
utils.BadRequest(w, "filters", query.Filters, err)
|
||||
return
|
||||
var libpodFilters = []string{}
|
||||
if _, found := r.URL.Query()["filters"]; found {
|
||||
for k, v := range query.Filters {
|
||||
libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
|
||||
}
|
||||
}
|
||||
|
||||
var libpodFilters = make([]string, len(filters))
|
||||
for k, v := range filters {
|
||||
libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
|
||||
}
|
||||
|
||||
libpodEvents, err := getRuntime(r).GetEvents(libpodFilters)
|
||||
if err != nil {
|
||||
utils.BadRequest(w, "filters", query.Filters, err)
|
||||
utils.BadRequest(w, "filters", strings.Join(r.URL.Query()["filters"], ", "), err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -62,14 +62,14 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
|
||||
// 200 no error
|
||||
// 500 internal
|
||||
var (
|
||||
dangling bool = true
|
||||
dangling = true
|
||||
err error
|
||||
)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
|
||||
query := struct {
|
||||
filters map[string]string
|
||||
Filters map[string][]string `schema:"filters"`
|
||||
}{
|
||||
// This is where you can override the golang default value for one of fields
|
||||
}
|
||||
@ -79,26 +79,25 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// FIXME This is likely wrong due to it not being a map[string][]string
|
||||
|
||||
// until ts is not supported on podman prune
|
||||
if len(query.filters["until"]) > 0 {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "until is not supported yet"))
|
||||
if v, found := query.Filters["until"]; found {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "until=%s is not supported yet", v))
|
||||
return
|
||||
}
|
||||
// labels are not supported on podman prune
|
||||
if len(query.filters["label"]) > 0 {
|
||||
if _, found := query.Filters["since"]; found {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "labelis not supported yet"))
|
||||
return
|
||||
}
|
||||
|
||||
if len(query.filters["dangling"]) > 0 {
|
||||
dangling, err = strconv.ParseBool(query.filters["dangling"])
|
||||
if v, found := query.Filters["dangling"]; found {
|
||||
dangling, err = strconv.ParseBool(v[0])
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "processing dangling filter"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
idr := []types.ImageDeleteResponseItem{}
|
||||
//
|
||||
// This code needs to be migrated to libpod to work correctly. I could not
|
||||
|
@ -15,10 +15,11 @@ func getVar(r *http.Request, k string) string {
|
||||
return mux.Vars(r)[k]
|
||||
}
|
||||
|
||||
func hasVar(r *http.Request, k string) bool {
|
||||
_, found := mux.Vars(r)[k]
|
||||
return found
|
||||
}
|
||||
// func hasVar(r *http.Request, k string) bool {
|
||||
// _, found := mux.Vars(r)[k]
|
||||
// return found
|
||||
// }
|
||||
|
||||
func getName(r *http.Request) string {
|
||||
return getVar(r, "name")
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package libpod
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -42,12 +43,12 @@ func ImageExists(w http.ResponseWriter, r *http.Request) {
|
||||
func ImageTree(w http.ResponseWriter, r *http.Request) {
|
||||
// tree is a bit of a mess ... logic is in adapter and therefore not callable from here. needs rework
|
||||
|
||||
//name := mux.Vars(r)["name"]
|
||||
//_, layerInfoMap, _, err := s.Runtime.Tree(name)
|
||||
//if err != nil {
|
||||
// name := mux.Vars(r)["name"]
|
||||
// _, layerInfoMap, _, err := s.Runtime.Tree(name)
|
||||
// if err != nil {
|
||||
// Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to find image information for %q", name))
|
||||
// return
|
||||
//}
|
||||
// }
|
||||
// it is not clear to me how to deal with this given all the processing of the image
|
||||
// is in main. we need to discuss how that really should be and return something useful.
|
||||
handlers.UnsupportedHandler(w, r)
|
||||
@ -95,8 +96,8 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
query := struct {
|
||||
All bool `schema:"all"`
|
||||
Filters []string `schema:"filters"`
|
||||
All bool `schema:"all"`
|
||||
Filters map[string][]string `schema:"filters"`
|
||||
}{
|
||||
// override any golang type defaults
|
||||
}
|
||||
@ -106,7 +107,14 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
|
||||
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
|
||||
return
|
||||
}
|
||||
cids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, query.Filters)
|
||||
|
||||
var libpodFilters = []string{}
|
||||
if _, found := r.URL.Query()["filters"]; found {
|
||||
for k, v := range query.Filters {
|
||||
libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
|
||||
}
|
||||
}
|
||||
cids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, libpodFilters)
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
|
||||
return
|
||||
|
@ -108,7 +108,7 @@ func Pods(w http.ResponseWriter, r *http.Request) {
|
||||
)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
query := struct {
|
||||
filters []string `schema:"filters"`
|
||||
Filters map[string][]string `schema:"filters"`
|
||||
}{
|
||||
// override any golang type defaults
|
||||
}
|
||||
@ -117,10 +117,12 @@ func Pods(w http.ResponseWriter, r *http.Request) {
|
||||
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
|
||||
return
|
||||
}
|
||||
if len(query.filters) > 0 {
|
||||
|
||||
if _, found := r.URL.Query()["filters"]; found {
|
||||
utils.Error(w, "filters are not implemented yet", http.StatusInternalServerError, define.ErrNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
pods, err := runtime.GetAllPods()
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
|
||||
@ -164,7 +166,7 @@ func PodStop(w http.ResponseWriter, r *http.Request) {
|
||||
decoder = r.Context().Value("decoder").(*schema.Decoder)
|
||||
)
|
||||
query := struct {
|
||||
timeout int `schema:"t"`
|
||||
Timeout int `schema:"t"`
|
||||
}{
|
||||
// override any golang type defaults
|
||||
}
|
||||
@ -207,8 +209,8 @@ func PodStop(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if query.timeout > 0 {
|
||||
_, stopError = pod.StopWithTimeout(r.Context(), false, query.timeout)
|
||||
if query.Timeout > 0 {
|
||||
_, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout)
|
||||
} else {
|
||||
_, stopError = pod.Stop(r.Context(), false)
|
||||
}
|
||||
|
@ -15,17 +15,18 @@ 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
|
||||
// all bool # all is currently unused
|
||||
Filters map[string][]string `schema:"filters"`
|
||||
// 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 {
|
||||
|
||||
var filters = []string{}
|
||||
if _, found := r.URL.Query()["filters"]; found {
|
||||
filters = append(filters, fmt.Sprintf("reference=%s", ""))
|
||||
}
|
||||
return runtime.ImageRuntime().GetImagesWithFilters(filters)
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/pkg/api/handlers"
|
||||
"github.com/coreos/go-systemd/activation"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/schema"
|
||||
@ -71,7 +72,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
|
||||
ReadTimeout: 20 * time.Second,
|
||||
WriteTimeout: 2 * time.Minute,
|
||||
},
|
||||
Decoder: schema.NewDecoder(),
|
||||
Decoder: handlers.NewAPIDecoder(),
|
||||
Context: nil,
|
||||
Runtime: runtime,
|
||||
Listener: *listener,
|
||||
@ -85,6 +86,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
|
||||
})
|
||||
|
||||
ctx, cancelFn := context.WithCancel(context.Background())
|
||||
server.CancelFunc = cancelFn
|
||||
|
||||
// TODO: Use ConnContext when ported to go 1.13
|
||||
ctx = context.WithValue(ctx, "decoder", server.Decoder)
|
||||
@ -92,9 +94,6 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
|
||||
ctx = context.WithValue(ctx, "shutdownFunc", server.Shutdown)
|
||||
server.Context = ctx
|
||||
|
||||
server.CancelFunc = cancelFn
|
||||
server.Decoder.IgnoreUnknownKeys(true)
|
||||
|
||||
router.NotFoundHandler = http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
// We can track user errors...
|
||||
|
Reference in New Issue
Block a user