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 }