github.com/criteo/command-launcher@v0.0.0-20230407142452-fb616f546e98/internal/pkg/default-package.go (about)

     1  package pkg
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/fs"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"github.com/criteo/command-launcher/internal/command"
    11  	"gopkg.in/yaml.v3"
    12  )
    13  
    14  type defaultPackageManifest struct {
    15  	PkgName     string                    `json:"pkgName" yaml:"pkgName"`
    16  	PkgVersion  string                    `json:"version" yaml:"version"`
    17  	PkgCommands []*command.DefaultCommand `json:"cmds" yaml:"cmds"`
    18  }
    19  
    20  func (mf *defaultPackageManifest) Name() string {
    21  	return mf.PkgName
    22  }
    23  
    24  func (mf *defaultPackageManifest) Version() string {
    25  	return mf.PkgVersion
    26  }
    27  
    28  func (mf *defaultPackageManifest) Commands() []command.Command {
    29  	cmds := make([]command.Command, 0)
    30  	for _, cmd := range mf.PkgCommands {
    31  		//newCmd := cmd
    32  		cmds = append(cmds, cmd)
    33  	}
    34  	return cmds
    35  }
    36  
    37  type defaultPackage struct {
    38  	Manifest command.PackageManifest
    39  	// store the repository id, indicates which registry/repository that the package belongs to
    40  	repositoryID string
    41  }
    42  
    43  func (pkg *defaultPackage) Name() string {
    44  	return pkg.Manifest.Name()
    45  }
    46  
    47  func (pkg *defaultPackage) Version() string {
    48  	return pkg.Manifest.Version()
    49  }
    50  
    51  func (pkg *defaultPackage) Commands() []command.Command {
    52  	return pkg.Manifest.Commands()
    53  }
    54  
    55  func (pkg *defaultPackage) RepositoryID() string {
    56  	return pkg.repositoryID
    57  }
    58  
    59  func ReadManifest(file fs.File) (command.PackageManifest, error) {
    60  	stat, err := file.Stat()
    61  	if err != nil {
    62  		return nil, fmt.Errorf("cannot read the manifest file handle (%s)", err)
    63  	}
    64  
    65  	var payload = make([]byte, stat.Size())
    66  	nb, err := file.Read(payload)
    67  	if err != nil && err != io.EOF || nb != int(stat.Size()) {
    68  		return nil, fmt.Errorf("cannot read the manifest (%s)", err)
    69  	}
    70  
    71  	var mf = defaultPackageManifest{}
    72  	// YAML is super set of json, should work with JSON as well
    73  	err = yaml.Unmarshal(payload, &mf)
    74  	if err != nil {
    75  		return nil, fmt.Errorf("cannot read the manifest content, it is neither a valid JSON nor YAML (%s)", err)
    76  	}
    77  
    78  	return &mf, nil
    79  }
    80  
    81  func (pkg *defaultPackage) RunSetup(pkgDir string) error {
    82  	return ExecSetupHookFromPackage(pkg, pkgDir)
    83  }
    84  
    85  func copyFolder(srcFolder string, dstFolder string) error {
    86  	info, err := os.Stat(srcFolder)
    87  	if err != nil {
    88  		return err
    89  	}
    90  
    91  	if err = os.MkdirAll(dstFolder, info.Mode()); err != nil {
    92  		return err
    93  	}
    94  
    95  	files, err := os.ReadDir(srcFolder)
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	for _, fd := range files {
   101  		src := filepath.Join(srcFolder, fd.Name())
   102  		dst := filepath.Join(dstFolder, fd.Name())
   103  
   104  		if fd.IsDir() {
   105  			if err = copyFolder(src, dst); err != nil {
   106  				return err
   107  			}
   108  		} else {
   109  			if err = copyFile(src, dst); err != nil {
   110  				return err
   111  			}
   112  		}
   113  	}
   114  
   115  	return nil
   116  }
   117  
   118  func copyFile(src string, dst string) error {
   119  	srcFile, err := os.Open(src)
   120  	if err != nil {
   121  		return err
   122  	}
   123  	defer srcFile.Close()
   124  
   125  	dstFile, err := os.Create(dst)
   126  	if err != nil {
   127  		return fmt.Errorf("file extraction failed: %s", err)
   128  	}
   129  	defer dstFile.Close()
   130  
   131  	if _, err = io.Copy(dstFile, srcFile); err != nil {
   132  		return err
   133  	}
   134  
   135  	srcInfo, err := os.Stat(src)
   136  	if err != nil {
   137  		return err
   138  	}
   139  
   140  	return os.Chmod(dst, srcInfo.Mode())
   141  }
   142  
   143  func ExecSetupHookFromPackage(pkg command.PackageManifest, pkgDir string) error {
   144  	for _, c := range pkg.Commands() {
   145  		if c.Name() == "__setup__" && c.Type() == "system" {
   146  			if pkgDir != "" {
   147  				c.SetPackageDir(pkgDir)
   148  			}
   149  			// Execute the __setup__ hook
   150  			_, err := c.Execute(os.Environ())
   151  			if err != nil {
   152  				return fmt.Errorf("setup hook of package %s failed to execute: %v", pkg.Name(), err)
   153  			}
   154  			return nil
   155  		}
   156  	}
   157  	return fmt.Errorf("no setup hook found in the package")
   158  }