diff options
-rw-r--r-- | pkg/api/handlers/libpod/images.go | 39 | ||||
-rw-r--r-- | pkg/bindings/connection.go | 9 | ||||
-rw-r--r-- | pkg/bindings/containers/containers.go | 45 | ||||
-rw-r--r-- | pkg/bindings/images/images.go | 82 | ||||
-rw-r--r-- | pkg/bindings/images/search.go | 9 | ||||
-rw-r--r-- | pkg/bindings/pods/pods.go | 17 | ||||
-rw-r--r-- | pkg/bindings/test/common_test.go | 13 | ||||
-rw-r--r-- | pkg/bindings/test/images_test.go | 112 | ||||
-rw-r--r-- | pkg/bindings/volumes/volumes.go | 5 |
9 files changed, 252 insertions, 79 deletions
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/bindings/connection.go b/pkg/bindings/connection.go index 75f1fc6a5..ba5f9c3aa 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -206,7 +206,7 @@ func unixClient(_url *url.URL) (*http.Client, error) { } // DoRequest assembles the http request and returns the response -func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, queryParams map[string]string, pathValues ...string) (*APIResponse, error) { +func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, queryParams url.Values, pathValues ...string) (*APIResponse, error) { var ( err error response *http.Response @@ -225,12 +225,7 @@ func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, return nil, err } if len(queryParams) > 0 { - // if more desirable we could use url to form the encoded endpoint with params - r := req.URL.Query() - for k, v := range queryParams { - r.Add(k, v) - } - req.URL.RawQuery = r.Encode() + req.URL.RawQuery = queryParams.Encode() } // Give the Do three chances in the case of a comm/service hiccup for i := 0; i < 3; i++ { diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index a437e9a9b..2985787a6 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -3,6 +3,7 @@ package containers import ( "context" "net/http" + "net/url" "strconv" "github.com/containers/libpod/libpod" @@ -21,28 +22,28 @@ func List(ctx context.Context, filters map[string][]string, all *bool, last *int return nil, err } var containers []lpapiv2.ListContainer - params := make(map[string]string) + params := url.Values{} if all != nil { - params["all"] = strconv.FormatBool(*all) + params.Set("all", strconv.FormatBool(*all)) } if last != nil { - params["last"] = strconv.Itoa(*last) + params.Set("last", strconv.Itoa(*last)) } if pod != nil { - params["pod"] = strconv.FormatBool(*pod) + params.Set("pod", strconv.FormatBool(*pod)) } if size != nil { - params["size"] = strconv.FormatBool(*size) + params.Set("size", strconv.FormatBool(*size)) } if sync != nil { - params["sync"] = strconv.FormatBool(*sync) + params.Set("sync", strconv.FormatBool(*sync)) } if filters != nil { filterString, err := bindings.FiltersToString(filters) if err != nil { return nil, err } - params["filters"] = filterString + params.Set("filters", filterString) } response, err := conn.DoRequest(nil, http.MethodGet, "/containers/json", params) if err != nil { @@ -63,13 +64,13 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) { if err != nil { return nil, err } - params := make(map[string]string) + params := url.Values{} if filters != nil { filterString, err := bindings.FiltersToString(filters) if err != nil { return nil, err } - params["filters"] = filterString + params.Set("filters", filterString) } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/prune", params) if err != nil { @@ -86,12 +87,12 @@ func Remove(ctx context.Context, nameOrID string, force, volumes *bool) error { if err != nil { return err } - params := make(map[string]string) + params := url.Values{} if force != nil { - params["force"] = strconv.FormatBool(*force) + params.Set("force", strconv.FormatBool(*force)) } if volumes != nil { - params["vols"] = strconv.FormatBool(*volumes) + params.Set("vols", strconv.FormatBool(*volumes)) } response, err := conn.DoRequest(nil, http.MethodDelete, "/containers/%s", params, nameOrID) if err != nil { @@ -109,9 +110,9 @@ func Inspect(ctx context.Context, nameOrID string, size *bool) (*libpod.InspectC if err != nil { return nil, err } - params := make(map[string]string) + params := url.Values{} if size != nil { - params["size"] = strconv.FormatBool(*size) + params.Set("size", strconv.FormatBool(*size)) } response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/json", params, nameOrID) if err != nil { @@ -129,8 +130,8 @@ func Kill(ctx context.Context, nameOrID string, signal string) error { if err != nil { return err } - params := make(map[string]string) - params["signal"] = signal + params := url.Values{} + params.Set("signal", signal) response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/kill", params, nameOrID) if err != nil { return err @@ -162,9 +163,9 @@ func Restart(ctx context.Context, nameOrID string, timeout *int) error { if err != nil { return err } - params := make(map[string]string) + params := url.Values{} if timeout != nil { - params["t"] = strconv.Itoa(*timeout) + params.Set("t", strconv.Itoa(*timeout)) } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restart", params, nameOrID) if err != nil { @@ -181,9 +182,9 @@ func Start(ctx context.Context, nameOrID string, detachKeys *string) error { if err != nil { return err } - params := make(map[string]string) + params := url.Values{} if detachKeys != nil { - params["detachKeys"] = *detachKeys + params.Set("detachKeys", *detachKeys) } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/start", params, nameOrID) if err != nil { @@ -242,13 +243,13 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) { // Stop stops a running container. The timeout is optional. The nameOrID can be a container name // or a partial/full ID func Stop(ctx context.Context, nameOrID string, timeout *int) error { - params := make(map[string]string) + params := url.Values{} conn, err := bindings.GetClient(ctx) if err != nil { return err } if timeout != nil { - params["t"] = strconv.Itoa(*timeout) + params.Set("t", strconv.Itoa(*timeout)) } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/stop", params, nameOrID) if err != nil { diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 271d58952..c84aa4601 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -2,8 +2,10 @@ package images import ( "context" + "errors" "io" "net/http" + "net/url" "strconv" "github.com/containers/libpod/pkg/api/handlers" @@ -33,16 +35,16 @@ func List(ctx context.Context, all *bool, filters map[string][]string) ([]*handl if err != nil { return nil, err } - params := make(map[string]string) + params := url.Values{} if all != nil { - params["all"] = strconv.FormatBool(*all) + params.Set("all", strconv.FormatBool(*all)) } if filters != nil { strFilters, err := bindings.FiltersToString(filters) if err != nil { return nil, err } - params["filters"] = strFilters + params.Set("filters", strFilters) } response, err := conn.DoRequest(nil, http.MethodGet, "/images/json", params) if err != nil { @@ -58,9 +60,9 @@ func GetImage(ctx context.Context, nameOrID string, size *bool) (*inspect.ImageD if err != nil { return nil, err } - params := make(map[string]string) + params := url.Values{} if size != nil { - params["size"] = strconv.FormatBool(*size) + params.Set("size", strconv.FormatBool(*size)) } inspectedData := inspect.ImageData{} response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/json", params, nameOrID) @@ -88,15 +90,21 @@ func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse, return history, response.Process(&history) } -func Load(ctx context.Context, r io.Reader) error { +func Load(ctx context.Context, r io.Reader, name *string) (string, error) { + var id handlers.IDResponse conn, err := bindings.GetClient(ctx) if err != nil { - return err + return "", err } - // TODO this still needs error handling added - //_, err := http.Post(c.makeEndpoint("/images/loads"), "application/json", r) //nolint - _ = conn - return bindings.ErrNotImplemented + params := url.Values{} + if name != nil { + params.Set("reference", *name) + } + response, err := conn.DoRequest(r, http.MethodPost, "/images/load", params) + if err != nil { + return "", err + } + return id.ID, response.Process(&id) } // Remove deletes an image from local storage. The optional force parameter will forcibly remove @@ -107,9 +115,9 @@ func Remove(ctx context.Context, nameOrID string, force *bool) ([]map[string]str if err != nil { return nil, err } - params := make(map[string]string) + params := url.Values{} if force != nil { - params["force"] = strconv.FormatBool(*force) + params.Set("force", strconv.FormatBool(*force)) } response, err := conn.DoRequest(nil, http.MethodDelete, "/images/%s", params, nameOrID) if err != nil { @@ -125,12 +133,12 @@ func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, c if err != nil { return err } - params := make(map[string]string) + params := url.Values{} if format != nil { - params["format"] = *format + params.Set("format", *format) } if compress != nil { - params["compress"] = strconv.FormatBool(*compress) + params.Set("compress", strconv.FormatBool(*compress)) } response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/get", params, nameOrID) if err != nil { @@ -153,13 +161,13 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) { if err != nil { return nil, err } - params := make(map[string]string) + params := url.Values{} if filters != nil { stringFilter, err := bindings.FiltersToString(filters) if err != nil { return nil, err } - params["filters"] = stringFilter + params.Set("filters", stringFilter) } response, err := conn.DoRequest(nil, http.MethodPost, "/images/prune", params) if err != nil { @@ -174,9 +182,9 @@ func Tag(ctx context.Context, nameOrID, tag, repo string) error { if err != nil { return err } - params := make(map[string]string) - params["tag"] = tag - params["repo"] = repo + params := url.Values{} + params.Set("tag", tag) + params.Set("repo", repo) response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/tag", params, nameOrID) if err != nil { return err @@ -185,3 +193,35 @@ func Tag(ctx context.Context, nameOrID, tag, repo string) error { } func Build(nameOrId string) {} + +// Imports adds the given image to the local image store. This can be done by file and the given reader +// or via the url parameter. Additional metadata can be associated with the image by using the changes and +// message parameters. The image can also be tagged given a reference. One of url OR r must be provided. +func Import(ctx context.Context, changes []string, message, reference, u *string, r io.Reader) (string, error) { + var id handlers.IDResponse + if r != nil && u != nil { + return "", errors.New("url and r parameters cannot be used together") + } + conn, err := bindings.GetClient(ctx) + if err != nil { + return "", err + } + params := url.Values{} + for _, change := range changes { + params.Add("changes", change) + } + if message != nil { + params.Set("message", *message) + } + if reference != nil { + params.Set("reference", *reference) + } + if u != nil { + params.Set("url", *u) + } + response, err := conn.DoRequest(r, http.MethodPost, "/images/import", params) + if err != nil { + return "", err + } + return id.ID, response.Process(&id) +} diff --git a/pkg/bindings/images/search.go b/pkg/bindings/images/search.go index dca1b0e63..183ff3d77 100644 --- a/pkg/bindings/images/search.go +++ b/pkg/bindings/images/search.go @@ -3,6 +3,7 @@ package images import ( "context" "net/http" + "net/url" "strconv" "github.com/containers/libpod/libpod/image" @@ -20,17 +21,17 @@ func Search(ctx context.Context, term string, limit *int, filters map[string][]s if err != nil { return nil, err } - params := make(map[string]string) - params["term"] = term + params := url.Values{} + params.Set("term", term) if limit != nil { - params["limit"] = strconv.Itoa(*limit) + params.Set("limit", strconv.Itoa(*limit)) } if filters != nil { stringFilter, err := bindings.FiltersToString(filters) if err != nil { return nil, err } - params["filters"] = stringFilter + params.Set("filters", stringFilter) } response, err := conn.DoRequest(nil, http.MethodGet, "/images/search", params) if err != nil { diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go index 838b22e43..1a8c31be1 100644 --- a/pkg/bindings/pods/pods.go +++ b/pkg/bindings/pods/pods.go @@ -3,6 +3,7 @@ package pods import ( "context" "net/http" + "net/url" "strconv" "github.com/containers/libpod/libpod" @@ -48,9 +49,9 @@ func Kill(ctx context.Context, nameOrID string, signal *string) error { if err != nil { return err } - params := make(map[string]string) + params := url.Values{} if signal != nil { - params["signal"] = *signal + params.Set("signal", *signal) } response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/kill", params, nameOrID) if err != nil { @@ -95,13 +96,13 @@ func List(ctx context.Context, filters map[string][]string) ([]*libpod.PodInspec if err != nil { return nil, err } - params := make(map[string]string) + params := url.Values{} if filters != nil { stringFilter, err := bindings.FiltersToString(filters) if err != nil { return nil, err } - params["filters"] = stringFilter + params.Set("filters", stringFilter) } response, err := conn.DoRequest(nil, http.MethodGet, "/pods/json", params) if err != nil { @@ -130,9 +131,9 @@ func Remove(ctx context.Context, nameOrID string, force *bool) error { if err != nil { return err } - params := make(map[string]string) + params := url.Values{} if force != nil { - params["force"] = strconv.FormatBool(*force) + params.Set("force", strconv.FormatBool(*force)) } response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s", params, nameOrID) if err != nil { @@ -166,9 +167,9 @@ func Stop(ctx context.Context, nameOrID string, timeout *int) error { if err != nil { return err } - params := make(map[string]string) + params := url.Values{} if timeout != nil { - params["t"] = strconv.Itoa(*timeout) + params.Set("t", strconv.Itoa(*timeout)) } response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/stop", params, nameOrID) if err != nil { diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go index 98d64bbaa..38f5014ca 100644 --- a/pkg/bindings/test/common_test.go +++ b/pkg/bindings/test/common_test.go @@ -20,9 +20,18 @@ type testImage struct { } const ( + devPodmanBinaryLocation string = "../../../bin/podman" defaultPodmanBinaryLocation string = "/usr/bin/podman" ) +func getPodmanBinary() string { + _, err := os.Stat(devPodmanBinaryLocation) + if os.IsNotExist(err) { + return defaultPodmanBinaryLocation + } + return devPodmanBinaryLocation +} + var ( ImageCacheDir = "/tmp/podman/imagecachedir" LockTmpDir string @@ -50,7 +59,7 @@ type bindingTest struct { func (b *bindingTest) runPodman(command []string) *gexec.Session { var cmd []string - podmanBinary := defaultPodmanBinaryLocation + podmanBinary := getPodmanBinary() val, ok := os.LookupEnv("PODMAN_BINARY") if ok { podmanBinary = val @@ -166,7 +175,7 @@ func (b *bindingTest) restoreImageFromCache(i testImage) { // and add or append the alpine image to it func (b *bindingTest) RunTopContainer(containerName *string, insidePod *bool, podName *string) { cmd := []string{"run", "-dt"} - if *insidePod && podName != nil { + if insidePod != nil && podName != nil { pName := *podName cmd = append(cmd, "--pod", pName) } else if containerName != nil { diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go index 0b51c8c9e..c51ce4a32 100644 --- a/pkg/bindings/test/images_test.go +++ b/pkg/bindings/test/images_test.go @@ -3,6 +3,8 @@ package test_bindings import ( "context" "net/http" + "os" + "path/filepath" "time" "github.com/containers/libpod/pkg/bindings" @@ -71,6 +73,14 @@ var _ = Describe("Podman images", func() { // Inspect by long name _, err = images.GetImage(connText, alpine.name, nil) Expect(err).To(BeNil()) + // TODO it looks like the images API alwaays returns size regardless + // of bool or not. What should we do ? + //Expect(data.Size).To(BeZero()) + + // Enabling the size parameter should result in size being populated + data, err = images.GetImage(connText, alpine.name, &trueFlag) + Expect(err).To(BeNil()) + Expect(data.Size).To(BeNumerically(">", 0)) }) // Test to validate the remove image api @@ -181,4 +191,106 @@ var _ = Describe("Podman images", func() { Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) }) + It("Image Exists", func() { + // exists on bogus image should be false, with no error + exists, err := images.Exists(connText, "foobar") + Expect(err).To(BeNil()) + Expect(exists).To(BeFalse()) + + // exists with shortname should be true + exists, err = images.Exists(connText, alpine.shortName) + Expect(err).To(BeNil()) + Expect(exists).To(BeTrue()) + + // exists with fqname should be true + exists, err = images.Exists(connText, alpine.name) + Expect(err).To(BeNil()) + Expect(exists).To(BeTrue()) + }) + + It("Load|Import Image", func() { + // load an image + _, err := images.Remove(connText, alpine.name, nil) + Expect(err).To(BeNil()) + exists, err := images.Exists(connText, alpine.name) + Expect(err).To(BeNil()) + Expect(exists).To(BeFalse()) + f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) + defer f.Close() + Expect(err).To(BeNil()) + names, err := images.Load(connText, f, nil) + Expect(err).To(BeNil()) + Expect(names).To(Equal(alpine.name)) + exists, err = images.Exists(connText, alpine.name) + Expect(err).To(BeNil()) + Expect(exists).To(BeTrue()) + + // load with a repo name + f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) + Expect(err).To(BeNil()) + _, err = images.Remove(connText, alpine.name, nil) + Expect(err).To(BeNil()) + exists, err = images.Exists(connText, alpine.name) + Expect(err).To(BeNil()) + Expect(exists).To(BeFalse()) + newName := "quay.io/newname:fizzle" + names, err = images.Load(connText, f, &newName) + Expect(err).To(BeNil()) + Expect(names).To(Equal(alpine.name)) + exists, err = images.Exists(connText, newName) + Expect(err).To(BeNil()) + Expect(exists).To(BeTrue()) + + // load with a bad repo name should trigger a 500 + f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) + Expect(err).To(BeNil()) + _, err = images.Remove(connText, alpine.name, nil) + Expect(err).To(BeNil()) + exists, err = images.Exists(connText, alpine.name) + Expect(err).To(BeNil()) + Expect(exists).To(BeFalse()) + badName := "quay.io/newName:fizzle" + _, err = images.Load(connText, f, &badName) + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + }) + + It("Export Image", func() { + // Export an image + exportPath := filepath.Join(bt.tempDirPath, alpine.tarballName) + w, err := os.Create(filepath.Join(bt.tempDirPath, alpine.tarballName)) + defer w.Close() + Expect(err).To(BeNil()) + err = images.Export(connText, alpine.name, w, nil, nil) + Expect(err).To(BeNil()) + _, err = os.Stat(exportPath) + Expect(err).To(BeNil()) + + // TODO how do we verify that a format change worked? + }) + + It("Import Image", func() { + // load an image + _, err = images.Remove(connText, alpine.name, nil) + Expect(err).To(BeNil()) + exists, err := images.Exists(connText, alpine.name) + Expect(err).To(BeNil()) + Expect(exists).To(BeFalse()) + f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) + defer f.Close() + Expect(err).To(BeNil()) + changes := []string{"CMD /bin/foobar"} + testMessage := "test_import" + _, err = images.Import(connText, changes, &testMessage, &alpine.name, nil, f) + Expect(err).To(BeNil()) + exists, err = images.Exists(connText, alpine.name) + Expect(err).To(BeNil()) + Expect(exists).To(BeTrue()) + data, err := images.GetImage(connText, alpine.name, nil) + Expect(err).To(BeNil()) + Expect(data.Comment).To(Equal(testMessage)) + + }) + }) diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go index 8313a7460..7f6a9cc9b 100644 --- a/pkg/bindings/volumes/volumes.go +++ b/pkg/bindings/volumes/volumes.go @@ -3,6 +3,7 @@ package volumes import ( "context" "net/http" + "net/url" "strconv" "github.com/containers/libpod/libpod" @@ -73,9 +74,9 @@ func Remove(ctx context.Context, nameOrID string, force *bool) error { if err != nil { return err } - params := make(map[string]string) + params := url.Values{} if force != nil { - params["force"] = strconv.FormatBool(*force) + params.Set("force", strconv.FormatBool(*force)) } response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/%s/prune", params, nameOrID) if err != nil { |