blob: 77131ac5979a4c01dc2bde8b48f1a00b2aa60938 [file] [log] [blame]
// Copyright 2017 The LUCI Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package main
import (
"context"
"os"
"os/exec"
"path/filepath"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/common/system/environ"
)
// recipeEngine can invoke the recipe engine configured with some single recipe.
type recipeEngine struct {
// put these args at the beginning of the Cmd. Must include the python
// interpreter if necessary.
cmdPrefix []string
recipeName string // name of the recipe
properties map[string]interface{} // input properties
workDir string // a directory where to run the recipe
outputResultJSONFile string // path to the result file
}
// commandRun prepares a command that runs a recipe.
func (eng *recipeEngine) commandRun(ctx context.Context, tdir string, env environ.Env) (*exec.Cmd, error) {
if len(eng.cmdPrefix) == 0 {
return nil, errors.New("empty command prefix")
}
if err := ensureDir(tdir); err != nil {
return nil, err
}
// Pass properties in a file.
propertiesPath := filepath.Join(tdir, "properties.json")
if err := encodeJSONToPath(propertiesPath, eng.properties); err != nil {
return nil, errors.Annotate(err, "could not write properties file at %q", propertiesPath).Err()
}
// Build our command (arguments first).
args := []string{}
args = append(args, eng.cmdPrefix...)
args = append(args,
"run",
"--properties-file", propertiesPath,
// TODO(iannucci): Remove `--workdir` entirely and make the recipe engine
// operate in $CWD.
"--workdir", eng.workDir,
)
if eng.outputResultJSONFile != "" {
args = append(args, "--output-result-json", eng.outputResultJSONFile)
}
args = append(args, eng.recipeName)
// Build the final exec.Cmd.
recipeCmd := exec.CommandContext(ctx, args[0], args[1:]...)
recipeCmd.Dir = eng.workDir
recipeCmd.Env = env.Sorted()
return recipeCmd, nil
}
// prepareWorkDir verifies and normalizes a workdir is suitable for a recipe
// run.
func prepareRecipeRunWorkDir(workdir string) (string, error) {
if workdir == "" {
return "", errors.New("workdir is empty")
}
abs, err := filepath.Abs(workdir)
if err != nil {
return "", errors.Annotate(err, "could not make %q absolute", workdir).Err()
}
workdir = abs
switch hasFiles, err := dirHasFiles(workdir); {
case os.IsNotExist(err):
return workdir, ensureDir(workdir)
case err != nil:
return "", errors.Annotate(err, "could not read dir %q", workdir).Err()
case hasFiles:
return "", errors.Annotate(err, "workdir %q is not empty", workdir).Err()
default:
return workdir, nil
}
}