mirror of
				https://github.com/containers/podman.git
				synced 2025-10-31 18:08:51 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			414 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			414 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package bindings_test
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"time"
 | |
| 
 | |
| 	podmanRegistry "github.com/containers/podman/v4/hack/podman-registry-go"
 | |
| 	"github.com/containers/podman/v4/pkg/bindings"
 | |
| 	"github.com/containers/podman/v4/pkg/bindings/containers"
 | |
| 	"github.com/containers/podman/v4/pkg/bindings/images"
 | |
| 	"github.com/containers/podman/v4/pkg/domain/entities"
 | |
| 	. "github.com/onsi/ginkgo"
 | |
| 	. "github.com/onsi/gomega"
 | |
| 	. "github.com/onsi/gomega/gexec"
 | |
| 	. "github.com/onsi/gomega/gstruct"
 | |
| )
 | |
| 
 | |
| var _ = Describe("Podman images", func() {
 | |
| 	var (
 | |
| 		// tempdir    string
 | |
| 		// err        error
 | |
| 		// podmanTest *PodmanTestIntegration
 | |
| 		bt  *bindingTest
 | |
| 		s   *Session
 | |
| 		err error
 | |
| 	)
 | |
| 
 | |
| 	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)
 | |
| 		err := bt.NewConnection()
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 	})
 | |
| 
 | |
| 	AfterEach(func() {
 | |
| 		// podmanTest.Cleanup()
 | |
| 		// f := CurrentGinkgoTestDescription()
 | |
| 		// processTestResult(f)
 | |
| 		s.Kill()
 | |
| 		bt.cleanup()
 | |
| 	})
 | |
| 
 | |
