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

     1  // This file contains unit tests for all of the common state functions
     2  package statemachine
     3  
     4  import (
     5  	"bytes"
     6  	"crypto/rand"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strconv"
    11  	"strings"
    12  	"testing"
    13  
    14  	diskfs "github.com/diskfs/go-diskfs"
    15  	"github.com/google/uuid"
    16  	"github.com/snapcore/snapd/gadget"
    17  	"github.com/snapcore/snapd/gadget/quantity"
    18  	"github.com/snapcore/snapd/osutil"
    19  
    20  	"github.com/canonical/ubuntu-image/internal/helper"
    21  )
    22  
    23  // TestLoadGadgetYaml tests a successful load of gadget.yaml. It also tests that the unpack
    24  // directory is preserved if the relevant environment variable is set
    25  func TestLoadGadgetYaml(t *testing.T) {
    26  	asserter := helper.Asserter{T: t}
    27  	var stateMachine StateMachine
    28  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
    29  	stateMachine.YamlFilePath = filepath.Join("testdata", "gadget_tree", "meta", "gadget.yaml")
    30  
    31  	err := stateMachine.makeTemporaryDirectories()
    32  	asserter.AssertErrNil(err, true)
    33  
    34  	preserveDir := filepath.Join("/tmp", "ubuntu-image-"+uuid.NewString())
    35  	os.Setenv("UBUNTU_IMAGE_PRESERVE_UNPACK", preserveDir)
    36  	defer func() {
    37  		os.Unsetenv("UBUNTU_IMAGE_PRESERVE_UNPACK")
    38  	}()
    39  	// ensure unpack exists
    40  	err = os.MkdirAll(stateMachine.tempDirs.unpack, 0755)
    41  	asserter.AssertErrNil(err, true)
    42  	t.Cleanup(func() { os.RemoveAll(preserveDir) })
    43  	err = stateMachine.loadGadgetYaml()
    44  	asserter.AssertErrNil(err, true)
    45  
    46  	// check that unpack was preserved
    47  	preserveUnpack := filepath.Join(preserveDir, "unpack")
    48  	if _, err := os.Stat(preserveUnpack); err != nil {
    49  		t.Errorf("Preserve unpack directory %s does not exist", preserveUnpack)
    50  	}
    51  	os.RemoveAll(stateMachine.stateMachineFlags.WorkDir)
    52  }
    53  
    54  // TestFailedLoadGadgetYaml tests failures in the loadGadgetYaml state
    55  // This is achieved by providing an invalid gadget.yaml and mocking
    56  // os.MkdirAll, iotuil.ReadFile, osutil.CopyFile, and osutil.CopySpecialFile
    57  func TestFailedLoadGadgetYaml(t *testing.T) {
    58  	asserter := helper.Asserter{T: t}
    59  	var stateMachine StateMachine
    60  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
    61  
    62  	err := stateMachine.makeTemporaryDirectories()
    63  	asserter.AssertErrNil(err, true)
    64  
    65  	stateMachine.YamlFilePath = filepath.Join("testdata",
    66  		"gadget_tree", "meta", "gadget.yaml")
    67  	// mock osutil.CopySpecialFile
    68  	osutilCopyFile = mockCopyFile
    69  	defer func() {
    70  		osutilCopyFile = osutil.CopyFile
    71  	}()
    72  	err = stateMachine.loadGadgetYaml()
    73  	asserter.AssertErrContains(err, "Error copying gadget.yaml")
    74  	asserter.AssertErrContains(err, "\nThe gadget.yaml file is expected to be located in a \"meta\" subdirectory of the provided built gadget directory.\n")
    75  	osutilCopyFile = osutil.CopyFile
    76  
    77  	// mock osReadFile
    78  	osReadFile = mockReadFile
    79  	defer func() {
    80  		osReadFile = os.ReadFile
    81  	}()
    82  	err = stateMachine.loadGadgetYaml()
    83  	asserter.AssertErrContains(err, "Error reading gadget.yaml bytes")
    84  	osReadFile = os.ReadFile
    85  
    86  	// now test with the invalid yaml file
    87  	stateMachine.YamlFilePath = filepath.Join("testdata",
    88  		"gadget_tree_invalid", "meta", "gadget.yaml")
    89  	err = stateMachine.loadGadgetYaml()
    90  	asserter.AssertErrContains(err, "Error running InfoFromGadgetYaml")
    91  
    92  	// set a valid yaml file and preserveDir
    93  	stateMachine.YamlFilePath = filepath.Join("testdata",
    94  		"gadget_tree", "meta", "gadget.yaml")
    95  
    96  	// mock os.MkdirAll
    97  	osMkdirAll = mockMkdirAll
    98  	defer func() {
    99  		osMkdirAll = os.MkdirAll
   100  	}()
   101  	// run with and without the environment variable set
   102  	err = stateMachine.loadGadgetYaml()
   103  	asserter.AssertErrContains(err, "Error creating volume dir")
   104  
   105  	preserveDir := filepath.Join("/tmp", "ubuntu-image-"+uuid.NewString())
   106  	os.Setenv("UBUNTU_IMAGE_PRESERVE_UNPACK", preserveDir)
   107  	defer func() {
   108  		os.Unsetenv("UBUNTU_IMAGE_PRESERVE_UNPACK")
   109  	}()
   110  	t.Cleanup(func() { os.RemoveAll(preserveDir) })
   111  	err = stateMachine.loadGadgetYaml()
   112  	asserter.AssertErrContains(err, "Error creating preserve unpack directory")
   113  	osMkdirAll = os.MkdirAll
   114  
   115  	// mock osutil.CopySpecialFile
   116  	osutilCopySpecialFile = mockCopySpecialFile
   117  	defer func() {
   118  		osutilCopySpecialFile = osutil.CopySpecialFile
   119  	}()
   120  	err = stateMachine.loadGadgetYaml()
   121  	asserter.AssertErrContains(err, "Error preserving unpack dir")
   122  	osutilCopySpecialFile = osutil.CopySpecialFile
   123  	os.Unsetenv("UBUNTU_IMAGE_PRESERVE_UNPACK")
   124  
   125  	// set an invalid --image-size argument to cause a failure
   126  	stateMachine.commonFlags.Size = "test"
   127  	err = stateMachine.loadGadgetYaml()
   128  	asserter.AssertErrContains(err, "Failed to parse argument to --image-size")
   129  
   130  	os.RemoveAll(stateMachine.stateMachineFlags.WorkDir)
   131  }
   132  
   133  // TestGenerateDiskInfo tests that diskInfo can be generated
   134  func TestGenerateDiskInfo(t *testing.T) {
   135  	asserter := helper.Asserter{T: t}
   136  	var stateMachine StateMachine
   137  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   138  	stateMachine.commonFlags.DiskInfo = filepath.Join("testdata", "disk_info")
   139  
   140  	err := stateMachine.makeTemporaryDirectories()
   141  	asserter.AssertErrNil(err, true)
   142  
   143  	t.Cleanup(func() { os.RemoveAll(stateMachine.stateMachineFlags.WorkDir) })
   144  
   145  	err = stateMachine.generateDiskInfo()
   146  	asserter.AssertErrNil(err, true)
   147  
   148  	// make sure rootfs/.disk/info exists
   149  	_, err = os.Stat(filepath.Join(stateMachine.tempDirs.rootfs, ".disk", "info"))
   150  	if err != nil {
   151  		if os.IsNotExist(err) {
   152  			t.Errorf("Disk Info file should exist, but does not")
   153  		}
   154  	}
   155  
   156  	os.RemoveAll(stateMachine.stateMachineFlags.WorkDir)
   157  }
   158  
   159  // TestFailedGenerateDiskInfo tests failure scenarios in the generate_disk_info state
   160  func TestFailedGenerateDiskInfo(t *testing.T) {
   161  	asserter := helper.Asserter{T: t}
   162  	var stateMachine StateMachine
   163  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   164  	stateMachine.commonFlags.DiskInfo = filepath.Join("testdata", "fake_disk_info")
   165  
   166  	err := stateMachine.makeTemporaryDirectories()
   167  	asserter.AssertErrNil(err, true)
   168  
   169  	// mock os.Mkdir
   170  	osMkdir = mockMkdir
   171  	defer func() {
   172  		osMkdir = os.Mkdir
   173  	}()
   174  	err = stateMachine.generateDiskInfo()
   175  	asserter.AssertErrContains(err, "Failed to create disk info directory")
   176  	osMkdir = os.Mkdir
   177  
   178  	// mock osutil.CopyFile
   179  	osutilCopyFile = mockCopyFile
   180  	defer func() {
   181  		osutilCopyFile = osutil.CopyFile
   182  	}()
   183  	err = stateMachine.generateDiskInfo()
   184  	asserter.AssertErrContains(err, "Failed to copy Disk Info file")
   185  	osutilCopyFile = osutil.CopyFile
   186  
   187  	os.RemoveAll(stateMachine.stateMachineFlags.WorkDir)
   188  }
   189  
   190  // TestCalculateRootfsSizeNoImageSize tests that the rootfs size can be
   191  // calculated by using du commands when the image size is not specified
   192  // this is accomplished by setting the test gadget tree as rootfs and
   193  // verifying that the size is calculated correctly
   194  func TestCalculateRootfsSizeNoImageSize(t *testing.T) {
   195  	asserter := helper.Asserter{T: t}
   196  	var stateMachine StateMachine
   197  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   198  	stateMachine.tempDirs.rootfs = filepath.Join("testdata", "gadget_tree")
   199  
   200  	err := stateMachine.makeTemporaryDirectories()
   201  	asserter.AssertErrNil(err, true)
   202  
   203  	// set a valid yaml file and load it in
   204  	stateMachine.YamlFilePath = filepath.Join("testdata",
   205  		"gadget_tree", "meta", "gadget.yaml")
   206  	// ensure unpack exists
   207  	err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack, "gadget"), 0755)
   208  	asserter.AssertErrNil(err, true)
   209  	err = stateMachine.loadGadgetYaml()
   210  	asserter.AssertErrNil(err, true)
   211  
   212  	err = stateMachine.calculateRootfsSize()
   213  	asserter.AssertErrNil(err, true)
   214  
   215  	// rootfs size will be slightly different in different environments
   216  	correctSizeLower, err := quantity.ParseSize("8M")
   217  	asserter.AssertErrNil(err, true)
   218  	correctSizeUpper := correctSizeLower + 100000 // 0.1 MB range
   219  	if stateMachine.RootfsSize > correctSizeUpper ||
   220  		stateMachine.RootfsSize < correctSizeLower {
   221  		t.Errorf("expected rootfs size between %s and %s, got %s",
   222  			correctSizeLower.IECString(),
   223  			correctSizeUpper.IECString(),
   224  			stateMachine.RootfsSize.IECString())
   225  	}
   226  
   227  	os.RemoveAll(stateMachine.stateMachineFlags.WorkDir)
   228  }
   229  
   230  // TestCalculateRootfsSizeImageSize tests that the rootfs size can be
   231  // accurately calculated when the image size is specified
   232  func TestCalculateRootfsSizeImageSize(t *testing.T) {
   233  	testCases := []struct {
   234  		name         string
   235  		sizeArg      string
   236  		expectedSize quantity.Size
   237  	}{
   238  		{"one_image_size", "4G", 4183818240},
   239  		{"image_size_per_volume", "pc:4G", 4183818240},
   240  	}
   241  	for _, tc := range testCases {
   242  		t.Run("test_calculate_rootfs_size_image_size", func(t *testing.T) {
   243  			asserter := helper.Asserter{T: t}
   244  			var stateMachine StateMachine
   245  			stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   246  			stateMachine.tempDirs.rootfs = filepath.Join("testdata", "gadget_tree")
   247  			stateMachine.commonFlags.Size = tc.sizeArg
   248  
   249  			err := stateMachine.makeTemporaryDirectories()
   250  			asserter.AssertErrNil(err, true)
   251  
   252  			// set a valid yaml file and load it in
   253  			stateMachine.YamlFilePath = filepath.Join("testdata",
   254  				"gadget_tree", "meta", "gadget.yaml")
   255  			// ensure unpack exists
   256  			err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack, "gadget"), 0755)
   257  			asserter.AssertErrNil(err, true)
   258  			err = stateMachine.loadGadgetYaml()
   259  			asserter.AssertErrNil(err, true)
   260  
   261  			err = stateMachine.calculateRootfsSize()
   262  			asserter.AssertErrNil(err, true)
   263  
   264  			if stateMachine.RootfsSize != tc.expectedSize {
   265  				t.Errorf("Expected rootfs size %d, but got %d",
   266  					tc.expectedSize, stateMachine.RootfsSize)
   267  			}
   268  
   269  			os.RemoveAll(stateMachine.stateMachineFlags.WorkDir)
   270  		})
   271  	}
   272  }
   273  
   274  // TestFailedCalculateRootfsSize tests a failure when calculating the rootfs size
   275  // this is accomplished by setting rootfs to a directory that does not exist
   276  func TestFailedCalculateRootfsSize(t *testing.T) {
   277  	asserter := helper.Asserter{T: t}
   278  	var stateMachine StateMachine
   279  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   280  	stateMachine.tempDirs.rootfs = filepath.Join("testdata", uuid.NewString())
   281  
   282  	err := stateMachine.calculateRootfsSize()
   283  	asserter.AssertErrContains(err, "Error getting rootfs size")
   284  
   285  	// now set a value of --image-size that is too small to hold the rootfs
   286  	stateMachine.commonFlags.Size = "1M"
   287  
   288  	err = stateMachine.makeTemporaryDirectories()
   289  	asserter.AssertErrNil(err, true)
   290  
   291  	// set a valid yaml file and load it in
   292  	stateMachine.YamlFilePath = filepath.Join("testdata",
   293  		"gadget_tree", "meta", "gadget.yaml")
   294  	// ensure unpack exists
   295  	err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack, "gadget"), 0755)
   296  	asserter.AssertErrNil(err, true)
   297  	err = stateMachine.loadGadgetYaml()
   298  	asserter.AssertErrNil(err, true)
   299  
   300  	err = stateMachine.calculateRootfsSize()
   301  	asserter.AssertErrContains(err, "smaller than actual rootfs contents")
   302  }
   303  
   304  // TestPopulateBootfsContents tests a successful run of the populateBootfsContents state
   305  // and ensures that the appropriate files are placed in the bootfs
   306  func TestPopulateBootfsContents(t *testing.T) {
   307  	asserter := helper.Asserter{T: t}
   308  	var stateMachine StateMachine
   309  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   310  
   311  	err := stateMachine.makeTemporaryDirectories()
   312  	asserter.AssertErrNil(err, true)
   313  	t.Cleanup(func() { os.RemoveAll(stateMachine.stateMachineFlags.WorkDir) })
   314  
   315  	// set a valid yaml file and load it in
   316  	stateMachine.YamlFilePath = filepath.Join("testdata",
   317  		"gadget_tree", "meta", "gadget.yaml")
   318  	// ensure unpack exists
   319  	err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack, "gadget"), 0755)
   320  	asserter.AssertErrNil(err, true)
   321  	err = stateMachine.loadGadgetYaml()
   322  	asserter.AssertErrNil(err, true)
   323  
   324  	// populate unpack
   325  	files, err := os.ReadDir(filepath.Join("testdata", "gadget_tree"))
   326  	asserter.AssertErrNil(err, true)
   327  	for _, srcFile := range files {
   328  		srcFile := filepath.Join("testdata", "gadget_tree", srcFile.Name())
   329  		err = osutilCopySpecialFile(srcFile, filepath.Join(stateMachine.tempDirs.unpack, "gadget"))
   330  		asserter.AssertErrNil(err, true)
   331  	}
   332  
   333  	// ensure volumes exists
   334  	err = os.MkdirAll(stateMachine.tempDirs.volumes, 0755)
   335  	asserter.AssertErrNil(err, true)
   336  	err = stateMachine.populateBootfsContents()
   337  	asserter.AssertErrNil(err, true)
   338  
   339  	// check that bootfs contents were actually populated
   340  	bootFiles := []string{"boot", "ubuntu"}
   341  	for _, file := range bootFiles {
   342  		fullPath := filepath.Join(stateMachine.tempDirs.volumes,
   343  			"pc", "part2", "EFI", file)
   344  		if _, err := os.Stat(fullPath); err != nil {
   345  			t.Errorf("Expected %s to exist, but it does not", fullPath)
   346  		}
   347  	}
   348  }
   349  
   350  // TestPopulateBootfsContentsPiboot tests a successful run of the
   351  // populateBootfsContents state and ensures that the appropriate files are
   352  // placed in the bootfs, for the piboot bootloader.
   353  func TestPopulateBootfsContentsPiboot(t *testing.T) {
   354  	asserter := helper.Asserter{T: t}
   355  	var stateMachine StateMachine
   356  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   357  
   358  	err := stateMachine.makeTemporaryDirectories()
   359  	asserter.AssertErrNil(err, true)
   360  	t.Cleanup(func() { os.RemoveAll(stateMachine.stateMachineFlags.WorkDir) })
   361  
   362  	// set a valid yaml file and load it in
   363  	stateMachine.YamlFilePath = filepath.Join("testdata",
   364  		"gadget_tree_piboot", "meta", "gadget.yaml")
   365  	// ensure unpack exists
   366  	err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack, "gadget"), 0755)
   367  	asserter.AssertErrNil(err, true)
   368  	err = stateMachine.loadGadgetYaml()
   369  	asserter.AssertErrNil(err, true)
   370  
   371  	// populate unpack
   372  	files, err := os.ReadDir(filepath.Join("testdata", "gadget_tree_piboot"))
   373  	asserter.AssertErrNil(err, true)
   374  	for _, srcFile := range files {
   375  		srcFile := filepath.Join("testdata", "gadget_tree_piboot", srcFile.Name())
   376  		err = osutilCopySpecialFile(srcFile, filepath.Join(stateMachine.tempDirs.unpack, "gadget"))
   377  		asserter.AssertErrNil(err, true)
   378  	}
   379  
   380  	// ensure volumes exists
   381  	err = os.MkdirAll(stateMachine.tempDirs.volumes, 0755)
   382  	asserter.AssertErrNil(err, true)
   383  	err = stateMachine.populateBootfsContents()
   384  	asserter.AssertErrNil(err, true)
   385  
   386  	// check that bootfs contents were actually populated
   387  	bootFiles := []string{"config.txt", "cmdline.txt"}
   388  	for _, file := range bootFiles {
   389  		fullPath := filepath.Join(stateMachine.stateMachineFlags.WorkDir,
   390  			"root", file)
   391  		if _, err := os.Stat(fullPath); err != nil {
   392  			t.Errorf("Expected %s to exist, but it does not", fullPath)
   393  		}
   394  	}
   395  }
   396  
   397  // TestFailedPopulateBootfsContents tests failures in the populateBootfsContents state
   398  func TestFailedPopulateBootfsContents(t *testing.T) {
   399  	asserter := helper.Asserter{T: t}
   400  	var stateMachine StateMachine
   401  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   402  
   403  	err := stateMachine.makeTemporaryDirectories()
   404  	asserter.AssertErrNil(err, true)
   405  	t.Cleanup(func() { os.RemoveAll(stateMachine.stateMachineFlags.WorkDir) })
   406  
   407  	// set a valid yaml file and load it in
   408  	stateMachine.YamlFilePath = filepath.Join("testdata", "gadget-seed.yaml")
   409  	// ensure unpack exists
   410  	err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack, "gadget"), 0755)
   411  	asserter.AssertErrNil(err, true)
   412  	err = stateMachine.loadGadgetYaml()
   413  	asserter.AssertErrNil(err, true)
   414  
   415  	// ensure volumes exists
   416  	err = os.MkdirAll(stateMachine.tempDirs.volumes, 0755)
   417  	asserter.AssertErrNil(err, true)
   418  
   419  	// populate unpack
   420  	files, err := os.ReadDir(filepath.Join("testdata", "gadget_tree"))
   421  	asserter.AssertErrNil(err, true)
   422  	for _, srcFile := range files {
   423  		srcFile := filepath.Join("testdata", "gadget_tree", srcFile.Name())
   424  		err = osutilCopySpecialFile(srcFile, filepath.Join(stateMachine.tempDirs.unpack, "gadget"))
   425  		asserter.AssertErrNil(err, true)
   426  	}
   427  
   428  	// mock gadget.LayoutVolume
   429  	gadgetLayoutVolume = mockLayoutVolume
   430  	defer func() {
   431  		gadgetLayoutVolume = gadget.LayoutVolume
   432  	}()
   433  	err = stateMachine.populateBootfsContents()
   434  	asserter.AssertErrContains(err, "Error laying out bootfs contents")
   435  	gadgetLayoutVolume = gadget.LayoutVolume
   436  
   437  	// mock gadget.NewMountedFilesystemWriter
   438  	gadgetNewMountedFilesystemWriter = mockNewMountedFilesystemWriter
   439  	defer func() {
   440  		gadgetNewMountedFilesystemWriter = gadget.NewMountedFilesystemWriter
   441  	}()
   442  	err = stateMachine.populateBootfsContents()
   443  	asserter.AssertErrContains(err, "Error creating NewMountedFilesystemWriter")
   444  	gadgetNewMountedFilesystemWriter = gadget.NewMountedFilesystemWriter
   445  
   446  	// set rootfs to an empty string in order to trigger a failure in Write()
   447  	oldRootfs := stateMachine.tempDirs.rootfs
   448  	stateMachine.tempDirs.rootfs = ""
   449  	err = stateMachine.populateBootfsContents()
   450  	asserter.AssertErrContains(err, "Error in mountedFilesystem.Write")
   451  	// restore rootfs
   452  	stateMachine.tempDirs.rootfs = oldRootfs
   453  
   454  	// cause a failure in handleSecureBoot. First change to un-seeded yaml file and load it in
   455  	stateMachine.YamlFilePath = filepath.Join("testdata",
   456  		"gadget_tree", "meta", "gadget.yaml")
   457  	// ensure unpack exists
   458  	err = stateMachine.loadGadgetYaml()
   459  	asserter.AssertErrNil(err, true)
   460  	stateMachine.IsSeeded = false
   461  	// now ensure grub dir exists
   462  	err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack,
   463  		"image", "boot", "grub"), 0755)
   464  	asserter.AssertErrNil(err, true)
   465  	// mock os.MkdirAll
   466  	osMkdirAll = mockMkdirAll
   467  	defer func() {
   468  		osMkdirAll = os.MkdirAll
   469  	}()
   470  	err = stateMachine.populateBootfsContents()
   471  	asserter.AssertErrContains(err, "Error creating ubuntu dir")
   472  	osMkdirAll = os.MkdirAll
   473  }
   474  
   475  // TestPopulatePreparePartitions tests a successful run of the populatePreparePartitions state
   476  // and ensures that the appropriate .img files are created. It also tests that sizes smaller than
   477  // the rootfs size are corrected
   478  func TestPopulatePreparePartitions(t *testing.T) {
   479  	asserter := helper.Asserter{T: t}
   480  	var stateMachine StateMachine
   481  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   482  
   483  	err := stateMachine.makeTemporaryDirectories()
   484  	asserter.AssertErrNil(err, true)
   485  	t.Cleanup(func() { os.RemoveAll(stateMachine.stateMachineFlags.WorkDir) })
   486  
   487  	// set a valid yaml file and load it in
   488  	stateMachine.YamlFilePath = filepath.Join("testdata",
   489  		"gadget_tree", "meta", "gadget.yaml")
   490  	// ensure unpack exists
   491  	err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack, "gadget"), 0755)
   492  	asserter.AssertErrNil(err, true)
   493  	err = stateMachine.loadGadgetYaml()
   494  	asserter.AssertErrNil(err, true)
   495  
   496  	// ensure volumes exists
   497  	err = os.MkdirAll(stateMachine.tempDirs.volumes, 0755)
   498  	asserter.AssertErrNil(err, true)
   499  
   500  	// populate unpack
   501  	files, err := os.ReadDir(filepath.Join("testdata", "gadget_tree"))
   502  	asserter.AssertErrNil(err, true)
   503  	for _, srcFile := range files {
   504  		srcFile := filepath.Join("testdata", "gadget_tree", srcFile.Name())
   505  		err = osutilCopySpecialFile(srcFile, filepath.Join(stateMachine.tempDirs.unpack, "gadget"))
   506  		asserter.AssertErrNil(err, true)
   507  	}
   508  
   509  	// populate bootfs contents to ensure no failures there
   510  	err = stateMachine.populateBootfsContents()
   511  	asserter.AssertErrNil(err, true)
   512  
   513  	// calculate rootfs size so the partition sizes can be set correctly
   514  	err = stateMachine.calculateRootfsSize()
   515  	asserter.AssertErrNil(err, true)
   516  
   517  	err = stateMachine.populatePreparePartitions()
   518  	asserter.AssertErrNil(err, true)
   519  
   520  	// ensure the .img files were created
   521  	for ii := 0; ii < 4; ii++ {
   522  		partImg := filepath.Join(stateMachine.tempDirs.volumes,
   523  			"pc", "part"+strconv.Itoa(ii)+".img")
   524  		if _, err := os.Stat(partImg); err != nil {
   525  			t.Errorf("File %s should exist, but does not", partImg)
   526  		}
   527  	}
   528  
   529  	// check the contents of part0.img
   530  	partImg := filepath.Join(stateMachine.tempDirs.volumes,
   531  		"pc", "part0.img")
   532  	partImgBytes, err := os.ReadFile(partImg)
   533  	asserter.AssertErrNil(err, true)
   534  	dataBytes := make([]byte, 440)
   535  	// partImg should consist of these 11 bytes and 429 null bytes
   536  	copy(dataBytes[:11], []byte{84, 69, 83, 84, 32, 70, 73, 76, 69, 10})
   537  	if !bytes.Equal(partImgBytes, dataBytes) {
   538  		t.Errorf("Expected part0.img to contain %v, instead got %v %d",
   539  			dataBytes, partImgBytes, len(partImgBytes))
   540  	}
   541  }
   542  
   543  // TestFailedPopulatePreparePartitions tests failures in the populatePreparePartitions state
   544  func TestFailedPopulatePreparePartitions(t *testing.T) {
   545  	asserter := helper.Asserter{T: t}
   546  	var stateMachine StateMachine
   547  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   548  
   549  	err := stateMachine.makeTemporaryDirectories()
   550  	asserter.AssertErrNil(err, true)
   551  	t.Cleanup(func() { os.RemoveAll(stateMachine.stateMachineFlags.WorkDir) })
   552  
   553  	// set a valid yaml file and load it in
   554  	stateMachine.YamlFilePath = filepath.Join("testdata",
   555  		"gadget_tree", "meta", "gadget.yaml")
   556  	// ensure unpack exists
   557  	err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack, "gadget"), 0755)
   558  	asserter.AssertErrNil(err, true)
   559  	err = stateMachine.loadGadgetYaml()
   560  	asserter.AssertErrNil(err, true)
   561  
   562  	// ensure volumes exists
   563  	err = os.MkdirAll(stateMachine.tempDirs.volumes, 0755)
   564  	asserter.AssertErrNil(err, true)
   565  
   566  	// populate unpack
   567  	files, err := os.ReadDir(filepath.Join("testdata", "gadget_tree"))
   568  	asserter.AssertErrNil(err, true)
   569  	for _, srcFile := range files {
   570  		srcFile := filepath.Join("testdata", "gadget_tree", srcFile.Name())
   571  		err = osutilCopySpecialFile(srcFile, filepath.Join(stateMachine.tempDirs.unpack, "gadget"))
   572  		asserter.AssertErrNil(err, true)
   573  	}
   574  
   575  	// populate bootfs contents to ensure no failures there
   576  	err = stateMachine.populateBootfsContents()
   577  	asserter.AssertErrNil(err, true)
   578  
   579  	// now mock helper.CopyBlob to cause an error in copyStructureContent
   580  	helperCopyBlob = mockCopyBlob
   581  	defer func() {
   582  		helperCopyBlob = helper.CopyBlob
   583  	}()
   584  	err = stateMachine.populatePreparePartitions()
   585  	asserter.AssertErrContains(err, "Error zeroing partition")
   586  	helperCopyBlob = helper.CopyBlob
   587  
   588  	// set a bootloader to lk and mock mkdir to cause a failure in that function
   589  	for _, volume := range stateMachine.GadgetInfo.Volumes {
   590  		volume.Bootloader = "lk"
   591  	}
   592  	osMkdir = mockMkdir
   593  	defer func() {
   594  		osMkdir = os.Mkdir
   595  	}()
   596  	err = stateMachine.populatePreparePartitions()
   597  	asserter.AssertErrContains(err, "got lk bootloader but directory")
   598  	osMkdir = os.Mkdir
   599  }
   600  
   601  // TestEmptyPartPopulatePreparePartitions performs a successful run a gadget.yaml that has,
   602  // besides regular partitions, one empty partition and makes sure that a partition image file
   603  // has been created for it (LP: #1947863)
   604  func TestEmptyPartPopulatePreparePartitions(t *testing.T) {
   605  	asserter := helper.Asserter{T: t}
   606  	var stateMachine StateMachine
   607  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   608  
   609  	err := stateMachine.makeTemporaryDirectories()
   610  	asserter.AssertErrNil(err, true)
   611  	t.Cleanup(func() { os.RemoveAll(stateMachine.stateMachineFlags.WorkDir) })
   612  
   613  	// set a valid yaml file and load it in
   614  	// we use a special gadget.yaml here, special for this testcase
   615  	stateMachine.YamlFilePath = filepath.Join("testdata",
   616  		"gadget-empty-part.yaml")
   617  	// ensure unpack exists
   618  	err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack, "gadget"), 0755)
   619  	asserter.AssertErrNil(err, true)
   620  	err = stateMachine.loadGadgetYaml()
   621  	asserter.AssertErrNil(err, true)
   622  
   623  	// ensure volumes exists
   624  	err = os.MkdirAll(stateMachine.tempDirs.volumes, 0755)
   625  	asserter.AssertErrNil(err, true)
   626  
   627  	// populate unpack
   628  	files, err := os.ReadDir(filepath.Join("testdata", "gadget_tree"))
   629  	asserter.AssertErrNil(err, true)
   630  	for _, srcFile := range files {
   631  		srcFile := filepath.Join("testdata", "gadget_tree", srcFile.Name())
   632  		err = osutilCopySpecialFile(srcFile, filepath.Join(stateMachine.tempDirs.unpack, "gadget"))
   633  		asserter.AssertErrNil(err, true)
   634  	}
   635  
   636  	// populate bootfs contents to ensure no failures there
   637  	err = stateMachine.populateBootfsContents()
   638  	asserter.AssertErrNil(err, true)
   639  
   640  	// calculate rootfs size so the partition sizes can be set correctly
   641  	err = stateMachine.calculateRootfsSize()
   642  	asserter.AssertErrNil(err, true)
   643  
   644  	err = stateMachine.populatePreparePartitions()
   645  	asserter.AssertErrNil(err, true)
   646  
   647  	// ensure the .img files were created
   648  	for ii := 0; ii < 5; ii++ {
   649  		partImg := filepath.Join(stateMachine.tempDirs.volumes,
   650  			"pc", "part"+strconv.Itoa(ii)+".img")
   651  		if _, err := os.Stat(partImg); err != nil {
   652  			t.Errorf("File %s should exist, but does not", partImg)
   653  		}
   654  	}
   655  
   656  	// check part2.img, it should be empty and have a 4K size
   657  	partImg := filepath.Join(stateMachine.tempDirs.volumes,
   658  		"pc", "part2.img")
   659  	partImgBytes, err := os.ReadFile(partImg)
   660  	asserter.AssertErrNil(err, true)
   661  	// these are all zeroes
   662  	dataBytes := make([]byte, 4096)
   663  	if !bytes.Equal(partImgBytes, dataBytes) {
   664  		t.Errorf("Expected part2.img to contain %d zeroes, got something different (size %d)",
   665  			len(dataBytes), len(partImgBytes))
   666  	}
   667  }
   668  
   669  // TestMakeDiskPartitionSchemes tests that makeDisk() can successfully parse
   670  // mbr, gpt, and hybrid schemes. It then runs "dumpe2fs" to ensure the
   671  // resulting disk has the correct type of partition table.
   672  // We also check various sector sizes while at it and rootfs placements
   673  func TestMakeDiskPartitionSchemes(t *testing.T) {
   674  	testCases := []struct {
   675  		name          string
   676  		tableType     string
   677  		sectorSize    string
   678  		rootfsVolName string
   679  		rootfsPartNum int
   680  	}{
   681  		{"gpt", "gpt", "512", "pc", 3},
   682  		{"mbr", "dos", "512", "pc", 3},
   683  		{"hybrid", "gpt", "512", "pc", 3},
   684  		{"gpt4k", "PMBR", "4096", "pc", 3}, // PMBR still seems valid GPT
   685  		{"gpt-efi-only", "gpt", "512", "pc", 2},
   686  	}
   687  	for _, tc := range testCases {
   688  		t.Run("test_make_disk_partition_type_"+tc.name, func(t *testing.T) {
   689  			asserter := helper.Asserter{T: t}
   690  			var stateMachine StateMachine
   691  			stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   692  
   693  			// set the sector size to the one needed during testing
   694  			stateMachine.commonFlags.SectorSize = tc.sectorSize
   695  
   696  			err := stateMachine.makeTemporaryDirectories()
   697  			asserter.AssertErrNil(err, true)
   698  			t.Cleanup(func() { os.RemoveAll(stateMachine.stateMachineFlags.WorkDir) })
   699  
   700  			// also set up an output directory
   701  			outDir, err := os.MkdirTemp("/tmp", "ubuntu-image-")
   702  			asserter.AssertErrNil(err, true)
   703  			t.Cleanup(func() { os.RemoveAll(outDir) })
   704  			stateMachine.commonFlags.OutputDir = outDir
   705  
   706  			// set up volume names
   707  			stateMachine.VolumeNames = map[string]string{
   708  				"pc": "pc.img",
   709  			}
   710  
   711  			// set a valid yaml file and load it in
   712  			stateMachine.YamlFilePath = filepath.Join("testdata",
   713  				"gadget-"+tc.name+".yaml")
   714  			// ensure unpack exists
   715  			err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack, "gadget"), 0755)
   716  			asserter.AssertErrNil(err, true)
   717  			err = stateMachine.loadGadgetYaml()
   718  			asserter.AssertErrNil(err, true)
   719  
   720  			// set up a "rootfs" that we can eventually copy into the disk
   721  			err = os.MkdirAll(stateMachine.tempDirs.rootfs, 0755)
   722  			asserter.AssertErrNil(err, true)
   723  			err = osutil.CopySpecialFile(filepath.Join("testdata", "gadget_tree"), stateMachine.tempDirs.rootfs)
   724  			asserter.AssertErrNil(err, true)
   725  
   726  			// also need to set the rootfs size to avoid partition errors
   727  			err = stateMachine.calculateRootfsSize()
   728  			asserter.AssertErrNil(err, true)
   729  
   730  			// ensure volumes exists
   731  			err = os.MkdirAll(stateMachine.tempDirs.volumes, 0755)
   732  			asserter.AssertErrNil(err, true)
   733  
   734  			// populate unpack
   735  			files, err := os.ReadDir(filepath.Join("testdata", "gadget_tree"))
   736  			asserter.AssertErrNil(err, true)
   737  			for _, srcFile := range files {
   738  				srcFile := filepath.Join("testdata", "gadget_tree", srcFile.Name())
   739  				err = osutil.CopySpecialFile(srcFile, filepath.Join(stateMachine.tempDirs.unpack, "gadget"))
   740  				asserter.AssertErrNil(err, true)
   741  			}
   742  
   743  			// run through the rest of the states
   744  			err = stateMachine.populateBootfsContents()
   745  			asserter.AssertErrNil(err, true)
   746  
   747  			err = stateMachine.populatePreparePartitions()
   748  			asserter.AssertErrNil(err, true)
   749  
   750  			err = stateMachine.makeDisk()
   751  			asserter.AssertErrNil(err, true)
   752  
   753  			// now run "dumpe2fs" to ensure the correct type of partition table exists
   754  			imgFile := filepath.Join(stateMachine.commonFlags.OutputDir, "pc.img")
   755  			dumpe2fsCommand := *exec.Command("dumpe2fs", imgFile)
   756  
   757  			dumpe2fsBytes, _ := dumpe2fsCommand.CombinedOutput() // nolint: errcheck
   758  			// The command will return an error because the image itself is not valid but we do
   759  			// not care here.
   760  			if !strings.Contains(string(dumpe2fsBytes), tc.tableType) {
   761  				t.Errorf("File %s should have partition table %s, instead got \"%s\"",
   762  					imgFile, tc.tableType, string(dumpe2fsBytes))
   763  			}
   764  
   765  			// ensure the resulting image file is a multiple of the block size
   766  			diskImg, err := diskfs.Open(imgFile)
   767  			asserter.AssertErrNil(err, true)
   768  			defer diskImg.File.Close()
   769  			if diskImg.Size%int64(stateMachine.SectorSize) != 0 {
   770  				t.Errorf("Disk image size %d is not an multiple of the block size: %d",
   771  					diskImg.Size, int64(stateMachine.SectorSize))
   772  			}
   773  
   774  			// while at it, ensure that the root partition has been found
   775  			if stateMachine.RootfsPartNum != tc.rootfsPartNum || stateMachine.RootfsVolName != tc.rootfsVolName {
   776  				t.Errorf("Root partition volume/numbe not detected correctly, expected %s/%d, got %s/%d",
   777  					tc.rootfsVolName, tc.rootfsPartNum, stateMachine.RootfsVolName, stateMachine.RootfsPartNum)
   778  			}
   779  		})
   780  	}
   781  }
   782  
   783  // TestFailedMakeDisk tests failures in the MakeDisk state
   784  func TestFailedMakeDisk(t *testing.T) {
   785  	asserter := helper.Asserter{T: t}
   786  	var stateMachine StateMachine
   787  	stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   788  
   789  	err := stateMachine.makeTemporaryDirectories()
   790  	asserter.AssertErrNil(err, true)
   791  	t.Cleanup(func() { os.RemoveAll(stateMachine.stateMachineFlags.WorkDir) })
   792  
   793  	// also set up an output directory
   794  	outDir, err := os.MkdirTemp("/tmp", "ubuntu-image-")
   795  	asserter.AssertErrNil(err, true)
   796  	t.Cleanup(func() { os.RemoveAll(outDir) })
   797  	stateMachine.commonFlags.OutputDir = outDir
   798  	err = stateMachine.determineOutputDirectory()
   799  	asserter.AssertErrNil(err, true)
   800  
   801  	// set up volume names
   802  	stateMachine.VolumeNames = map[string]string{
   803  		"pc": "pc.img",
   804  	}
   805  
   806  	// set a valid yaml file and load it in
   807  	stateMachine.YamlFilePath = filepath.Join("testdata", "gadget-mbr.yaml")
   808  	// ensure unpack exists
   809  	err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack, "gadget"), 0755)
   810  	asserter.AssertErrNil(err, true)
   811  	err = stateMachine.loadGadgetYaml()
   812  	asserter.AssertErrNil(err, true)
   813  
   814  	// also need to set the rootfs size to avoid partition errors
   815  	err = stateMachine.calculateRootfsSize()
   816  	asserter.AssertErrNil(err, true)
   817  
   818  	// ensure volumes exists
   819  	err = os.MkdirAll(stateMachine.tempDirs.volumes, 0755)
   820  	asserter.AssertErrNil(err, true)
   821  
   822  	// populate unpack
   823  	files, err := os.ReadDir(filepath.Join("testdata", "gadget_tree"))
   824  	asserter.AssertErrNil(err, true)
   825  	for _, srcFile := range files {
   826  		srcFile := filepath.Join("testdata", "gadget_tree", srcFile.Name())
   827  		err = osutilCopySpecialFile(srcFile, filepath.Join(stateMachine.tempDirs.unpack, "gadget"))
   828  		asserter.AssertErrNil(err, true)
   829  	}
   830  
   831  	// mock os.RemoveAll
   832  	osRemoveAll = mockRemoveAll
   833  	defer func() {
   834  		osRemoveAll = os.RemoveAll
   835  	}()
   836  	err = stateMachine.makeDisk()
   837  	asserter.AssertErrContains(err, "Error removing old disk image")
   838  	osRemoveAll = os.RemoveAll
   839  
   840  	// mock diskfs.Create
   841  	diskfsCreate = mockDiskfsCreate
   842  	defer func() {
   843  		diskfsCreate = diskfs.Create
   844  	}()
   845  	err = stateMachine.makeDisk()
   846  	asserter.AssertErrContains(err, "Error creating disk image")
   847  	diskfsCreate = diskfs.Create
   848  
   849  	// mock os.Truncate
   850  	osTruncate = mockTruncate
   851  	defer func() {
   852  		osTruncate = os.Truncate
   853  	}()
   854  	err = stateMachine.makeDisk()
   855  	asserter.AssertErrContains(err, "Error resizing disk image")
   856  	osTruncate = os.Truncate
   857  
   858  	// mock diskfs.Create to create a read only disk
   859  	diskfsCreate = readOnlyDiskfsCreate
   860  	defer func() {
   861  		diskfsCreate = diskfs.Create
   862  	}()
   863  	err = stateMachine.makeDisk()
   864  	asserter.AssertErrContains(err, "Error partitioning image file")
   865  	diskfsCreate = diskfs.Create
   866  
   867  	// mock os.OpenFile
   868  	// errors in file.WriteAt()
   869  	osOpenFile = mockOpenFile
   870  	defer func() {
   871  		osOpenFile = os.OpenFile
   872  	}()
   873  	err = stateMachine.makeDisk()
   874  	asserter.AssertErrContains(err, "Error opening disk to write MBR disk identifier")
   875  	osOpenFile = os.OpenFile
   876  
   877  	// mock rand.Read
   878  	// errors in generateUniqueDiskID()
   879  	randRead = mockRandRead
   880  	defer func() {
   881  		randRead = rand.Read
   882  	}()
   883  	err = stateMachine.makeDisk()
   884  	asserter.AssertErrContains(err, "Error generating disk ID")
   885  	randRead = rand.Read
   886  
   887  	// mock os.OpenFile to force it to use os.O_APPEND, which causes
   888  	// errors in file.WriteAt()
   889  	osOpenFile = mockOpenFileAppend
   890  	defer func() {
   891  		osOpenFile = os.OpenFile
   892  	}()
   893  	err = stateMachine.makeDisk()
   894  	asserter.AssertErrContains(err, "Error writing MBR disk identifier")
   895  	osOpenFile = os.OpenFile
   896  
   897  	// mock helper.CopyBlob to simulate a failure in copyDataToImage
   898  	helperCopyBlob = mockCopyBlob
   899  	defer func() {
   900  		helperCopyBlob = helper.CopyBlob
   901  	}()
   902  	err = stateMachine.makeDisk()
   903  	asserter.AssertErrContains(err, "Error writing disk image")
   904  	helperCopyBlob = helper.CopyBlob
   905  
   906  	// Change to GPT for these next tests
   907  	stateMachine.YamlFilePath = filepath.Join("testdata", "gadget-gpt.yaml")
   908  	err = stateMachine.loadGadgetYaml()
   909  	asserter.AssertErrNil(err, true)
   910  
   911  	err = stateMachine.populateBootfsContents()
   912  	asserter.AssertErrNil(err, true)
   913  
   914  	err = stateMachine.populatePreparePartitions()
   915  	asserter.AssertErrNil(err, true)
   916  
   917  	// mock os.OpenFile to simulate a failure in writeOffsetValues
   918  	osOpenFile = mockOpenFile
   919  	defer func() {
   920  		osOpenFile = os.OpenFile
   921  	}()
   922  	// also mock helperCopyBlob to ignore missing files and return success
   923  	helperCopyBlob = mockCopyBlobSuccess
   924  	defer func() {
   925  		helperCopyBlob = helper.CopyBlob
   926  	}()
   927  	err = stateMachine.makeDisk()
   928  	asserter.AssertErrContains(err, "Error opening image file")
   929  	osOpenFile = os.OpenFile
   930  	helperCopyBlob = helper.CopyBlob
   931  
   932  	helperCopyBlob = mockCopyBlob
   933  	defer func() {
   934  		helperCopyBlob = helper.CopyBlob
   935  	}()
   936  	stateMachine.cleanWorkDir = true // for coverage!
   937  	stateMachine.commonFlags.OutputDir = ""
   938  	defer os.Remove("pc.img")
   939  	err = stateMachine.makeDisk()
   940  	asserter.AssertErrContains(err, "Error writing disk image")
   941  	helperCopyBlob = helper.CopyBlob
   942  
   943  	// make sure with no OutputDir the image was created in the cwd
   944  	_, err = os.Stat("pc.img")
   945  	asserter.AssertErrNil(err, true)
   946  }
   947  
   948  // TestImageSizeFlag performs a successful call to StateMachine.MakeDisk with the
   949  // --image-size flag, and ensures that the resulting image is the size specified
   950  // with the flag (LP: #1947867)
   951  func TestImageSizeFlag(t *testing.T) {
   952  	testCases := []struct {
   953  		name       string
   954  		sizeArg    string
   955  		gadgetTree string
   956  		imageSize  map[string]quantity.Size
   957  		volNames   map[string]string
   958  	}{
   959  		{
   960  			"one_volume",
   961  			"4G",
   962  			filepath.Join("testdata", "gadget_tree"),
   963  			map[string]quantity.Size{
   964  				"pc": 4 * quantity.SizeGiB,
   965  			},
   966  			map[string]string{
   967  				"pc": "pc.img",
   968  			},
   969  		},
   970  		{
   971  			"multi_volume",
   972  			"first:4G,second:1G",
   973  			filepath.Join("testdata", "gadget_tree_multi"),
   974  			map[string]quantity.Size{
   975  				"first":  4 * quantity.SizeGiB,
   976  				"second": 1 * quantity.SizeGiB,
   977  			},
   978  			map[string]string{
   979  				"first":  "first.img",
   980  				"second": "second.img",
   981  			},
   982  		},
   983  	}
   984  	for _, tc := range testCases {
   985  		t.Run(tc.name, func(t *testing.T) {
   986  			asserter := helper.Asserter{T: t}
   987  			var stateMachine StateMachine
   988  			stateMachine.commonFlags, stateMachine.stateMachineFlags = helper.InitCommonOpts()
   989  			stateMachine.IsSeeded = true
   990  			stateMachine.commonFlags.Size = tc.sizeArg
   991  
   992  			err := stateMachine.makeTemporaryDirectories()
   993  			asserter.AssertErrNil(err, true)
   994  			//t.Cleanup(func() { os.RemoveAll(stateMachine.stateMachineFlags.WorkDir) })
   995  
   996  			// also set up an output directory
   997  			outDir, err := os.MkdirTemp("/tmp", "ubuntu-image-")
   998  			asserter.AssertErrNil(err, true)
   999  			//t.Cleanup(func() { os.RemoveAll(outDir) })
  1000  			stateMachine.commonFlags.OutputDir = outDir
  1001  
  1002  			// set up volume names
  1003  			stateMachine.VolumeNames = tc.volNames
  1004  
  1005  			// set up a "rootfs" that we can eventually copy into the disk
  1006  			err = os.MkdirAll(stateMachine.tempDirs.rootfs, 0755)
  1007  			asserter.AssertErrNil(err, true)
  1008  			err = osutil.CopySpecialFile(tc.gadgetTree, stateMachine.tempDirs.rootfs)
  1009  			asserter.AssertErrNil(err, true)
  1010  
  1011  			// set a valid yaml file and load it in
  1012  			stateMachine.YamlFilePath = filepath.Join(tc.gadgetTree, "meta", "gadget.yaml")
  1013  			// ensure unpack exists
  1014  			err = os.MkdirAll(filepath.Join(stateMachine.tempDirs.unpack, "gadget"), 0755)
  1015  			asserter.AssertErrNil(err, true)
  1016  			err = stateMachine.loadGadgetYaml()
  1017  			asserter.AssertErrNil(err, true)
  1018  
  1019  			// ensure volumes exists
  1020  			err = os.MkdirAll(stateMachine.tempDirs.volumes, 0755)
  1021  			asserter.AssertErrNil(err, true)
  1022  			// populate unpack
  1023  			files, err := os.ReadDir(tc.gadgetTree)
  1024  			asserter.AssertErrNil(err, true)
  1025  			for _, srcFile := range files {
  1026  				srcFile := filepath.Join(tc.gadgetTree, srcFile.Name())
  1027  				err = osutil.CopySpecialFile(srcFile, filepath.Join(stateMachine.tempDirs.unpack, "gadget"))
  1028  				asserter.AssertErrNil(err, true)
  1029  			}
  1030  
  1031  			// also need to set the rootfs size to avoid partition errors
  1032  			err = stateMachine.calculateRootfsSize()
  1033  			asserter.AssertErrNil(err, true)
  1034  
  1035  			// run through the rest of the states
  1036  			err = stateMachine.populateBootfsContents()
  1037  			asserter.AssertErrNil(err, true)
  1038  
  1039  			err = stateMachine.populatePreparePartitions()
  1040  			asserter.AssertErrNil(err, true)
  1041  
  1042  			err = stateMachine.makeDisk()
  1043  			asserter.AssertErrNil(err, true)
  1044  
  1045  			// check the size of the disk(s)
  1046  			for volume, expectedSize := range tc.imageSize {
  1047  				imgFile := filepath.Join(stateMachine.commonFlags.OutputDir, volume+".img")
  1048  				diskImg, err := os.Stat(imgFile)
  1049  				asserter.AssertErrNil(err, true)
  1050  				if diskImg.Size() != int64(expectedSize) {
  1051  					t.Errorf("--image-size %d was specified, but resulting image is %d bytes",
  1052  						expectedSize, diskImg.Size())
  1053  				}
  1054  			}
  1055  		})
  1056  
  1057  	}
  1058  }