github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/bindings/test/images_test.go (about)

     1  package bindings_test
     2  
     3  import (
     4  	"net/http"
     5  	"os"
     6  	"path/filepath"
     7  	"time"
     8  
     9  	"github.com/hanks177/podman/v4/pkg/bindings"
    10  	"github.com/hanks177/podman/v4/pkg/bindings/containers"
    11  	"github.com/hanks177/podman/v4/pkg/bindings/images"
    12  	"github.com/hanks177/podman/v4/pkg/domain/entities"
    13  	. "github.com/onsi/ginkgo"
    14  	. "github.com/onsi/gomega"
    15  	. "github.com/onsi/gomega/gexec"
    16  	. "github.com/onsi/gomega/gstruct"
    17  )
    18  
    19  var _ = Describe("Podman images", func() {
    20  	var (
    21  		// tempdir    string
    22  		// err        error
    23  		// podmanTest *PodmanTestIntegration
    24  		bt  *bindingTest
    25  		s   *Session
    26  		err error
    27  	)
    28  
    29  	BeforeEach(func() {
    30  		// tempdir, err = CreateTempDirInTempDir()
    31  		// if err != nil {
    32  		//	os.Exit(1)
    33  		// }
    34  		// podmanTest = PodmanTestCreate(tempdir)
    35  		// podmanTest.Setup()
    36  		// podmanTest.SeedImages()
    37  		bt = newBindingTest()
    38  		bt.RestoreImagesFromCache()
    39  		s = bt.startAPIService()
    40  		time.Sleep(1 * time.Second)
    41  		err := bt.NewConnection()
    42  		Expect(err).ToNot(HaveOccurred())
    43  	})
    44  
    45  	AfterEach(func() {
    46  		// podmanTest.Cleanup()
    47  		// f := CurrentGinkgoTestDescription()
    48  		// processTestResult(f)
    49  		s.Kill()
    50  		bt.cleanup()
    51  	})
    52  
    53  	It("inspect image", func() {
    54  		// Inspect invalid image be 404
    55  		_, err = images.GetImage(bt.conn, "foobar5000", nil)
    56  		Expect(err).ToNot(BeNil())
    57  		code, _ := bindings.CheckResponseCode(err)
    58  		Expect(code).To(BeNumerically("==", http.StatusNotFound))
    59  
    60  		// Inspect by short name
    61  		data, err := images.GetImage(bt.conn, alpine.shortName, nil)
    62  		Expect(err).ToNot(HaveOccurred())
    63  
    64  		// Inspect with full ID
    65  		_, err = images.GetImage(bt.conn, data.ID, nil)
    66  		Expect(err).ToNot(HaveOccurred())
    67  
    68  		// Inspect with partial ID
    69  		_, err = images.GetImage(bt.conn, data.ID[0:12], nil)
    70  		Expect(err).ToNot(HaveOccurred())
    71  
    72  		// Inspect by long name
    73  		_, err = images.GetImage(bt.conn, alpine.name, nil)
    74  		Expect(err).ToNot(HaveOccurred())
    75  		// TODO it looks like the images API always returns size regardless
    76  		// of bool or not. What should we do ?
    77  		// Expect(data.Size).To(BeZero())
    78  
    79  		options := new(images.GetOptions).WithSize(true)
    80  		// Enabling the size parameter should result in size being populated
    81  		data, err = images.GetImage(bt.conn, alpine.name, options)
    82  		Expect(err).ToNot(HaveOccurred())
    83  		Expect(data.Size).To(BeNumerically(">", 0))
    84  	})
    85  
    86  	// Test to validate the remove image api
    87  	It("remove image", func() {
    88  		// NOTE that removing an image that does not exist will still
    89  		// return a 200 http status.  The response, however, includes
    90  		// the exit code that podman-remote should exit with.
    91  		//
    92  		// The libpod/images/remove endpoint supports batch removal of
    93  		// images for performance reasons and for hiding the logic of
    94  		// deciding which exit code to use from the client.
    95  		response, errs := images.Remove(bt.conn, []string{"foobar5000"}, nil)
    96  		Expect(len(errs)).To(BeNumerically(">", 0))
    97  		Expect(response.ExitCode).To(BeNumerically("==", 1)) // podman-remote would exit with 1
    98  
    99  		// Remove an image by name, validate image is removed and error is nil
   100  		inspectData, err := images.GetImage(bt.conn, busybox.shortName, nil)
   101  		Expect(err).ToNot(HaveOccurred())
   102  		response, errs = images.Remove(bt.conn, []string{busybox.shortName}, nil)
   103  		Expect(len(errs)).To(BeZero())
   104  
   105  		Expect(inspectData.ID).To(Equal(response.Deleted[0]))
   106  		_, err = images.GetImage(bt.conn, busybox.shortName, nil)
   107  		code, _ := bindings.CheckResponseCode(err)
   108  		Expect(code).To(BeNumerically("==", http.StatusNotFound))
   109  
   110  		// Start a container with alpine image
   111  		var top string = "top"
   112  		_, err = bt.RunTopContainer(&top, nil)
   113  		Expect(err).ToNot(HaveOccurred())
   114  		// we should now have a container called "top" running
   115  		containerResponse, err := containers.Inspect(bt.conn, "top", nil)
   116  		Expect(err).ToNot(HaveOccurred())
   117  		Expect(containerResponse.Name).To(Equal("top"))
   118  
   119  		// try to remove the image "alpine". This should fail since we are not force
   120  		// deleting hence image cannot be deleted until the container is deleted.
   121  		_, errs = images.Remove(bt.conn, []string{alpine.shortName}, nil)
   122  		code, _ = bindings.CheckResponseCode(errs[0])
   123  		// FIXME FIXME FIXME: #12441: another invalid error
   124  		// FIXME FIXME FIXME: this time msg="Image used by SHA: ..."
   125  		Expect(code).To(BeNumerically("==", -1))
   126  
   127  		// Removing the image "alpine" where force = true
   128  		options := new(images.RemoveOptions).WithForce(true)
   129  		_, errs = images.Remove(bt.conn, []string{alpine.shortName}, options)
   130  		Expect(errs).To(Or(HaveLen(0), BeNil()))
   131  		// To be extra sure, check if the previously created container
   132  		// is gone as well.
   133  		_, err = containers.Inspect(bt.conn, "top", nil)
   134  		code, _ = bindings.CheckResponseCode(err)
   135  		Expect(code).To(BeNumerically("==", http.StatusNotFound))
   136  
   137  		// Now make sure both images are gone.
   138  		_, err = images.GetImage(bt.conn, busybox.shortName, nil)
   139  		code, _ = bindings.CheckResponseCode(err)
   140  		Expect(code).To(BeNumerically("==", http.StatusNotFound))
   141  
   142  		_, err = images.GetImage(bt.conn, alpine.shortName, nil)
   143  		code, _ = bindings.CheckResponseCode(err)
   144  		Expect(code).To(BeNumerically("==", http.StatusNotFound))
   145  	})
   146  
   147  	// Tests to validate the image tag command.
   148  	It("tag image", func() {
   149  
   150  		// Validates if invalid image name is given a bad response is encountered.
   151  		err = images.Tag(bt.conn, "dummy", "demo", alpine.shortName, nil)
   152  		Expect(err).ToNot(BeNil())
   153  		code, _ := bindings.CheckResponseCode(err)
   154  		Expect(code).To(BeNumerically("==", http.StatusNotFound))
   155  
   156  		// Validates if the image is tagged successfully.
   157  		err = images.Tag(bt.conn, alpine.shortName, "demo", alpine.shortName, nil)
   158  		Expect(err).ToNot(HaveOccurred())
   159  
   160  		// Validates if name updates when the image is retagged.
   161  		_, err := images.GetImage(bt.conn, "alpine:demo", nil)
   162  		Expect(err).ToNot(HaveOccurred())
   163  
   164  	})
   165  
   166  	// Test to validate the List images command.
   167  	It("List image", func() {
   168  		// Array to hold the list of images returned
   169  		imageSummary, err := images.List(bt.conn, nil)
   170  		// There Should be no errors in the response.
   171  		Expect(err).ToNot(HaveOccurred())
   172  		// Since in the begin context two images are created the
   173  		// list context should have only 2 images
   174  		Expect(len(imageSummary)).To(Equal(2))
   175  
   176  		// Adding one more image. There Should be no errors in the response.
   177  		// And the count should be three now.
   178  		bt.Pull("testimage:20200929")
   179  		imageSummary, err = images.List(bt.conn, nil)
   180  		Expect(err).ToNot(HaveOccurred())
   181  		Expect(len(imageSummary)).To(BeNumerically(">=", 2))
   182  
   183  		// Validate the image names.
   184  		var names []string
   185  		for _, i := range imageSummary {
   186  			names = append(names, i.RepoTags...)
   187  		}
   188  		Expect(names).To(ContainElement(alpine.name))
   189  		Expect(names).To(ContainElement(busybox.name))
   190  
   191  		// List  images with a filter
   192  		filters := make(map[string][]string)
   193  		filters["reference"] = []string{alpine.name}
   194  		options := new(images.ListOptions).WithFilters(filters).WithAll(false)
   195  		filteredImages, err := images.List(bt.conn, options)
   196  		Expect(err).ToNot(HaveOccurred())
   197  		Expect(len(filteredImages)).To(BeNumerically("==", 1))
   198  
   199  		// List  images with a bad filter
   200  		filters["name"] = []string{alpine.name}
   201  		options = new(images.ListOptions).WithFilters(filters)
   202  		_, err = images.List(bt.conn, options)
   203  		Expect(err).ToNot(BeNil())
   204  		code, _ := bindings.CheckResponseCode(err)
   205  		Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
   206  	})
   207  
   208  	It("Image Exists", func() {
   209  		// exists on bogus image should be false, with no error
   210  		exists, err := images.Exists(bt.conn, "foobar", nil)
   211  		Expect(err).ToNot(HaveOccurred())
   212  		Expect(exists).To(BeFalse())
   213  
   214  		// exists with shortname should be true
   215  		exists, err = images.Exists(bt.conn, alpine.shortName, nil)
   216  		Expect(err).ToNot(HaveOccurred())
   217  		Expect(exists).To(BeTrue())
   218  
   219  		// exists with fqname should be true
   220  		exists, err = images.Exists(bt.conn, alpine.name, nil)
   221  		Expect(err).ToNot(HaveOccurred())
   222  		Expect(exists).To(BeTrue())
   223  	})
   224  
   225  	It("Load|Import Image", func() {
   226  		// load an image
   227  		_, errs := images.Remove(bt.conn, []string{alpine.name}, nil)
   228  		Expect(len(errs)).To(BeZero())
   229  		exists, err := images.Exists(bt.conn, alpine.name, nil)
   230  		Expect(err).ToNot(HaveOccurred())
   231  		Expect(exists).To(BeFalse())
   232  		f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
   233  		Expect(err).ToNot(HaveOccurred())
   234  		defer f.Close()
   235  		names, err := images.Load(bt.conn, f)
   236  		Expect(err).ToNot(HaveOccurred())
   237  		Expect(names.Names[0]).To(Equal(alpine.name))
   238  		exists, err = images.Exists(bt.conn, alpine.name, nil)
   239  		Expect(err).ToNot(HaveOccurred())
   240  		Expect(exists).To(BeTrue())
   241  
   242  		// load with a repo name
   243  		f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
   244  		Expect(err).ToNot(HaveOccurred())
   245  		_, errs = images.Remove(bt.conn, []string{alpine.name}, nil)
   246  		Expect(len(errs)).To(BeZero())
   247  		exists, err = images.Exists(bt.conn, alpine.name, nil)
   248  		Expect(err).ToNot(HaveOccurred())
   249  		Expect(exists).To(BeFalse())
   250  		names, err = images.Load(bt.conn, f)
   251  		Expect(err).ToNot(HaveOccurred())
   252  		Expect(names.Names[0]).To(Equal(alpine.name))
   253  
   254  		// load with a bad repo name should trigger a 500
   255  		_, errs = images.Remove(bt.conn, []string{alpine.name}, nil)
   256  		Expect(len(errs)).To(BeZero())
   257  		exists, err = images.Exists(bt.conn, alpine.name, nil)
   258  		Expect(err).ToNot(HaveOccurred())
   259  		Expect(exists).To(BeFalse())
   260  	})
   261  
   262  	It("Export Image", func() {
   263  		// Export an image
   264  		exportPath := filepath.Join(bt.tempDirPath, alpine.tarballName)
   265  		w, err := os.Create(filepath.Join(bt.tempDirPath, alpine.tarballName))
   266  		Expect(err).ToNot(HaveOccurred())
   267  		defer w.Close()
   268  		err = images.Export(bt.conn, []string{alpine.name}, w, nil)
   269  		Expect(err).ToNot(HaveOccurred())
   270  		_, err = os.Stat(exportPath)
   271  		Expect(err).ToNot(HaveOccurred())
   272  
   273  		// TODO how do we verify that a format change worked?
   274  	})
   275  
   276  	It("Import Image", func() {
   277  		// load an image
   278  		_, errs := images.Remove(bt.conn, []string{alpine.name}, nil)
   279  		Expect(len(errs)).To(BeZero())
   280  		exists, err := images.Exists(bt.conn, alpine.name, nil)
   281  		Expect(err).ToNot(HaveOccurred())
   282  		Expect(exists).To(BeFalse())
   283  		f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
   284  		Expect(err).ToNot(HaveOccurred())
   285  		defer f.Close()
   286  		changes := []string{"CMD /bin/foobar"}
   287  		testMessage := "test_import"
   288  		options := new(images.ImportOptions).WithMessage(testMessage).WithChanges(changes).WithReference(alpine.name)
   289  		_, err = images.Import(bt.conn, f, options)
   290  		Expect(err).ToNot(HaveOccurred())
   291  		exists, err = images.Exists(bt.conn, alpine.name, nil)
   292  		Expect(err).ToNot(HaveOccurred())
   293  		Expect(exists).To(BeTrue())
   294  		data, err := images.GetImage(bt.conn, alpine.name, nil)
   295  		Expect(err).ToNot(HaveOccurred())
   296  		Expect(data.Comment).To(Equal(testMessage))
   297  
   298  	})
   299  
   300  	It("History Image", func() {
   301  		// a bogus name should return a 404
   302  		_, err := images.History(bt.conn, "foobar", nil)
   303  		Expect(err).To(Not(BeNil()))
   304  		code, _ := bindings.CheckResponseCode(err)
   305  		Expect(code).To(BeNumerically("==", http.StatusNotFound))
   306  
   307  		var foundID bool
   308  		data, err := images.GetImage(bt.conn, alpine.name, nil)
   309  		Expect(err).ToNot(HaveOccurred())
   310  		history, err := images.History(bt.conn, alpine.name, nil)
   311  		Expect(err).ToNot(HaveOccurred())
   312  		for _, i := range history {
   313  			if i.ID == data.ID {
   314  				foundID = true
   315  				break
   316  			}
   317  		}
   318  		Expect(foundID).To(BeTrue())
   319  	})
   320  
   321  	It("Search for an image", func() {
   322  		reports, err := images.Search(bt.conn, "alpine", nil)
   323  		Expect(err).ToNot(HaveOccurred())
   324  		Expect(len(reports)).To(BeNumerically(">", 1))
   325  		var foundAlpine bool
   326  		for _, i := range reports {
   327  			if i.Name == "docker.io/library/alpine" {
   328  				foundAlpine = true
   329  				break
   330  			}
   331  		}
   332  		Expect(foundAlpine).To(BeTrue())
   333  
   334  		// Search for alpine with a limit of 10
   335  		options := new(images.SearchOptions).WithLimit(10)
   336  		reports, err = images.Search(bt.conn, "docker.io/alpine", options)
   337  		Expect(err).ToNot(HaveOccurred())
   338  		Expect(len(reports)).To(BeNumerically("<=", 10))
   339  
   340  		filters := make(map[string][]string)
   341  		filters["stars"] = []string{"100"}
   342  		// Search for alpine with stars greater than 100
   343  		options = new(images.SearchOptions).WithFilters(filters)
   344  		reports, err = images.Search(bt.conn, "docker.io/alpine", options)
   345  		Expect(err).ToNot(HaveOccurred())
   346  		for _, i := range reports {
   347  			Expect(i.Stars).To(BeNumerically(">=", 100))
   348  		}
   349  
   350  		//	Search with a fqdn
   351  		reports, err = images.Search(bt.conn, "quay.io/libpod/alpine_nginx", nil)
   352  		Expect(err).To(BeNil(), "Error in images.Search()")
   353  		Expect(len(reports)).To(BeNumerically(">=", 1))
   354  	})
   355  
   356  	It("Prune images", func() {
   357  		options := new(images.PruneOptions).WithAll(true)
   358  		results, err := images.Prune(bt.conn, options)
   359  		Expect(err).NotTo(HaveOccurred())
   360  		Expect(len(results)).To(BeNumerically(">", 0))
   361  	})
   362  
   363  	// TODO: we really need to extent to pull tests once we have a more sophisticated CI.
   364  	It("Image Pull", func() {
   365  		rawImage := "docker.io/library/busybox:latest"
   366  
   367  		pulledImages, err := images.Pull(bt.conn, rawImage, nil)
   368  		Expect(err).NotTo(HaveOccurred())
   369  		Expect(len(pulledImages)).To(Equal(1))
   370  
   371  		exists, err := images.Exists(bt.conn, rawImage, nil)
   372  		Expect(err).NotTo(HaveOccurred())
   373  		Expect(exists).To(BeTrue())
   374  
   375  		// Make sure the normalization AND the full-transport reference works.
   376  		_, err = images.Pull(bt.conn, "docker://"+rawImage, nil)
   377  		Expect(err).NotTo(HaveOccurred())
   378  
   379  		// The v2 endpoint only supports the docker transport.  Let's see if that's really true.
   380  		_, err = images.Pull(bt.conn, "bogus-transport:bogus.com/image:reference", nil)
   381  		Expect(err).To(HaveOccurred())
   382  	})
   383  
   384  	It("Build no options", func() {
   385  		results, err := images.Build(bt.conn, []string{"fixture/Containerfile"}, entities.BuildOptions{})
   386  		Expect(err).ToNot(HaveOccurred())
   387  		Expect(*results).To(MatchFields(IgnoreMissing, Fields{
   388  			"ID": Not(BeEmpty()),
   389  		}))
   390  	})
   391  })