diff options
Diffstat (limited to 'pkg/api/handlers')
-rw-r--r-- | pkg/api/handlers/decoder.go | 13 | ||||
-rw-r--r-- | pkg/api/handlers/generic/containers_create.go (renamed from pkg/api/handlers/containers_create.go) | 34 | ||||
-rw-r--r-- | pkg/api/handlers/generic/containers_stats.go | 16 | ||||
-rw-r--r-- | pkg/api/handlers/generic/images.go | 50 | ||||
-rw-r--r-- | pkg/api/handlers/generic/swagger.go | 6 | ||||
-rw-r--r-- | pkg/api/handlers/generic/types.go | 55 | ||||
-rw-r--r-- | pkg/api/handlers/images.go | 36 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/containers_create.go | 29 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images.go | 39 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/pods.go | 6 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/volumes.go | 137 | ||||
-rw-r--r-- | pkg/api/handlers/types.go | 51 | ||||
-rw-r--r-- | pkg/api/handlers/utils/containers.go | 27 | ||||
-rw-r--r-- | pkg/api/handlers/utils/images.go | 6 |
14 files changed, 369 insertions, 136 deletions
diff --git a/pkg/api/handlers/decoder.go b/pkg/api/handlers/decoder.go index 890d77ecc..03b86275d 100644 --- a/pkg/api/handlers/decoder.go +++ b/pkg/api/handlers/decoder.go @@ -3,8 +3,10 @@ package handlers import ( "encoding/json" "reflect" + "syscall" "time" + "github.com/containers/libpod/pkg/util" "github.com/gorilla/schema" "github.com/sirupsen/logrus" ) @@ -17,6 +19,9 @@ func NewAPIDecoder() *schema.Decoder { d.IgnoreUnknownKeys(true) d.RegisterConverter(map[string][]string{}, convertUrlValuesString) d.RegisterConverter(time.Time{}, convertTimeString) + + var Signal syscall.Signal + d.RegisterConverter(Signal, convertSignal) return d } @@ -89,3 +94,11 @@ func convertTimeString(query string) reflect.Value { func ParseDateTime(query string) time.Time { return convertTimeString(query).Interface().(time.Time) } + +func convertSignal(query string) reflect.Value { + signal, err := util.ParseSignal(query) + if err != nil { + logrus.Infof("convertSignal: Failed to parse %s: %s", query, err.Error()) + } + return reflect.ValueOf(signal) +} diff --git a/pkg/api/handlers/containers_create.go b/pkg/api/handlers/generic/containers_create.go index 48f0de94d..7e542752f 100644 --- a/pkg/api/handlers/containers_create.go +++ b/pkg/api/handlers/generic/containers_create.go @@ -1,4 +1,4 @@ -package handlers +package generic import ( "encoding/json" @@ -6,10 +6,10 @@ import ( "net/http" "strings" - "github.com/containers/libpod/cmd/podman/shared" "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" "github.com/containers/libpod/pkg/signal" @@ -17,14 +17,13 @@ import ( "github.com/containers/storage" "github.com/gorilla/schema" "github.com/pkg/errors" - log "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) func CreateContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) - input := CreateContainerConfig{} + input := handlers.CreateContainerConfig{} query := struct { Name string `schema:"name"` }{ @@ -52,34 +51,11 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "makeCreatConfig()")) return } - cc.Name = query.Name - var pod *libpod.Pod - ctr, err := shared.CreateContainerFromCreateConfig(runtime, &cc, r.Context(), pod) - if err != nil { - if strings.Contains(err.Error(), "invalid log driver") { - // this does not quite work yet and needs a little more massaging - w.Header().Set("Content-Type", "text/plain; charset=us-ascii") - w.WriteHeader(http.StatusInternalServerError) - msg := fmt.Sprintf("logger: no log driver named '%s' is registered", input.HostConfig.LogConfig.Type) - if _, err := fmt.Fprintln(w, msg); err != nil { - log.Errorf("%s: %q", msg, err) - } - //s.WriteResponse(w, http.StatusInternalServerError, fmt.Sprintf("logger: no log driver named '%s' is registered", input.HostConfig.LogConfig.Type)) - return - } - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "CreateContainerFromCreateConfig()")) - return - } - - response := ContainerCreateResponse{ - ID: ctr.ID(), - Warnings: []string{}} - - utils.WriteResponse(w, http.StatusCreated, response) + utils.CreateContainer(r.Context(), w, runtime, &cc) } -func makeCreateConfig(input CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) { +func makeCreateConfig(input handlers.CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) { var ( err error init bool diff --git a/pkg/api/handlers/generic/containers_stats.go b/pkg/api/handlers/generic/containers_stats.go index 19e2cc882..977979741 100644 --- a/pkg/api/handlers/generic/containers_stats.go +++ b/pkg/api/handlers/generic/containers_stats.go @@ -7,7 +7,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" "github.com/containers/libpod/pkg/cgroups" docker "github.com/docker/docker/api/types" @@ -58,17 +57,18 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { } var preRead time.Time - var preCPUStats docker.CPUStats + var preCPUStats CPUStats if query.Stream { preRead = time.Now() systemUsage, _ := cgroups.GetSystemCPUUsage() - preCPUStats = docker.CPUStats{ + preCPUStats = CPUStats{ CPUUsage: docker.CPUUsage{ TotalUsage: stats.CPUNano, PercpuUsage: stats.PerCPU, UsageInKernelmode: stats.CPUSystemNano, UsageInUsermode: stats.CPUNano - stats.CPUSystemNano, }, + CPU: stats.CPU, SystemUsage: systemUsage, OnlineCPUs: 0, ThrottlingData: docker.ThrottlingData{}, @@ -124,9 +124,8 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { } systemUsage, _ := cgroups.GetSystemCPUUsage() - - s := handlers.Stats{StatsJSON: docker.StatsJSON{ - Stats: docker.Stats{ + s := StatsJSON{ + Stats: Stats{ Read: time.Now(), PreRead: preRead, PidsStats: docker.PidsStats{ @@ -143,13 +142,14 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { IoTimeRecursive: nil, SectorsRecursive: nil, }, - CPUStats: docker.CPUStats{ + CPUStats: CPUStats{ CPUUsage: docker.CPUUsage{ TotalUsage: cgroupStat.CPU.Usage.Total, PercpuUsage: cgroupStat.CPU.Usage.PerCPU, UsageInKernelmode: cgroupStat.CPU.Usage.Kernel, UsageInUsermode: cgroupStat.CPU.Usage.Total - cgroupStat.CPU.Usage.Kernel, }, + CPU: stats.CPU, SystemUsage: systemUsage, OnlineCPUs: uint32(len(cgroupStat.CPU.Usage.PerCPU)), ThrottlingData: docker.ThrottlingData{ @@ -173,7 +173,7 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { Name: stats.Name, ID: stats.ContainerID, Networks: net, - }} + } utils.WriteJSON(w, http.StatusOK, s) if flusher, ok := w.(http.Flusher); ok { diff --git a/pkg/api/handlers/generic/images.go b/pkg/api/handlers/generic/images.go index c65db7575..1ced499d9 100644 --- a/pkg/api/handlers/generic/images.go +++ b/pkg/api/handlers/generic/images.go @@ -106,14 +106,14 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) query := struct { - author string - changes string - comment string - container string + Author string `schema:"author"` + Changes string `schema:"changes"` + Comment string `schema:"comment"` + Container string `schema:"container"` //fromSrc string # fromSrc is currently unused - pause bool - repo string - tag string + Pause bool `schema:"pause"` + Repo string `schema:"repo"` + Tag string `schema:"tag"` }{ // This is where you can override the golang default value for one of fields } @@ -145,22 +145,22 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { return } - if len(query.tag) > 0 { - tag = query.tag + if len(query.Tag) > 0 { + tag = query.Tag } - options.Message = query.comment - options.Author = query.author - options.Pause = query.pause - options.Changes = strings.Fields(query.changes) - ctr, err := runtime.LookupContainer(query.container) + options.Message = query.Comment + options.Author = query.Author + options.Pause = query.Pause + options.Changes = strings.Fields(query.Changes) + ctr, err := runtime.LookupContainer(query.Container) if err != nil { utils.Error(w, "Something went wrong.", http.StatusNotFound, err) return } // I know mitr hates this ... but doing for now - if len(query.repo) > 1 { - destImage = fmt.Sprintf("%s:%s", query.repo, tag) + if len(query.Repo) > 1 { + destImage = fmt.Sprintf("%s:%s", query.Repo, tag) } commitImage, err := ctr.Commit(r.Context(), destImage, options) @@ -179,8 +179,8 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) query := struct { - fromSrc string - changes []string + FromSrc string `schema:"fromSrc"` + Changes []string `schema:"changes"` }{ // This is where you can override the golang default value for one of fields } @@ -190,7 +190,7 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { return } // fromSrc – Source to import. The value may be a URL from which the image can be retrieved or - to read the image from the request body. This parameter may only be used when importing an image. - source := query.fromSrc + source := query.FromSrc if source == "-" { f, err := ioutil.TempFile("", "api_load.tar") if err != nil { @@ -202,7 +202,7 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to write temporary file")) } } - iid, err := runtime.Import(r.Context(), source, "", query.changes, "", false) + iid, err := runtime.Import(r.Context(), source, "", query.Changes, "", false) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to import tarball")) return @@ -238,8 +238,8 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) query := struct { - fromImage string - tag string + FromImage string `schema:"fromImage"` + Tag string `schema:"tag"` }{ // This is where you can override the golang default value for one of fields } @@ -254,9 +254,9 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { repo – Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image. tag – Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled. */ - fromImage := query.fromImage - if len(query.tag) < 1 { - fromImage = fmt.Sprintf("%s:%s", fromImage, query.tag) + fromImage := query.FromImage + if len(query.Tag) >= 1 { + fromImage = fmt.Sprintf("%s:%s", fromImage, query.Tag) } // TODO diff --git a/pkg/api/handlers/generic/swagger.go b/pkg/api/handlers/generic/swagger.go index bfe527c41..c9c9610bb 100644 --- a/pkg/api/handlers/generic/swagger.go +++ b/pkg/api/handlers/generic/swagger.go @@ -1,13 +1,15 @@ package generic -import "github.com/containers/libpod/pkg/api/handlers" +import ( + "github.com/containers/libpod/pkg/api/handlers/utils" +) // Create container // swagger:response ContainerCreateResponse type swagCtrCreateResponse struct { // in:body Body struct { - handlers.ContainerCreateResponse + utils.ContainerCreateResponse } } diff --git a/pkg/api/handlers/generic/types.go b/pkg/api/handlers/generic/types.go new file mode 100644 index 000000000..f068ac011 --- /dev/null +++ b/pkg/api/handlers/generic/types.go @@ -0,0 +1,55 @@ +package generic + +import ( + "time" + + docker "github.com/docker/docker/api/types" +) + +// CPUStats aggregates and wraps all CPU related info of container +type CPUStats struct { + // CPU Usage. Linux and Windows. + CPUUsage docker.CPUUsage `json:"cpu_usage"` + + // System Usage. Linux only. + SystemUsage uint64 `json:"system_cpu_usage,omitempty"` + + // Online CPUs. Linux only. + OnlineCPUs uint32 `json:"online_cpus,omitempty"` + + // Usage of CPU in %. Linux only. + CPU float64 `json:"cpu"` + + // Throttling Data. Linux only. + ThrottlingData docker.ThrottlingData `json:"throttling_data,omitempty"` +} + +// Stats is Ultimate struct aggregating all types of stats of one container +type Stats struct { + // Common stats + Read time.Time `json:"read"` + PreRead time.Time `json:"preread"` + + // Linux specific stats, not populated on Windows. + PidsStats docker.PidsStats `json:"pids_stats,omitempty"` + BlkioStats docker.BlkioStats `json:"blkio_stats,omitempty"` + + // Windows specific stats, not populated on Linux. + NumProcs uint32 `json:"num_procs"` + StorageStats docker.StorageStats `json:"storage_stats,omitempty"` + + // Shared stats + CPUStats CPUStats `json:"cpu_stats,omitempty"` + PreCPUStats CPUStats `json:"precpu_stats,omitempty"` // "Pre"="Previous" + MemoryStats docker.MemoryStats `json:"memory_stats,omitempty"` +} + +type StatsJSON struct { + Stats + + Name string `json:"name,omitempty"` + ID string `json:"id,omitempty"` + + // Networks request version >=1.21 + Networks map[string]docker.NetworkStats `json:"networks,omitempty"` +} diff --git a/pkg/api/handlers/images.go b/pkg/api/handlers/images.go index cd3c0b93f..2086ce748 100644 --- a/pkg/api/handlers/images.go +++ b/pkg/api/handlers/images.go @@ -7,6 +7,7 @@ import ( "os" "strconv" + "github.com/containers/image/v5/types" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/api/handlers/utils" @@ -147,15 +148,42 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) return } - // TODO filters are a bit undefined here in terms of what exactly the input looks - // like. We need to understand that a bit more. + + filter := image.SearchFilter{} + if len(query.Filters) > 0 { + if len(query.Filters["stars"]) > 0 { + stars, err := strconv.Atoi(query.Filters["stars"][0]) + if err != nil { + utils.InternalServerError(w, err) + return + } + filter.Stars = stars + } + if len(query.Filters["is-official"]) > 0 { + isOfficial, err := strconv.ParseBool(query.Filters["is-official"][0]) + if err != nil { + utils.InternalServerError(w, err) + return + } + filter.IsOfficial = types.NewOptionalBool(isOfficial) + } + if len(query.Filters["is-automated"]) > 0 { + isAutomated, err := strconv.ParseBool(query.Filters["is-automated"][0]) + if err != nil { + utils.InternalServerError(w, err) + return + } + filter.IsAutomated = types.NewOptionalBool(isAutomated) + } + } options := image.SearchOptions{ - Filter: image.SearchFilter{}, + Filter: filter, Limit: query.Limit, } results, err := image.SearchImages(query.Term, options) if err != nil { - utils.InternalServerError(w, err) + utils.BadRequest(w, "term", query.Term, err) + return } utils.WriteResponse(w, http.StatusOK, results) } diff --git a/pkg/api/handlers/libpod/containers_create.go b/pkg/api/handlers/libpod/containers_create.go new file mode 100644 index 000000000..ebca41151 --- /dev/null +++ b/pkg/api/handlers/libpod/containers_create.go @@ -0,0 +1,29 @@ +package libpod + +import ( + "encoding/json" + "net/http" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/specgen" + "github.com/pkg/errors" +) + +// CreateContainer takes a specgenerator and makes a container. It returns +// the new container ID on success along with any warnings. +func CreateContainer(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + var sg specgen.SpecGenerator + if err := json.NewDecoder(r.Body).Decode(&sg); err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) + return + } + ctr, err := sg.MakeContainer(runtime) + if err != nil { + utils.InternalServerError(w, err) + return + } + response := utils.ContainerCreateResponse{ID: ctr.ID()} + utils.WriteJSON(w, http.StatusCreated, response) +} diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index eac0e4dad..71603e6cc 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "strconv" + "strings" "github.com/containers/image/v5/docker" "github.com/containers/image/v5/docker/reference" @@ -133,11 +134,16 @@ func PruneImages(w http.ResponseWriter, r *http.Request) { 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 + dangling := query.Filters["all"] + if len(dangling) > 0 { + all, err = strconv.ParseBool(query.Filters["all"][0]) + if err != nil { + utils.InternalServerError(w, err) + return + } } + // dangling is special and not implemented in the libpod side of things + delete(query.Filters, "dangling") for k, v := range query.Filters { libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0])) } @@ -157,7 +163,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { Compress bool `schema:"compress"` Format string `schema:"format"` }{ - // override any golang type defaults + Format: "docker-archive", } if err := decoder.Decode(&query, r.URL.Query()); err != nil { @@ -166,11 +172,6 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { return } - if len(query.Format) < 1 { - utils.InternalServerError(w, errors.New("format parameter cannot be empty.")) - return - } - tmpfile, err := ioutil.TempFile("", "api.tar") if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile")) @@ -186,6 +187,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { utils.ImageNotFound(w, name, err) return } + if err := newImage.Save(r.Context(), name, query.Format, tmpfile.Name(), []string{}, false, query.Compress); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return @@ -234,8 +236,20 @@ func ImagesLoad(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to load image")) return } - - utils.WriteResponse(w, http.StatusOK, []handlers.LibpodImagesLoadReport{{ID: loadedImage}}) + split := strings.Split(loadedImage, ",") + newImage, err := runtime.ImageRuntime().NewFromLocal(split[0]) + if err != nil { + utils.InternalServerError(w, err) + return + } + // TODO this should go into libpod proper at some point. + if len(query.Reference) > 0 { + if err := newImage.TagImage(query.Reference); err != nil { + utils.InternalServerError(w, err) + return + } + } + utils.WriteResponse(w, http.StatusOK, handlers.LibpodImagesLoadReport{ID: loadedImage}) } func ImagesImport(w http.ResponseWriter, r *http.Request) { @@ -275,7 +289,6 @@ func ImagesImport(w http.ResponseWriter, r *http.Request) { tmpfile.Close() source = tmpfile.Name() } - importedImage, err := runtime.Import(context.Background(), source, query.Reference, query.Changes, query.Message, true) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to import image")) diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index d043b1204..f5700579b 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -99,12 +99,10 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http_code, err) return } - utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: pod.CgroupParent()}) + utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: pod.ID()}) } func Pods(w http.ResponseWriter, r *http.Request) { - // 200 ok - // 500 internal var ( runtime = r.Context().Value("runtime").(*libpod.Runtime) podInspectData []*libpod.PodInspect @@ -121,7 +119,7 @@ func Pods(w http.ResponseWriter, r *http.Request) { return } - if _, found := r.URL.Query()["filters"]; found { + if len(query.Filters) > 0 { utils.Error(w, "filters are not implemented yet", http.StatusInternalServerError, define.ErrNotImplemented) return } diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go index 7e7e46718..9b10ee890 100644 --- a/pkg/api/handlers/libpod/volumes.go +++ b/pkg/api/handlers/libpod/volumes.go @@ -3,9 +3,11 @@ package libpod import ( "encoding/json" "net/http" + "strings" "github.com/containers/libpod/cmd/podman/shared" "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" "github.com/gorilla/schema" @@ -29,7 +31,6 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) return } - // decode params from body if err := json.NewDecoder(r.Body).Decode(&input); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) @@ -49,14 +50,21 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { parsedOptions, err := shared.ParseVolumeOptions(input.Opts) if err != nil { utils.InternalServerError(w, err) + return } volumeOptions = append(volumeOptions, parsedOptions...) } vol, err := runtime.NewVolume(r.Context(), volumeOptions...) if err != nil { utils.InternalServerError(w, err) + return + } + config, err := vol.Config() + if err != nil { + utils.InternalServerError(w, err) + return } - utils.WriteResponse(w, http.StatusOK, vol.Name()) + utils.WriteResponse(w, http.StatusOK, config) } func InspectVolume(w http.ResponseWriter, r *http.Request) { @@ -76,25 +84,46 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { } func ListVolumes(w http.ResponseWriter, r *http.Request) { - //var ( - // runtime = r.Context().Value("runtime").(*libpod.Runtime) - // decoder = r.Context().Value("decoder").(*schema.Decoder) - //) - //query := struct { - // Filter string `json:"filter"` - //}{ - // // 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 - //} - /* - This is all in main in cmd and needs to be extracted from there first. - */ + var ( + decoder = r.Context().Value("decoder").(*schema.Decoder) + err error + runtime = r.Context().Value("runtime").(*libpod.Runtime) + volumeConfigs []*libpod.VolumeConfig + volumeFilters []libpod.VolumeFilter + ) + query := struct { + Filters map[string][]string `schema:"filters"` + }{ + // 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 len(query.Filters) > 0 { + volumeFilters, err = generateVolumeFilters(query.Filters) + if err != nil { + utils.InternalServerError(w, err) + return + } + } + vols, err := runtime.Volumes(volumeFilters...) + if err != nil { + utils.InternalServerError(w, err) + return + } + for _, v := range vols { + config, err := v.Config() + if err != nil { + utils.InternalServerError(w, err) + return + } + volumeConfigs = append(volumeConfigs, config) + } + utils.WriteResponse(w, http.StatusOK, volumeConfigs) } func PruneVolumes(w http.ResponseWriter, r *http.Request) { @@ -133,9 +162,77 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) { vol, err := runtime.LookupVolume(name) if err != nil { utils.VolumeNotFound(w, name, err) + return } if err := runtime.RemoveVolume(r.Context(), vol, query.Force); err != nil { + if errors.Cause(err) == define.ErrVolumeBeingUsed { + utils.Error(w, "volumes being used", http.StatusConflict, err) + return + } utils.InternalServerError(w, err) + return } utils.WriteResponse(w, http.StatusNoContent, "") } + +func generateVolumeFilters(filters map[string][]string) ([]libpod.VolumeFilter, error) { + var vf []libpod.VolumeFilter + for filter, v := range filters { + for _, val := range v { + switch filter { + case "name": + nameVal := val + vf = append(vf, func(v *libpod.Volume) bool { + return nameVal == v.Name() + }) + case "driver": + driverVal := val + vf = append(vf, func(v *libpod.Volume) bool { + return v.Driver() == driverVal + }) + case "scope": + scopeVal := val + vf = append(vf, func(v *libpod.Volume) bool { + return v.Scope() == scopeVal + }) + case "label": + filterArray := strings.SplitN(val, "=", 2) + filterKey := filterArray[0] + var filterVal string + if len(filterArray) > 1 { + filterVal = filterArray[1] + } else { + filterVal = "" + } + vf = append(vf, func(v *libpod.Volume) bool { + for labelKey, labelValue := range v.Labels() { + if labelKey == filterKey && ("" == filterVal || labelValue == filterVal) { + return true + } + } + return false + }) + case "opt": + filterArray := strings.SplitN(val, "=", 2) + filterKey := filterArray[0] + var filterVal string + if len(filterArray) > 1 { + filterVal = filterArray[1] + } else { + filterVal = "" + } + vf = append(vf, func(v *libpod.Volume) bool { + for labelKey, labelValue := range v.Options() { + if labelKey == filterKey && ("" == filterVal || labelValue == filterVal) { + return true + } + } + return false + }) + default: + return nil, errors.Errorf("%q is in an invalid volume filter", filter) + } + } + } + return vf, nil +} diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index f5d9f9ad5..2930a9567 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -78,10 +78,6 @@ type Container struct { docker.ContainerCreateConfig } -type ContainerStats struct { - docker.ContainerStats -} - type Version struct { docker.Version } @@ -132,25 +128,27 @@ type CreateContainerConfig struct { NetworkingConfig dockerNetwork.NetworkingConfig } +// swagger:model VolumeCreate type VolumeCreateConfig struct { - Name string `json:"name"` - Driver string `schema:"driver"` - Label map[string]string `schema:"label"` - Opts map[string]string `schema:"opts"` + // New volume's name. Can be left blank + Name string `schema:"name"` + // Volume driver to use + Driver string `schema:"driver"` + // User-defined key/value metadata. + Label map[string]string `schema:"label"` + // Mapping of driver options and values. + Opts map[string]string `schema:"opts"` } type IDResponse struct { ID string `json:"id"` } -type Stats struct { - docker.StatsJSON -} - type ContainerTopOKBody struct { dockerContainer.ContainerTopOKBody } +// swagger:model PodCreateConfig type PodCreateConfig struct { Name string `json:"name"` CGroupParent string `json:"cgroup-parent"` @@ -351,18 +349,21 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI func LibpodToContainer(l *libpod.Container, infoData []define.InfoData) (*Container, error) { imageId, imageName := l.Image() - sizeRW, err := l.RWSize() - if err != nil { + + var ( + err error + sizeRootFs int64 + sizeRW int64 + state define.ContainerStatus + ) + + if state, err = l.State(); err != nil { return nil, err } - - SizeRootFs, err := l.RootFsSize() - if err != nil { + if sizeRW, err = l.RWSize(); err != nil { return nil, err } - - state, err := l.State() - if err != nil { + if sizeRootFs, err = l.RootFsSize(); err != nil { return nil, err } @@ -375,7 +376,7 @@ func LibpodToContainer(l *libpod.Container, infoData []define.InfoData) (*Contai Created: l.CreatedTime().Unix(), Ports: nil, SizeRw: sizeRW, - SizeRootFs: SizeRootFs, + SizeRootFs: sizeRootFs, Labels: l.Labels(), State: string(state), Status: "", @@ -545,11 +546,3 @@ 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/containers.go b/pkg/api/handlers/utils/containers.go index 74485edf2..402005581 100644 --- a/pkg/api/handlers/utils/containers.go +++ b/pkg/api/handlers/utils/containers.go @@ -1,6 +1,7 @@ package utils import ( + "context" "fmt" "net/http" "syscall" @@ -9,13 +10,22 @@ import ( "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" + createconfig "github.com/containers/libpod/pkg/spec" "github.com/gorilla/schema" "github.com/pkg/errors" ) +// 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 KillContainer(w http.ResponseWriter, r *http.Request) (*libpod.Container, error) { runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decorder").(*schema.Decoder) + decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { Signal syscall.Signal `schema:"signal"` }{ @@ -119,3 +129,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/images.go b/pkg/api/handlers/utils/images.go index d0dfbf3ab..f68a71561 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -31,12 +31,16 @@ func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) { if _, found := mux.Vars(r)["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) + } |