mirror of
https://github.com/containers/podman.git
synced 2025-06-07 07:24:01 +08:00
Merge pull request #4958 from jwhonce/wip/filters
[CI:DOCS] Add query parameter converters for complex types
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
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -11,30 +12,24 @@ import (
|
|||||||
|
|
||||||
func GetEvents(w http.ResponseWriter, r *http.Request) {
|
func GetEvents(w http.ResponseWriter, r *http.Request) {
|
||||||
query := struct {
|
query := struct {
|
||||||
Since string `json:"since"`
|
Since time.Time `schema:"since"`
|
||||||
Until string `json:"until"`
|
Until time.Time `schema:"until"`
|
||||||
Filters string `json:"filters"`
|
Filters map[string][]string `schema:"filters"`
|
||||||
}{}
|
}{}
|
||||||
if err := decodeQuery(r, &query); err != nil {
|
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()))
|
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{}
|
var libpodFilters = []string{}
|
||||||
if found := hasVar(r, "filters"); found {
|
if _, found := r.URL.Query()["filters"]; found {
|
||||||
if err := json.Unmarshal([]byte(query.Filters), &filters); err != nil {
|
for k, v := range query.Filters {
|
||||||
utils.BadRequest(w, "filters", query.Filters, err)
|
libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
libpodEvents, err := getRuntime(r).GetEvents(libpodFilters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.BadRequest(w, "filters", query.Filters, err)
|
utils.BadRequest(w, "filters", strings.Join(r.URL.Query()["filters"], ", "), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,14 +62,14 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 200 no error
|
// 200 no error
|
||||||
// 500 internal
|
// 500 internal
|
||||||
var (
|
var (
|
||||||
dangling bool = true
|
dangling = true
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||||
|
|
||||||
query := struct {
|
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
|
// 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME This is likely wrong due to it not being a map[string][]string
|
|
||||||
|
|
||||||
// until ts is not supported on podman prune
|
// until ts is not supported on podman prune
|
||||||
if len(query.filters["until"]) > 0 {
|
if v, found := query.Filters["until"]; found {
|
||||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "until is not supported yet"))
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "until=%s is not supported yet", v))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// labels are not supported on podman prune
|
// 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"))
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "labelis not supported yet"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(query.filters["dangling"]) > 0 {
|
if v, found := query.Filters["dangling"]; found {
|
||||||
dangling, err = strconv.ParseBool(query.filters["dangling"])
|
dangling, err = strconv.ParseBool(v[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "processing dangling filter"))
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "processing dangling filter"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
idr := []types.ImageDeleteResponseItem{}
|
idr := []types.ImageDeleteResponseItem{}
|
||||||
//
|
//
|
||||||
// This code needs to be migrated to libpod to work correctly. I could not
|
// 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]
|
return mux.Vars(r)[k]
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasVar(r *http.Request, k string) bool {
|
// func hasVar(r *http.Request, k string) bool {
|
||||||
_, found := mux.Vars(r)[k]
|
// _, found := mux.Vars(r)[k]
|
||||||
return found
|
// return found
|
||||||
}
|
// }
|
||||||
|
|
||||||
func getName(r *http.Request) string {
|
func getName(r *http.Request) string {
|
||||||
return getVar(r, "name")
|
return getVar(r, "name")
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package libpod
|
package libpod
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -42,12 +43,12 @@ func ImageExists(w http.ResponseWriter, r *http.Request) {
|
|||||||
func ImageTree(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
|
// tree is a bit of a mess ... logic is in adapter and therefore not callable from here. needs rework
|
||||||
|
|
||||||
//name := mux.Vars(r)["name"]
|
// name := mux.Vars(r)["name"]
|
||||||
//_, layerInfoMap, _, err := s.Runtime.Tree(name)
|
// _, layerInfoMap, _, err := s.Runtime.Tree(name)
|
||||||
//if err != nil {
|
// if err != nil {
|
||||||
// Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to find image information for %q", name))
|
// Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to find image information for %q", name))
|
||||||
// return
|
// return
|
||||||
//}
|
// }
|
||||||
// it is not clear to me how to deal with this given all the processing of the image
|
// 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.
|
// is in main. we need to discuss how that really should be and return something useful.
|
||||||
handlers.UnsupportedHandler(w, r)
|
handlers.UnsupportedHandler(w, r)
|
||||||
@ -95,8 +96,8 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
|
|||||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||||
query := struct {
|
query := struct {
|
||||||
All bool `schema:"all"`
|
All bool `schema:"all"`
|
||||||
Filters []string `schema:"filters"`
|
Filters map[string][]string `schema:"filters"`
|
||||||
}{
|
}{
|
||||||
// override any golang type defaults
|
// 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()))
|
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
|
@ -108,7 +108,7 @@ func Pods(w http.ResponseWriter, r *http.Request) {
|
|||||||
)
|
)
|
||||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||||
query := struct {
|
query := struct {
|
||||||
filters []string `schema:"filters"`
|
Filters map[string][]string `schema:"filters"`
|
||||||
}{
|
}{
|
||||||
// override any golang type defaults
|
// 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()))
|
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
|
||||||
return
|
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)
|
utils.Error(w, "filters are not implemented yet", http.StatusInternalServerError, define.ErrNotImplemented)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pods, err := runtime.GetAllPods()
|
pods, err := runtime.GetAllPods()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
|
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)
|
decoder = r.Context().Value("decoder").(*schema.Decoder)
|
||||||
)
|
)
|
||||||
query := struct {
|
query := struct {
|
||||||
timeout int `schema:"t"`
|
Timeout int `schema:"t"`
|
||||||
}{
|
}{
|
||||||
// override any golang type defaults
|
// override any golang type defaults
|
||||||
}
|
}
|
||||||
@ -207,8 +209,8 @@ func PodStop(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if query.timeout > 0 {
|
if query.Timeout > 0 {
|
||||||
_, stopError = pod.StopWithTimeout(r.Context(), false, query.timeout)
|
_, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout)
|
||||||
} else {
|
} else {
|
||||||
_, stopError = pod.Stop(r.Context(), false)
|
_, 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)
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||||
query := struct {
|
query := struct {
|
||||||
//all bool # all is currently unused
|
// all bool # all is currently unused
|
||||||
filters []string
|
Filters map[string][]string `schema:"filters"`
|
||||||
//digests bool # digests is currently unused
|
// digests bool # digests is currently unused
|
||||||
}{
|
}{
|
||||||
// This is where you can override the golang default value for one of fields
|
// This is where you can override the golang default value for one of fields
|
||||||
}
|
}
|
||||||
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||||
return nil, err
|
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", ""))
|
filters = append(filters, fmt.Sprintf("reference=%s", ""))
|
||||||
}
|
}
|
||||||
return runtime.ImageRuntime().GetImagesWithFilters(filters)
|
return runtime.ImageRuntime().GetImagesWithFilters(filters)
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/pkg/api/handlers"
|
||||||
"github.com/coreos/go-systemd/activation"
|
"github.com/coreos/go-systemd/activation"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/schema"
|
"github.com/gorilla/schema"
|
||||||
@ -71,7 +72,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
|
|||||||
ReadTimeout: 20 * time.Second,
|
ReadTimeout: 20 * time.Second,
|
||||||
WriteTimeout: 2 * time.Minute,
|
WriteTimeout: 2 * time.Minute,
|
||||||
},
|
},
|
||||||
Decoder: schema.NewDecoder(),
|
Decoder: handlers.NewAPIDecoder(),
|
||||||
Context: nil,
|
Context: nil,
|
||||||
Runtime: runtime,
|
Runtime: runtime,
|
||||||
Listener: *listener,
|
Listener: *listener,
|
||||||
@ -85,6 +86,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
|
|||||||
})
|
})
|
||||||
|
|
||||||
ctx, cancelFn := context.WithCancel(context.Background())
|
ctx, cancelFn := context.WithCancel(context.Background())
|
||||||
|
server.CancelFunc = cancelFn
|
||||||
|
|
||||||
// TODO: Use ConnContext when ported to go 1.13
|
// TODO: Use ConnContext when ported to go 1.13
|
||||||
ctx = context.WithValue(ctx, "decoder", server.Decoder)
|
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)
|
ctx = context.WithValue(ctx, "shutdownFunc", server.Shutdown)
|
||||||
server.Context = ctx
|
server.Context = ctx
|
||||||
|
|
||||||
server.CancelFunc = cancelFn
|
|
||||||
server.Decoder.IgnoreUnknownKeys(true)
|
|
||||||
|
|
||||||
router.NotFoundHandler = http.HandlerFunc(
|
router.NotFoundHandler = http.HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
// We can track user errors...
|
// We can track user errors...
|
||||||
|
Reference in New Issue
Block a user