diff options
Diffstat (limited to 'pkg/bindings')
-rw-r--r-- | pkg/bindings/containers/create.go | 2 | ||||
-rw-r--r-- | pkg/bindings/test/common_test.go | 4 | ||||
-rw-r--r-- | pkg/bindings/test/containers_test.go | 253 | ||||
-rw-r--r-- | pkg/bindings/test/volumes_test.go | 174 | ||||
-rw-r--r-- | pkg/bindings/volumes/volumes.go | 52 |
5 files changed, 470 insertions, 15 deletions
diff --git a/pkg/bindings/containers/create.go b/pkg/bindings/containers/create.go index 2943cb522..43a3ef02d 100644 --- a/pkg/bindings/containers/create.go +++ b/pkg/bindings/containers/create.go @@ -19,7 +19,7 @@ func CreateWithSpec(ctx context.Context, s specgen.SpecGenerator) (utils.Contain } specgenString, err := jsoniter.MarshalToString(s) if err != nil { - return ccr, nil + return ccr, err } stringReader := strings.NewReader(specgenString) response, err := conn.DoRequest(stringReader, http.MethodPost, "/containers/create", nil) diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go index 38f5014ca..1fc774074 100644 --- a/pkg/bindings/test/common_test.go +++ b/pkg/bindings/test/common_test.go @@ -240,3 +240,7 @@ func createCache() { } b.cleanup() } + +func isStopped(state string) bool { + return state == "exited" || state == "stopped" +} diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go new file mode 100644 index 000000000..5a0bdebe6 --- /dev/null +++ b/pkg/bindings/test/containers_test.go @@ -0,0 +1,253 @@ +package test_bindings + +import ( + "context" + "net/http" + "strconv" + "time" + + "github.com/containers/libpod/pkg/bindings" + "github.com/containers/libpod/pkg/bindings/containers" + "github.com/containers/libpod/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Podman containers ", func() { + var ( + bt *bindingTest + s *gexec.Session + connText context.Context + err error + falseFlag bool = false + trueFlag bool = true + ) + + BeforeEach(func() { + bt = newBindingTest() + bt.RestoreImagesFromCache() + s = bt.startAPIService() + time.Sleep(1 * time.Second) + connText, err = bindings.NewConnection(context.Background(), bt.sock) + Expect(err).To(BeNil()) + }) + + AfterEach(func() { + s.Kill() + bt.cleanup() + }) + + It("podman pause a bogus container", func() { + // Pausing bogus container should return 404 + err = containers.Pause(connText, "foobar") + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusNotFound)) + }) + + It("podman unpause a bogus container", func() { + // Unpausing bogus container should return 404 + err = containers.Unpause(connText, "foobar") + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusNotFound)) + }) + + It("podman pause a running container by name", func() { + // Pausing by name should work + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + err := containers.Pause(connText, name) + Expect(err).To(BeNil()) + + // Ensure container is paused + data, err := containers.Inspect(connText, name, nil) + Expect(err).To(BeNil()) + Expect(data.State.Status).To(Equal("paused")) + }) + + It("podman pause a running container by id", func() { + // Pausing by id should work + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + data, err := containers.Inspect(connText, name, nil) + Expect(err).To(BeNil()) + err = containers.Pause(connText, data.ID) + Expect(err).To(BeNil()) + + // Ensure container is paused + data, err = containers.Inspect(connText, data.ID, nil) + Expect(data.State.Status).To(Equal("paused")) + }) + + It("podman unpause a running container by name", func() { + // Unpausing by name should work + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + err := containers.Pause(connText, name) + Expect(err).To(BeNil()) + err = containers.Unpause(connText, name) + Expect(err).To(BeNil()) + + // Ensure container is unpaused + data, err := containers.Inspect(connText, name, nil) + Expect(data.State.Status).To(Equal("running")) + }) + + It("podman unpause a running container by ID", func() { + // Unpausing by ID should work + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + // Pause by name + err := containers.Pause(connText, name) + data, err := containers.Inspect(connText, name, nil) + Expect(err).To(BeNil()) + err = containers.Unpause(connText, data.ID) + Expect(err).To(BeNil()) + + // Ensure container is unpaused + data, err = containers.Inspect(connText, name, nil) + Expect(data.State.Status).To(Equal("running")) + }) + + It("podman pause a paused container by name", func() { + // Pausing a paused container by name should fail + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + err := containers.Pause(connText, name) + Expect(err).To(BeNil()) + err = containers.Pause(connText, name) + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + }) + + It("podman pause a paused container by id", func() { + // Pausing a paused container by id should fail + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + data, err := containers.Inspect(connText, name, nil) + Expect(err).To(BeNil()) + err = containers.Pause(connText, data.ID) + Expect(err).To(BeNil()) + err = containers.Pause(connText, data.ID) + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + }) + + It("podman pause a stopped container by name", func() { + // Pausing a stopped container by name should fail + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + err := containers.Stop(connText, name, nil) + Expect(err).To(BeNil()) + err = containers.Pause(connText, name) + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + }) + + It("podman pause a stopped container by id", func() { + // Pausing a stopped container by id should fail + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + data, err := containers.Inspect(connText, name, nil) + err = containers.Stop(connText, data.ID, nil) + Expect(err).To(BeNil()) + err = containers.Pause(connText, data.ID) + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + }) + + It("podman remove a paused container by id without force", func() { + // Removing a paused container without force should fail + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + data, err := containers.Inspect(connText, name, nil) + Expect(err).To(BeNil()) + err = containers.Pause(connText, data.ID) + Expect(err).To(BeNil()) + err = containers.Remove(connText, data.ID, &falseFlag, &falseFlag) + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + }) + + It("podman remove a paused container by id with force", func() { + // FIXME: Skip on F31 and later + host := utils.GetHostDistributionInfo() + osVer, err := strconv.Atoi(host.Version) + Expect(err).To(BeNil()) + if host.Distribution == "fedora" && osVer >= 31 { + Skip("FIXME: https://github.com/containers/libpod/issues/5325") + } + + // Removing a paused container with force should work + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + data, err := containers.Inspect(connText, name, nil) + Expect(err).To(BeNil()) + err = containers.Pause(connText, data.ID) + Expect(err).To(BeNil()) + err = containers.Remove(connText, data.ID, &trueFlag, &falseFlag) + Expect(err).To(BeNil()) + }) + + It("podman stop a paused container by name", func() { + // Stopping a paused container by name should fail + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + err := containers.Pause(connText, name) + Expect(err).To(BeNil()) + err = containers.Stop(connText, name, nil) + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + }) + + It("podman stop a paused container by id", func() { + // Stopping a paused container by id should fail + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + data, err := containers.Inspect(connText, name, nil) + Expect(err).To(BeNil()) + err = containers.Pause(connText, data.ID) + Expect(err).To(BeNil()) + err = containers.Stop(connText, data.ID, nil) + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + }) + + It("podman stop a running container by name", func() { + // Stopping a running container by name should work + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + err := containers.Stop(connText, name, nil) + Expect(err).To(BeNil()) + + // Ensure container is stopped + data, err := containers.Inspect(connText, name, nil) + Expect(err).To(BeNil()) + Expect(isStopped(data.State.Status)).To(BeTrue()) + }) + + It("podman stop a running container by ID", func() { + // Stopping a running container by ID should work + var name = "top" + bt.RunTopContainer(&name, &falseFlag, nil) + data, err := containers.Inspect(connText, name, nil) + Expect(err).To(BeNil()) + err = containers.Stop(connText, data.ID, nil) + Expect(err).To(BeNil()) + + // Ensure container is stopped + data, err = containers.Inspect(connText, name, nil) + Expect(err).To(BeNil()) + Expect(isStopped(data.State.Status)).To(BeTrue()) + }) + +}) diff --git a/pkg/bindings/test/volumes_test.go b/pkg/bindings/test/volumes_test.go new file mode 100644 index 000000000..c8940d46e --- /dev/null +++ b/pkg/bindings/test/volumes_test.go @@ -0,0 +1,174 @@ +package test_bindings + +import ( + "context" + "fmt" + "github.com/containers/libpod/pkg/api/handlers" + "github.com/containers/libpod/pkg/bindings/containers" + "github.com/containers/libpod/pkg/bindings/volumes" + "net/http" + "time" + + "github.com/containers/libpod/pkg/bindings" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Podman volumes", func() { + var ( + //tempdir string + //err error + //podmanTest *PodmanTestIntegration + bt *bindingTest + s *gexec.Session + connText context.Context + err error + trueFlag = true + ) + + BeforeEach(func() { + //tempdir, err = CreateTempDirInTempDir() + //if err != nil { + // os.Exit(1) + //} + //podmanTest = PodmanTestCreate(tempdir) + //podmanTest.Setup() + //podmanTest.SeedImages() + bt = newBindingTest() + bt.RestoreImagesFromCache() + s = bt.startAPIService() + time.Sleep(1 * time.Second) + connText, err = bindings.NewConnection(context.Background(), bt.sock) + Expect(err).To(BeNil()) + }) + + AfterEach(func() { + //podmanTest.Cleanup() + //f := CurrentGinkgoTestDescription() + //processTestResult(f) + s.Kill() + bt.cleanup() + }) + + It("create volume", func() { + // create a volume with blank config should work + _, err := volumes.Create(connText, handlers.VolumeCreateConfig{}) + Expect(err).To(BeNil()) + + vcc := handlers.VolumeCreateConfig{ + Name: "foobar", + Label: nil, + Opts: nil, + } + vol, err := volumes.Create(connText, vcc) + Expect(err).To(BeNil()) + Expect(vol.Name).To(Equal("foobar")) + + // create volume with same name should 500 + _, err = volumes.Create(connText, vcc) + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + }) + + It("inspect volume", func() { + vol, err := volumes.Create(connText, handlers.VolumeCreateConfig{}) + Expect(err).To(BeNil()) + data, err := volumes.Inspect(connText, vol.Name) + Expect(err).To(BeNil()) + Expect(data.Name).To(Equal(vol.Name)) + }) + + It("remove volume", func() { + // removing a bogus volume should result in 404 + err := volumes.Remove(connText, "foobar", nil) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusNotFound)) + + // Removing an unused volume should work + vol, err := volumes.Create(connText, handlers.VolumeCreateConfig{}) + Expect(err).To(BeNil()) + err = volumes.Remove(connText, vol.Name, nil) + Expect(err).To(BeNil()) + + // Removing a volume that is being used without force should be 409 + vol, err = volumes.Create(connText, handlers.VolumeCreateConfig{}) + Expect(err).To(BeNil()) + session := bt.runPodman([]string{"run", "-dt", "-v", fmt.Sprintf("%s:/foobar", vol.Name), "--name", "vtest", alpine.name, "top"}) + session.Wait(45) + err = volumes.Remove(connText, vol.Name, nil) + Expect(err).ToNot(BeNil()) + code, _ = bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusConflict)) + + // Removing with a volume in use with force should work with a stopped container + zero := 0 + err = containers.Stop(connText, "vtest", &zero) + Expect(err).To(BeNil()) + err = volumes.Remove(connText, vol.Name, &trueFlag) + Expect(err).To(BeNil()) + }) + + It("list volumes", func() { + // no volumes should be ok + vols, err := volumes.List(connText, nil) + Expect(err).To(BeNil()) + Expect(len(vols)).To(BeZero()) + + // create a bunch of named volumes and make verify with list + volNames := []string{"homer", "bart", "lisa", "maggie", "marge"} + for i := 0; i < 5; i++ { + _, err = volumes.Create(connText, handlers.VolumeCreateConfig{Name: volNames[i]}) + Expect(err).To(BeNil()) + } + vols, err = volumes.List(connText, nil) + Expect(err).To(BeNil()) + Expect(len(vols)).To(BeNumerically("==", 5)) + for _, v := range vols { + Expect(StringInSlice(v.Name, volNames)).To(BeTrue()) + } + + // list with bad filter should be 500 + filters := make(map[string][]string) + filters["foobar"] = []string{"1234"} + _, err = volumes.List(connText, filters) + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + + filters = make(map[string][]string) + filters["name"] = []string{"homer"} + vols, err = volumes.List(connText, filters) + Expect(err).To(BeNil()) + Expect(len(vols)).To(BeNumerically("==", 1)) + Expect(vols[0].Name).To(Equal("homer")) + }) + + // TODO we need to add filtering to tests + It("prune unused volume", func() { + // Pruning when no volumes present should be ok + _, err := volumes.Prune(connText) + Expect(err).To(BeNil()) + + // Removing an unused volume should work + _, err = volumes.Create(connText, handlers.VolumeCreateConfig{}) + Expect(err).To(BeNil()) + vols, err := volumes.Prune(connText) + Expect(err).To(BeNil()) + Expect(len(vols)).To(BeNumerically("==", 1)) + + _, err = volumes.Create(connText, handlers.VolumeCreateConfig{Name: "homer"}) + Expect(err).To(BeNil()) + _, err = volumes.Create(connText, handlers.VolumeCreateConfig{}) + Expect(err).To(BeNil()) + session := bt.runPodman([]string{"run", "-dt", "-v", fmt.Sprintf("%s:/homer", "homer"), "--name", "vtest", alpine.name, "top"}) + session.Wait(45) + vols, err = volumes.Prune(connText) + Expect(err).To(BeNil()) + Expect(len(vols)).To(BeNumerically("==", 1)) + _, err = volumes.Inspect(connText, "homer") + Expect(err).To(BeNil()) + }) + +}) diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go index 7f6a9cc9b..0bc818605 100644 --- a/pkg/bindings/volumes/volumes.go +++ b/pkg/bindings/volumes/volumes.go @@ -5,27 +5,33 @@ import ( "net/http" "net/url" "strconv" + "strings" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/bindings" + jsoniter "github.com/json-iterator/go" ) // Create creates a volume given its configuration. -func Create(ctx context.Context, config handlers.VolumeCreateConfig) (string, error) { - // TODO This is incomplete. The config needs to be sent via the body +func Create(ctx context.Context, config handlers.VolumeCreateConfig) (*libpod.VolumeConfig, error) { var ( - volumeID string + v libpod.VolumeConfig ) conn, err := bindings.GetClient(ctx) if err != nil { - return "", err + return nil, err + } + createString, err := jsoniter.MarshalToString(config) + if err != nil { + return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/create", nil) + stringReader := strings.NewReader(createString) + response, err := conn.DoRequest(stringReader, http.MethodPost, "/volumes/create", nil) if err != nil { - return volumeID, err + return nil, err } - return volumeID, response.Process(&volumeID) + return &v, response.Process(&v) } // Inspect returns low-level information about a volume. @@ -37,18 +43,36 @@ func Inspect(ctx context.Context, nameOrID string) (*libpod.InspectVolumeData, e if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/%s/json", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/volumes/%s/json", nil, nameOrID) if err != nil { return &inspect, err } return &inspect, response.Process(&inspect) } -func List() error { - // TODO - // The API side of things for this one does a lot in main and therefore - // is not implemented yet. - return bindings.ErrNotImplemented // nolint:typecheck +// List returns the configurations for existing volumes in the form of a slice. Optionally, filters +// can be used to refine the list of volumes. +func List(ctx context.Context, filters map[string][]string) ([]*libpod.VolumeConfig, error) { + var ( + vols []*libpod.VolumeConfig + ) + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + params := url.Values{} + if len(filters) > 0 { + strFilters, err := bindings.FiltersToString(filters) + if err != nil { + return nil, err + } + params.Set("filters", strFilters) + } + response, err := conn.DoRequest(nil, http.MethodGet, "/volumes/json", params) + if err != nil { + return vols, err + } + return vols, response.Process(&vols) } // Prune removes unused volumes from the local filesystem. @@ -78,7 +102,7 @@ func Remove(ctx context.Context, nameOrID string, force *bool) error { if force != nil { params.Set("force", strconv.FormatBool(*force)) } - response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/%s/prune", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodDelete, "/volumes/%s", params, nameOrID) if err != nil { return err } |