github.com/rohankumardubey/draft-classic@v0.16.0/pkg/tasks/tasks.go (about)

     1  package tasks
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  	"regexp"
     9  	"strings"
    10  
    11  	"github.com/BurntSushi/toml"
    12  )
    13  
    14  var (
    15  	ErrNoTaskFile = errors.New(".draft-tasks.toml not found")
    16  )
    17  
    18  const (
    19  	PreUp      = "PreUp"
    20  	PostDeploy = "PostDeploy"
    21  	PostDelete = "PostDelete"
    22  )
    23  
    24  var (
    25  	// reEnvironmentVariable matches environment variables embedded in
    26  	// strings. Only simple expressions ($FOO) are supported. Variables
    27  	// may escaped to avoid interpolation, in the form $$FOO or \$FOO
    28  	reEnvironmentVariable = regexp.MustCompile(`([\\\$]?\$[a-zA-Z_][a-zA-Z0-9_]*)`)
    29  )
    30  
    31  // Runner runs the given command. An alternative to DefaultRunner can
    32  // be used in tests.
    33  type Runner func(c *exec.Cmd) error
    34  
    35  // DefaultRunner runs the given command
    36  var DefaultRunner = func(c *exec.Cmd) error { return c.Run() }
    37  
    38  type Tasks struct {
    39  	PreUp      map[string]string `toml:"pre-up"`
    40  	PostDeploy map[string]string `toml:"post-deploy"`
    41  	PostDelete map[string]string `toml:"cleanup"`
    42  }
    43  
    44  type Result struct {
    45  	Kind    string
    46  	Command []string
    47  	Pass    bool
    48  	Message string
    49  }
    50  
    51  // Load takes a path to file where tasks are defined and loads them in tasks
    52  func Load(path string) (*Tasks, error) {
    53  	if _, err := os.Stat(path); err != nil {
    54  		if os.IsNotExist(err) {
    55  			return nil, ErrNoTaskFile
    56  		}
    57  		return nil, err
    58  	}
    59  
    60  	t := Tasks{}
    61  	if _, err := toml.DecodeFile(path, &t); err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	return &t, nil
    66  }
    67  
    68  func (t *Tasks) Run(runner Runner, kind, podName string) ([]Result, error) {
    69  	results := []Result{}
    70  
    71  	switch kind {
    72  	case PreUp:
    73  		for _, task := range t.PreUp {
    74  			result := executeTask(runner, task, kind)
    75  			results = append(results, result)
    76  		}
    77  	case PostDeploy:
    78  		for _, task := range t.PostDeploy {
    79  			cmd := preparePostDeployTask(evaluateArgs(task), podName)
    80  			result := runTask(runner, cmd, kind)
    81  			results = append(results, result)
    82  		}
    83  	case PostDelete:
    84  		for _, task := range t.PostDelete {
    85  			result := executeTask(runner, task, kind)
    86  			results = append(results, result)
    87  		}
    88  	default:
    89  		return results, fmt.Errorf("Task kind: %s not supported", kind)
    90  	}
    91  
    92  	return results, nil
    93  }
    94  
    95  func executeTask(runner Runner, task, kind string) Result {
    96  	args := evaluateArgs(task)
    97  	cmd := prepareTask(args)
    98  	return runTask(runner, cmd, kind)
    99  }
   100  
   101  func runTask(runner Runner, cmd *exec.Cmd, kind string) Result {
   102  	result := Result{Kind: kind, Pass: false}
   103  	result.Command = append([]string{cmd.Path}, cmd.Args[0:]...)
   104  
   105  	cmd.Stdout = os.Stdout
   106  	cmd.Stderr = os.Stderr
   107  	err := runner(cmd)
   108  	if err != nil {
   109  		result.Pass = false
   110  		result.Message = err.Error()
   111  		return result
   112  	}
   113  	result.Pass = true
   114  
   115  	return result
   116  }
   117  
   118  func prepareTask(args []string) *exec.Cmd {
   119  	var cmd *exec.Cmd
   120  	if len(args) < 2 {
   121  		cmd = exec.Command(args[0])
   122  	} else {
   123  		cmd = exec.Command(args[0], args[1:]...)
   124  	}
   125  	return cmd
   126  }
   127  
   128  func preparePostDeployTask(args []string, podName string) *exec.Cmd {
   129  	args = append([]string{"exec", podName, "--"}, args[0:]...)
   130  	return exec.Command("kubectl", args[0:]...)
   131  }
   132  
   133  func evaluateArgs(task string) []string {
   134  	args := strings.Split(task, " ")
   135  	for i, arg := range args {
   136  		args[i] = reEnvironmentVariable.ReplaceAllStringFunc(arg, func(expr string) string {
   137  			// $$FOO and \$FOO are kept as-is
   138  			if strings.HasPrefix(expr, "$$") || strings.HasPrefix(expr, "\\$") {
   139  				return expr[1:]
   140  			}
   141  
   142  			return os.Getenv(expr[1:])
   143  		})
   144  	}
   145  	return args
   146  }