github.com/informationsea/shellflow@v0.1.3/main.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"flag"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path"
    10  	"strings"
    11  
    12  	"bufio"
    13  
    14  	"github.com/chzyer/readline"
    15  	"github.com/informationsea/shellflow/flowscript"
    16  )
    17  
    18  func main() {
    19  	if len(os.Args) <= 1 {
    20  		helpMode([]string{})
    21  		return
    22  	}
    23  
    24  	var err error
    25  
    26  	switch os.Args[1] {
    27  	case "flowscript":
    28  		runFlowscriptIntepreter()
    29  	case "run":
    30  		err = runMode()
    31  	case "dot":
    32  		err = dotMode()
    33  	case "filelog":
    34  		err = fileLogMode()
    35  	case "viewlog":
    36  		err = viewLogMode()
    37  	case "-h", "-?", "help":
    38  		helpMode(os.Args[2:])
    39  	default:
    40  		fmt.Fprintf(os.Stderr, "Unknown command: %s\n", os.Args[1])
    41  	}
    42  
    43  	if err != nil {
    44  		fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
    45  		os.Exit(1)
    46  	}
    47  }
    48  
    49  func helpMode(args []string) error {
    50  	_, err := fmt.Print(`shellflow @DEV@: shell-script like workflow management system
    51  
    52  Commands:
    53    run         Run workflow
    54    dot         Export workflow as dot language for visualization
    55    flowscript  Launch flowscript interpreter
    56    viewlog     Show execution log
    57    filelog     Create a file log file, which contains SHA256 hash, modification date and so on
    58    help        Show this help
    59  `)
    60  	return err
    61  }
    62  
    63  func viewLogMode() error {
    64  	f := flag.NewFlagSet("shellflow filelog", flag.ExitOnError)
    65  	var showAll bool
    66  	var failedOnly bool
    67  	f.BoolVar(&showAll, "all", false, "Show All")
    68  	f.BoolVar(&failedOnly, "failed", false, "Show Failed Job Only")
    69  	f.Parse(os.Args[2:])
    70  
    71  	var err error
    72  	if len(f.Args()) > 0 {
    73  		err = ViewLogDetail(f.Args(), failedOnly)
    74  	} else {
    75  		err = ViewLog(showAll, failedOnly)
    76  	}
    77  	return err
    78  }
    79  
    80  func fileLogMode() error {
    81  	f := flag.NewFlagSet("shellflow filelog", flag.ExitOnError)
    82  	var output string
    83  	f.StringVar(&output, "output", "", "output json file")
    84  	f.Parse(os.Args[2:])
    85  	var writer io.WriteCloser
    86  	var err error
    87  
    88  	files, err := CreateFileLog(f.Args(), false, MaximumContentLogSize)
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	if output == "" || output == "-" {
    94  		writer = os.Stdout
    95  	} else {
    96  		writer, err = os.OpenFile(output, os.O_CREATE|os.O_WRONLY, 0644)
    97  		if err != nil {
    98  			return err
    99  		}
   100  	}
   101  	defer writer.Close()
   102  
   103  	encoder := json.NewEncoder(writer)
   104  	encoder.SetIndent("", "  ")
   105  	err = encoder.Encode(files)
   106  	if err != nil {
   107  		return err
   108  	}
   109  	return nil
   110  }
   111  
   112  func loadParameter(paramFile string) (map[string]interface{}, error) {
   113  	parameters := make(map[string]interface{})
   114  
   115  	if paramFile != "" {
   116  		if strings.HasSuffix(paramFile, ".json") {
   117  
   118  			paramReader, err := os.Open(paramFile)
   119  			if err != nil {
   120  				return nil, err
   121  			}
   122  			decoder := json.NewDecoder(paramReader)
   123  			err = decoder.Decode(&parameters)
   124  			if err != nil {
   125  				return nil, err
   126  			}
   127  			//fmt.Println(parameters)
   128  
   129  		} else {
   130  			return nil, fmt.Errorf("Unknown file type %s", paramFile)
   131  		}
   132  	}
   133  	return parameters, nil
   134  }
   135  
   136  func dotMode() error {
   137  	paramFile := ""
   138  
   139  	f := flag.NewFlagSet("shellflow dot", flag.ExitOnError)
   140  	f.StringVar(&paramFile, "param", "", "Parameter File")
   141  	f.Parse(os.Args[2:])
   142  
   143  	if len(f.Args()) != 1 {
   144  		helpMode([]string{"dot"})
   145  		return fmt.Errorf("No workflow file")
   146  	}
   147  
   148  	env := NewEnvironment()
   149  	parameters, err := loadParameter(paramFile)
   150  	if err != nil {
   151  		return err
   152  	}
   153  
   154  	builder, err := parse(env, f.Args()[0], parameters)
   155  	if err != nil {
   156  		return err
   157  	}
   158  	dag := builder.CreateDag()
   159  	fmt.Println(dag)
   160  	return nil
   161  }
   162  
   163  func runMode() error {
   164  	f := flag.NewFlagSet("shellflow run", flag.ExitOnError)
   165  
   166  	useSge := false
   167  	paramFile := ""
   168  
   169  	env := NewEnvironment()
   170  	f.BoolVar(&env.skipSha, "skip-sha", false, "Skip SHA256 calculation")
   171  	f.BoolVar(&env.dryRun, "dry-run", false, "Print jobs to run without execute")
   172  	f.BoolVar(&env.scriptsOnly, "scripts-only", false, "Generate scripts only")
   173  	f.BoolVar(&env.rerunAll, "rerun", false, "Rerun all commands even if contents are not changed")
   174  	f.BoolVar(&useSge, "sge", false, "Use SGE/UGE instead of local executer")
   175  	f.StringVar(&paramFile, "param", "", "Parameter File")
   176  	f.Parse(os.Args[2:])
   177  
   178  	if len(f.Args()) == 0 {
   179  		helpMode([]string{"run"})
   180  		return fmt.Errorf("No workflow file")
   181  	}
   182  
   183  	parameters, err := loadParameter(paramFile)
   184  	if err != nil {
   185  		return err
   186  	}
   187  
   188  	builder, err := parse(env, f.Args()[0], parameters)
   189  	if err != nil {
   190  		return err
   191  	}
   192  	//fmt.Printf("%s\n", f.Args())
   193  
   194  	if env.dryRun {
   195  		for _, v := range builder.Tasks {
   196  			if !v.ShouldSkip || env.rerunAll {
   197  				fmt.Printf("%s\n", v.ShellScript)
   198  			}
   199  		}
   200  		return nil
   201  	}
   202  
   203  	if env.rerunAll {
   204  		for _, v := range builder.Tasks {
   205  			v.ShouldSkip = false
   206  		}
   207  	}
   208  
   209  	gen, err := GenerateTaskScripts(f.Args()[0], paramFile, env, builder)
   210  	if err != nil {
   211  		return err
   212  	}
   213  
   214  	if !env.scriptsOnly {
   215  		if useSge {
   216  			err = ExecuteInSge(gen)
   217  		} else {
   218  			err = ExecuteLocalSingle(gen)
   219  		}
   220  
   221  		if err != nil {
   222  			fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
   223  			exeErr, ok := err.(*executationError)
   224  			if ok {
   225  				jobLog, err := CollectLogsForOneJob(exeErr.jobRoot, exeErr.shellTask)
   226  				var errReader io.ReadCloser
   227  				if jobLog.ScriptExitCode == 0 {
   228  					errReader, err = os.Open(path.Join(exeErr.jobRoot, "run.stderr"))
   229  				} else {
   230  					errReader, err = os.Open(path.Join(exeErr.jobRoot, "script.stderr"))
   231  				}
   232  				if err != nil {
   233  					return err
   234  				}
   235  				defer errReader.Close()
   236  				io.Copy(os.Stderr, errReader)
   237  
   238  				os.Exit(1)
   239  			} else {
   240  				return err
   241  			}
   242  		}
   243  	}
   244  	return nil
   245  }
   246  
   247  //func setupRoot() {
   248  //	rootInfo, err := os.Stat(*workflowRoot)
   249  //	if err != nil && os.IsNotExist(err) {
   250  //		os.MkdirAll(*workflowRoot, 0750)
   251  //	} else if err != nil || !rootInfo.IsDir() {
   252  //		panic(fmt.Sprintf("workflow root directory path is not directory: %s", *workflowRoot))
   253  //	}
   254  //}
   255  
   256  func parse(env *Environment, file string, param map[string]interface{}) (*ShellTaskBuilder, error) {
   257  	reader, err := os.Open(file)
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  	builder, err := ParseShellflow(bufio.NewReader(reader), env, param)
   262  
   263  	return builder, err
   264  }
   265  
   266  func runFlowscriptIntepreter() {
   267  	rl, err := readline.New("> ")
   268  	if err != nil {
   269  		panic(err)
   270  	}
   271  
   272  	defer rl.Close()
   273  
   274  	ge := flowscript.NewGlobalEnvironment()
   275  
   276  	for {
   277  		line, err := rl.Readline()
   278  		if err != nil || line == "exit" {
   279  			break
   280  		}
   281  
   282  		ev, err := flowscript.EvaluateScript(line, ge)
   283  
   284  		if err == nil {
   285  			fmt.Printf("%s\n", ev.String())
   286  		} else {
   287  			fmt.Printf("Error: %s\n", err.Error())
   288  		}
   289  
   290  	}
   291  }