github.com/canonical/ubuntu-image@v0.0.0-20240430122802-2202fe98b290/internal/statemachine/tests_helper_test.go (about)

     1  package statemachine
     2  
     3  import (
     4  	"os"
     5  	"os/exec"
     6  	"path/filepath"
     7  	"sync"
     8  
     9  	"github.com/google/uuid"
    10  	"github.com/snapcore/snapd/osutil"
    11  
    12  	"github.com/canonical/ubuntu-image/internal/helper"
    13  	"github.com/canonical/ubuntu-image/internal/imagedefinition"
    14  )
    15  
    16  var basicImageDef = imagedefinition.ImageDefinition{
    17  	Architecture: getHostArch(),
    18  	Series:       getHostSuite(),
    19  	Rootfs: &imagedefinition.Rootfs{
    20  		Archive:           "ubuntu",
    21  		SourcesListDeb822: helper.BoolPtr(false),
    22  	},
    23  	Customization: &imagedefinition.Customization{},
    24  }
    25  
    26  // basicChroot holds the path to the basic chroot
    27  // this variable should be treated as a singleton
    28  var basicChroot *basicChrooter
    29  
    30  // basicChrooter provides a way to manage a basic chroot
    31  type basicChrooter struct {
    32  	m    sync.Mutex
    33  	path string
    34  }
    35  
    36  // NewBasicChroot creates but do not really initializes
    37  // a basicChroot. Initialization will be done by the first
    38  // user of the basicChroot
    39  func NewBasicChroot() *basicChrooter {
    40  	return &basicChrooter{
    41  		m: sync.Mutex{},
    42  	}
    43  }
    44  
    45  // isInit safely checks if the basicChroot is initialized
    46  func (b *basicChrooter) isInit() bool {
    47  	b.m.Lock()
    48  	defer b.m.Unlock()
    49  	return len(b.path) != 0
    50  }
    51  
    52  // init initializes the basicChroot
    53  func (b *basicChrooter) init() error {
    54  	// We need to protect the whole time the chroot is being created
    55  	// because we do not want to trigger multiple chroot creation in parallel
    56  	// This may block several tests during ~1-2min but this is an acceptable
    57  	// drawback to then win some time.
    58  	b.m.Lock()
    59  	defer b.m.Unlock()
    60  
    61  	var stateMachine ClassicStateMachine
    62  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
    63  	stateMachine.parent = &stateMachine
    64  	stateMachine.ImageDef = basicImageDef
    65  	path := filepath.Join("/tmp", "ubuntu-image-chroot-"+uuid.NewString())
    66  	stateMachine.tempDirs.chroot = path
    67  
    68  	err := helper.SetDefaults(&stateMachine.ImageDef)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	err = stateMachine.createChroot()
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	b.path = path
    79  
    80  	return nil
    81  }
    82  
    83  // Clean removes the chroot
    84  // This method is expected to be called only once at the end of the
    85  // test suite.
    86  func (b *basicChrooter) Clean() {
    87  	os.RemoveAll(b.path)
    88  }
    89  
    90  // getBasicChroot initializes and/or set the basicChroot path to
    91  // the given stateMachine
    92  func getBasicChroot(s StateMachine) error {
    93  	if !basicChroot.isInit() {
    94  		err := basicChroot.init()
    95  		if err != nil {
    96  			return err
    97  		}
    98  	}
    99  
   100  	return provideChroot(s)
   101  }
   102  
   103  // provideChroot provides a copy of the basicChroot to the given stateMachine
   104  func provideChroot(s StateMachine) error {
   105  	basicChroot.m.Lock()
   106  	defer basicChroot.m.Unlock()
   107  
   108  	return osutil.CopySpecialFile(basicChroot.path, s.tempDirs.chroot)
   109  }
   110  
   111  type mockRunCmd struct {
   112  	cmds []*exec.Cmd
   113  }
   114  
   115  func NewMockRunCommand() *mockRunCmd {
   116  	return &mockRunCmd{}
   117  }
   118  
   119  func (m *mockRunCmd) runCmd(cmd *exec.Cmd, debug bool) error {
   120  	m.cmds = append(m.cmds, cmd)
   121  	return nil
   122  }
   123  
   124  type mockExecCmd struct{}
   125  
   126  func NewMockExecCommand() *mockExecCmd {
   127  	return &mockExecCmd{}
   128  }
   129  
   130  func (m *mockExecCmd) Command(cmd string, args ...string) *exec.Cmd {
   131  	// Replace the command with an echo of it
   132  	//nolint:gosec,G204
   133  	return exec.Command("echo", append([]string{cmd}, args...)...)
   134  }