...

Source file src/edge-infra.dev/pkg/sds/patching/patchmanager/patchextract_test.go

Documentation: edge-infra.dev/pkg/sds/patching/patchmanager

     1  package patchmanager
     2  
     3  import (
     4  	"archive/tar"
     5  	"compress/gzip"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	fsPkg "io/fs"
    10  	"os"
    11  	"path/filepath"
    12  	"reflect"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/go-logr/logr"
    17  	versionPkg "github.com/hashicorp/go-version"
    18  	"gotest.tools/v3/fs"
    19  
    20  	"edge-infra.dev/pkg/lib/logging"
    21  	common "edge-infra.dev/pkg/sds/patching/common"
    22  )
    23  
    24  func newConfig(t *testing.T) common.Config {
    25  	dir := fs.NewDir(t, "testing")
    26  	MountPath := dir.Path()
    27  
    28  	c := common.Config{
    29  		MountPath:         MountPath,
    30  		ArtefactsPath:     MountPath + "/versions",
    31  		ArtefactsTempPath: MountPath + "/versions/.tmp",
    32  		LiveBootPath:      MountPath + "/live",
    33  		ScriptsPath:       MountPath + "/patching",
    34  		ScriptsTempPath:   MountPath + "/patching/.tmp",
    35  		EnvFilePath:       MountPath + "/patching/patching.env",
    36  		LegacyScriptsPath: MountPath + "/upgrade",
    37  
    38  		BootFiles:   MountPath + "/boot/",
    39  		ScriptFiles: MountPath + "/patching/",
    40  
    41  		PatchsetMount: MountPath + "/mnt/patchset",
    42  		RebootPath:    MountPath + "/mnt/run/reboot-required",
    43  	}
    44  	return c
    45  }
    46  
    47  func createFileTree(cfg common.Config) error {
    48  	items := []string{
    49  		cfg.MountPath,
    50  		cfg.ArtefactsPath,
    51  		cfg.ArtefactsTempPath,
    52  		cfg.LiveBootPath,
    53  		cfg.ScriptsPath,
    54  		cfg.ScriptsTempPath,
    55  		cfg.EnvFilePath,
    56  		cfg.LegacyScriptsPath,
    57  		cfg.BootFiles,
    58  		cfg.ScriptFiles,
    59  		cfg.MountPath + "/mnt",
    60  		cfg.MountPath + "/mnt/run",
    61  		cfg.MountPath + "/boot",
    62  	}
    63  
    64  	for _, item := range items {
    65  		if err := os.MkdirAll(item, 0744); err != nil {
    66  			return err
    67  		}
    68  	}
    69  	return nil
    70  }
    71  
    72  func createTempFile(dir string, count int) ([]os.File, error) {
    73  	tempFiles := make([]os.File, count)
    74  
    75  	for i := 0; i < count; i++ {
    76  		tempFile, err := os.CreateTemp(dir, "")
    77  		if err != nil {
    78  			return nil, fmt.Errorf("error creating tempfile: %s", err)
    79  		}
    80  
    81  		tempFiles[i] = *tempFile
    82  		tempFile.Close()
    83  	}
    84  	return tempFiles, nil
    85  }
    86  
    87  func setupTarArchive(filePath string, tempFiles []os.File, cfg common.Config) (*os.File, error) {
    88  	writeArchive, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0744)
    89  	if err != nil {
    90  		return nil, fmt.Errorf("error opening archive file: %s", err)
    91  	}
    92  
    93  	gw := gzip.NewWriter(writeArchive)
    94  	defer gw.Close()
    95  	tw := tar.NewWriter(gw)
    96  	defer tw.Close()
    97  
    98  	for _, f := range tempFiles {
    99  		file, err := os.Open(f.Name())
   100  		if err != nil {
   101  			return nil, fmt.Errorf("error opening tempfile: %s", err)
   102  		}
   103  
   104  		tempFileInfo, err := file.Stat()
   105  		if err != nil {
   106  			return nil, fmt.Errorf("%s", err)
   107  		}
   108  
   109  		header, err := tar.FileInfoHeader(tempFileInfo, tempFileInfo.Name())
   110  		if err != nil {
   111  			return nil, fmt.Errorf("error creating tempfile header: %s", err)
   112  		}
   113  
   114  		header.Name = strings.TrimPrefix(strings.Replace(file.Name(), cfg.MountPath, "", -1), string(filepath.Separator))
   115  
   116  		err = tw.WriteHeader(header)
   117  		if err != nil {
   118  			return nil, fmt.Errorf("error writing header to archive: %s", err)
   119  		}
   120  
   121  		_, err = io.Copy(tw, file)
   122  		if err != nil {
   123  			return nil, fmt.Errorf("error copying tempfile to archive: %s", err)
   124  		}
   125  
   126  		if err := file.Close(); err != nil {
   127  			return nil, fmt.Errorf("error closing tempfile: %s", err)
   128  		}
   129  	}
   130  
   131  	readArchive, err := os.OpenFile(filePath, os.O_RDONLY, 0744)
   132  	if err != nil {
   133  		return nil, fmt.Errorf("error opening archive file: %s", err)
   134  	}
   135  	return readArchive, nil
   136  }
   137  
   138  func createDummyScripts(t *testing.T, filePath, phaseScriptsTempPath, phaseScriptsPath string) {
   139  	if err := os.MkdirAll(phaseScriptsTempPath, 0744); err != nil {
   140  		t.Errorf("unable to create temp script phase directories: %s", err)
   141  	}
   142  
   143  	if err := os.MkdirAll(phaseScriptsPath, 0744); err != nil {
   144  		t.Errorf("unable to create script phase directories: %s", err)
   145  	}
   146  
   147  	file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0744)
   148  	if err != nil {
   149  		t.Errorf("unable to create dummy scripts: %s", err)
   150  	}
   151  	defer file.Close()
   152  }
   153  
   154  func TestGetArtefactsPath(t *testing.T) {
   155  	cfg := newConfig(t)
   156  	version := "v1.10.1"
   157  
   158  	got := GetArtefactsPath(version, cfg)
   159  	expect := cfg.MountPath + "/versions/v1.10.1"
   160  	if got != expect {
   161  		t.Errorf("unexpected result. Wanted: %s; Got: %s", expect, got)
   162  	}
   163  
   164  	t.Logf("ArtefactsPath: %s", got)
   165  }
   166  
   167  func TestValidateChecksum(t *testing.T) {
   168  	scenarios := []struct {
   169  		cfg                common.Config
   170  		checksum, contents string
   171  	}{
   172  		{cfg: newConfig(t), checksum: "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2", contents: "foobar"},
   173  	}
   174  
   175  	for _, s := range scenarios {
   176  		file, err := os.CreateTemp(s.cfg.MountPath, "TestValidateChecksum")
   177  		if err != nil {
   178  			t.Errorf("error while creating temp file: %s", err)
   179  		}
   180  		defer file.Close()
   181  
   182  		_, err = file.Write([]byte(s.contents))
   183  		if err != nil {
   184  			t.Errorf("error while writing to temp file: %s", err)
   185  		}
   186  
   187  		err = ValidateChecksum(file.Name(), s.checksum)
   188  		if err != nil {
   189  			t.Errorf("error: Got %s. Wanted %s.", err, s.checksum)
   190  		}
   191  
   192  		t.Logf("Checksum: %s", s.checksum)
   193  	}
   194  }
   195  
   196  func TestGetSha256(t *testing.T) {
   197  	scenarios := []struct {
   198  		cfg                common.Config
   199  		checksum, contents string
   200  	}{
   201  		{cfg: newConfig(t), checksum: "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2", contents: "foobar"},
   202  	}
   203  
   204  	for _, s := range scenarios {
   205  		file, err := os.CreateTemp(s.cfg.MountPath, "TestValidateChecksum")
   206  		if err != nil {
   207  			t.Errorf("error while creating temp file: %s", err)
   208  		}
   209  		defer file.Close()
   210  
   211  		_, err = file.Write([]byte(s.contents))
   212  		if err != nil {
   213  			t.Errorf("error while writing to temp file: %s", err)
   214  		}
   215  
   216  		sha, err := GetSha256(file.Name())
   217  		if len(sha) != 64 {
   218  			t.Errorf("SHA256sum value is invalid; not 64 characters")
   219  		} else if err != nil {
   220  			t.Errorf("error: Got %s. Wanted %s.", err, s.checksum)
   221  		}
   222  	}
   223  }
   224  
   225  func TestLoadDirectivesConfig(t *testing.T) {
   226  	scenarios := []struct {
   227  		cfg      common.Config
   228  		versions []string
   229  		source   string
   230  		expect   []DirectivesConfig
   231  	}{
   232  		{ // Multiple directives and script options
   233  			cfg: newConfig(t),
   234  			versions: []string{
   235  				"v1.11.0",
   236  			},
   237  			source: `directives:
   238    - regex: (v1.10.1)
   239      script_options:
   240        - name: v1.09.4
   241          skip: false
   242          weight: 1
   243        - name: v1.10.1
   244          skip: true
   245          weight: 2
   246        - name: v1.10.1
   247    - regex: (v1.11.1)
   248      script_options:
   249        - name: v1.11.1
   250          skip: false
   251          weight: 3`,
   252  			expect: []DirectivesConfig{
   253  				{
   254  
   255  					Directives: []struct {
   256  						Regex         string `yaml:"regex"`
   257  						ScriptOptions []struct {
   258  							Name   string
   259  							Skip   bool
   260  							Weight int
   261  						} `yaml:"script_options"`
   262  					}{
   263  						{
   264  							Regex: "(v1.10.1)",
   265  							ScriptOptions: []struct {
   266  								Name   string
   267  								Skip   bool
   268  								Weight int
   269  							}{
   270  								{
   271  									Name:   "v1.09.4",
   272  									Skip:   false,
   273  									Weight: 1,
   274  								},
   275  								{
   276  									Name:   "v1.10.1",
   277  									Skip:   true,
   278  									Weight: 2,
   279  								},
   280  								{
   281  									Name:   "v1.10.1", // Skip and Weight default to equivalent zero-values
   282  									Skip:   false,
   283  									Weight: 0,
   284  								},
   285  							},
   286  						},
   287  						{
   288  							Regex: "(v1.11.1)",
   289  							ScriptOptions: []struct {
   290  								Name   string
   291  								Skip   bool
   292  								Weight int
   293  							}{
   294  								{
   295  									Name:   "v1.11.1",
   296  									Skip:   false,
   297  									Weight: 3,
   298  								},
   299  							},
   300  						},
   301  					},
   302  				},
   303  			},
   304  		},
   305  	}
   306  
   307  	for _, s := range scenarios {
   308  		versions := make([]*versionPkg.Version, len(s.versions))
   309  
   310  		for i, v := range s.versions {
   311  			version, err := versionPkg.NewVersion(v)
   312  			if err != nil {
   313  				t.Errorf("error converting slice of version strings into version structs: %s", err)
   314  			}
   315  			versions[i] = version
   316  
   317  			configPath := filepath.Join(s.cfg.ScriptsTempPath, version.Original(), "config.yaml")
   318  
   319  			if err := os.MkdirAll(filepath.Dir(configPath), 0744); err != nil {
   320  				t.Errorf("error creating filepath: %s", err)
   321  			}
   322  
   323  			file, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE, 0744)
   324  			if err != nil {
   325  				t.Errorf("error creating config.yaml: %s", err)
   326  			}
   327  
   328  			_, err = file.Write([]byte(s.source))
   329  			if err != nil {
   330  				t.Errorf("error writing YAML string to config.yaml: %s", err)
   331  			}
   332  
   333  			if err := file.Close(); err != nil {
   334  				t.Errorf("error closing file: %s", err)
   335  			}
   336  		}
   337  
   338  		got, err := loadDirectivesConfig(versions, s.cfg)
   339  		if err != nil {
   340  			t.Errorf("%s", err)
   341  		}
   342  
   343  		if reflect.DeepEqual(got, s.expect) != true {
   344  			t.Errorf("unexpected return value for loadDirectivesConfig. Got %v. Expected %v", got, s.expect)
   345  		}
   346  	}
   347  }
   348  
   349  func TestInstallScriptsVersion(t *testing.T) {
   350  	scenarios := []struct {
   351  		cfg                 common.Config
   352  		version, currentVer string
   353  		isControlPlane      bool
   354  		log                 logr.Logger
   355  		hashMaps            map[string]map[string]string
   356  		configs             []DirectivesConfig
   357  	}{
   358  		{ // Multiple directives and script options
   359  			cfg:            newConfig(t),
   360  			version:        "v1.11.0",
   361  			currentVer:     "v1.10.1",
   362  			isControlPlane: true,
   363  			log:            logging.NewLogger().Logger,
   364  			hashMaps:       make(map[string]map[string]string),
   365  			configs: []DirectivesConfig{
   366  				{
   367  					Directives: []struct {
   368  						Regex         string `yaml:"regex"`
   369  						ScriptOptions []struct {
   370  							Name   string
   371  							Skip   bool
   372  							Weight int
   373  						} `yaml:"script_options"`
   374  					}{
   375  						{
   376  							Regex: "v1.10.1",
   377  							ScriptOptions: []struct {
   378  								Name   string
   379  								Skip   bool
   380  								Weight int
   381  							}{
   382  								{
   383  									Name:   "01_script.sh",
   384  									Skip:   false,
   385  									Weight: 1,
   386  								},
   387  							},
   388  						},
   389  					},
   390  				},
   391  			},
   392  		},
   393  	}
   394  
   395  	for _, s := range scenarios {
   396  		version, err := versionPkg.NewVersion(s.version)
   397  		if err != nil {
   398  			t.Errorf("error converting slice of version strings into version structs: %s", err)
   399  		}
   400  
   401  		currentVer, err := versionPkg.NewVersion(s.currentVer)
   402  		if err != nil {
   403  			t.Errorf("error converting slice of version strings into version structs: %s", err)
   404  		}
   405  
   406  		for _, phase := range upgradePhases {
   407  			var (
   408  				phaseScriptsTempPath = filepath.Join(s.cfg.ScriptsTempPath, version.Original(), phase)
   409  				phaseScriptsPath     = filepath.Join(s.cfg.ScriptsPath, phase)
   410  				filePaths            []string
   411  			)
   412  
   413  			filePaths = append(filePaths, filepath.Join(phaseScriptsTempPath, "01_script.sh"))
   414  			if phase == "post-reboot" {
   415  				filePaths = append(filePaths, filepath.Join(phaseScriptsTempPath, "02_script.sh")) // tests skip duplicate scripts branch
   416  			}
   417  
   418  			for _, filePath := range filePaths {
   419  				createDummyScripts(t, filePath, phaseScriptsPath, phaseScriptsTempPath)
   420  			}
   421  			s.hashMaps[phase] = make(map[string]string)
   422  		}
   423  		err = installScriptsVersion(currentVer, version, s.configs, s.isControlPlane, s.log, s.cfg, s.hashMaps)
   424  		if err != nil {
   425  			t.Errorf("%s", err)
   426  		}
   427  	}
   428  }
   429  
   430  func TestExtractTarFile(t *testing.T) {
   431  	scenarios := []struct {
   432  		cfg     common.Config
   433  		log     logr.Logger
   434  		subPath string
   435  	}{
   436  		{cfg: newConfig(t), log: logging.NewLogger().Logger, subPath: "boot"},
   437  		{cfg: newConfig(t), log: logging.NewLogger().Logger, subPath: "patching"},
   438  	}
   439  
   440  	for _, s := range scenarios {
   441  		var dir = filepath.Join(s.cfg.MountPath, s.subPath)
   442  		var path = filepath.Join(dir, "archive.tar.gz")
   443  
   444  		if err := createFileTree(s.cfg); err != nil {
   445  			t.Errorf("unable to create file tree: %s", err)
   446  		}
   447  
   448  		tempFiles, err := createTempFile(dir, 1)
   449  		if err != nil {
   450  			t.Errorf("error creating tempfile: %s", err)
   451  		}
   452  
   453  		archive, err := setupTarArchive(path, tempFiles, s.cfg)
   454  		if err != nil {
   455  			t.Errorf("error creating archive file: %s", err)
   456  		}
   457  
   458  		err = extractTarFile(archive, s.log, s.cfg)
   459  		if err != nil {
   460  			t.Errorf("error extracting tar archive: %s", err)
   461  		}
   462  	}
   463  }
   464  
   465  func TestPrepareDirectories(t *testing.T) {
   466  	scenarios := []struct {
   467  		cfg        common.Config
   468  		targetPath string
   469  	}{
   470  		{cfg: newConfig(t), targetPath: "/boot/versions/v1.11.0"},
   471  	}
   472  
   473  	for _, s := range scenarios {
   474  		if err := prepareDirectories(s.targetPath, s.cfg); err != nil {
   475  			t.Errorf("%s", err)
   476  		}
   477  
   478  		dirs, err := os.ReadDir(s.cfg.ScriptsPath)
   479  		if err != nil {
   480  			t.Errorf("error reading prepared directories: %s", err)
   481  		}
   482  
   483  		t.Logf("Script directories: %v", dirs)
   484  	}
   485  }
   486  
   487  func TestCreateScriptDirectories(t *testing.T) {
   488  	scenarios := []struct {
   489  		cfg common.Config
   490  	}{
   491  		{cfg: newConfig(t)},
   492  	}
   493  
   494  	for _, s := range scenarios {
   495  		if err := createScriptDirectories(s.cfg); err != nil {
   496  			t.Errorf("%s", err)
   497  		}
   498  
   499  		dirs, err := os.ReadDir(s.cfg.ScriptsPath)
   500  		if err != nil {
   501  			t.Errorf("error reading prepared directories: %s", err)
   502  		}
   503  
   504  		t.Logf("Script directories: %v", dirs)
   505  	}
   506  }
   507  
   508  func TestHousekeepVersions(t *testing.T) {
   509  	scenarios := []struct {
   510  		cfg           common.Config
   511  		retentionVers []string
   512  		log           logr.Logger
   513  	}{
   514  		{
   515  			cfg:           newConfig(t),
   516  			retentionVers: []string{"v1.10.1", "v1.11.0"},
   517  			log:           logging.NewLogger().Logger,
   518  		},
   519  	}
   520  
   521  	for _, s := range scenarios {
   522  		if err := createFileTree(s.cfg); err != nil {
   523  			t.Errorf("unable to create file tree: %s", err)
   524  		}
   525  
   526  		versions := []string{"v1.9.2", "v1.10.1", "v1.11.0", "v1.12.0"}
   527  		for _, v := range versions {
   528  			_, err := os.Create(filepath.Join(s.cfg.ArtefactsPath, v))
   529  			if err != nil {
   530  				t.Errorf("unable to create version files: %s", err)
   531  			}
   532  		}
   533  
   534  		if err := housekeepVersions(s.retentionVers, s.log, s.cfg); err != nil {
   535  			t.Errorf("%s", err)
   536  		}
   537  	}
   538  }
   539  
   540  func TestVersionSort(t *testing.T) {
   541  	scenarios := []struct {
   542  		cfg                      common.Config
   543  		unsortedVersions, expect []string
   544  	}{
   545  		{
   546  			cfg:              newConfig(t),
   547  			unsortedVersions: []string{"v1.12.0", "v1.9.2", "v1.11.0", "v1.10.1", "v1.12.0-123456"},
   548  			expect:           []string{"v1.9.2", "v1.10.1", "v1.11.0", "v1.12.0-123456", "v1.12.0"},
   549  		},
   550  	}
   551  
   552  	for _, s := range scenarios {
   553  		var (
   554  			versionsRaw    = make([]fsPkg.FileInfo, len(s.unsortedVersions))
   555  			sortedVersions = make([]string, len(s.unsortedVersions))
   556  		)
   557  
   558  		if err := createFileTree(s.cfg); err != nil {
   559  			t.Errorf("unable to create file tree: %s", err)
   560  		}
   561  
   562  		for i, v := range s.unsortedVersions {
   563  			filePath := filepath.Join(s.cfg.ArtefactsPath, v)
   564  
   565  			_, err := os.Create(filePath)
   566  			if err != nil {
   567  				t.Errorf("unable to create version files: %s", err)
   568  			}
   569  
   570  			fileInfo, err := os.Stat(filePath)
   571  			if err != nil {
   572  				t.Errorf("%s", err)
   573  			}
   574  
   575  			versionsRaw[i] = fileInfo
   576  		}
   577  
   578  		got, err := versionSort(versionsRaw)
   579  		if err != nil {
   580  			t.Errorf("%s", err)
   581  		}
   582  
   583  		for i := range got {
   584  			sortedVersions[i] = got[i].Original()
   585  		}
   586  
   587  		if reflect.DeepEqual(sortedVersions, s.expect) != true {
   588  			t.Errorf("unexpected return value for versionSort. Got %v. Expected %v", got, s.expect)
   589  		}
   590  
   591  		t.Logf("Sorted versions: %s", sortedVersions)
   592  	}
   593  }
   594  
   595  func TestPrepareLegacy(t *testing.T) {
   596  	scenarios := []struct {
   597  		cfg        common.Config
   598  		currentVer string
   599  		log        logr.Logger
   600  		legacy     bool
   601  	}{
   602  		{
   603  			cfg:        newConfig(t),
   604  			currentVer: "v1.11.0",
   605  			log:        logging.NewLogger().Logger,
   606  			legacy:     false,
   607  		},
   608  		{
   609  			cfg:        newConfig(t),
   610  			currentVer: "v1.9.2",
   611  			log:        logging.NewLogger().Logger,
   612  			legacy:     false,
   613  		},
   614  		{
   615  			cfg:        newConfig(t),
   616  			currentVer: "v1.9.2",
   617  			log:        logging.NewLogger().Logger,
   618  			legacy:     true,
   619  		},
   620  	}
   621  
   622  	for _, s := range scenarios {
   623  		if s.legacy == true {
   624  			if err := os.MkdirAll(s.cfg.LegacyScriptsPath, 0744); err != nil {
   625  				t.Errorf("error creating upgrade directory: %s", err)
   626  			}
   627  		}
   628  
   629  		if err := prepareLegacy(s.currentVer, s.log, s.cfg); err != nil {
   630  			t.Errorf("%s", err)
   631  		}
   632  	}
   633  }
   634  
   635  func TestPadVersion(t *testing.T) {
   636  	scenarios := []struct {
   637  		versionString, expect string
   638  	}{
   639  		{
   640  			versionString: "v1.11.0",
   641  			expect:        "v01.11.00",
   642  		},
   643  		{
   644  			versionString: "v1.11.0-beta+012345",
   645  			expect:        "v01.11.00-beta",
   646  		},
   647  	}
   648  
   649  	for _, s := range scenarios {
   650  		version, err := versionPkg.NewVersion(s.versionString)
   651  		if err != nil {
   652  			t.Errorf("%s", err)
   653  		}
   654  
   655  		got := padVersion(version)
   656  		if got != s.expect {
   657  			t.Errorf("unexpected return value. Got: %s, expected: %s", got, s.expect)
   658  		}
   659  
   660  		t.Logf("Padded version: %s", got)
   661  	}
   662  }
   663  
   664  func TestRoleFilter(t *testing.T) {
   665  	scenarios := []struct {
   666  		fileName               string
   667  		isControlPlane, expect bool
   668  	}{
   669  		{
   670  			fileName:       "01_script.cp.sh",
   671  			isControlPlane: true,
   672  			expect:         false,
   673  		},
   674  		{
   675  			fileName:       "01_script.cp.sh",
   676  			isControlPlane: false,
   677  			expect:         true,
   678  		},
   679  		{
   680  			fileName:       "01_script.tp.sh",
   681  			isControlPlane: true,
   682  			expect:         true,
   683  		},
   684  		{
   685  			fileName:       "01_script.tp.sh",
   686  			isControlPlane: false,
   687  			expect:         false,
   688  		},
   689  		{
   690  			fileName:       "01_script.sh",
   691  			isControlPlane: true,
   692  			expect:         false,
   693  		},
   694  		{
   695  			fileName:       "01_script.sh",
   696  			isControlPlane: false,
   697  			expect:         false,
   698  		},
   699  	}
   700  
   701  	for _, s := range scenarios {
   702  		if got := roleFilter(s.fileName, s.isControlPlane); got != s.expect {
   703  			t.Errorf("unexpected return value. Got: %t, expected: %t\n", got, s.expect)
   704  		}
   705  	}
   706  }
   707  
   708  func TestIsExecutable(t *testing.T) {
   709  	scenarios := []struct {
   710  		cfg      common.Config
   711  		filePath string
   712  		expect   bool
   713  	}{
   714  		{
   715  			cfg:      newConfig(t),
   716  			filePath: "01_script.cp.sh",
   717  			expect:   true,
   718  		},
   719  		{
   720  			cfg:      newConfig(t),
   721  			filePath: "01_script.cp.sh",
   722  			expect:   false,
   723  		},
   724  	}
   725  
   726  	for _, s := range scenarios {
   727  		if err := createFileTree(s.cfg); err != nil {
   728  			t.Errorf("error creating file tree: %s", err)
   729  		}
   730  
   731  		var filePath = filepath.Join(s.cfg.MountPath, s.filePath)
   732  		file, err := os.Create(filePath)
   733  		if err != nil {
   734  			t.Errorf("%s", err)
   735  		}
   736  
   737  		switch s.expect {
   738  		case true:
   739  			if err := os.Chmod(file.Name(), 0777); err != nil {
   740  				t.Errorf("an error occurred when modifying file permissions: %s", err)
   741  			}
   742  		case false:
   743  			if err := os.Chmod(file.Name(), 0666); err != nil {
   744  				t.Errorf("an error occurred when modifying file permissions: %s", err)
   745  			}
   746  		}
   747  
   748  		if got := IsExecutable(filePath); got != s.expect {
   749  			t.Errorf("unexpected return value. Got: %t, expected: %t\n", got, s.expect)
   750  		}
   751  	}
   752  }
   753  
   754  func TestCheckDirectives(t *testing.T) {
   755  	scenarios := []struct {
   756  		name, currentVer string
   757  		expect           struct {
   758  			skip   bool
   759  			weight int
   760  		}
   761  	}{
   762  		{
   763  			name:       "01_script.sh",
   764  			currentVer: "v1.11.1", // Regex won't match
   765  			expect: struct {
   766  				skip   bool
   767  				weight int
   768  			}{
   769  				skip:   false,
   770  				weight: 10,
   771  			},
   772  		},
   773  		{
   774  			name:       "100_script.sh", // Script doesn't exist
   775  			currentVer: "v1.10.1",
   776  			expect: struct {
   777  				skip   bool
   778  				weight int
   779  			}{
   780  				skip:   false,
   781  				weight: 10,
   782  			},
   783  		},
   784  		{
   785  			name:       "01_script.sh",
   786  			currentVer: "v1.10.1",
   787  			expect: struct {
   788  				skip   bool
   789  				weight int
   790  			}{
   791  				skip:   true,
   792  				weight: 1,
   793  			},
   794  		},
   795  	}
   796  
   797  	config := []DirectivesConfig{
   798  		{
   799  			Directives: []struct {
   800  				Regex         string `yaml:"regex"`
   801  				ScriptOptions []struct {
   802  					Name   string
   803  					Skip   bool
   804  					Weight int
   805  				} `yaml:"script_options"`
   806  			}{
   807  				{
   808  					Regex: "v1.10.1",
   809  					ScriptOptions: []struct {
   810  						Name   string
   811  						Skip   bool
   812  						Weight int
   813  					}{
   814  						{
   815  							Name:   "01_script.sh",
   816  							Skip:   true,
   817  							Weight: 1,
   818  						},
   819  					},
   820  				},
   821  			},
   822  		},
   823  	}
   824  
   825  	for _, s := range scenarios {
   826  		skip, weight := checkDirectives(config, s.currentVer, s.name)
   827  		if skip != s.expect.skip {
   828  			t.Errorf("unexpected return value. Got: %+v, expected: %+v\n", skip, s.expect.skip)
   829  		}
   830  		if weight != s.expect.weight {
   831  			t.Errorf("unexpected return value. Got: %+v, expected: %+v\n", weight, s.expect.weight)
   832  		}
   833  	}
   834  }
   835  
   836  func TestGetDestPath(t *testing.T) {
   837  	scenarios := []struct {
   838  		cfg     common.Config
   839  		subPath string
   840  	}{
   841  		{
   842  			cfg:     newConfig(t),
   843  			subPath: "boot",
   844  		},
   845  		{
   846  			cfg:     newConfig(t),
   847  			subPath: "patching",
   848  		},
   849  	}
   850  
   851  	for _, s := range scenarios {
   852  		var path = filepath.Join(s.cfg.MountPath, s.subPath)
   853  		if err := createFileTree(s.cfg); err != nil {
   854  			t.Errorf("error creating file tree: %s", err)
   855  		}
   856  
   857  		tempFile, err := os.CreateTemp(path, "")
   858  		if err != nil {
   859  			t.Errorf("error creating tempfile: %s", err)
   860  		}
   861  
   862  		var header = strings.TrimPrefix(strings.Replace(tempFile.Name(), s.cfg.MountPath, "", -1), "/")
   863  		got, err := getDestPath(header, s.cfg)
   864  		if err != nil {
   865  			t.Errorf("%s", err)
   866  		}
   867  
   868  		var expect string
   869  		switch s.subPath {
   870  		case "boot":
   871  			var fileName = strings.TrimPrefix(strings.Replace(tempFile.Name(), s.cfg.BootFiles, "", -1), "/")
   872  			expect = filepath.Join(s.cfg.ArtefactsTempPath, fileName)
   873  		case "patching":
   874  			var fileName = strings.TrimPrefix(strings.Replace(tempFile.Name(), s.cfg.ScriptFiles, "", -1), "/")
   875  			expect = filepath.Join(s.cfg.ScriptsTempPath, fileName)
   876  		}
   877  
   878  		if got != expect {
   879  			t.Errorf("unexpected return value. Got: %s, expected: %s", got, expect)
   880  		}
   881  
   882  		t.Logf("DestPath for %s: %s", s.subPath, got)
   883  	}
   884  }
   885  
   886  func TestWriteFile(t *testing.T) {
   887  	scenarios := []struct {
   888  		cfg       common.Config
   889  		log       logr.Logger
   890  		path, dir string
   891  	}{
   892  		{
   893  			cfg: newConfig(t),
   894  			log: logging.NewLogger().Logger,
   895  		},
   896  	}
   897  
   898  	for _, s := range scenarios {
   899  		if err := createFileTree(s.cfg); err != nil {
   900  			t.Errorf("error creating file tree: %s", err)
   901  		}
   902  
   903  		tempFiles, err := createTempFile(s.cfg.ArtefactsTempPath, 2)
   904  		if err != nil {
   905  			t.Errorf("error creating tempfiles: %s", err)
   906  		}
   907  
   908  		var filepath = filepath.Join(s.cfg.ArtefactsTempPath, "archive.tar.gz")
   909  		archive, err := setupTarArchive(filepath, tempFiles, s.cfg)
   910  		if err != nil {
   911  			t.Errorf("error creating archive: %s", err)
   912  		}
   913  
   914  		gr, err := gzip.NewReader(archive)
   915  		if err != nil {
   916  			t.Errorf("error creating gzip reader: %s", err)
   917  		}
   918  		defer gr.Close()
   919  		tr := tar.NewReader(gr)
   920  
   921  		for _, file := range tempFiles {
   922  			filePath := file.Name()
   923  			if err := os.Remove(filePath); err != nil {
   924  				t.Errorf("error deleting file: %s", err)
   925  			}
   926  
   927  			header, err := tr.Next()
   928  			if err != nil {
   929  				t.Errorf("error advancing to next archive entry: %s", err)
   930  			}
   931  
   932  			if err := writeFile(filePath, tr, header, s.log); err != nil {
   933  				t.Errorf("error extracting patchset: %s", err)
   934  			}
   935  
   936  			file, err := os.OpenFile(filePath, os.O_RDONLY, 0644)
   937  			if errors.Is(err, fsPkg.ErrNotExist) {
   938  				t.Errorf("file does not exist: %s", err)
   939  			} else if err != nil {
   940  				t.Errorf("error opening file: %s", err)
   941  			}
   942  			defer file.Close()
   943  		}
   944  	}
   945  }
   946  
   947  func TestExtractPatchset(t *testing.T) {
   948  	scenarios := []struct {
   949  		cfg           common.Config
   950  		log           logr.Logger
   951  		targetVersion string
   952  	}{
   953  		{
   954  			cfg:           newConfig(t),
   955  			log:           logging.NewLogger().Logger,
   956  			targetVersion: "v1.11.0",
   957  		},
   958  	}
   959  
   960  	for _, s := range scenarios {
   961  		if err := createFileTree(s.cfg); err != nil {
   962  			t.Errorf("error creating file tree: %s", err)
   963  		}
   964  
   965  		tempFiles, err := createTempFile(s.cfg.BootFiles, 2)
   966  		if err != nil {
   967  			t.Errorf("error creating temp files: %s", err)
   968  		}
   969  		_, err = setupTarArchive(s.cfg.PatchsetMount, tempFiles, s.cfg)
   970  		if err != nil {
   971  			t.Errorf("error creating archive: %s", err)
   972  		}
   973  
   974  		if err := extractPatchset(s.targetVersion, s.log, s.cfg); err != nil {
   975  			t.Errorf("error extracting patchset, %s", err)
   976  		}
   977  	}
   978  }
   979  
   980  func TestMain(m *testing.M) {
   981  	os.Exit(m.Run())
   982  }
   983  

View as plain text