summaryrefslogtreecommitdiff
path: root/test/utils/utils.go
diff options
context:
space:
mode:
Diffstat (limited to 'test/utils/utils.go')
-rw-r--r--test/utils/utils.go431
1 files changed, 431 insertions, 0 deletions
diff --git a/test/utils/utils.go b/test/utils/utils.go
new file mode 100644
index 000000000..e61171269
--- /dev/null
+++ b/test/utils/utils.go
@@ -0,0 +1,431 @@
+package utils
+
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "strings"
+ "time"
+
+ "github.com/containers/storage/pkg/parsers/kernel"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/onsi/gomega/gexec"
+)
+
+var (
+ defaultWaitTimeout = 90
+ OSReleasePath = "/etc/os-release"
+ ProcessOneCgroupPath = "/proc/1/cgroup"
+)
+
+// PodmanTestCommon contains common functions will be updated later in
+// the inheritance structs
+type PodmanTestCommon interface {
+ MakeOptions(args []string) []string
+ WaitForContainer() bool
+ WaitContainerReady(id string, expStr string, timeout int, step int) bool
+}
+
+// PodmanTest struct for command line options
+type PodmanTest struct {
+ PodmanMakeOptions func(args []string) []string
+ PodmanBinary string
+ ArtifactPath string
+ TempDir string
+}
+
+// PodmanSession wraps the gexec.session so we can extend it
+type PodmanSession struct {
+ *gexec.Session
+}
+
+// HostOS is a simple struct for the test os
+type HostOS struct {
+ Distribution string
+ Version string
+ Arch string
+}
+
+// MakeOptions assembles all podman options
+func (p *PodmanTest) MakeOptions(args []string) []string {
+ return p.PodmanMakeOptions(args)
+}
+
+// PodmanAsUser exec podman as user. uid and gid is set for credentials useage. env is used
+// to record the env for debugging
+func (p *PodmanTest) PodmanAsUser(args []string, uid, gid uint32, env []string) *PodmanSession {
+ var command *exec.Cmd
+ podmanOptions := p.MakeOptions(args)
+
+ if env == nil {
+ fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " "))
+ } else {
+ fmt.Printf("Running: (env: %v) %s %s\n", env, p.PodmanBinary, strings.Join(podmanOptions, " "))
+ }
+ if uid != 0 || gid != 0 {
+ nsEnterOpts := append([]string{"--userspec", fmt.Sprintf("%d:%d", uid, gid), "/", p.PodmanBinary}, podmanOptions...)
+ command = exec.Command("chroot", nsEnterOpts...)
+ } else {
+ command = exec.Command(p.PodmanBinary, podmanOptions...)
+ }
+ if env != nil {
+ command.Env = env
+ }
+
+ session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
+ if err != nil {
+ Fail(fmt.Sprintf("unable to run podman command: %s\n%v", strings.Join(podmanOptions, " "), err))
+ }
+ return &PodmanSession{session}
+}
+
+// PodmanBase exec podman with default env.
+func (p *PodmanTest) PodmanBase(args []string) *PodmanSession {
+ return p.PodmanAsUser(args, 0, 0, nil)
+}
+
+// WaitForContainer waits on a started container
+func (p *PodmanTest) WaitForContainer() bool {
+ for i := 0; i < 10; i++ {
+ if p.NumberOfContainersRunning() > 0 {
+ return true
+ }
+ time.Sleep(1 * time.Second)
+ }
+ return false
+}
+
+// NumberOfContainersRunning returns an int of how many
+// containers are currently running.
+func (p *PodmanTest) NumberOfContainersRunning() int {
+ var containers []string
+ ps := p.PodmanBase([]string{"ps", "-q"})
+ ps.WaitWithDefaultTimeout()
+ Expect(ps.ExitCode()).To(Equal(0))
+ for _, i := range ps.OutputToStringArray() {
+ if i != "" {
+ containers = append(containers, i)
+ }
+ }
+ return len(containers)
+}
+
+// NumberOfContainers returns an int of how many
+// containers are currently defined.
+func (p *PodmanTest) NumberOfContainers() int {
+ var containers []string
+ ps := p.PodmanBase([]string{"ps", "-aq"})
+ ps.WaitWithDefaultTimeout()
+ Expect(ps.ExitCode()).To(Equal(0))
+ for _, i := range ps.OutputToStringArray() {
+ if i != "" {
+ containers = append(containers, i)
+ }
+ }
+ return len(containers)
+}
+
+// NumberOfPods returns an int of how many
+// pods are currently defined.
+func (p *PodmanTest) NumberOfPods() int {
+ var pods []string
+ ps := p.PodmanBase([]string{"pod", "ps", "-q"})
+ ps.WaitWithDefaultTimeout()
+ Expect(ps.ExitCode()).To(Equal(0))
+ for _, i := range ps.OutputToStringArray() {
+ if i != "" {
+ pods = append(pods, i)
+ }
+ }
+ return len(pods)
+}
+
+// GetContainerStatus returns the containers state.
+// This function assumes only one container is active.
+func (p *PodmanTest) GetContainerStatus() string {
+ var podmanArgs = []string{"ps"}
+ podmanArgs = append(podmanArgs, "--all", "--format={{.Status}}")
+ session := p.PodmanBase(podmanArgs)
+ session.WaitWithDefaultTimeout()
+ return session.OutputToString()
+}
+
+// WaitContainerReady waits process or service inside container start, and ready to be used.
+func (p *PodmanTest) WaitContainerReady(id string, expStr string, timeout int, step int) bool {
+ startTime := time.Now()
+ s := p.PodmanBase([]string{"logs", id})
+ s.WaitWithDefaultTimeout()
+
+ for {
+ if time.Since(startTime) >= time.Duration(timeout)*time.Second {
+ fmt.Printf("Container %s is not ready in %ds", id, timeout)
+ return false
+ }
+
+ if strings.Contains(s.OutputToString(), expStr) {
+ return true
+ }
+ time.Sleep(time.Duration(step) * time.Second)
+ s = p.PodmanBase([]string{"logs", id})
+ s.WaitWithDefaultTimeout()
+ }
+}
+
+// WaitForContainer is a wrapper function for accept inheritance PodmanTest struct.
+func WaitForContainer(p PodmanTestCommon) bool {
+ return p.WaitForContainer()
+}
+
+// WaitForContainerReady is a wrapper function for accept inheritance PodmanTest struct.
+func WaitContainerReady(p PodmanTestCommon, id string, expStr string, timeout int, step int) bool {
+ return p.WaitContainerReady(id, expStr, timeout, step)
+}
+
+// OutputToString formats session output to string
+func (s *PodmanSession) OutputToString() string {
+ fields := strings.Fields(fmt.Sprintf("%s", s.Out.Contents()))
+ return strings.Join(fields, " ")
+}
+
+// OutputToStringArray returns the output as a []string
+// where each array item is a line split by newline
+func (s *PodmanSession) OutputToStringArray() []string {
+ var results []string
+ output := fmt.Sprintf("%s", s.Out.Contents())
+ for _, line := range strings.Split(output, "\n") {
+ if line != "" {
+ results = append(results, line)
+ }
+ }
+ return results
+}
+
+// ErrorToString formats session stderr to string
+func (s *PodmanSession) ErrorToString() string {
+ fields := strings.Fields(fmt.Sprintf("%s", s.Err.Contents()))
+ return strings.Join(fields, " ")
+}
+
+// ErrorToStringArray returns the stderr output as a []string
+// where each array item is a line split by newline
+func (s *PodmanSession) ErrorToStringArray() []string {
+ output := fmt.Sprintf("%s", s.Err.Contents())
+ return strings.Split(output, "\n")
+}
+
+// GrepString takes session output and behaves like grep. it returns a bool
+// if successful and an array of strings on positive matches
+func (s *PodmanSession) GrepString(term string) (bool, []string) {
+ var (
+ greps []string
+ matches bool
+ )
+
+ for _, line := range s.OutputToStringArray() {
+ if strings.Contains(line, term) {
+ matches = true
+ greps = append(greps, line)
+ }
+ }
+ return matches, greps
+}
+
+// ErrorGrepString takes session stderr output and behaves like grep. it returns a bool
+// if successful and an array of strings on positive matches
+func (s *PodmanSession) ErrorGrepString(term string) (bool, []string) {
+ var (
+ greps []string
+ matches bool
+ )
+
+ for _, line := range s.ErrorToStringArray() {
+ if strings.Contains(line, term) {
+ matches = true
+ greps = append(greps, line)
+ }
+ }
+ return matches, greps
+}
+
+//LineInOutputStartsWith returns true if a line in a
+// session output starts with the supplied string
+func (s *PodmanSession) LineInOuputStartsWith(term string) bool {
+ for _, i := range s.OutputToStringArray() {
+ if strings.HasPrefix(i, term) {
+ return true
+ }
+ }
+ return false
+}
+
+//LineInOutputContains returns true if a line in a
+// session output starts with the supplied string
+func (s *PodmanSession) LineInOutputContains(term string) bool {
+ for _, i := range s.OutputToStringArray() {
+ if strings.Contains(i, term) {
+ return true
+ }
+ }
+ return false
+}
+
+//LineInOutputContainsTag returns true if a line in the
+// session's output contains the repo-tag pair as returned
+// by podman-images(1).
+func (s *PodmanSession) LineInOutputContainsTag(repo, tag string) bool {
+ tagMap := tagOutputToMap(s.OutputToStringArray())
+ for r, t := range tagMap {
+ if repo == r && tag == t {
+ return true
+ }
+ }
+ return false
+}
+
+// IsJSONOutputValid attempts to unmarshal the session buffer
+// and if successful, returns true, else false
+func (s *PodmanSession) IsJSONOutputValid() bool {
+ var i interface{}
+ if err := json.Unmarshal(s.Out.Contents(), &i); err != nil {
+ fmt.Println(err)
+ return false
+ }
+ return true
+}
+
+// WaitWithDefaultTimeout waits for process finished with defaultWaitTimeout
+func (s *PodmanSession) WaitWithDefaultTimeout() {
+ s.Wait(defaultWaitTimeout)
+ fmt.Println("output:", s.OutputToString())
+}
+
+// CreateTempDirinTempDir create a temp dir with prefix podman_test
+func CreateTempDirInTempDir() (string, error) {
+ return ioutil.TempDir("", "podman_test")
+}
+
+// SystemExec is used to exec a system command to check its exit code or output
+func SystemExec(command string, args []string) *PodmanSession {
+ c := exec.Command(command, args...)
+ session, err := gexec.Start(c, GinkgoWriter, GinkgoWriter)
+ if err != nil {
+ Fail(fmt.Sprintf("unable to run command: %s %s", command, strings.Join(args, " ")))
+ }
+ return &PodmanSession{session}
+}
+
+// StringInSlice determines if a string is in a string slice, returns bool
+func StringInSlice(s string, sl []string) bool {
+ for _, i := range sl {
+ if i == s {
+ return true
+ }
+ }
+ return false
+}
+
+//tagOutPutToMap parses each string in imagesOutput and returns
+// a map of repo:tag pairs. Notice, the first array item will
+// be skipped as it's considered to be the header.
+func tagOutputToMap(imagesOutput []string) map[string]string {
+ m := make(map[string]string)
+ // iterate over output but skip the header
+ for _, i := range imagesOutput[1:] {
+ tmp := []string{}
+ for _, x := range strings.Split(i, " ") {
+ if x != "" {
+ tmp = append(tmp, x)
+ }
+ }
+ // podman-images(1) return a list like output
+ // in the format of "Repository Tag [...]"
+ if len(tmp) < 2 {
+ continue
+ }
+ m[tmp[0]] = tmp[1]
+ }
+ return m
+}
+
+//GetHostDistributionInfo returns a struct with its distribution name and version
+func GetHostDistributionInfo() HostOS {
+ f, err := os.Open(OSReleasePath)
+ defer f.Close()
+ if err != nil {
+ return HostOS{}
+ }
+
+ l := bufio.NewScanner(f)
+ host := HostOS{}
+ host.Arch = runtime.GOARCH
+ for l.Scan() {
+ if strings.HasPrefix(l.Text(), "ID=") {
+ host.Distribution = strings.Replace(strings.TrimSpace(strings.Join(strings.Split(l.Text(), "=")[1:], "")), "\"", "", -1)
+ }
+ if strings.HasPrefix(l.Text(), "VERSION_ID=") {
+ host.Version = strings.Replace(strings.TrimSpace(strings.Join(strings.Split(l.Text(), "=")[1:], "")), "\"", "", -1)
+ }
+ }
+ return host
+}
+
+// IsKernelNewerThan compares the current kernel version to one provided. If
+// the kernel is equal to or greater, returns true
+func IsKernelNewerThan(version string) (bool, error) {
+ inputVersion, err := kernel.ParseRelease(version)
+ if err != nil {
+ return false, err
+ }
+ kv, err := kernel.GetKernelVersion()
+ if err != nil {
+ return false, err
+ }
+
+ // CompareKernelVersion compares two kernel.VersionInfo structs.
+ // Returns -1 if a < b, 0 if a == b, 1 it a > b
+ result := kernel.CompareKernelVersion(*kv, *inputVersion)
+ if result >= 0 {
+ return true, nil
+ }
+ return false, nil
+
+}
+
+//IsCommandAvaible check if command exist
+func IsCommandAvailable(command string) bool {
+ check := exec.Command("bash", "-c", strings.Join([]string{"command -v", command}, " "))
+ err := check.Run()
+ if err != nil {
+ return false
+ }
+ return true
+}
+
+// WriteJsonFile write json format data to a json file
+func WriteJsonFile(data []byte, filePath string) error {
+ var jsonData map[string]interface{}
+ json.Unmarshal(data, &jsonData)
+ formatJson, _ := json.MarshalIndent(jsonData, "", " ")
+ return ioutil.WriteFile(filePath, formatJson, 0644)
+}
+
+// Containerized check the podman command run inside container
+func Containerized() bool {
+ container := os.Getenv("container")
+ if container != "" {
+ return true
+ }
+ b, err := ioutil.ReadFile(ProcessOneCgroupPath)
+ if err != nil {
+ // shrug, if we cannot read that file, return false
+ return false
+ }
+ if strings.Index(string(b), "docker") > -1 {
+ return true
+ }
+ return false
+}