diff options
Diffstat (limited to 'pkg/api')
22 files changed, 311 insertions, 229 deletions
diff --git a/pkg/api/handlers/generic/containers_create.go b/pkg/api/handlers/containers_create.go index edefd5757..4781b23bc 100644 --- a/pkg/api/handlers/generic/containers_create.go +++ b/pkg/api/handlers/containers_create.go @@ -1,4 +1,4 @@ -package generic +package handlers import ( "encoding/json" @@ -10,7 +10,6 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" image2 "github.com/containers/libpod/libpod/image" - "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" "github.com/containers/libpod/pkg/namespaces" createconfig "github.com/containers/libpod/pkg/spec" @@ -25,7 +24,7 @@ import ( func CreateContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) - input := handlers.CreateContainerConfig{} + input := CreateContainerConfig{} query := struct { Name string `schema:"name"` }{ @@ -74,13 +73,13 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { } response := ContainerCreateResponse{ - Id: ctr.ID(), + ID: ctr.ID(), Warnings: []string{}} utils.WriteResponse(w, http.StatusCreated, response) } -func makeCreateConfig(input handlers.CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) { +func makeCreateConfig(input CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) { var ( err error init bool diff --git a/pkg/api/handlers/decoder.go b/pkg/api/handlers/decoder.go new file mode 100644 index 000000000..890d77ecc --- /dev/null +++ b/pkg/api/handlers/decoder.go @@ -0,0 +1,91 @@ +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 { + _ = ParseDateTime + + 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) +} + +// isZero() can be used to determine if parsing failed. +func convertTimeString(query string) reflect.Value { + var ( + err error + t time.Time + ) + + for _, f := range []string{ + time.UnixDate, + time.ANSIC, + time.RFC1123, + time.RFC1123Z, + time.RFC3339, + time.RFC3339Nano, + time.RFC822, + time.RFC822Z, + time.RFC850, + time.RubyDate, + time.Stamp, + time.StampMicro, + time.StampMilli, + time.StampNano, + } { + t, err = time.Parse(f, query) + if err == nil { + return reflect.ValueOf(t) + } + + if _, isParseError := err.(*time.ParseError); isParseError { + // Try next format + continue + } else { + break + } + } + + // We've exhausted all formats, or something bad happened + if err != nil { + logrus.Infof("convertTimeString: Failed to parse %s: %s", query, err.Error()) + } + return reflect.ValueOf(time.Time{}) +} + +// ParseDateTime is a helper function to aid in parsing different Time/Date formats +// isZero() can be used to determine if parsing failed. +func ParseDateTime(query string) time.Time { + return convertTimeString(query).Interface().(time.Time) +} diff --git a/pkg/api/handlers/events.go b/pkg/api/handlers/events.go index 900efa3da..44bf35254 100644 --- a/pkg/api/handlers/events.go +++ b/pkg/api/handlers/events.go @@ -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 } diff --git a/pkg/api/handlers/generic/config.go b/pkg/api/handlers/generic/config.go deleted file mode 100644 index f715d25eb..000000000 --- a/pkg/api/handlers/generic/config.go +++ /dev/null @@ -1,9 +0,0 @@ -package generic - -// 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"` -} diff --git a/pkg/api/handlers/generic/images.go b/pkg/api/handlers/generic/images.go index 395f64064..ba1cf47b4 100644 --- a/pkg/api/handlers/generic/images.go +++ b/pkg/api/handlers/generic/images.go @@ -6,7 +6,6 @@ import ( "io/ioutil" "net/http" "os" - "strconv" "strings" "github.com/containers/buildah" @@ -16,12 +15,10 @@ import ( "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" "github.com/containers/libpod/pkg/util" - "github.com/containers/storage" "github.com/docker/docker/api/types" "github.com/gorilla/mux" "github.com/gorilla/schema" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) func ExportImage(w http.ResponseWriter, r *http.Request) { @@ -59,17 +56,14 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { } func PruneImages(w http.ResponseWriter, r *http.Request) { - // 200 no error - // 500 internal var ( - dangling bool = true - err error + filters []string ) 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,61 +73,24 @@ 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")) - return - } - // labels are not supported on podman prune - if len(query.filters["label"]) > 0 { - 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 err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "processing dangling filter")) - return + idr := []types.ImageDeleteResponseItem{} + for k, v := range query.Filters { + for _, val := range v { + filters = append(filters, fmt.Sprintf("%s=%s", k, val)) } } - idr := []types.ImageDeleteResponseItem{} - // - // This code needs to be migrated to libpod to work correctly. I could not - // work my around the information docker needs with the existing prune in libpod. - // - pruneImages, err := runtime.ImageRuntime().GetPruneImages(!dangling, []image2.ImageFilter{}) + pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), false, filters) if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to get images to prune")) + utils.InternalServerError(w, err) return } - for _, p := range pruneImages { - repotags, err := p.RepoTags() - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to get repotags for image")) - return - } - if err := p.Remove(r.Context(), true); err != nil { - if errors.Cause(err) == storage.ErrImageUsedByContainer { - logrus.Warnf("Failed to prune image %s as it is in use: %v", p.ID(), err) - continue - } - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to prune image")) - return - } - // newimageevent is not export therefore we cannot record the event. this will be fixed - // when the prune is fixed in libpod - // defer p.newImageEvent(events.Prune) - response := types.ImageDeleteResponseItem{ - Deleted: fmt.Sprintf("sha256:%s", p.ID()), // I ack this is not ideal - } - if len(repotags) > 0 { - response.Untagged = repotags[0] - } - idr = append(idr, response) + for _, p := range pruneCids { + idr = append(idr, types.ImageDeleteResponseItem{ + Deleted: p, + }) } + + //FIXME/TODO to do this exacty correct, pruneimages needs to return idrs and space-reclaimed, then we are golden ipr := types.ImagesPruneReport{ ImagesDeleted: idr, SpaceReclaimed: 1, // TODO we cannot supply this right now @@ -343,8 +300,6 @@ func GetImage(w http.ResponseWriter, r *http.Request) { } func GetImages(w http.ResponseWriter, r *http.Request) { - // 200 ok - // 500 internal images, err := utils.GetImages(w, r) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images")) diff --git a/pkg/api/handlers/generic/ping.go b/pkg/api/handlers/generic/ping.go index 44a67d53f..00afd86bc 100644 --- a/pkg/api/handlers/generic/ping.go +++ b/pkg/api/handlers/generic/ping.go @@ -3,6 +3,8 @@ package generic import ( "fmt" "net/http" + + "github.com/containers/libpod/pkg/api/handlers" ) func PingGET(w http.ResponseWriter, _ *http.Request) { @@ -16,7 +18,7 @@ func PingHEAD(w http.ResponseWriter, _ *http.Request) { } func setHeaders(w http.ResponseWriter) { - w.Header().Set("API-Version", DefaultApiVersion) + w.Header().Set("API-Version", handlers.DefaultApiVersion) w.Header().Set("BuildKit-Version", "") w.Header().Set("Docker-Experimental", "true") w.Header().Set("Cache-Control", "no-cache") diff --git a/pkg/api/handlers/generic/swagger.go b/pkg/api/handlers/generic/swagger.go index 27e1fc18d..bfe527c41 100644 --- a/pkg/api/handlers/generic/swagger.go +++ b/pkg/api/handlers/generic/swagger.go @@ -1,11 +1,13 @@ package generic +import "github.com/containers/libpod/pkg/api/handlers" + // Create container // swagger:response ContainerCreateResponse type swagCtrCreateResponse struct { // in:body Body struct { - ContainerCreateResponse + handlers.ContainerCreateResponse } } diff --git a/pkg/api/handlers/handler.go b/pkg/api/handlers/handler.go index 4f303f6ab..d60a5b239 100644 --- a/pkg/api/handlers/handler.go +++ b/pkg/api/handlers/handler.go @@ -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") } diff --git a/pkg/api/handlers/images.go b/pkg/api/handlers/images.go index b4acdc312..3f66a63c8 100644 --- a/pkg/api/handlers/images.go +++ b/pkg/api/handlers/images.go @@ -3,7 +3,6 @@ package handlers import ( "fmt" "io" - "io/ioutil" "net/http" "os" "strconv" @@ -127,46 +126,6 @@ func GetImage(r *http.Request, name string) (*image.Image, error) { return runtime.ImageRuntime().NewFromLocal(name) } -func LoadImage(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) - - query := struct { - //quiet bool # quiet 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 { - utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) - return - } - - var ( - err error - writer io.Writer - ) - f, err := ioutil.TempFile("", "api_load.tar") - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to create tempfile")) - return - } - if err := SaveFromBody(f, r); err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to write temporary file")) - return - } - id, err := runtime.LoadImage(r.Context(), "", f.Name(), writer, "") - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to load image")) - return - } - utils.WriteResponse(w, http.StatusOK, struct { - Stream string `json:"stream"` - }{ - Stream: fmt.Sprintf("Loaded image: %s\n", id), - }) -} - func SaveFromBody(f *os.File, r *http.Request) error { // nolint if _, err := io.Copy(f, r.Body); err != nil { return err diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index df16843c7..db1cf26ff 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -19,8 +19,6 @@ func StopContainer(w http.ResponseWriter, r *http.Request) { } func ContainerExists(w http.ResponseWriter, r *http.Request) { - // 404 no such container - // 200 ok runtime := r.Context().Value("runtime").(*libpod.Runtime) name := mux.Vars(r)["name"] _, err := runtime.LookupContainer(name) @@ -147,10 +145,6 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { // tail string } -func CreateContainer(w http.ResponseWriter, r *http.Request) { - -} - func UnmountContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) name := mux.Vars(r)["name"] diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 0d4e220a8..9f0876618 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -1,9 +1,12 @@ package libpod import ( + "fmt" + "io" "io/ioutil" "net/http" "os" + "strconv" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/api/handlers" @@ -42,12 +45,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) @@ -90,13 +93,14 @@ func GetImages(w http.ResponseWriter, r *http.Request) { } func PruneImages(w http.ResponseWriter, r *http.Request) { - // 200 ok - // 500 internal + var ( + all bool + err error + ) runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { - All bool `schema:"all"` - Filters []string `schema:"filters"` + Filters map[string][]string `schema:"filters"` }{ // override any golang type defaults } @@ -106,7 +110,19 @@ 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 { + all, err = strconv.ParseBool(query.Filters["all"][0]) + if err != nil { + utils.InternalServerError(w, err) + return + } + for k, v := range query.Filters { + libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0])) + } + } + cids, err := runtime.ImageRuntime().PruneImages(r.Context(), all, libpodFilters) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return @@ -163,3 +179,47 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { defer os.Remove(tmpfile.Name()) utils.WriteResponse(w, http.StatusOK, rdr) } + +func ImportImage(w http.ResponseWriter, r *http.Request) { + // TODO this is basically wrong + decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value("runtime").(*libpod.Runtime) + + query := struct { + Changes map[string]string `json:"changes"` + Message string `json:"message"` + Quiet bool `json:"quiet"` + }{ + // This is where you can override the golang default value for one of fields + } + + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) + return + } + + var ( + err error + writer io.Writer + ) + f, err := ioutil.TempFile("", "api_load.tar") + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to create tempfile")) + return + } + if err := handlers.SaveFromBody(f, r); err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to write temporary file")) + return + } + id, err := runtime.LoadImage(r.Context(), "", f.Name(), writer, "") + //id, err := runtime.Import(r.Context()) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to load image")) + return + } + utils.WriteResponse(w, http.StatusOK, struct { + Stream string `json:"stream"` + }{ + Stream: fmt.Sprintf("Loaded image: %s\n", id), + }) +} diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 14f8e8de7..50d763338 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -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) } @@ -266,7 +268,7 @@ func PodDelete(w http.ResponseWriter, r *http.Request) { decoder = r.Context().Value("decoder").(*schema.Decoder) ) query := struct { - force bool `schema:"force"` + Force bool `schema:"force"` }{ // override any golang type defaults } @@ -282,7 +284,7 @@ func PodDelete(w http.ResponseWriter, r *http.Request) { utils.PodNotFound(w, name, err) return } - if err := runtime.RemovePod(r.Context(), pod, true, query.force); err != nil { + if err := runtime.RemovePod(r.Context(), pod, true, query.Force); err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } @@ -307,43 +309,14 @@ func PodRestart(w http.ResponseWriter, r *http.Request) { func PodPrune(w http.ResponseWriter, r *http.Request) { var ( - err error - pods []*libpod.Pod runtime = r.Context().Value("runtime").(*libpod.Runtime) - decoder = r.Context().Value("decoder").(*schema.Decoder) ) - query := struct { - force bool `schema:"force"` - }{ - // override any golang type defaults - } - - if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) - return - } - - if query.force { - pods, err = runtime.GetAllPods() - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) - return - } - } else { - // TODO We need to make a libpod.PruneVolumes or this code will be a mess. Volumes - // already does this right. It will also help clean this code path up with less - // conditionals. We do this when we integrate with libpod again. - utils.Error(w, "not implemented", http.StatusInternalServerError, errors.New("not implemented")) + pruned, err := runtime.PrunePods() + if err != nil { + utils.InternalServerError(w, err) return } - for _, p := range pods { - if err := runtime.RemovePod(r.Context(), p, true, query.force); err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) - return - } - } - utils.WriteResponse(w, http.StatusNoContent, "") + utils.WriteResponse(w, http.StatusOK, pruned) } func PodPause(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index 33cd51164..33cf1e95d 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -537,3 +537,11 @@ func portsToPortSet(input map[string]struct{}) (nat.PortSet, error) { } return ports, nil } + +// 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"` +} 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/images.go b/pkg/api/handlers/utils/images.go index 9445298ca..2b651584a 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -6,6 +6,7 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" + "github.com/gorilla/mux" "github.com/gorilla/schema" ) @@ -15,17 +16,23 @@ 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 + Filters map[string][]string `schema:"filters"` + 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 } - filters := query.filters - if len(filters) < 1 { + var filters = []string{} + if _, found := mux.Vars(r)["digests"]; found && query.Digests { + UnSupportedParameter("digests") + } + + if _, found := r.URL.Query()["filters"]; found { filters = append(filters, fmt.Sprintf("reference=%s", "")) } return runtime.ImageRuntime().GetImagesWithFilters(filters) diff --git a/pkg/api/handlers/generic/version.go b/pkg/api/handlers/version.go index 39423914d..94166952c 100644 --- a/pkg/api/handlers/generic/version.go +++ b/pkg/api/handlers/version.go @@ -1,4 +1,4 @@ -package generic +package handlers import ( "fmt" @@ -8,7 +8,6 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" - "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" docker "github.com/docker/docker/api/types" "github.com/pkg/errors" @@ -53,7 +52,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) { }, }} - utils.WriteResponse(w, http.StatusOK, handlers.Version{Version: docker.Version{ + utils.WriteResponse(w, http.StatusOK, Version{Version: docker.Version{ Platform: struct { Name string }{ diff --git a/pkg/api/server/docs.go b/pkg/api/server/docs.go index e028c6302..c989c7927 100644 --- a/pkg/api/server/docs.go +++ b/pkg/api/server/docs.go @@ -12,7 +12,8 @@ // Version: 0.0.1 // License: Apache-2.0 https://opensource.org/licenses/Apache-2.0 // Contact: Podman <podman@lists.podman.io> https://podman.io/community/ -// Extensions: +// +// InfoExtensions: // x-logo: // - url: https://raw.githubusercontent.com/containers/libpod/master/logo/podman-logo.png // - altText: "Podman logo" diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index b2d2ab388..abae81c38 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -33,7 +33,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/ConflictError" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/create"), APIHandler(s.Context, generic.CreateContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/create"), APIHandler(s.Context, handlers.CreateContainer)).Methods(http.MethodPost) // swagger:operation GET /containers/json compat listContainers // --- // tags: @@ -550,7 +550,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { libpod endpoints */ - r.HandleFunc(VersionedPath("/libpod/containers/create"), APIHandler(s.Context, libpod.CreateContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/create"), APIHandler(s.Context, handlers.CreateContainer)).Methods(http.MethodPost) // swagger:operation GET /libpod/containers/json libpod libpodListContainers // --- // tags: diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 6e8b79313..f1cc0574c 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -55,6 +55,27 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - images (compat) // summary: List Images // description: Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image. + // parameters: + // - name: all + // in: query + // description: "Show all images. Only images from a final layer (no children) are shown by default." + // type: boolean + // default: false + // - name: filters + // in: query + // description: | + // A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + // - `before`=(`<image-name>[:<tag>]`, `<image id>` or `<image@digest>`) + // - `dangling=true` + // - `label=key` or `label="key=value"` of an image label + // - `reference`=(`<image-name>[:<tag>]`) + // - `since`=(`<image-name>[:<tag>]`, `<image id>` or `<image@digest>`) + // type: string + // - name: digests + // in: query + // description: Not supported + // type: boolean + // default: false // produces: // - application/json // responses: @@ -63,7 +84,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // 500: // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/images/json"), APIHandler(s.Context, generic.GetImages)).Methods(http.MethodGet) - // swagger:operation POST /images/load compat loadImage + // swagger:operation POST /images/load compat importImage // --- // tags: // - images (compat) @@ -86,7 +107,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: no error // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/images/load"), APIHandler(s.Context, handlers.LoadImage)).Methods(http.MethodPost) + r.Handle(VersionedPath("/images/load"), APIHandler(s.Context, libpod.ImportImage)).Methods(http.MethodPost) // swagger:operation POST /images/prune compat pruneImages // --- // tags: @@ -174,7 +195,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/ConflictError' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/images/name"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete) + r.Handle(VersionedPath("/images/{name}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete) // swagger:operation GET /images/{name}/get compat exportImage // --- // tags: @@ -585,6 +606,22 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - images // summary: List Images // description: Returns a list of images on the server + // parameters: + // - name: all + // in: query + // description: Show all images. Only images from a final layer (no children) are shown by default. + // type: boolean + // default: false + // - name: filters + // in: query + // description: | + // A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + // - `before`=(`<image-name>[:<tag>]`, `<image id>` or `<image@digest>`) + // - `dangling=true` + // - `label=key` or `label="key=value"` of an image label + // - `reference`=(`<image-name>[:<tag>]`) + // - `since`=(`<image-name>[:<tag>]`, `<image id>` or `<image@digest>`) + // type: string // produces: // - application/json // responses: @@ -593,7 +630,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // 500: // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/json"), APIHandler(s.Context, libpod.GetImages)).Methods(http.MethodGet) - // swagger:operation POST /libpod/images/load libpod libpodLoadImage + // swagger:operation POST /libpod/images/load libpod libpodImportImage // --- // tags: // - images @@ -604,6 +641,14 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // name: quiet // type: boolean // description: not supported + // - in: query + // name: change + // description: "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR. JSON encoded string" + // type: string + // - in: query + // name: message + // description: Set commit message for imported image + // type: string // - in: body // name: request // description: tarball of container image @@ -617,7 +662,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: no error // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/load"), APIHandler(s.Context, handlers.LoadImage)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/images/load"), APIHandler(s.Context, libpod.ImportImage)).Methods(http.MethodPost) // swagger:operation POST /libpod/images/prune libpod libpodPruneImages // --- // tags: @@ -635,10 +680,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // (or `0`), all unused images are pruned. // - `until=<string>` Prune images created before this timestamp. The `<timestamp>` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. // - `label` (`label=<key>`, `label=<key>=<value>`, `label!=<key>`, or `label!=<key>=<value>`) Prune images with (or without, in case `label!=...` is used) the specified labels. - // - in: query - // name: all - // type: boolean - // description: prune all images // produces: // - application/json // responses: @@ -707,7 +748,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/ConflictError' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/name"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete) + r.Handle(VersionedPath("/libpod/images/{name}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete) // swagger:operation GET /libpod/images/{name}/get libpod libpoodExportImage // --- // tags: diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go index 1ef14b58c..5c7b51871 100644 --- a/pkg/api/server/register_pods.go +++ b/pkg/api/server/register_pods.go @@ -30,17 +30,15 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // swagger:operation POST /libpod/pods/prune pods PrunePods // --- // summary: Prune unused pods - // parameters: - // - in: query - // name: force - // description: force delete - // type: boolean - // default: false // produces: // - application/json // responses: - // 204: - // description: no error + // 200: + // description: tbd + // schema: + // type: object + // additionalProperties: + // type: string // 400: // $ref: "#/responses/BadParamError" // 500: @@ -60,7 +58,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // - in: query // name: force // type: boolean - // description: force delete + // description : force removal of a running pod by first stopping all containers, then removing all containers in the pod // responses: // 204: // description: no error diff --git a/pkg/api/server/register_version.go b/pkg/api/server/register_version.go index 94216b1b6..d3b47c2a9 100644 --- a/pkg/api/server/register_version.go +++ b/pkg/api/server/register_version.go @@ -1,12 +1,12 @@ package server import ( - "github.com/containers/libpod/pkg/api/handlers/generic" + "github.com/containers/libpod/pkg/api/handlers" "github.com/gorilla/mux" ) func (s *APIServer) registerVersionHandlers(r *mux.Router) error { - r.Handle("/version", APIHandler(s.Context, generic.VersionHandler)) - r.Handle(VersionedPath("/version"), APIHandler(s.Context, generic.VersionHandler)) + r.Handle("/version", APIHandler(s.Context, handlers.VersionHandler)) + r.Handle(VersionedPath("/version"), APIHandler(s.Context, handlers.VersionHandler)) return nil } diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 847d11c3c..8c940763e 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -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... |