github.com/containers/podman/v4@v4.9.4/pkg/machine/e2e/config_test.go (about)

     1  package e2e_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/containers/podman/v4/pkg/machine"
    14  	"github.com/containers/podman/v4/pkg/util"
    15  	"github.com/containers/storage/pkg/stringid"
    16  	. "github.com/onsi/ginkgo/v2"
    17  	. "github.com/onsi/gomega"
    18  	"github.com/onsi/gomega/format"
    19  	. "github.com/onsi/gomega/gexec"
    20  	"github.com/onsi/gomega/types"
    21  )
    22  
    23  var originalHomeDir = os.Getenv("HOME")
    24  
    25  const (
    26  	defaultTimeout = 90 * time.Second
    27  )
    28  
    29  type machineCommand interface {
    30  	buildCmd(m *machineTestBuilder) []string
    31  }
    32  
    33  type MachineTestBuilder interface {
    34  	setName(name string) *MachineTestBuilder
    35  	setCmd(mc machineCommand) *MachineTestBuilder
    36  	setTimeout(duration time.Duration) *MachineTestBuilder
    37  	run() (*machineSession, error)
    38  }
    39  type machineSession struct {
    40  	*Session
    41  }
    42  
    43  type machineTestBuilder struct {
    44  	cmd          []string
    45  	imagePath    string
    46  	name         string
    47  	names        []string
    48  	podmanBinary string
    49  	timeout      time.Duration
    50  }
    51  
    52  // waitWithTimeout waits for a command to complete for a given
    53  // number of seconds
    54  func (ms *machineSession) waitWithTimeout(timeout time.Duration) {
    55  	Eventually(ms, timeout).Should(Exit())
    56  }
    57  
    58  func (ms *machineSession) Bytes() []byte {
    59  	return []byte(ms.outputToString())
    60  }
    61  
    62  func (ms *machineSession) outputToStringSlice() []string {
    63  	var results []string
    64  	output := string(ms.Out.Contents())
    65  	for _, line := range strings.Split(output, "\n") {
    66  		if line != "" {
    67  			results = append(results, line)
    68  		}
    69  	}
    70  	return results
    71  }
    72  
    73  // outputToString returns the output from a session in string form
    74  func (ms *machineSession) outputToString() string {
    75  	if ms == nil || ms.Out == nil || ms.Out.Contents() == nil {
    76  		return ""
    77  	}
    78  
    79  	fields := strings.Fields(string(ms.Out.Contents()))
    80  	return strings.Join(fields, " ")
    81  }
    82  
    83  // errorToString returns the error output from a session in string form
    84  func (ms *machineSession) errorToString() string {
    85  	if ms == nil || ms.Err == nil || ms.Err.Contents() == nil {
    86  		return ""
    87  	}
    88  	return string(ms.Err.Contents())
    89  }
    90  
    91  // newMB constructor for machine test builders
    92  func newMB() (*machineTestBuilder, error) {
    93  	mb := machineTestBuilder{
    94  		timeout: defaultTimeout,
    95  	}
    96  	cwd, err := os.Getwd()
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	mb.podmanBinary = filepath.Join(cwd, podmanBinary)
   101  	if os.Getenv("PODMAN_BINARY") != "" {
   102  		mb.podmanBinary = os.Getenv("PODMAN_BINARY")
   103  	}
   104  	if os.Getenv("MACHINE_TEST_TIMEOUT") != "" {
   105  		seconds, err := strconv.Atoi(os.Getenv("MACHINE_TEST_TIMEOUT"))
   106  		if err != nil {
   107  			return nil, err
   108  		}
   109  		mb.timeout = time.Duration(seconds) * time.Second
   110  	}
   111  	return &mb, nil
   112  }
   113  
   114  // setName sets the name of the virtuaql machine for the command
   115  func (m *machineTestBuilder) setName(name string) *machineTestBuilder {
   116  	m.name = name
   117  	return m
   118  }
   119  
   120  // setCmd takes a machineCommand struct and assembles a cmd line
   121  // representation of the podman machine command
   122  func (m *machineTestBuilder) setCmd(mc machineCommand) *machineTestBuilder {
   123  	// If no name for the machine exists, we set a random name.
   124  	if !util.StringInSlice(m.name, m.names) {
   125  		if len(m.name) < 1 {
   126  			m.name = randomString()
   127  		}
   128  		m.names = append(m.names, m.name)
   129  	}
   130  	m.cmd = mc.buildCmd(m)
   131  	return m
   132  }
   133  
   134  func (m *machineTestBuilder) setTimeout(timeout time.Duration) *machineTestBuilder { //nolint: unparam
   135  	m.timeout = timeout
   136  	return m
   137  }
   138  
   139  // toQemuInspectInfo is only for inspecting qemu machines.  Other providers will need
   140  // to make their own.
   141  func (m *machineTestBuilder) toQemuInspectInfo() ([]machine.InspectInfo, int, error) {
   142  	args := []string{"machine", "inspect"}
   143  	args = append(args, m.names...)
   144  	session, err := runWrapper(m.podmanBinary, args, defaultTimeout, true)
   145  	if err != nil {
   146  		return nil, -1, err
   147  	}
   148  	mii := []machine.InspectInfo{}
   149  	err = json.Unmarshal(session.Bytes(), &mii)
   150  	return mii, session.ExitCode(), err
   151  }
   152  
   153  func (m *machineTestBuilder) runWithoutWait() (*machineSession, error) {
   154  	return runWrapper(m.podmanBinary, m.cmd, m.timeout, false)
   155  }
   156  
   157  func (m *machineTestBuilder) run() (*machineSession, error) {
   158  	return runWrapper(m.podmanBinary, m.cmd, m.timeout, true)
   159  }
   160  
   161  func runWrapper(podmanBinary string, cmdArgs []string, timeout time.Duration, wait bool) (*machineSession, error) {
   162  	if len(os.Getenv("DEBUG")) > 0 {
   163  		cmdArgs = append([]string{"--log-level=debug"}, cmdArgs...)
   164  	}
   165  	GinkgoWriter.Println(podmanBinary + " " + strings.Join(cmdArgs, " "))
   166  	c := exec.Command(podmanBinary, cmdArgs...)
   167  	session, err := Start(c, GinkgoWriter, GinkgoWriter)
   168  	if err != nil {
   169  		Fail(fmt.Sprintf("Unable to start session: %q", err))
   170  		return nil, err
   171  	}
   172  	ms := machineSession{session}
   173  	if wait {
   174  		ms.waitWithTimeout(timeout)
   175  	}
   176  	return &ms, nil
   177  }
   178  
   179  // randomString returns a string of given length composed of random characters
   180  func randomString() string {
   181  	return stringid.GenerateRandomID()[0:12]
   182  }
   183  
   184  type ValidJSONMatcher struct {
   185  	types.GomegaMatcher
   186  }
   187  
   188  func BeValidJSON() *ValidJSONMatcher {
   189  	return &ValidJSONMatcher{}
   190  }
   191  
   192  func (matcher *ValidJSONMatcher) Match(actual interface{}) (success bool, err error) {
   193  	s, ok := actual.(string)
   194  	if !ok {
   195  		return false, fmt.Errorf("ValidJSONMatcher expects a string, not %q", actual)
   196  	}
   197  
   198  	var i interface{}
   199  	if err := json.Unmarshal([]byte(s), &i); err != nil {
   200  		return false, err
   201  	}
   202  	return true, nil
   203  }
   204  
   205  func (matcher *ValidJSONMatcher) FailureMessage(actual interface{}) (message string) {
   206  	return format.Message(actual, "to be valid JSON")
   207  }
   208  
   209  func (matcher *ValidJSONMatcher) NegatedFailureMessage(actual interface{}) (message string) {
   210  	return format.Message(actual, "to _not_ be valid JSON")
   211  }
   212  
   213  func skipIfVmtype(vmType machine.VMType, message string) {
   214  	if isVmtype(vmType) {
   215  		Skip(message)
   216  	}
   217  }
   218  
   219  func skipIfNotVmtype(vmType machine.VMType, message string) {
   220  	if !isVmtype(vmType) {
   221  		Skip(message)
   222  	}
   223  }
   224  
   225  func skipIfWSL(message string) {
   226  	skipIfVmtype(machine.WSLVirt, message)
   227  }
   228  
   229  func isVmtype(vmType machine.VMType) bool {
   230  	return testProvider.VMType() == vmType
   231  }
   232  
   233  // isWSL is a simple wrapper to determine if the testprovider is WSL
   234  func isWSL() bool {
   235  	return isVmtype(machine.WSLVirt)
   236  }