github.com/containers/podman/v4@v4.9.4/test/e2e/common_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"math/rand"
    10  	"net"
    11  	"net/url"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"sort"
    16  	"strconv"
    17  	"strings"
    18  	"sync"
    19  	"syscall"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/containers/common/pkg/cgroups"
    24  	"github.com/containers/podman/v4/libpod/define"
    25  	"github.com/containers/podman/v4/pkg/inspect"
    26  	"github.com/containers/podman/v4/pkg/util"
    27  	. "github.com/containers/podman/v4/test/utils"
    28  	"github.com/containers/storage/pkg/lockfile"
    29  	"github.com/containers/storage/pkg/reexec"
    30  	"github.com/containers/storage/pkg/stringid"
    31  	jsoniter "github.com/json-iterator/go"
    32  	. "github.com/onsi/ginkgo/v2"
    33  	. "github.com/onsi/gomega"
    34  	. "github.com/onsi/gomega/gexec"
    35  	"github.com/sirupsen/logrus"
    36  	"golang.org/x/sys/unix"
    37  )
    38  
    39  var (
    40  	//lint:ignore ST1003
    41  	PODMAN_BINARY      string                              //nolint:revive,stylecheck
    42  	INTEGRATION_ROOT   string                              //nolint:revive,stylecheck
    43  	CGROUP_MANAGER     = "systemd"                         //nolint:revive,stylecheck
    44  	RESTORE_IMAGES     = []string{ALPINE, BB, NGINX_IMAGE} //nolint:revive,stylecheck
    45  	defaultWaitTimeout = 90
    46  	CGROUPSV2, _       = cgroups.IsCgroup2UnifiedMode()
    47  )
    48  
    49  // PodmanTestIntegration struct for command line options
    50  type PodmanTestIntegration struct {
    51  	PodmanTest
    52  	ConmonBinary        string
    53  	QuadletBinary       string
    54  	Root                string
    55  	NetworkConfigDir    string
    56  	OCIRuntime          string
    57  	RunRoot             string
    58  	StorageOptions      string
    59  	SignaturePolicyPath string
    60  	CgroupManager       string
    61  	Host                HostOS
    62  	TmpDir              string
    63  }
    64  
    65  var LockTmpDir string
    66  
    67  // PodmanSessionIntegration struct for command line session
    68  type PodmanSessionIntegration struct {
    69  	*PodmanSession
    70  }
    71  
    72  type testResult struct {
    73  	name   string
    74  	length float64
    75  }
    76  
    77  type testResultsSorted []testResult
    78  
    79  func (a testResultsSorted) Len() int      { return len(a) }
    80  func (a testResultsSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
    81  
    82  type testResultsSortedLength struct{ testResultsSorted }
    83  
    84  func (a testResultsSorted) Less(i, j int) bool { return a[i].length < a[j].length }
    85  
    86  func TestMain(m *testing.M) {
    87  	if reexec.Init() {
    88  		return
    89  	}
    90  	os.Exit(m.Run())
    91  }
    92  
    93  // TestLibpod ginkgo master function
    94  func TestLibpod(t *testing.T) {
    95  	if os.Getenv("NOCACHE") == "1" {
    96  		CACHE_IMAGES = []string{}
    97  		RESTORE_IMAGES = []string{}
    98  	}
    99  	RegisterFailHandler(Fail)
   100  	RunSpecs(t, "Libpod Suite")
   101  }
   102  
   103  var (
   104  	tempdir      string
   105  	err          error
   106  	podmanTest   *PodmanTestIntegration
   107  	safeIPOctets [2]uint8
   108  	timingsFile  *os.File
   109  
   110  	_ = BeforeEach(func() {
   111  		tempdir, err = CreateTempDirInTempDir()
   112  		Expect(err).ToNot(HaveOccurred())
   113  		podmanTest = PodmanTestCreate(tempdir)
   114  		podmanTest.Setup()
   115  		// see GetSafeIPAddress() below
   116  		safeIPOctets[0] = uint8(GinkgoT().ParallelProcess()) + 128
   117  		safeIPOctets[1] = 2
   118  	})
   119  
   120  	_ = AfterEach(func() {
   121  		// First unset CONTAINERS_CONF before doing Cleanup() to prevent
   122  		// invalid containers.conf files to fail the cleanup.
   123  		os.Unsetenv("CONTAINERS_CONF")
   124  		os.Unsetenv("CONTAINERS_CONF_OVERRIDE")
   125  		podmanTest.Cleanup()
   126  		f := CurrentSpecReport()
   127  		processTestResult(f)
   128  	})
   129  )
   130  
   131  const (
   132  	// lockdir - do not use directly use LockTmpDir
   133  	lockdir = "libpodlock"
   134  	// imageCacheDir - do not use directly use ImageCacheDir
   135  	imageCacheDir = "imagecachedir"
   136  )
   137  
   138  var _ = SynchronizedBeforeSuite(func() []byte {
   139  	globalTmpDir := GinkgoT().TempDir()
   140  
   141  	// make cache dir
   142  	ImageCacheDir = filepath.Join(globalTmpDir, imageCacheDir)
   143  	if err := os.MkdirAll(ImageCacheDir, 0700); err != nil {
   144  		GinkgoWriter.Printf("%q\n", err)
   145  		os.Exit(1)
   146  	}
   147  
   148  	// Cache images
   149  	cwd, _ := os.Getwd()
   150  	INTEGRATION_ROOT = filepath.Join(cwd, "../../")
   151  	podman := PodmanTestSetup(GinkgoT().TempDir())
   152  
   153  	// Pull cirros but don't put it into the cache
   154  	pullImages := []string{CIRROS_IMAGE, fedoraToolbox, volumeTest}
   155  	pullImages = append(pullImages, CACHE_IMAGES...)
   156  	for _, image := range pullImages {
   157  		podman.createArtifact(image)
   158  	}
   159  
   160  	if err := os.MkdirAll(filepath.Join(ImageCacheDir, podman.ImageCacheFS+"-images"), 0777); err != nil {
   161  		GinkgoWriter.Printf("%q\n", err)
   162  		os.Exit(1)
   163  	}
   164  	podman.Root = ImageCacheDir
   165  	// If running localized tests, the cache dir is created and populated. if the
   166  	// tests are remote, this is a no-op
   167  	populateCache(podman)
   168  
   169  	if err := os.MkdirAll(filepath.Join(globalTmpDir, lockdir), 0700); err != nil {
   170  		GinkgoWriter.Printf("%q\n", err)
   171  		os.Exit(1)
   172  	}
   173  
   174  	// If running remote, we need to stop the associated podman system service
   175  	if podman.RemoteTest {
   176  		podman.StopRemoteService()
   177  	}
   178  
   179  	// remove temporary podman files, images are now cached in ImageCacheDir
   180  	rmAll(podman.PodmanBinary, podman.TempDir)
   181  
   182  	return []byte(globalTmpDir)
   183  }, func(data []byte) {
   184  	cwd, _ := os.Getwd()
   185  	INTEGRATION_ROOT = filepath.Join(cwd, "../../")
   186  	globalTmpDir := string(data)
   187  	ImageCacheDir = filepath.Join(globalTmpDir, imageCacheDir)
   188  	LockTmpDir = filepath.Join(globalTmpDir, lockdir)
   189  
   190  	timingsFile, err = os.Create(fmt.Sprintf("%s/timings-%d", LockTmpDir, GinkgoParallelProcess()))
   191  	Expect(err).ToNot(HaveOccurred())
   192  })
   193  
   194  func (p *PodmanTestIntegration) Setup() {
   195  	cwd, _ := os.Getwd()
   196  	INTEGRATION_ROOT = filepath.Join(cwd, "../../")
   197  }
   198  
   199  var _ = SynchronizedAfterSuite(func() {
   200  	timingsFile.Close()
   201  	timingsFile = nil
   202  },
   203  	func() {
   204  		testTimings := make(testResultsSorted, 0, 2000)
   205  		for i := 1; i <= GinkgoT().ParallelTotal(); i++ {
   206  			f, err := os.Open(fmt.Sprintf("%s/timings-%d", LockTmpDir, i))
   207  			Expect(err).ToNot(HaveOccurred())
   208  			defer f.Close()
   209  			scanner := bufio.NewScanner(f)
   210  			for scanner.Scan() {
   211  				text := scanner.Text()
   212  				timing := strings.SplitN(text, "\t\t", 2)
   213  				if len(timing) != 2 {
   214  					Fail(fmt.Sprintf("incorrect timing line: %q", text))
   215  				}
   216  				name := timing[0]
   217  				duration, err := strconv.ParseFloat(timing[1], 64)
   218  				Expect(err).ToNot(HaveOccurred(), "failed to parse float from timings file")
   219  				testTimings = append(testTimings, testResult{name: name, length: duration})
   220  			}
   221  			if err := scanner.Err(); err != nil {
   222  				Expect(err).ToNot(HaveOccurred(), "read timings %d", i)
   223  			}
   224  		}
   225  		sort.Sort(testResultsSortedLength{testTimings})
   226  		GinkgoWriter.Println("integration timing results")
   227  		for _, result := range testTimings {
   228  			GinkgoWriter.Printf("%s\t\t%f\n", result.name, result.length)
   229  		}
   230  
   231  		cwd, _ := os.Getwd()
   232  		rmAll(getPodmanBinary(cwd), ImageCacheDir)
   233  	})
   234  
   235  func getPodmanBinary(cwd string) string {
   236  	podmanBinary := filepath.Join(cwd, "../../bin/podman")
   237  	if os.Getenv("PODMAN_BINARY") != "" {
   238  		podmanBinary = os.Getenv("PODMAN_BINARY")
   239  	}
   240  	return podmanBinary
   241  }
   242  
   243  // PodmanTestCreate creates a PodmanTestIntegration instance for the tests
   244  func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration {
   245  	var podmanRemoteBinary string
   246  
   247  	host := GetHostDistributionInfo()
   248  	cwd, _ := os.Getwd()
   249  
   250  	root := filepath.Join(tempDir, "root")
   251  	podmanBinary := getPodmanBinary(cwd)
   252  
   253  	podmanRemoteBinary = filepath.Join(cwd, "../../bin/podman-remote")
   254  	if os.Getenv("PODMAN_REMOTE_BINARY") != "" {
   255  		podmanRemoteBinary = os.Getenv("PODMAN_REMOTE_BINARY")
   256  	}
   257  
   258  	quadletBinary := filepath.Join(cwd, "../../bin/quadlet")
   259  	if os.Getenv("QUADLET_BINARY") != "" {
   260  		quadletBinary = os.Getenv("QUADLET_BINARY")
   261  	}
   262  
   263  	conmonBinary := "/usr/libexec/podman/conmon"
   264  	altConmonBinary := "/usr/bin/conmon"
   265  	if _, err := os.Stat(conmonBinary); os.IsNotExist(err) {
   266  		conmonBinary = altConmonBinary
   267  	}
   268  	if os.Getenv("CONMON_BINARY") != "" {
   269  		conmonBinary = os.Getenv("CONMON_BINARY")
   270  	}
   271  	storageOptions := STORAGE_OPTIONS
   272  	if os.Getenv("STORAGE_OPTIONS") != "" {
   273  		storageOptions = os.Getenv("STORAGE_OPTIONS")
   274  	}
   275  
   276  	cgroupManager := CGROUP_MANAGER
   277  	if isRootless() {
   278  		cgroupManager = "cgroupfs"
   279  	}
   280  	if os.Getenv("CGROUP_MANAGER") != "" {
   281  		cgroupManager = os.Getenv("CGROUP_MANAGER")
   282  	}
   283  
   284  	ociRuntime := os.Getenv("OCI_RUNTIME")
   285  	if ociRuntime == "" {
   286  		ociRuntime = "crun"
   287  	}
   288  	os.Setenv("DISABLE_HC_SYSTEMD", "true")
   289  
   290  	dbBackend := "sqlite"
   291  	if os.Getenv("PODMAN_DB") == "boltdb" {
   292  		dbBackend = "boltdb"
   293  	}
   294  
   295  	networkBackend := Netavark
   296  	networkConfigDir := "/etc/containers/networks"
   297  	if isRootless() {
   298  		networkConfigDir = filepath.Join(root, "etc", "networks")
   299  	}
   300  
   301  	if strings.ToLower(os.Getenv("NETWORK_BACKEND")) == "cni" {
   302  		networkBackend = CNI
   303  		networkConfigDir = "/etc/cni/net.d"
   304  		if isRootless() {
   305  			networkConfigDir = filepath.Join(os.Getenv("HOME"), ".config/cni/net.d")
   306  		}
   307  	}
   308  
   309  	if err := os.MkdirAll(root, 0755); err != nil {
   310  		panic(err)
   311  	}
   312  
   313  	if err := os.MkdirAll(networkConfigDir, 0755); err != nil {
   314  		panic(err)
   315  	}
   316  
   317  	storageFs := STORAGE_FS
   318  	if isRootless() {
   319  		storageFs = ROOTLESS_STORAGE_FS
   320  	}
   321  	if os.Getenv("STORAGE_FS") != "" {
   322  		storageFs = os.Getenv("STORAGE_FS")
   323  		storageOptions = "--storage-driver " + storageFs
   324  	}
   325  
   326  	p := &PodmanTestIntegration{
   327  		PodmanTest: PodmanTest{
   328  			PodmanBinary:       podmanBinary,
   329  			RemotePodmanBinary: podmanRemoteBinary,
   330  			TempDir:            tempDir,
   331  			RemoteTest:         remote,
   332  			ImageCacheFS:       storageFs,
   333  			ImageCacheDir:      ImageCacheDir,
   334  			NetworkBackend:     networkBackend,
   335  			DatabaseBackend:    dbBackend,
   336  		},
   337  		ConmonBinary:        conmonBinary,
   338  		QuadletBinary:       quadletBinary,
   339  		Root:                root,
   340  		TmpDir:              tempDir,
   341  		NetworkConfigDir:    networkConfigDir,
   342  		OCIRuntime:          ociRuntime,
   343  		RunRoot:             filepath.Join(tempDir, "runroot"),
   344  		StorageOptions:      storageOptions,
   345  		SignaturePolicyPath: filepath.Join(INTEGRATION_ROOT, "test/policy.json"),
   346  		CgroupManager:       cgroupManager,
   347  		Host:                host,
   348  	}
   349  
   350  	if remote {
   351  		var pathPrefix string
   352  		if !isRootless() {
   353  			pathPrefix = "/run/podman/podman"
   354  		} else {
   355  			runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
   356  			pathPrefix = filepath.Join(runtimeDir, "podman")
   357  		}
   358  		// We want to avoid collisions in socket paths, but using the
   359  		// socket directly for a collision check doesn’t work; bind(2) on AF_UNIX
   360  		// creates the file, and we need to pass a unique path now before the bind(2)
   361  		// happens. So, use a podman-%s.sock-lock empty file as a marker.
   362  		tries := 0
   363  		for {
   364  			uuid := stringid.GenerateRandomID()
   365  			lockPath := fmt.Sprintf("%s-%s.sock-lock", pathPrefix, uuid)
   366  			lockFile, err := os.OpenFile(lockPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0700)
   367  			if err == nil {
   368  				lockFile.Close()
   369  				p.RemoteSocketLock = lockPath
   370  				p.RemoteSocket = fmt.Sprintf("unix://%s-%s.sock", pathPrefix, uuid)
   371  				break
   372  			}
   373  			tries++
   374  			if tries >= 1000 {
   375  				panic("Too many RemoteSocket collisions")
   376  			}
   377  		}
   378  	}
   379  
   380  	// Set up registries.conf ENV variable
   381  	p.setDefaultRegistriesConfigEnv()
   382  	// Rewrite the PodmanAsUser function
   383  	p.PodmanMakeOptions = p.makeOptions
   384  	return p
   385  }
   386  
   387  func (p PodmanTestIntegration) AddImageToRWStore(image string) {
   388  	if err := p.RestoreArtifact(image); err != nil {
   389  		logrus.Errorf("Unable to restore %s to RW store", image)
   390  	}
   391  }
   392  
   393  func imageTarPath(image string) string {
   394  	cacheDir := os.Getenv("PODMAN_TEST_IMAGE_CACHE_DIR")
   395  	if cacheDir == "" {
   396  		cacheDir = os.Getenv("TMPDIR")
   397  		if cacheDir == "" {
   398  			cacheDir = "/tmp"
   399  		}
   400  	}
   401  
   402  	// e.g., registry.com/fubar:latest -> registry.com-fubar-latest.tar
   403  	imageCacheName := strings.ReplaceAll(strings.ReplaceAll(image, ":", "-"), "/", "-") + ".tar"
   404  
   405  	return filepath.Join(cacheDir, imageCacheName)
   406  }
   407  
   408  // createArtifact creates a cached image tarball in a local directory
   409  func (p *PodmanTestIntegration) createArtifact(image string) {
   410  	if os.Getenv("NO_TEST_CACHE") != "" {
   411  		return
   412  	}
   413  	destName := imageTarPath(image)
   414  	if _, err := os.Stat(destName); os.IsNotExist(err) {
   415  		GinkgoWriter.Printf("Caching %s at %s...\n", image, destName)
   416  		for try := 0; try < 3; try++ {
   417  			pull := p.PodmanNoCache([]string{"pull", image})
   418  			pull.Wait(440)
   419  			if pull.ExitCode() == 0 {
   420  				break
   421  			}
   422  			if try == 2 {
   423  				Expect(pull).Should(Exit(0), "Failed after many retries")
   424  			}
   425  
   426  			GinkgoWriter.Println("Will wait and retry")
   427  			time.Sleep(time.Duration(try+1) * 5 * time.Second)
   428  		}
   429  
   430  		save := p.PodmanNoCache([]string{"save", "-o", destName, image})
   431  		save.Wait(90)
   432  		Expect(save).Should(Exit(0))
   433  		GinkgoWriter.Printf("\n")
   434  	} else {
   435  		GinkgoWriter.Printf("[image already cached: %s]\n", destName)
   436  	}
   437  }
   438  
   439  // InspectImageJSON takes the session output of an inspect
   440  // image and returns json
   441  func (s *PodmanSessionIntegration) InspectImageJSON() []inspect.ImageData {
   442  	var i []inspect.ImageData
   443  	err := jsoniter.Unmarshal(s.Out.Contents(), &i)
   444  	Expect(err).ToNot(HaveOccurred())
   445  	return i
   446  }
   447  
   448  // InspectContainer returns a container's inspect data in JSON format
   449  func (p *PodmanTestIntegration) InspectContainer(name string) []define.InspectContainerData {
   450  	cmd := []string{"inspect", name}
   451  	session := p.Podman(cmd)
   452  	session.WaitWithDefaultTimeout()
   453  	Expect(session).Should(Exit(0))
   454  	return session.InspectContainerToJSON()
   455  }
   456  
   457  func processTestResult(r SpecReport) {
   458  	tr := testResult{length: r.RunTime.Seconds(), name: r.FullText()}
   459  	_, err := timingsFile.WriteString(fmt.Sprintf("%s\t\t%f\n", tr.name, tr.length))
   460  	Expect(err).ToNot(HaveOccurred(), "write timings")
   461  }
   462  
   463  func GetPortLock(port string) *lockfile.LockFile {
   464  	lockFile := filepath.Join(LockTmpDir, port)
   465  	lock, err := lockfile.GetLockFile(lockFile)
   466  	if err != nil {
   467  		GinkgoWriter.Println(err)
   468  		os.Exit(1)
   469  	}
   470  	lock.Lock()
   471  	return lock
   472  }
   473  
   474  // GetSafeIPAddress returns a sequentially allocated IP address that _should_
   475  // be safe and unique across parallel tasks
   476  //
   477  // Used by tests which want to use "--ip SOMETHING-SAFE". Picking at random
   478  // just doesn't work: we get occasional collisions. Our current approach
   479  // allocates a /24 subnet for each ginkgo process, starting at .128.x, see
   480  // BeforeEach() above. Unfortunately, CNI remembers each address assigned
   481  // and assigns <previous+1> by default -- so other parallel jobs may
   482  // get IPs in our block. The +10 leaves a gap for that. (Netavark works
   483  // differently, allocating sequentially from .0.0, hence our .128.x).
   484  // This heuristic will fail if run in parallel on >127 processors or if
   485  // one test calls us more than 25 times or if some other test runs more
   486  // than ten networked containers at the same time as any test that
   487  // relies on GetSafeIPAddress(). I'm finding it hard to care.
   488  //
   489  // DO NOT USE THIS FUNCTION unless there is no possible alternative. In
   490  // most cases you should use 'podman network create' + 'podman run --network'.
   491  func GetSafeIPAddress() string {
   492  	safeIPOctets[1] += 10
   493  	return fmt.Sprintf("10.88.%d.%d", safeIPOctets[0], safeIPOctets[1])
   494  }
   495  
   496  // RunTopContainer runs a simple container in the background that
   497  // runs top.  If the name passed != "", it will have a name
   498  func (p *PodmanTestIntegration) RunTopContainer(name string) *PodmanSessionIntegration {
   499  	return p.RunTopContainerWithArgs(name, nil)
   500  }
   501  
   502  // RunTopContainerWithArgs runs a simple container in the background that
   503  // runs top.  If the name passed != "", it will have a name, command args can also be passed in
   504  func (p *PodmanTestIntegration) RunTopContainerWithArgs(name string, args []string) *PodmanSessionIntegration {
   505  	// In proxy environment, some tests need to the --http-proxy=false option (#16684)
   506  	var podmanArgs = []string{"run", "--http-proxy=false"}
   507  	if name != "" {
   508  		podmanArgs = append(podmanArgs, "--name", name)
   509  	}
   510  	podmanArgs = append(podmanArgs, args...)
   511  	podmanArgs = append(podmanArgs, "-d", ALPINE, "top")
   512  	return p.Podman(podmanArgs)
   513  }
   514  
   515  // RunLsContainer runs a simple container in the background that
   516  // simply runs ls. If the name passed != "", it will have a name
   517  func (p *PodmanTestIntegration) RunLsContainer(name string) (*PodmanSessionIntegration, int, string) {
   518  	var podmanArgs = []string{"run"}
   519  	if name != "" {
   520  		podmanArgs = append(podmanArgs, "--name", name)
   521  	}
   522  	podmanArgs = append(podmanArgs, "-d", ALPINE, "ls")
   523  	session := p.Podman(podmanArgs)
   524  	session.WaitWithDefaultTimeout()
   525  	if session.ExitCode() != 0 {
   526  		return session, session.ExitCode(), session.OutputToString()
   527  	}
   528  	cid := session.OutputToString()
   529  
   530  	wsession := p.Podman([]string{"wait", cid})
   531  	wsession.WaitWithDefaultTimeout()
   532  	return session, wsession.ExitCode(), cid
   533  }
   534  
   535  // RunNginxWithHealthCheck runs the alpine nginx container with an optional name and adds a healthcheck into it
   536  func (p *PodmanTestIntegration) RunNginxWithHealthCheck(name string) (*PodmanSessionIntegration, string) {
   537  	var podmanArgs = []string{"run"}
   538  	if name != "" {
   539  		podmanArgs = append(podmanArgs, "--name", name)
   540  	}
   541  	// curl without -f exits 0 even if http code >= 400!
   542  	podmanArgs = append(podmanArgs, "-dt", "-P", "--health-cmd", "curl -f http://localhost/", NGINX_IMAGE)
   543  	session := p.Podman(podmanArgs)
   544  	session.WaitWithDefaultTimeout()
   545  	return session, session.OutputToString()
   546  }
   547  
   548  // RunContainerWithNetworkTest runs the fedoraMinimal curl with the specified network mode.
   549  func (p *PodmanTestIntegration) RunContainerWithNetworkTest(mode string) *PodmanSessionIntegration {
   550  	var podmanArgs = []string{"run"}
   551  	if mode != "" {
   552  		podmanArgs = append(podmanArgs, "--network", mode)
   553  	}
   554  	podmanArgs = append(podmanArgs, fedoraMinimal, "curl", "-s", "-S", "-k", "-o", "/dev/null", "http://www.redhat.com:80")
   555  	session := p.Podman(podmanArgs)
   556  	return session
   557  }
   558  
   559  func (p *PodmanTestIntegration) RunLsContainerInPod(name, pod string) (*PodmanSessionIntegration, int, string) {
   560  	var podmanArgs = []string{"run", "--pod", pod}
   561  	if name != "" {
   562  		podmanArgs = append(podmanArgs, "--name", name)
   563  	}
   564  	podmanArgs = append(podmanArgs, "-d", ALPINE, "ls")
   565  	session := p.Podman(podmanArgs)
   566  	session.WaitWithDefaultTimeout()
   567  	if session.ExitCode() != 0 {
   568  		return session, session.ExitCode(), session.OutputToString()
   569  	}
   570  	cid := session.OutputToString()
   571  
   572  	wsession := p.Podman([]string{"wait", cid})
   573  	wsession.WaitWithDefaultTimeout()
   574  	return session, wsession.ExitCode(), cid
   575  }
   576  
   577  // BuildImage uses podman build and buildah to build an image
   578  // called imageName based on a string dockerfile
   579  func (p *PodmanTestIntegration) BuildImage(dockerfile, imageName string, layers string) string {
   580  	return p.buildImage(dockerfile, imageName, layers, "")
   581  }
   582  
   583  // BuildImageWithLabel uses podman build and buildah to build an image
   584  // called imageName based on a string dockerfile, adds desired label to paramset
   585  func (p *PodmanTestIntegration) BuildImageWithLabel(dockerfile, imageName string, layers string, label string) string {
   586  	return p.buildImage(dockerfile, imageName, layers, label)
   587  }
   588  
   589  // PodmanPID execs podman and returns its PID
   590  func (p *PodmanTestIntegration) PodmanPID(args []string) (*PodmanSessionIntegration, int) {
   591  	podmanOptions := p.MakeOptions(args, false, false)
   592  	GinkgoWriter.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " "))
   593  
   594  	command := exec.Command(p.PodmanBinary, podmanOptions...)
   595  	session, err := Start(command, GinkgoWriter, GinkgoWriter)
   596  	if err != nil {
   597  		Fail("unable to run podman command: " + strings.Join(podmanOptions, " "))
   598  	}
   599  	podmanSession := &PodmanSession{Session: session}
   600  	return &PodmanSessionIntegration{podmanSession}, command.Process.Pid
   601  }
   602  
   603  func (p *PodmanTestIntegration) Quadlet(args []string, sourceDir string) *PodmanSessionIntegration {
   604  	GinkgoWriter.Printf("Running: %s %s with QUADLET_UNIT_DIRS=%s\n", p.QuadletBinary, strings.Join(args, " "), sourceDir)
   605  
   606  	// quadlet uses PODMAN env to get a stable podman path
   607  	podmanPath, found := os.LookupEnv("PODMAN")
   608  	if !found {
   609  		podmanPath = p.PodmanBinary
   610  	}
   611  
   612  	command := exec.Command(p.QuadletBinary, args...)
   613  	command.Env = []string{
   614  		fmt.Sprintf("QUADLET_UNIT_DIRS=%s", sourceDir),
   615  		fmt.Sprintf("PODMAN=%s", podmanPath),
   616  	}
   617  	session, err := Start(command, GinkgoWriter, GinkgoWriter)
   618  	if err != nil {
   619  		Fail("unable to run quadlet command: " + strings.Join(args, " "))
   620  	}
   621  	quadletSession := &PodmanSession{Session: session}
   622  	return &PodmanSessionIntegration{quadletSession}
   623  }
   624  
   625  // Cleanup cleans up the temporary store
   626  func (p *PodmanTestIntegration) Cleanup() {
   627  	// ginkgo v2 still goes into AfterEach() when Skip() was called,
   628  	// some tests call skip before the podman test is initialized.
   629  	if p == nil {
   630  		return
   631  	}
   632  
   633  	// first stop everything, rm -fa is unreliable
   634  	// https://github.com/containers/podman/issues/18180
   635  	stop := p.Podman([]string{"stop", "--all", "-t", "0"})
   636  	stop.WaitWithDefaultTimeout()
   637  
   638  	// Remove all pods...
   639  	podrm := p.Podman([]string{"pod", "rm", "-fa", "-t", "0"})
   640  	podrm.WaitWithDefaultTimeout()
   641  
   642  	// ...and containers
   643  	rmall := p.Podman([]string{"rm", "-fa", "-t", "0"})
   644  	rmall.WaitWithDefaultTimeout()
   645  
   646  	p.StopRemoteService()
   647  	// Nuke tempdir
   648  	rmAll(p.PodmanBinary, p.TempDir)
   649  
   650  	// Clean up the registries configuration file ENV variable set in Create
   651  	resetRegistriesConfigEnv()
   652  
   653  	// Make sure to only check exit codes after all cleanup is done.
   654  	// An error would cause it to stop and return early otherwise.
   655  	Expect(stop).To(Exit(0), "command: %v\nstdout: %s\nstderr: %s", stop.Command.Args, stop.OutputToString(), stop.ErrorToString())
   656  	Expect(podrm).To(Exit(0), "command: %v\nstdout: %s\nstderr: %s", podrm.Command.Args, podrm.OutputToString(), podrm.ErrorToString())
   657  	Expect(rmall).To(Exit(0), "command: %v\nstdout: %s\nstderr: %s", rmall.Command.Args, rmall.OutputToString(), rmall.ErrorToString())
   658  }
   659  
   660  // CleanupVolume cleans up the volumes and containers.
   661  // This already calls Cleanup() internally.
   662  func (p *PodmanTestIntegration) CleanupVolume() {
   663  	// Remove all containers
   664  	session := p.Podman([]string{"volume", "rm", "-fa"})
   665  	session.WaitWithDefaultTimeout()
   666  	Expect(session).To(Exit(0), "command: %v\nstdout: %s\nstderr: %s", session.Command.Args, session.OutputToString(), session.ErrorToString())
   667  }
   668  
   669  // CleanupSecret cleans up the secrets and containers.
   670  // This already calls Cleanup() internally.
   671  func (p *PodmanTestIntegration) CleanupSecrets() {
   672  	// Remove all containers
   673  	session := p.Podman([]string{"secret", "rm", "-a"})
   674  	session.Wait(90)
   675  	Expect(session).To(Exit(0), "command: %v\nstdout: %s\nstderr: %s", session.Command.Args, session.OutputToString(), session.ErrorToString())
   676  }
   677  
   678  // InspectContainerToJSON takes the session output of an inspect
   679  // container and returns json
   680  func (s *PodmanSessionIntegration) InspectContainerToJSON() []define.InspectContainerData {
   681  	var i []define.InspectContainerData
   682  	err := jsoniter.Unmarshal(s.Out.Contents(), &i)
   683  	Expect(err).ToNot(HaveOccurred())
   684  	return i
   685  }
   686  
   687  // InspectPodToJSON takes the sessions output from a pod inspect and returns json
   688  func (s *PodmanSessionIntegration) InspectPodToJSON() define.InspectPodData {
   689  	var i define.InspectPodData
   690  	err := jsoniter.Unmarshal(s.Out.Contents(), &i)
   691  	Expect(err).ToNot(HaveOccurred())
   692  	return i
   693  }
   694  
   695  // InspectPodToJSON takes the sessions output from an inspect and returns json
   696  func (s *PodmanSessionIntegration) InspectPodArrToJSON() []define.InspectPodData {
   697  	var i []define.InspectPodData
   698  	err := jsoniter.Unmarshal(s.Out.Contents(), &i)
   699  	Expect(err).ToNot(HaveOccurred())
   700  	return i
   701  }
   702  
   703  // CreatePod creates a pod with no infra container
   704  // it optionally takes a pod name
   705  func (p *PodmanTestIntegration) CreatePod(options map[string][]string) (*PodmanSessionIntegration, int, string) {
   706  	var args = []string{"pod", "create", "--infra=false", "--share", ""}
   707  	for k, values := range options {
   708  		for _, v := range values {
   709  			args = append(args, k+"="+v)
   710  		}
   711  	}
   712  
   713  	session := p.Podman(args)
   714  	session.WaitWithDefaultTimeout()
   715  	return session, session.ExitCode(), session.OutputToString()
   716  }
   717  
   718  func (p *PodmanTestIntegration) CreateVolume(options map[string][]string) (*PodmanSessionIntegration, int, string) {
   719  	var args = []string{"volume", "create"}
   720  	for k, values := range options {
   721  		for _, v := range values {
   722  			args = append(args, k+"="+v)
   723  		}
   724  	}
   725  
   726  	session := p.Podman(args)
   727  	session.WaitWithDefaultTimeout()
   728  	return session, session.ExitCode(), session.OutputToString()
   729  }
   730  
   731  func (p *PodmanTestIntegration) RunTopContainerInPod(name, pod string) *PodmanSessionIntegration {
   732  	return p.RunTopContainerWithArgs(name, []string{"--pod", pod})
   733  }
   734  
   735  func (p *PodmanTestIntegration) RunHealthCheck(cid string) error {
   736  	for i := 0; i < 10; i++ {
   737  		hc := p.Podman([]string{"healthcheck", "run", cid})
   738  		hc.WaitWithDefaultTimeout()
   739  		if hc.ExitCode() == 0 {
   740  			return nil
   741  		}
   742  		// Restart container if it's not running
   743  		ps := p.Podman([]string{"ps", "--no-trunc", "--quiet", "--filter", fmt.Sprintf("id=%s", cid)})
   744  		ps.WaitWithDefaultTimeout()
   745  		if ps.ExitCode() == 0 {
   746  			if !strings.Contains(ps.OutputToString(), cid) {
   747  				GinkgoWriter.Printf("Container %s is not running, restarting", cid)
   748  				restart := p.Podman([]string{"restart", cid})
   749  				restart.WaitWithDefaultTimeout()
   750  				if restart.ExitCode() != 0 {
   751  					return fmt.Errorf("unable to restart %s", cid)
   752  				}
   753  			}
   754  		}
   755  		GinkgoWriter.Printf("Waiting for %s to pass healthcheck\n", cid)
   756  		time.Sleep(1 * time.Second)
   757  	}
   758  	return fmt.Errorf("unable to detect %s as running", cid)
   759  }
   760  
   761  func (p *PodmanTestIntegration) CreateSeccompJSON(in []byte) (string, error) {
   762  	jsonFile := filepath.Join(p.TempDir, "seccomp.json")
   763  	err := WriteJSONFile(in, jsonFile)
   764  	if err != nil {
   765  		return "", err
   766  	}
   767  	return jsonFile, nil
   768  }
   769  
   770  func checkReason(reason string) {
   771  	if len(reason) < 5 {
   772  		panic("Test must specify a reason to skip")
   773  	}
   774  }
   775  
   776  func SkipIfRunc(p *PodmanTestIntegration, reason string) {
   777  	checkReason(reason)
   778  	if p.OCIRuntime == "runc" {
   779  		Skip("[runc]: " + reason)
   780  	}
   781  }
   782  
   783  func SkipIfRootlessCgroupsV1(reason string) {
   784  	checkReason(reason)
   785  	if isRootless() && !CGROUPSV2 {
   786  		Skip("[rootless]: " + reason)
   787  	}
   788  }
   789  
   790  func SkipIfRootless(reason string) {
   791  	checkReason(reason)
   792  	if isRootless() {
   793  		Skip("[rootless]: " + reason)
   794  	}
   795  }
   796  
   797  func SkipIfNotRootless(reason string) {
   798  	checkReason(reason)
   799  	if !isRootless() {
   800  		Skip("[notRootless]: " + reason)
   801  	}
   802  }
   803  
   804  func SkipIfSystemdNotRunning(reason string) {
   805  	checkReason(reason)
   806  
   807  	cmd := exec.Command("systemctl", "list-units")
   808  	err := cmd.Run()
   809  	if err != nil {
   810  		if _, ok := err.(*exec.Error); ok {
   811  			Skip("[notSystemd]: not running " + reason)
   812  		}
   813  		Expect(err).ToNot(HaveOccurred())
   814  	}
   815  }
   816  
   817  func SkipIfNotSystemd(manager, reason string) {
   818  	checkReason(reason)
   819  	if manager != "systemd" {
   820  		Skip("[notSystemd]: " + reason)
   821  	}
   822  }
   823  
   824  func SkipOnOSVersion(os, version string) {
   825  	info := GetHostDistributionInfo()
   826  	if info.Distribution == os && info.Version == version {
   827  		Skip(fmt.Sprintf("Test doesn't work on %s %s", os, version))
   828  	}
   829  }
   830  
   831  func SkipIfNotFedora(reason string) {
   832  	info := GetHostDistributionInfo()
   833  	if info.Distribution != "fedora" {
   834  		Skip(reason)
   835  	}
   836  }
   837  
   838  type journaldTests struct {
   839  	journaldSkip bool
   840  	journaldOnce sync.Once
   841  }
   842  
   843  var journald journaldTests
   844  
   845  func SkipIfJournaldUnavailable() {
   846  	f := func() {
   847  		journald.journaldSkip = false
   848  
   849  		// Check if journalctl is unavailable
   850  		cmd := exec.Command("journalctl", "-n", "1")
   851  		if err := cmd.Run(); err != nil {
   852  			journald.journaldSkip = true
   853  		}
   854  	}
   855  	journald.journaldOnce.Do(f)
   856  
   857  	// In container, journalctl does not return an error even if
   858  	// journald is unavailable
   859  	SkipIfInContainer("[journald]: journalctl inside a container doesn't work correctly")
   860  	if journald.journaldSkip {
   861  		Skip("[journald]: journald is unavailable")
   862  	}
   863  }
   864  
   865  // Use isRootless() instead of rootless.IsRootless()
   866  // This function can detect to join the user namespace by mistake
   867  func isRootless() bool {
   868  	return os.Geteuid() != 0
   869  }
   870  
   871  func isCgroupsV1() bool {
   872  	return !CGROUPSV2
   873  }
   874  
   875  func SkipIfCgroupV1(reason string) {
   876  	checkReason(reason)
   877  	if isCgroupsV1() {
   878  		Skip(reason)
   879  	}
   880  }
   881  
   882  func SkipIfCgroupV2(reason string) {
   883  	checkReason(reason)
   884  	if CGROUPSV2 {
   885  		Skip(reason)
   886  	}
   887  }
   888  
   889  func isContainerized() bool {
   890  	// This is set to "podman" by podman automatically
   891  	return os.Getenv("container") != ""
   892  }
   893  
   894  func SkipIfContainerized(reason string) {
   895  	checkReason(reason)
   896  	if isContainerized() {
   897  		Skip(reason)
   898  	}
   899  }
   900  
   901  func SkipIfRemote(reason string) {
   902  	checkReason(reason)
   903  	if !IsRemote() {
   904  		return
   905  	}
   906  	Skip("[remote]: " + reason)
   907  }
   908  
   909  func SkipIfNotRemote(reason string) {
   910  	checkReason(reason)
   911  	if IsRemote() {
   912  		return
   913  	}
   914  	Skip("[local]: " + reason)
   915  }
   916  
   917  // SkipIfInContainer skips a test if the test is run inside a container
   918  func SkipIfInContainer(reason string) {
   919  	checkReason(reason)
   920  	if os.Getenv("TEST_ENVIRON") == "container" {
   921  		Skip("[container]: " + reason)
   922  	}
   923  }
   924  
   925  // SkipIfNotActive skips a test if the given systemd unit is not active
   926  func SkipIfNotActive(unit string, reason string) {
   927  	checkReason(reason)
   928  
   929  	cmd := exec.Command("systemctl", "is-active", unit)
   930  	cmd.Stdout = GinkgoWriter
   931  	cmd.Stderr = GinkgoWriter
   932  	err := cmd.Run()
   933  	if cmd.ProcessState.ExitCode() == 0 {
   934  		return
   935  	}
   936  	Skip(fmt.Sprintf("[systemd]: unit %s is not active (%v): %s", unit, err, reason))
   937  }
   938  
   939  func SkipIfCNI(p *PodmanTestIntegration) {
   940  	if p.NetworkBackend == CNI {
   941  		Skip("this test is not compatible with the CNI network backend")
   942  	}
   943  }
   944  
   945  func SkipIfNetavark(p *PodmanTestIntegration) {
   946  	if p.NetworkBackend == Netavark {
   947  		Skip("This test is not compatible with the netavark network backend")
   948  	}
   949  }
   950  
   951  // PodmanAsUser is the exec call to podman on the filesystem with the specified uid/gid and environment
   952  func (p *PodmanTestIntegration) PodmanAsUser(args []string, uid, gid uint32, cwd string, env []string) *PodmanSessionIntegration {
   953  	podmanSession := p.PodmanAsUserBase(args, uid, gid, cwd, env, false, false, nil, nil)
   954  	return &PodmanSessionIntegration{podmanSession}
   955  }
   956  
   957  // RestartRemoteService stop and start API Server, usually to change config
   958  func (p *PodmanTestIntegration) RestartRemoteService() {
   959  	p.StopRemoteService()
   960  	p.StartRemoteService()
   961  }
   962  
   963  // RestoreArtifactToCache populates the imagecache from tarballs that were cached earlier
   964  func (p *PodmanTestIntegration) RestoreArtifactToCache(image string) error {
   965  	tarball := imageTarPath(image)
   966  	if _, err := os.Stat(tarball); err == nil {
   967  		GinkgoWriter.Printf("Restoring %s...\n", image)
   968  		p.Root = p.ImageCacheDir
   969  		restore := p.PodmanNoEvents([]string{"load", "-q", "-i", tarball})
   970  		restore.WaitWithDefaultTimeout()
   971  	}
   972  	return nil
   973  }
   974  
   975  func populateCache(podman *PodmanTestIntegration) {
   976  	for _, image := range CACHE_IMAGES {
   977  		err := podman.RestoreArtifactToCache(image)
   978  		Expect(err).ToNot(HaveOccurred())
   979  	}
   980  	// logformatter uses this to recognize the first test
   981  	GinkgoWriter.Printf("-----------------------------\n")
   982  }
   983  
   984  // rmAll removes the directory and its content, when running rootless we use
   985  // podman unshare to prevent any subuid/gid problems
   986  func rmAll(podmanBin string, path string) {
   987  	// Remove cache dirs
   988  	if isRootless() {
   989  		// If rootless, os.RemoveAll() is failed due to permission denied
   990  		cmd := exec.Command(podmanBin, "unshare", "rm", "-rf", path)
   991  		cmd.Stdout = GinkgoWriter
   992  		cmd.Stderr = GinkgoWriter
   993  		if err := cmd.Run(); err != nil {
   994  			GinkgoWriter.Printf("%v\n", err)
   995  		}
   996  	} else {
   997  		// When using overlay as root, podman leaves a stray mount behind.
   998  		// This leak causes remote tests to take a loooooong time, which
   999  		// then causes Cirrus to time out. Unmount that stray.
  1000  		overlayPath := path + "/root/overlay"
  1001  		if _, err := os.Stat(overlayPath); err == nil {
  1002  			if err = unix.Unmount(overlayPath, unix.MNT_DETACH); err != nil {
  1003  				GinkgoWriter.Printf("Error unmounting %s: %v\n", overlayPath, err)
  1004  			}
  1005  		}
  1006  
  1007  		if err = os.RemoveAll(path); err != nil {
  1008  			GinkgoWriter.Printf("%q\n", err)
  1009  		}
  1010  	}
  1011  }
  1012  
  1013  // PodmanNoCache calls the podman command with no configured imagecache
  1014  func (p *PodmanTestIntegration) PodmanNoCache(args []string) *PodmanSessionIntegration {
  1015  	podmanSession := p.PodmanBase(args, false, true)
  1016  	return &PodmanSessionIntegration{podmanSession}
  1017  }
  1018  
  1019  func PodmanTestSetup(tempDir string) *PodmanTestIntegration {
  1020  	return PodmanTestCreateUtil(tempDir, false)
  1021  }
  1022  
  1023  // PodmanNoEvents calls the Podman command without an imagecache and without an
  1024  // events backend. It is used mostly for caching and uncaching images.
  1025  func (p *PodmanTestIntegration) PodmanNoEvents(args []string) *PodmanSessionIntegration {
  1026  	podmanSession := p.PodmanBase(args, true, true)
  1027  	return &PodmanSessionIntegration{podmanSession}
  1028  }
  1029  
  1030  // MakeOptions assembles all the podman main options
  1031  func (p *PodmanTestIntegration) makeOptions(args []string, noEvents, noCache bool) []string {
  1032  	if p.RemoteTest {
  1033  		if !util.StringInSlice("--remote", args) {
  1034  			return append([]string{"--remote", "--url", p.RemoteSocket}, args...)
  1035  		}
  1036  		return args
  1037  	}
  1038  
  1039  	var debug string
  1040  	if _, ok := os.LookupEnv("E2E_DEBUG"); ok {
  1041  		debug = "--log-level=debug --syslog=true "
  1042  	}
  1043  
  1044  	eventsType := "file"
  1045  	if noEvents {
  1046  		eventsType = "none"
  1047  	}
  1048  
  1049  	podmanOptions := strings.Split(fmt.Sprintf("%s--root %s --runroot %s --runtime %s --conmon %s --network-config-dir %s --network-backend %s --cgroup-manager %s --tmpdir %s --events-backend %s --db-backend %s",
  1050  		debug, p.Root, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.NetworkConfigDir, p.NetworkBackend.ToString(), p.CgroupManager, p.TmpDir, eventsType, p.DatabaseBackend), " ")
  1051  
  1052  	podmanOptions = append(podmanOptions, strings.Split(p.StorageOptions, " ")...)
  1053  	if !noCache {
  1054  		cacheOptions := []string{"--storage-opt",
  1055  			fmt.Sprintf("%s.imagestore=%s", p.PodmanTest.ImageCacheFS, p.PodmanTest.ImageCacheDir)}
  1056  		podmanOptions = append(cacheOptions, podmanOptions...)
  1057  	}
  1058  	podmanOptions = append(podmanOptions, args...)
  1059  	return podmanOptions
  1060  }
  1061  
  1062  func writeConf(conf []byte, confPath string) {
  1063  	if _, err := os.Stat(filepath.Dir(confPath)); os.IsNotExist(err) {
  1064  		if err := os.MkdirAll(filepath.Dir(confPath), 0o777); err != nil {
  1065  			GinkgoWriter.Println(err)
  1066  		}
  1067  	}
  1068  	if err := os.WriteFile(confPath, conf, 0o777); err != nil {
  1069  		GinkgoWriter.Println(err)
  1070  	}
  1071  }
  1072  
  1073  func removeConf(confPath string) {
  1074  	if err := os.Remove(confPath); err != nil {
  1075  		GinkgoWriter.Println(err)
  1076  	}
  1077  }
  1078  
  1079  // generateNetworkConfig generates a CNI or Netavark config with a random name
  1080  // it returns the network name and the filepath
  1081  func generateNetworkConfig(p *PodmanTestIntegration) (string, string) {
  1082  	var (
  1083  		path string
  1084  		conf string
  1085  	)
  1086  	// generate a random name to prevent conflicts with other tests
  1087  	name := "net" + stringid.GenerateRandomID()
  1088  	if p.NetworkBackend != Netavark {
  1089  		path = filepath.Join(p.NetworkConfigDir, fmt.Sprintf("%s.conflist", name))
  1090  		conf = fmt.Sprintf(`{
  1091  		"cniVersion": "0.3.0",
  1092  		"name": "%s",
  1093  		"plugins": [
  1094  		  {
  1095  			"type": "bridge",
  1096  			"bridge": "cni1",
  1097  			"isGateway": true,
  1098  			"ipMasq": true,
  1099  			"ipam": {
  1100  				"type": "host-local",
  1101  				"subnet": "10.99.0.0/16",
  1102  				"routes": [
  1103  					{ "dst": "0.0.0.0/0" }
  1104  				]
  1105  			}
  1106  		  },
  1107  		  {
  1108  			"type": "portmap",
  1109  			"capabilities": {
  1110  			  "portMappings": true
  1111  			}
  1112  		  }
  1113  		]
  1114  	}`, name)
  1115  	} else {
  1116  		path = filepath.Join(p.NetworkConfigDir, fmt.Sprintf("%s.json", name))
  1117  		conf = fmt.Sprintf(`
  1118  {
  1119       "name": "%s",
  1120       "id": "e1ef2749024b88f5663ca693a9118e036d6bfc48bcfe460faf45e9614a513e5c",
  1121       "driver": "bridge",
  1122       "network_interface": "netavark1",
  1123       "created": "2022-01-05T14:15:10.975493521-06:00",
  1124       "subnets": [
  1125            {
  1126                 "subnet": "10.100.0.0/16",
  1127                 "gateway": "10.100.0.1"
  1128            }
  1129       ],
  1130       "ipv6_enabled": false,
  1131       "internal": false,
  1132       "dns_enabled": true,
  1133       "ipam_options": {
  1134            "driver": "host-local"
  1135       }
  1136  }
  1137  `, name)
  1138  	}
  1139  	writeConf([]byte(conf), path)
  1140  	return name, path
  1141  }
  1142  
  1143  func (p *PodmanTestIntegration) removeNetwork(name string) {
  1144  	session := p.Podman([]string{"network", "rm", "-f", name})
  1145  	session.WaitWithDefaultTimeout()
  1146  	Expect(session.ExitCode()).To(BeNumerically("<=", 1), "Exit code must be 0 or 1")
  1147  }
  1148  
  1149  // generatePolicyFile generates a signature verification policy file.
  1150  // it returns the policy file path.
  1151  func generatePolicyFile(tempDir string) string {
  1152  	keyPath := filepath.Join(tempDir, "key.gpg")
  1153  	policyPath := filepath.Join(tempDir, "policy.json")
  1154  	conf := fmt.Sprintf(`
  1155  {
  1156      "default": [
  1157          {
  1158              "type": "insecureAcceptAnything"
  1159          }
  1160      ],
  1161      "transports": {
  1162          "docker": {
  1163              "localhost:5000": [
  1164                  {
  1165                      "type": "signedBy",
  1166                      "keyType": "GPGKeys",
  1167                      "keyPath": "%s"
  1168                  }
  1169              ],
  1170              "localhost:5000/sigstore-signed": [
  1171                  {
  1172                      "type": "sigstoreSigned",
  1173                      "keyPath": "testdata/sigstore-key.pub"
  1174                  }
  1175              ],
  1176              "localhost:5000/sigstore-signed-params": [
  1177                  {
  1178                      "type": "sigstoreSigned",
  1179                      "keyPath": "testdata/sigstore-key.pub"
  1180                  }
  1181              ]
  1182          }
  1183      }
  1184  }
  1185  `, keyPath)
  1186  	writeConf([]byte(conf), policyPath)
  1187  	return policyPath
  1188  }
  1189  
  1190  func (s *PodmanSessionIntegration) jq(jqCommand string) (string, error) {
  1191  	var out bytes.Buffer
  1192  	cmd := exec.Command("jq", jqCommand)
  1193  	cmd.Stdin = strings.NewReader(s.OutputToString())
  1194  	cmd.Stdout = &out
  1195  	err := cmd.Run()
  1196  	return strings.TrimRight(out.String(), "\n"), err
  1197  }
  1198  
  1199  func (p *PodmanTestIntegration) buildImage(dockerfile, imageName string, layers string, label string) string {
  1200  	dockerfilePath := filepath.Join(p.TempDir, "Dockerfile-"+stringid.GenerateRandomID())
  1201  	err := os.WriteFile(dockerfilePath, []byte(dockerfile), 0755)
  1202  	Expect(err).ToNot(HaveOccurred())
  1203  	cmd := []string{"build", "--pull-never", "--layers=" + layers, "--file", dockerfilePath}
  1204  	if label != "" {
  1205  		cmd = append(cmd, "--label="+label)
  1206  	}
  1207  	if len(imageName) > 0 {
  1208  		cmd = append(cmd, []string{"-t", imageName}...)
  1209  	}
  1210  	cmd = append(cmd, p.TempDir)
  1211  	session := p.Podman(cmd)
  1212  	session.Wait(240)
  1213  	Expect(session).Should(Exit(0), fmt.Sprintf("BuildImage session output: %q", session.OutputToString()))
  1214  	output := session.OutputToStringArray()
  1215  	return output[len(output)-1]
  1216  }
  1217  
  1218  func writeYaml(content string, fileName string) error {
  1219  	f, err := os.Create(fileName)
  1220  	if err != nil {
  1221  		return err
  1222  	}
  1223  	defer f.Close()
  1224  
  1225  	_, err = f.WriteString(content)
  1226  	if err != nil {
  1227  		return err
  1228  	}
  1229  
  1230  	return nil
  1231  }
  1232  
  1233  // GetPort finds an unused TCP/IP port on the system, in the range 5000-5999
  1234  func GetPort() int {
  1235  	portMin := 5000
  1236  	portMax := 5999
  1237  	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
  1238  
  1239  	// Avoid dup-allocation races between parallel ginkgo processes
  1240  	nProcs := GinkgoT().ParallelTotal()
  1241  	myProc := GinkgoT().ParallelProcess() - 1
  1242  
  1243  	for i := 0; i < 50; i++ {
  1244  		// Random port within that range
  1245  		port := portMin + rng.Intn((portMax-portMin)/nProcs)*nProcs + myProc
  1246  
  1247  		used, err := net.Listen("tcp", "0.0.0.0:"+strconv.Itoa(port))
  1248  		if err == nil {
  1249  			// it's open. Return it.
  1250  			err = used.Close()
  1251  			Expect(err).ToNot(HaveOccurred(), "closing random port")
  1252  			return port
  1253  		}
  1254  	}
  1255  
  1256  	Fail(fmt.Sprintf("unable to get free port in range %d-%d", portMin, portMax))
  1257  	return 0 // notreached
  1258  }
  1259  
  1260  func ncz(port int) bool {
  1261  	timeout := 500 * time.Millisecond
  1262  	for i := 0; i < 5; i++ {
  1263  		ncCmd := []string{"-z", "localhost", strconv.Itoa(port)}
  1264  		GinkgoWriter.Printf("Running: nc %s\n", strings.Join(ncCmd, " "))
  1265  		check := SystemExec("nc", ncCmd)
  1266  		if check.ExitCode() == 0 {
  1267  			return true
  1268  		}
  1269  		time.Sleep(timeout)
  1270  		timeout++
  1271  	}
  1272  	return false
  1273  }
  1274  
  1275  func createNetworkName(name string) string {
  1276  	return name + stringid.GenerateRandomID()[:10]
  1277  }
  1278  
  1279  var IPRegex = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`
  1280  
  1281  // digShort execs into the given container and does a dig lookup with a timeout
  1282  // backoff.  If it gets a response, it ensures that the output is in the correct
  1283  // format and iterates a string array for match
  1284  func digShort(container, lookupName, expectedIP string, p *PodmanTestIntegration) {
  1285  	digInterval := time.Millisecond * 250
  1286  	for i := 0; i < 6; i++ {
  1287  		time.Sleep(digInterval * time.Duration(i))
  1288  		dig := p.Podman([]string{"exec", container, "dig", "+short", lookupName})
  1289  		dig.WaitWithDefaultTimeout()
  1290  		output := dig.OutputToString()
  1291  		if dig.ExitCode() == 0 && output != "" {
  1292  			Expect(output).To(Equal(expectedIP))
  1293  			// success
  1294  			return
  1295  		}
  1296  	}
  1297  	Fail("dns is not responding")
  1298  }
  1299  
  1300  // WaitForFile to be created in defaultWaitTimeout seconds, returns false if file not created
  1301  func WaitForFile(path string) (err error) {
  1302  	until := time.Now().Add(time.Duration(defaultWaitTimeout) * time.Second)
  1303  	for time.Now().Before(until) {
  1304  		_, err = os.Stat(path)
  1305  		switch {
  1306  		case err == nil:
  1307  			return nil
  1308  		case errors.Is(err, os.ErrNotExist):
  1309  			time.Sleep(10 * time.Millisecond)
  1310  		default:
  1311  			return err
  1312  		}
  1313  	}
  1314  	return err
  1315  }
  1316  
  1317  // WaitForService blocks for defaultWaitTimeout seconds, waiting for some service listening on given host:port
  1318  func WaitForService(address url.URL) {
  1319  	// Wait for podman to be ready
  1320  	var err error
  1321  	until := time.Now().Add(time.Duration(defaultWaitTimeout) * time.Second)
  1322  	for time.Now().Before(until) {
  1323  		var conn net.Conn
  1324  		conn, err = net.Dial("tcp", address.Host)
  1325  		if err == nil {
  1326  			conn.Close()
  1327  			break
  1328  		}
  1329  
  1330  		// Podman not available yet...
  1331  		time.Sleep(10 * time.Millisecond)
  1332  	}
  1333  	Expect(err).ShouldNot(HaveOccurred())
  1334  }
  1335  
  1336  // useCustomNetworkDir makes sure this test uses a custom network dir.
  1337  // This needs to be called for all test they may remove networks from other tests,
  1338  // so netwokr prune, system prune, or system reset.
  1339  // see https://github.com/containers/podman/issues/17946
  1340  func useCustomNetworkDir(podmanTest *PodmanTestIntegration, tempdir string) {
  1341  	// set custom network directory to prevent flakes since the dir is shared with all tests by default
  1342  	podmanTest.NetworkConfigDir = tempdir
  1343  	if IsRemote() {
  1344  		podmanTest.RestartRemoteService()
  1345  	}
  1346  }
  1347  
  1348  // copy directories recursively from source path to destination path
  1349  func CopyDirectory(srcDir, dest string) error {
  1350  	entries, err := os.ReadDir(srcDir)
  1351  	if err != nil {
  1352  		return err
  1353  	}
  1354  	for _, entry := range entries {
  1355  		sourcePath := filepath.Join(srcDir, entry.Name())
  1356  		destPath := filepath.Join(dest, entry.Name())
  1357  
  1358  		fileInfo, err := os.Stat(sourcePath)
  1359  		if err != nil {
  1360  			return err
  1361  		}
  1362  
  1363  		stat, ok := fileInfo.Sys().(*syscall.Stat_t)
  1364  		if !ok {
  1365  			return fmt.Errorf("failed to get raw syscall.Stat_t data for %q", sourcePath)
  1366  		}
  1367  
  1368  		switch fileInfo.Mode() & os.ModeType {
  1369  		case os.ModeDir:
  1370  			if err := os.MkdirAll(destPath, 0755); err != nil {
  1371  				return fmt.Errorf("failed to create directory: %q, error: %q", destPath, err.Error())
  1372  			}
  1373  			if err := CopyDirectory(sourcePath, destPath); err != nil {
  1374  				return err
  1375  			}
  1376  		case os.ModeSymlink:
  1377  			if err := CopySymLink(sourcePath, destPath); err != nil {
  1378  				return err
  1379  			}
  1380  		default:
  1381  			if err := Copy(sourcePath, destPath); err != nil {
  1382  				return err
  1383  			}
  1384  		}
  1385  
  1386  		if err := os.Lchown(destPath, int(stat.Uid), int(stat.Gid)); err != nil {
  1387  			return err
  1388  		}
  1389  
  1390  		fInfo, err := entry.Info()
  1391  		if err != nil {
  1392  			return err
  1393  		}
  1394  
  1395  		isSymlink := fInfo.Mode()&os.ModeSymlink != 0
  1396  		if !isSymlink {
  1397  			if err := os.Chmod(destPath, fInfo.Mode()); err != nil {
  1398  				return err
  1399  			}
  1400  		}
  1401  	}
  1402  	return nil
  1403  }
  1404  
  1405  func Copy(srcFile, dstFile string) error {
  1406  	out, err := os.Create(dstFile)
  1407  	if err != nil {
  1408  		return err
  1409  	}
  1410  
  1411  	defer out.Close()
  1412  
  1413  	in, err := os.Open(srcFile)
  1414  	if err != nil {
  1415  		return err
  1416  	}
  1417  
  1418  	_, err = io.Copy(out, in)
  1419  	if err != nil {
  1420  		return err
  1421  	}
  1422  	defer in.Close()
  1423  	return nil
  1424  }
  1425  
  1426  func CopySymLink(source, dest string) error {
  1427  	link, err := os.Readlink(source)
  1428  	if err != nil {
  1429  		return err
  1430  	}
  1431  	return os.Symlink(link, dest)
  1432  }