get.porter.sh/porter@v1.3.0/pkg/agent/agent.go (about)

     1  package agent
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/fs"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"get.porter.sh/porter/pkg"
    13  )
    14  
    15  // allow the tests to capture output
    16  var (
    17  	Stdout io.Writer = os.Stdout
    18  	Stderr io.Writer = os.Stderr
    19  )
    20  
    21  // The porter agent wraps the porter cli,
    22  // handling copying config files from a mounted
    23  // volume into PORTER_HOME
    24  // Returns any errors and if the porter command was executed
    25  func Execute(porterCommand []string, porterHome string, porterConfig string) (error, bool) {
    26  	porter := porterHome + "/porter"
    27  
    28  	// Copy config files into PORTER_HOME
    29  	err := filepath.Walk(porterConfig, func(path string, info fs.FileInfo, err error) error {
    30  		// if Walk sends a non nil err, then return it back
    31  		if err != nil {
    32  			return err
    33  		}
    34  		if info.IsDir() {
    35  			return nil
    36  		}
    37  
    38  		// Determine the relative path of the file we are copying
    39  		relPath, err := filepath.Rel(porterConfig, path)
    40  		if err != nil {
    41  			return err
    42  		}
    43  
    44  		// Skip hidden files, these are injected by k8s when the config volume is mounted
    45  		if strings.HasPrefix(relPath, ".") {
    46  			return nil
    47  		}
    48  
    49  		// If the files are symlinks then resolve them
    50  		// /porter-config
    51  		//    - config.toml (symlink to the file in ..data)
    52  		//    - ..data/config.toml
    53  		resolvedPath, err := filepath.EvalSymlinks(path)
    54  		if err != nil {
    55  			return err
    56  		}
    57  
    58  		resolvedInfo, err := os.Stat(resolvedPath)
    59  		if err != nil {
    60  			return err
    61  		}
    62  
    63  		return copyConfig(relPath, resolvedPath, resolvedInfo, porterHome)
    64  	})
    65  	if err != nil {
    66  		return err, false
    67  	}
    68  
    69  	// Remind everyone the version of Porter we are using
    70  	fmt.Fprintf(Stderr, "porter version\n")
    71  	cmd := exec.Command(porter, "version")
    72  	cmd.Stdout = Stderr // send all non-bundle output to stderr
    73  	cmd.Stderr = Stderr
    74  	if err = cmd.Run(); err != nil {
    75  		return fmt.Errorf("porter version check failed: %w", err), false
    76  	}
    77  
    78  	// Run the specified porter command
    79  	fmt.Fprintf(Stderr, "porter %s\n", strings.Join(porterCommand, " "))
    80  	cmd = exec.Command(porter, porterCommand...)
    81  	cmd.Stdout = Stdout
    82  	cmd.Stderr = Stderr
    83  	cmd.Stdin = os.Stdin
    84  	if err := cmd.Start(); err != nil {
    85  		return err, false
    86  	}
    87  	return cmd.Wait(), true
    88  }
    89  
    90  func copyConfig(relPath string, configFile string, fi os.FileInfo, porterHome string) error {
    91  	destFile := filepath.Join(porterHome, relPath)
    92  	fmt.Fprintln(Stderr, "Loading configuration", relPath, "into", destFile)
    93  	src, err := os.OpenFile(configFile, os.O_RDONLY, 0)
    94  	if err != nil {
    95  		return err
    96  	}
    97  	defer src.Close()
    98  
    99  	if err = os.MkdirAll(filepath.Dir(destFile), pkg.FileModeDirectory); err != nil {
   100  		return err
   101  	}
   102  	dest, err := os.OpenFile(destFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, fi.Mode())
   103  	if err != nil {
   104  		return err
   105  	}
   106  	defer dest.Close()
   107  
   108  	// Just copy the file
   109  	_, err = io.Copy(dest, src)
   110  	return err
   111  }