| 	It("inspect image", func() {
 | |
| 		// Inspect invalid image be 404
 | |
| 		_, err = images.GetImage(bt.conn, "foobar5000", nil)
 | |
| 		Expect(err).ToNot(BeNil())
 | |
| 		code, _ := bindings.CheckResponseCode(err)
 | |
| 		Expect(code).To(BeNumerically("==", http.StatusNotFound))
 | |
| 
 | |
| 		// Inspect by short name
 | |
| 		data, err := images.GetImage(bt.conn, alpine.shortName, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 
 | |
| 		// Inspect with full ID
 | |
| 		_, err = images.GetImage(bt.conn, data.ID, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 
 | |
| 		// Inspect with partial ID
 | |
| 		_, err = images.GetImage(bt.conn, data.ID[0:12], nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 
 | |
| 		// Inspect by long name
 | |
| 		_, err = images.GetImage(bt.conn, alpine.name, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		// TODO it looks like the images API always returns size regardless
 | |
| 		// of bool or not. What should we do ?
 | |
| 		// Expect(data.Size).To(BeZero())
 | |
| 
 | |
| 		options := new(images.GetOptions).WithSize(true)
 | |
| 		// Enabling the size parameter should result in size being populated
 | |
| 		data, err = images.GetImage(bt.conn, alpine.name, options)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(data.Size).To(BeNumerically(">", 0))
 | |
| 	})
 | |
| 
 | |
| 	// Test to validate the remove image api
 | |
| 	It("remove image", func() {
 | |
| 		// NOTE that removing an image that does not exist will still
 | |
| 		// return a 200 http status.  The response, however, includes
 | |
| 		// the exit code that podman-remote should exit with.
 | |
| 		//
 | |
| 		// The libpod/images/remove endpoint supports batch removal of
 | |
| 		// images for performance reasons and for hiding the logic of
 | |
| 		// deciding which exit code to use from the client.
 | |
| 		response, errs := images.Remove(bt.conn, []string{"foobar5000"}, nil)
 | |
| 		Expect(len(errs)).To(BeNumerically(">", 0))
 | |
| 		Expect(response.ExitCode).To(BeNumerically("==", 1)) // podman-remote would exit with 1
 | |
| 
 | |
| 		// Remove an image by name, validate image is removed and error is nil
 | |
| 		inspectData, err := images.GetImage(bt.conn, busybox.shortName, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		response, errs = images.Remove(bt.conn, []string{busybox.shortName}, nil)
 | |
| 		Expect(len(errs)).To(BeZero())
 | |
| 
 | |
| 		Expect(inspectData.ID).To(Equal(response.Deleted[0]))
 | |
| 		_, err = images.GetImage(bt.conn, busybox.shortName, nil)
 | |
| 		code, _ := bindings.CheckResponseCode(err)
 | |
| 		Expect(code).To(BeNumerically("==", http.StatusNotFound))
 | |
| 
 | |
| 		// Start a container with alpine image
 | |
| 		var top string = "top"
 | |
| 		_, err = bt.RunTopContainer(&top, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		// we should now have a container called "top" running
 | |
| 		containerResponse, err := containers.Inspect(bt.conn, "top", nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(containerResponse.Name).To(Equal("top"))
 | |
| 
 | |
| 		// try to remove the image "alpine". This should fail since we are not force
 | |
| 		// deleting hence image cannot be deleted until the container is deleted.
 | |
| 		_, errs = images.Remove(bt.conn, []string{alpine.shortName}, nil)
 | |
| 		code, _ = bindings.CheckResponseCode(errs[0])
 | |
| 		Expect(code).To(BeNumerically("==", -1))
 | |
| 
 | |
| 		// Removing the image "alpine" where force = true
 | |
| 		options := new(images.RemoveOptions).WithForce(true)
 | |
| 		_, errs = images.Remove(bt.conn, []string{alpine.shortName}, options)
 | |
| 		Expect(errs).To(Or(HaveLen(0), BeNil()))
 | |
| 		// To be extra sure, check if the previously created container
 | |
| 		// is gone as well.
 | |
| 		_, err = containers.Inspect(bt.conn, "top", nil)
 | |
| 		code, _ = bindings.CheckResponseCode(err)
 | |
| 		Expect(code).To(BeNumerically("==", http.StatusNotFound))
 | |
| 
 | |
| 		// Now make sure both images are gone.
 | |
| 		_, err = images.GetImage(bt.conn, busybox.shortName, nil)
 | |
| 		code, _ = bindings.CheckResponseCode(err)
 | |
| 		Expect(code).To(BeNumerically("==", http.StatusNotFound))
 | |
| 
 | |
| 		_, err = images.GetImage(bt.conn, alpine.shortName, nil)
 | |
| 		code, _ = bindings.CheckResponseCode(err)
 | |
| 		Expect(code).To(BeNumerically("==", http.StatusNotFound))
 | |
| 	})
 | |
| 
 | |
| 	// Tests to validate the image tag command.
 | |
| 	It("tag image", func() {
 | |
| 
 | |
| 		// Validates if invalid image name is given a bad response is encountered.
 | |
| 		err = images.Tag(bt.conn, "dummy", "demo", alpine.shortName, nil)
 | |
| 		Expect(err).ToNot(BeNil())
 | |
| 		code, _ := bindings.CheckResponseCode(err)
 | |
| 		Expect(code).To(BeNumerically("==", http.StatusNotFound))
 | |
| 
 | |
| 		// Validates if the image is tagged successfully.
 | |
| 		err = images.Tag(bt.conn, alpine.shortName, "demo", alpine.shortName, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 
 | |
| 		// Validates if name updates when the image is retagged.
 | |
| 		_, err := images.GetImage(bt.conn, "alpine:demo", nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 
 | |
| 	})
 | |
| 
 | |
| 	// Test to validate the List images command.
 | |
| 	It("List image", func() {
 | |
| 		// Array to hold the list of images returned
 | |
| 		imageSummary, err := images.List(bt.conn, nil)
 | |
| 		// There Should be no errors in the response.
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		// Since in the begin context two images are created the
 | |
| 		// list context should have only 2 images
 | |
| 		Expect(len(imageSummary)).To(Equal(2))
 | |
| 
 | |
| 		// Adding one more image. There Should be no errors in the response.
 | |
| 		// And the count should be three now.
 | |
| 		bt.Pull("testimage:20200929")
 | |
| 		imageSummary, err = images.List(bt.conn, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(len(imageSummary)).To(BeNumerically(">=", 2))
 | |
| 
 | |
| 		// Validate the image names.
 | |
| 		var names []string
 | |
| 		for _, i := range imageSummary {
 | |
| 			names = append(names, i.RepoTags...)
 | |
| 		}
 | |
| 		Expect(names).To(ContainElement(alpine.name))
 | |
| 		Expect(names).To(ContainElement(busybox.name))
 | |
| 
 | |
| 		// List  images with a filter
 | |
| 		filters := make(map[string][]string)
 | |
| 		filters["reference"] = []string{alpine.name}
 | |
| 		options := new(images.ListOptions).WithFilters(filters).WithAll(false)
 | |
| 		filteredImages, err := images.List(bt.conn, options)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(len(filteredImages)).To(BeNumerically("==", 1))
 | |
| 
 | |
| 		// List  images with a bad filter
 | |
| 		filters["name"] = []string{alpine.name}
 | |
| 		options = new(images.ListOptions).WithFilters(filters)
 | |
| 		_, err = images.List(bt.conn, options)
 | |
| 		Expect(err).ToNot(BeNil())
 | |
| 		code, _ := bindings.CheckResponseCode(err)
 | |
| 		Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
 | |
| 	})
 | |
| 
 | |
| 	It("Image Exists", func() {
 | |
| 		// exists on bogus image should be false, with no error
 | |
| 		exists, err := images.Exists(bt.conn, "foobar", nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(exists).To(BeFalse())
 | |
| 
 | |
| 		// exists with shortname should be true
 | |
| 		exists, err = images.Exists(bt.conn, alpine.shortName, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(exists).To(BeTrue())
 | |
| 
 | |
| 		// exists with fqname should be true
 | |
| 		exists, err = images.Exists(bt.conn, alpine.name, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(exists).To(BeTrue())
 | |
| 	})
 | |
| 
 | |
| 	It("Load|Import Image", func() {
 | |
| 		// load an image
 | |
| 		_, errs := images.Remove(bt.conn, []string{alpine.name}, nil)
 | |
| 		Expect(len(errs)).To(BeZero())
 | |
| 		exists, err := images.Exists(bt.conn, alpine.name, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(exists).To(BeFalse())
 | |
| 		f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		defer f.Close()
 | |
| 		names, err := images.Load(bt.conn, f)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(names.Names[0]).To(Equal(alpine.name))
 | |
| 		exists, err = images.Exists(bt.conn, alpine.name, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(exists).To(BeTrue())
 | |
| 
 | |
| 		// load with a repo name
 | |
| 		f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		_, errs = images.Remove(bt.conn, []string{alpine.name}, nil)
 | |
| 		Expect(len(errs)).To(BeZero())
 | |
| 		exists, err = images.Exists(bt.conn, alpine.name, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(exists).To(BeFalse())
 | |
| 		names, err = images.Load(bt.conn, f)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(names.Names[0]).To(Equal(alpine.name))
 | |
| 
 | |
| 		// load with a bad repo name should trigger a 500
 | |
| 		_, errs = images.Remove(bt.conn, []string{alpine.name}, nil)
 | |
| 		Expect(len(errs)).To(BeZero())
 | |
| 		exists, err = images.Exists(bt.conn, alpine.name, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(exists).To(BeFalse())
 | |
| 	})
 | |
| 
 | |
| 	It("Export Image", func() {
 | |
| 		// Export an image
 | |
| 		exportPath := filepath.Join(bt.tempDirPath, alpine.tarballName)
 | |
| 		w, err := os.Create(filepath.Join(bt.tempDirPath, alpine.tarballName))
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		defer w.Close()
 | |
| 		err = images.Export(bt.conn, []string{alpine.name}, w, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		_, err = os.Stat(exportPath)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 
 | |
| 		// TODO how do we verify that a format change worked?
 | |
| 	})
 | |
| 
 | |
| 	It("Import Image", func() {
 | |
| 		// load an image
 | |
| 		_, errs := images.Remove(bt.conn, []string{alpine.name}, nil)
 | |
| 		Expect(len(errs)).To(BeZero())
 | |
| 		exists, err := images.Exists(bt.conn, alpine.name, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(exists).To(BeFalse())
 | |
| 		f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		defer f.Close()
 | |
| 		changes := []string{"CMD /bin/foobar"}
 | |
| 		testMessage := "test_import"
 | |
| 		options := new(images.ImportOptions).WithMessage(testMessage).WithChanges(changes).WithReference(alpine.name)
 | |
| 		_, err = images.Import(bt.conn, f, options)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		exists, err = images.Exists(bt.conn, alpine.name, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(exists).To(BeTrue())
 | |
| 		data, err := images.GetImage(bt.conn, alpine.name, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(data.Comment).To(Equal(testMessage))
 | |
| 
 | |
| 	})
 | |
| 
 | |
| 	It("History Image", func() {
 | |
| 		// a bogus name should return a 404
 | |
| 		_, err := images.History(bt.conn, "foobar", nil)
 | |
| 		Expect(err).To(Not(BeNil()))
 | |
| 		code, _ := bindings.CheckResponseCode(err)
 | |
| 		Expect(code).To(BeNumerically("==", http.StatusNotFound))
 | |
| 
 | |
| 		var foundID bool
 | |
| 		data, err := images.GetImage(bt.conn, alpine.name, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		history, err := images.History(bt.conn, alpine.name, nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		for _, i := range history {
 | |
| 			if i.ID == data.ID {
 | |
| 				foundID = true
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 		Expect(foundID).To(BeTrue())
 | |
| 	})
 | |
| 
 | |
| 	It("Search for an image", func() {
 | |
| 		reports, err := images.Search(bt.conn, "alpine", nil)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(len(reports)).To(BeNumerically(">", 1))
 | |
| 		var foundAlpine bool
 | |
| 		for _, i := range reports {
 | |
| 			if i.Name == "docker.io/library/alpine" {
 | |
| 				foundAlpine = true
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 		Expect(foundAlpine).To(BeTrue())
 | |
| 
 | |
| 		// Search for alpine with a limit of 10
 | |
| 		options := new(images.SearchOptions).WithLimit(10)
 | |
| 		reports, err = images.Search(bt.conn, "docker.io/alpine", options)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(len(reports)).To(BeNumerically("<=", 10))
 | |
| 
 | |
| 		filters := make(map[string][]string)
 | |
| 		filters["stars"] = []string{"100"}
 | |
| 		// Search for alpine with stars greater than 100
 | |
| 		options = new(images.SearchOptions).WithFilters(filters)
 | |
| 		reports, err = images.Search(bt.conn, "docker.io/alpine", options)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		for _, i := range reports {
 | |
| 			Expect(i.Stars).To(BeNumerically(">=", 100))
 | |
| 		}
 | |
| 
 | |
| 		//	Search with a fqdn
 | |
| 		reports, err = images.Search(bt.conn, "quay.io/libpod/alpine_nginx", nil)
 | |
| 		Expect(err).To(BeNil(), "Error in images.Search()")
 | |
| 		Expect(len(reports)).To(BeNumerically(">=", 1))
 | |
| 	})
 | |
| 
 | |
| 	It("Prune images", func() {
 | |
| 		options := new(images.PruneOptions).WithAll(true)
 | |
| 		results, err := images.Prune(bt.conn, options)
 | |
| 		Expect(err).NotTo(HaveOccurred())
 | |
| 		Expect(len(results)).To(BeNumerically(">", 0))
 | |
| 	})
 | |
| 
 | |
| 	// TODO: we really need to extent to pull tests once we have a more sophisticated CI.
 | |
| 	It("Image Pull", func() {
 | |
| 		rawImage := "docker.io/library/busybox:latest"
 | |
| 
 | |
| 		var writer bytes.Buffer
 | |
| 		pullOpts := new(images.PullOptions).WithProgressWriter(&writer)
 | |
| 		pulledImages, err := images.Pull(bt.conn, rawImage, pullOpts)
 | |
| 		Expect(err).NotTo(HaveOccurred())
 | |
| 		Expect(len(pulledImages)).To(Equal(1))
 | |
| 		output := writer.String()
 | |
| 		Expect(output).To(ContainSubstring("Trying to pull "))
 | |
| 		Expect(output).To(ContainSubstring("Getting image source signatures"))
 | |
| 
 | |
| 		exists, err := images.Exists(bt.conn, rawImage, nil)
 | |
| 		Expect(err).NotTo(HaveOccurred())
 | |
| 		Expect(exists).To(BeTrue())
 | |
| 
 | |
| 		// Make sure the normalization AND the full-transport reference works.
 | |
| 		_, err = images.Pull(bt.conn, "docker://"+rawImage, nil)
 | |
| 		Expect(err).NotTo(HaveOccurred())
 | |
| 
 | |
| 		// The v2 endpoint only supports the docker transport.  Let's see if that's really true.
 | |
| 		_, err = images.Pull(bt.conn, "bogus-transport:bogus.com/image:reference", nil)
 | |
| 		Expect(err).To(HaveOccurred())
 | |
| 	})
 | |
| 
 | |
| 	It("Image Push", func() {
 | |
| 		registry, err := podmanRegistry.Start()
 | |
| 		Expect(err).To(BeNil())
 | |
| 
 | |
| 		var writer bytes.Buffer
 | |
| 		pushOpts := new(images.PushOptions).WithUsername(registry.User).WithPassword(registry.Password).WithSkipTLSVerify(true).WithProgressWriter(&writer).WithQuiet(false)
 | |
| 		err = images.Push(bt.conn, alpine.name, fmt.Sprintf("localhost:%s/test:latest", registry.Port), pushOpts)
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 
 | |
| 		output := writer.String()
 | |
| 		Expect(output).To(ContainSubstring("Copying blob "))
 | |
| 		Expect(output).To(ContainSubstring("Copying config "))
 | |
| 		Expect(output).To(ContainSubstring("Writing manifest to image destination"))
 | |
| 		Expect(output).To(ContainSubstring("Storing signatures"))
 | |
| 	})
 | |
| 
 | |
| 	It("Build no options", func() {
 | |
| 		results, err := images.Build(bt.conn, []string{"fixture/Containerfile"}, entities.BuildOptions{})
 | |
| 		Expect(err).ToNot(HaveOccurred())
 | |
| 		Expect(*results).To(MatchFields(IgnoreMissing, Fields{
 | |
| 			"ID": Not(BeEmpty()),
 | |
| 		}))
 | |
| 	})
 | |
| })
 | 
