<commit_before>package main

import (
	"errors"
	"fmt"
	"go/build"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"

	"goa.design/goa/v3/codegen"
	"golang.org/x/tools/go/packages"
)

// Generator is the code generation management data structure.
type Generator struct {
	// Command is the name of the command to run.
	Command string

	// DesignPath is the Go import path to the design package.
	DesignPath string

	// Output is the absolute path to the output directory.
	Output string

	// DesignVersion is the major component of the Goa version used by the design DSL.
	// DesignVersion is either 2 or 3.
	DesignVersion int

	// bin is the filename of the generated generator.
	bin string

	// tmpDir is the temporary directory used to compile the generator.
	tmpDir string

	// hasVendorDirectory is a flag to indicate whether the project uses vendoring
	hasVendorDirectory bool
}

// NewGenerator creates a Generator.
func NewGenerator(cmd string, path, output string) *Generator {
	bin := "goa"
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	var version int
	var hasVendorDirectory bool
	{
		version = 2
		matched := false
		pkgs, _ := packages.Load(&packages.Config{Mode: packages.NeedFiles | packages.NeedModule}, path)
		fset := token.NewFileSet()
		p := regexp.MustCompile(`goa.design/goa/v(\d+)/dsl`)
		for _, pkg := range pkgs {
			if _, err := os.Stat(filepath.Join(pkg.Module.Dir, "vendor")); !os.IsNotExist(err) {
				hasVendorDirectory = true
			}
			for _, gof := range pkg.GoFiles {
				if bs, err := ioutil.ReadFile(gof); err == nil {
					if f, err := parser.ParseFile(fset, "", string(bs), parser.ImportsOnly); err == nil {
						for _, s := range f.Imports {
							matches := p.FindStringSubmatch(s.Path.Value)
							if len(matches) == 2 {
								matched = true
								version, _ = strconv.Atoi(matches[1]) // We know it's an integer
							}
						}
					}
				}
				if matched {
					break
				}
			}
			if matched {
				break
			}
		}
	}

	return &Generator{
		Command:            cmd,
		DesignPath:         path,
		Output:             output,
		DesignVersion:      version,
		hasVendorDirectory: hasVendorDirectory,
		bin:                bin,
	}
}

// Write writes the main file.
func (g *Generator) Write(debug bool) error {
	var tmpDir string
	{
		wd := "."
		if cwd, err := os.Getwd(); err != nil {
			wd = cwd
		}
		tmp, err := ioutil.TempDir(wd, "goa")
		if err != nil {
			return err
		}
		tmpDir = tmp
	}
	g.tmpDir = tmpDir

	var sections []*codegen.SectionTemplate
	{
		data := map[string]interface{}{
			"Command":       g.Command,
			"CleanupDirs":   cleanupDirs(g.Command, g.Output),
			"DesignVersion": g.DesignVersion,
		}
		ver := ""
		if g.DesignVersion > 2 {
			ver = "v" + strconv.Itoa(g.DesignVersion) + "/"
		}
		imports := []*codegen.ImportSpec{
			codegen.SimpleImport("flag"),
			codegen.SimpleImport("fmt"),
			codegen.SimpleImport("os"),
			codegen.SimpleImport("path/filepath"),
			codegen.SimpleImport("sort"),
			codegen.SimpleImport("strconv"),
			codegen.SimpleImport("strings"),
			codegen.SimpleImport("goa.design/goa/" + ver + "codegen"),
			codegen.SimpleImport("goa.design/goa/" + ver + "codegen/generator"),
			codegen.SimpleImport("goa.design/goa/" + ver + "eval"),
			codegen.NewImport("goa", "goa.design/goa/"+ver+"pkg"),
			codegen.NewImport("_", g.DesignPath),
		}
		sections = []*codegen.SectionTemplate{
			codegen.Header("Code Generator", "main", imports),
			{
				Name:   "main",
				Source: mainT,
				Data:   data,
			},
		}
	}

	f := &codegen.File{Path: "main.go", SectionTemplates: sections}
	_, err := f.Render(tmpDir)
	return err
}

// Compile compiles the generator.
func (g *Generator) Compile() error {
	// We first need to go get the generated package to make sure that all
	// dependencies are added to go.sum prior to compiling.
	pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName}, fmt.Sprintf(".%c%s", filepath.Separator, g.tmpDir))
	if err != nil {
		return err
	}
	if len(pkgs) != 1 {
		return fmt.Errorf("expected to find one package in %s", g.tmpDir)
	}
	if !g.hasVendorDirectory {
		if err := g.runGoCmd("get", pkgs[0].PkgPath); err != nil {
			return err
		}
	}

	err = g.runGoCmd("build", "-o", g.bin)

	// If we're in vendor context we check the error string to see if it's an issue of unsatisfied dependencies
	if err != nil && g.hasVendorDirectory {
		if strings.Contains(err.Error(), "cannot find package") && strings.Contains(err.Error(), "/goa.design/goa/v3/codegen/generator") {
			return errors.New("generated code expected `goa.design/goa/v3/codegen/generator` to be present in the vendor directory, see documentation for more details")
		}
	}

	return err
}

// Run runs the compiled binary and return the output lines.
func (g *Generator) Run() ([]string, error) {
	var cmdl string
	{
		args := make([]string, len(os.Args)-1)
		gopaths := filepath.SplitList(os.Getenv("GOPATH"))
		if len(gopaths) == 0 {
			gopaths = []string{build.Default.GOPATH}
		}
		for i, a := range os.Args[1:] {
			for _, p := range gopaths {
				if strings.HasPrefix(a, p) {
					args[i] = strings.Replace(a, p, "$(GOPATH)", 1)
					break
				}
			}
			if args[i] == "" {
				args[i] = a
			}
		}
		cmdl = " " + strings.Join(args, " ")
		rawcmd := filepath.Base(os.Args[0])
		// Remove .exe suffix to avoid different output on Windows.
		rawcmd = strings.TrimSuffix(rawcmd, ".exe")

		cmdl = fmt.Sprintf("$ %s%s", rawcmd, cmdl)
	}

	args := []string{"--version=" + strconv.Itoa(g.DesignVersion), "--output=" + g.Output, "--cmd=" + cmdl}
	cmd := exec.Command(filepath.Join(g.tmpDir, g.bin), args...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("%s\n%s", err, string(out))
	}
	res := strings.Split(string(out), "\n")
	for (len(res) > 0) && (res[len(res)-1] == "") {
		res = res[:len(res)-1]
	}
	return res, nil
}

// Remove deletes the package files.
func (g *Generator) Remove() {
	if g.tmpDir != "" {
		os.RemoveAll(g.tmpDir)
		g.tmpDir = ""
	}
}

func (g *Generator) runGoCmd(args ...string) error {
	gobin, err := exec.LookPath("go")
	if err != nil {
		return fmt.Errorf(`failed to find a go compiler, looked in "%s"`, os.Getenv("PATH"))
	}
	if g.DesignVersion > 2 {
		os.Setenv("GO111MODULE", "on")
	}
	c := exec.Cmd{
		Path: gobin,
		Args: append([]string{gobin}, args...),
		Dir:  g.tmpDir,
	}
	out, err := c.CombinedOutput()
	if err != nil {
		if len(out) > 0 {
			return fmt.Errorf(string(out))
		}
		return fmt.Errorf("failed to compile generator: %s", err)
	}
	return nil
}

// cleanupDirs returns the paths of the subdirectories under gendir to delete
// before generating code.
func cleanupDirs(cmd, output string) []string {
	if cmd == "gen" {
		gendirPath := filepath.Join(output, codegen.Gendir)
		gendir, err := os.Open(gendirPath)
		if err != nil {
			return nil
		}
		defer gendir.Close()
		finfos, err := gendir.Readdir(-1)
		if err != nil {
			return []string{gendirPath}
		}
		dirs := []string{}
		for _, fi := range finfos {
			if fi.IsDir() {
				dirs = append(dirs, filepath.Join(gendirPath, fi.Name()))
			}
		}
		return dirs
	}
	return nil
}

// mainT is the template for the generator main.
const mainT = `func main() {
	var (
		out     = flag.String("output", "", "")
		version = flag.String("version", "", "")
		cmdl    = flag.String("cmd", "", "")
		ver int
	)
	{
		flag.Parse()
		if *out == "" {
			fail("missing output flag")
		}
		if *version == "" {
			fail("missing version flag")
		}
		if *cmdl == "" {
			fail("missing cmd flag")
		}
		v, err := strconv.Atoi(*version)
		if err != nil {
			fail("invalid version %s", *version)
		}
		ver = v
	}

	if ver > goa.Major {
		fail("cannot run goa %s on design using goa v%s\n", goa.Version(), *version)
	}
	if err := eval.Context.Errors; err != nil {
		fail(err.Error())
	}
	if err := eval.RunDSL(); err != nil {
		fail(err.Error())
	}
{{- range .CleanupDirs }}
	if err := os.RemoveAll({{ printf "%q" . }}); err != nil {
		fail(err.Error())
	}
{{- end }}
{{- if gt .DesignVersion 2 }}
	codegen.DesignVersion = ver
{{- end }}
	outputs, err := generator.Generate(*out, {{ printf "%q" .Command }})
	if err != nil {
		fail(err.Error())
	}

	fmt.Println(strings.Join(outputs, "\n"))
}

func fail(msg string, vals ...interface{}) {
	fmt.Fprintf(os.Stderr, msg, vals...)
	os.Exit(1)
}
`
<commit_msg>Added nil check in case packages.Load can't get module info (#3031)<commit_after>package main

import (
	"errors"
	"fmt"
	"go/build"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"

	"goa.design/goa/v3/codegen"
	"golang.org/x/tools/go/packages"
)

// Generator is the code generation management data structure.
type Generator struct {
	// Command is the name of the command to run.
	Command string

	// DesignPath is the Go import path to the design package.
	DesignPath string

	// Output is the absolute path to the output directory.
	Output string

	// DesignVersion is the major component of the Goa version used by the design DSL.
	// DesignVersion is either 2 or 3.
	DesignVersion int

	// bin is the filename of the generated generator.
	bin string

	// tmpDir is the temporary directory used to compile the generator.
	tmpDir string

	// hasVendorDirectory is a flag to indicate whether the project uses vendoring
	hasVendorDirectory bool
}

// NewGenerator creates a Generator.
func NewGenerator(cmd string, path, output string) *Generator {
	bin := "goa"
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	var version int
	var hasVendorDirectory bool
	{
		version = 2
		matched := false
		pkgs, _ := packages.Load(&packages.Config{Mode: packages.NeedFiles | packages.NeedModule}, path)
		fset := token.NewFileSet()
		p := regexp.MustCompile(`goa.design/goa/v(\d+)/dsl`)
		for _, pkg := range pkgs {
			// Nil check in case packages.Load can't get module info
			if pkg.Module != nil {
				if _, err := os.Stat(filepath.Join(pkg.Module.Dir, "vendor")); !os.IsNotExist(err) {
					hasVendorDirectory = true
				}
			}
			for _, gof := range pkg.GoFiles {
				if bs, err := ioutil.ReadFile(gof); err == nil {
					if f, err := parser.ParseFile(fset, "", string(bs), parser.ImportsOnly); err == nil {
						for _, s := range f.Imports {
							matches := p.FindStringSubmatch(s.Path.Value)
							if len(matches) == 2 {
								matched = true
								version, _ = strconv.Atoi(matches[1]) // We know it's an integer
							}
						}
					}
				}
				if matched {
					break
				}
			}
			if matched {
				break
			}
		}
	}

	return &Generator{
		Command:            cmd,
		DesignPath:         path,
		Output:             output,
		DesignVersion:      version,
		hasVendorDirectory: hasVendorDirectory,
		bin:                bin,
	}
}

// Write writes the main file.
func (g *Generator) Write(debug bool) error {
	var tmpDir string
	{
		wd := "."
		if cwd, err := os.Getwd(); err != nil {
			wd = cwd
		}
		tmp, err := ioutil.TempDir(wd, "goa")
		if err != nil {
			return err
		}
		tmpDir = tmp
	}
	g.tmpDir = tmpDir

	var sections []*codegen.SectionTemplate
	{
		data := map[string]interface{}{
			"Command":       g.Command,
			"CleanupDirs":   cleanupDirs(g.Command, g.Output),
			"DesignVersion": g.DesignVersion,
		}
		ver := ""
		if g.DesignVersion > 2 {
			ver = "v" + strconv.Itoa(g.DesignVersion) + "/"
		}
		imports := []*codegen.ImportSpec{
			codegen.SimpleImport("flag"),
			codegen.SimpleImport("fmt"),
			codegen.SimpleImport("os"),
			codegen.SimpleImport("path/filepath"),
			codegen.SimpleImport("sort"),
			codegen.SimpleImport("strconv"),
			codegen.SimpleImport("strings"),
			codegen.SimpleImport("goa.design/goa/" + ver + "codegen"),
			codegen.SimpleImport("goa.design/goa/" + ver + "codegen/generator"),
			codegen.SimpleImport("goa.design/goa/" + ver + "eval"),
			codegen.NewImport("goa", "goa.design/goa/"+ver+"pkg"),
			codegen.NewImport("_", g.DesignPath),
		}
		sections = []*codegen.SectionTemplate{
			codegen.Header("Code Generator", "main", imports),
			{
				Name:   "main",
				Source: mainT,
				Data:   data,
			},
		}
	}

	f := &codegen.File{Path: "main.go", SectionTemplates: sections}
	_, err := f.Render(tmpDir)
	return err
}

// Compile compiles the generator.
func (g *Generator) Compile() error {
	// We first need to go get the generated package to make sure that all
	// dependencies are added to go.sum prior to compiling.
	pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName}, fmt.Sprintf(".%c%s", filepath.Separator, g.tmpDir))
	if err != nil {
		return err
	}
	if len(pkgs) != 1 {
		return fmt.Errorf("expected to find one package in %s", g.tmpDir)
	}
	if !g.hasVendorDirectory {
		if err := g.runGoCmd("get", pkgs[0].PkgPath); err != nil {
			return err
		}
	}

	err = g.runGoCmd("build", "-o", g.bin)

	// If we're in vendor context we check the error string to see if it's an issue of unsatisfied dependencies
	if err != nil && g.hasVendorDirectory {
		if strings.Contains(err.Error(), "cannot find package") && strings.Contains(err.Error(), "/goa.design/goa/v3/codegen/generator") {
			return errors.New("generated code expected `goa.design/goa/v3/codegen/generator` to be present in the vendor directory, see documentation for more details")
		}
	}

	return err
}

// Run runs the compiled binary and return the output lines.
func (g *Generator) Run() ([]string, error) {
	var cmdl string
	{
		args := make([]string, len(os.Args)-1)
		gopaths := filepath.SplitList(os.Getenv("GOPATH"))
		if len(gopaths) == 0 {
			gopaths = []string{build.Default.GOPATH}
		}
		for i, a := range os.Args[1:] {
			for _, p := range gopaths {
				if strings.HasPrefix(a, p) {
					args[i] = strings.Replace(a, p, "$(GOPATH)", 1)
					break
				}
			}
			if args[i] == "" {
				args[i] = a
			}
		}
		cmdl = " " + strings.Join(args, " ")
		rawcmd := filepath.Base(os.Args[0])
		// Remove .exe suffix to avoid different output on Windows.
		rawcmd = strings.TrimSuffix(rawcmd, ".exe")

		cmdl = fmt.Sprintf("$ %s%s", rawcmd, cmdl)
	}

	args := []string{"--version=" + strconv.Itoa(g.DesignVersion), "--output=" + g.Output, "--cmd=" + cmdl}
	cmd := exec.Command(filepath.Join(g.tmpDir, g.bin), args...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("%s\n%s", err, string(out))
	}
	res := strings.Split(string(out), "\n")
	for (len(res) > 0) && (res[len(res)-1] == "") {
		res = res[:len(res)-1]
	}
	return res, nil
}

// Remove deletes the package files.
func (g *Generator) Remove() {
	if g.tmpDir != "" {
		os.RemoveAll(g.tmpDir)
		g.tmpDir = ""
	}
}

func (g *Generator) runGoCmd(args ...string) error {
	gobin, err := exec.LookPath("go")
	if err != nil {
		return fmt.Errorf(`failed to find a go compiler, looked in "%s"`, os.Getenv("PATH"))
	}
	if g.DesignVersion > 2 {
		os.Setenv("GO111MODULE", "on")
	}
	c := exec.Cmd{
		Path: gobin,
		Args: append([]string{gobin}, args...),
		Dir:  g.tmpDir,
	}
	out, err := c.CombinedOutput()
	if err != nil {
		if len(out) > 0 {
			return fmt.Errorf(string(out))
		}
		return fmt.Errorf("failed to compile generator: %s", err)
	}
	return nil
}

// cleanupDirs returns the paths of the subdirectories under gendir to delete
// before generating code.
func cleanupDirs(cmd, output string) []string {
	if cmd == "gen" {
		gendirPath := filepath.Join(output, codegen.Gendir)
		gendir, err := os.Open(gendirPath)
		if err != nil {
			return nil
		}
		defer gendir.Close()
		finfos, err := gendir.Readdir(-1)
		if err != nil {
			return []string{gendirPath}
		}
		dirs := []string{}
		for _, fi := range finfos {
			if fi.IsDir() {
				dirs = append(dirs, filepath.Join(gendirPath, fi.Name()))
			}
		}
		return dirs
	}
	return nil
}

// mainT is the template for the generator main.
const mainT = `func main() {
	var (
		out     = flag.String("output", "", "")
		version = flag.String("version", "", "")
		cmdl    = flag.String("cmd", "", "")
		ver int
	)
	{
		flag.Parse()
		if *out == "" {
			fail("missing output flag")
		}
		if *version == "" {
			fail("missing version flag")
		}
		if *cmdl == "" {
			fail("missing cmd flag")
		}
		v, err := strconv.Atoi(*version)
		if err != nil {
			fail("invalid version %s", *version)
		}
		ver = v
	}

	if ver > goa.Major {
		fail("cannot run goa %s on design using goa v%s\n", goa.Version(), *version)
	}
	if err := eval.Context.Errors; err != nil {
		fail(err.Error())
	}
	if err := eval.RunDSL(); err != nil {
		fail(err.Error())
	}
{{- range .CleanupDirs }}
	if err := os.RemoveAll({{ printf "%q" . }}); err != nil {
		fail(err.Error())
	}
{{- end }}
{{- if gt .DesignVersion 2 }}
	codegen.DesignVersion = ver
{{- end }}
	outputs, err := generator.Generate(*out, {{ printf "%q" .Command }})
	if err != nil {
		fail(err.Error())
	}

	fmt.Println(strings.Join(outputs, "\n"))
}

func fail(msg string, vals ...interface{}) {
	fmt.Fprintf(os.Stderr, msg, vals...)
	os.Exit(1)
}
`
<|endoftext|>


<commit_before>// Copyright 2014 Volker Dobler.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/vdobler/ht/ht"
)

var cmdExec = &Command{
	Run:         runExecute,
	Usage:       "exec [-serial] <suite>...",
	Description: "generate request and test response",
	Help: `
Exec loads the given suites, unrolls the tests, prepares
the tests and executes them.
	`,
}

func init() {
	cmdExec.Flag.BoolVar(&serialFlag, "serial", false,
		"run suites one after the other instead of concurrently")
	cmdExec.Flag.StringVar(&outputDir, "output", "",
		"save results to `dirname` instead of timestamp")
	addVariablesFlag(&cmdExec.Flag)
	addOnlyFlag(&cmdExec.Flag)
	addSkipFlag(&cmdExec.Flag)
	addVerbosityFlag(&cmdExec.Flag)
}

var (
	serialFlag bool
	outputDir  string
	sanitizer  = strings.NewReplacer(" ", "_", ":", "_", "@", "_at_", "/", "_",
		"*", "_", "?", "_", "#", "_", "$", "_", "<", "_", ">", "_", "~", "_",
		"ä", "ae", "ö", "oe", "ü", "ue", "Ä", "Ae", "Ö", "Oe", "Ü", "Ue",
		"%", "_", "&", "+", "(", "_", ")", "_", "'", "_", "`", "_", "^", "_")
)

func runExecute(cmd *Command, suites []*ht.Suite) {
	if outputDir == "" {
		outputDir = time.Now().Format("2006-01-02_15h04m05s")
	}
	os.MkdirAll(outputDir, 0766)
	var wg sync.WaitGroup
	for i := range suites {
		if serialFlag {
			suites[i].Execute()
			if suites[i].Status > ht.Pass {
				log.Printf("Suite %d %q failed: %s", i+1,
					suites[i].Name,
					suites[i].Error.Error())
			}
		} else {
			wg.Add(1)
			go func(i int) {
				suites[i].Execute()
				if suites[i].Status > ht.Pass {
					log.Printf("Suite %d %q failed: %s", i+1,
						suites[i].Name, suites[i].Error.Error())
				}
				wg.Done()
			}(i)
		}
	}
	wg.Wait()

	total, totalPass, totalError, totalSkiped, totalFailed, totalBogus := 0, 0, 0, 0, 0, 0
	for s := range suites {
		suites[s].PrintReport(os.Stdout)
		dirname := outputDir + "/" + sanitizer.Replace(suites[s].Name)

		fmt.Printf("Saveing result of suite %q to folder %q.\n", suites[s].Name, dirname)
		err := os.MkdirAll(dirname, 0766)
		if err != nil {
			log.Panic(err)
		}
		err = suites[s].HTMLReport(dirname)
		if err != nil {
			log.Panic(err)
		}
		junit, err := suites[s].JUnit4XML()
		if err != nil {
			log.Panic(err)
		}
		err = ioutil.WriteFile(dirname+"/junit-report.xml", []byte(junit), 0666)
	}
	fmt.Printf("Total %d,  Passed %d, Skipped %d,  Errored %d,  Failed %d,  Bogus %d\n",
		total, totalPass, totalSkiped, totalError, totalFailed, totalBogus)

}
<commit_msg>cmd/ht: fix running sum of tests<commit_after>// Copyright 2014 Volker Dobler.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/vdobler/ht/ht"
)

var cmdExec = &Command{
	Run:         runExecute,
	Usage:       "exec [-serial] <suite>...",
	Description: "generate request and test response",
	Help: `
Exec loads the given suites, unrolls the tests, prepares
the tests and executes them.
	`,
}

func init() {
	cmdExec.Flag.BoolVar(&serialFlag, "serial", false,
		"run suites one after the other instead of concurrently")
	cmdExec.Flag.StringVar(&outputDir, "output", "",
		"save results to `dirname` instead of timestamp")
	addVariablesFlag(&cmdExec.Flag)
	addOnlyFlag(&cmdExec.Flag)
	addSkipFlag(&cmdExec.Flag)
	addVerbosityFlag(&cmdExec.Flag)
}

var (
	serialFlag bool
	outputDir  string
	sanitizer  = strings.NewReplacer(" ", "_", ":", "_", "@", "_at_", "/", "_",
		"*", "_", "?", "_", "#", "_", "$", "_", "<", "_", ">", "_", "~", "_",
		"ä", "ae", "ö", "oe", "ü", "ue", "Ä", "Ae", "Ö", "Oe", "Ü", "Ue",
		"%", "_", "&", "+", "(", "_", ")", "_", "'", "_", "`", "_", "^", "_")
)

func runExecute(cmd *Command, suites []*ht.Suite) {
	if outputDir == "" {
		outputDir = time.Now().Format("2006-01-02_15h04m05s")
	}
	os.MkdirAll(outputDir, 0766)
	var wg sync.WaitGroup
	for i := range suites {
		if serialFlag {
			suites[i].Execute()
			if suites[i].Status > ht.Pass {
				log.Printf("Suite %d %q failed: %s", i+1,
					suites[i].Name,
					suites[i].Error.Error())
			}
		} else {
			wg.Add(1)
			go func(i int) {
				suites[i].Execute()
				if suites[i].Status > ht.Pass {
					log.Printf("Suite %d %q failed: %s", i+1,
						suites[i].Name, suites[i].Error.Error())
				}
				wg.Done()
			}(i)
		}
	}
	wg.Wait()

	total, totalPass, totalError, totalSkiped, totalFailed, totalBogus := 0, 0, 0, 0, 0, 0
	for s := range suites {
		suites[s].PrintReport(os.Stdout)

		// Statistics
		for _, r := range suites[s].AllTests() {
			switch r.Status {
			case ht.Pass:
				totalPass++
			case ht.Error:
				totalError++
			case ht.Skipped:
				totalSkiped++
			case ht.Fail:
				totalFailed++
			case ht.Bogus:
				totalBogus++
			}
			total++
		}

		dirname := outputDir + "/" + sanitizer.Replace(suites[s].Name)
		fmt.Printf("Saveing result of suite %q to folder %q.\n", suites[s].Name, dirname)
		err := os.MkdirAll(dirname, 0766)
		if err != nil {
			log.Panic(err)
		}
		err = suites[s].HTMLReport(dirname)
		if err != nil {
			log.Panic(err)
		}
		junit, err := suites[s].JUnit4XML()
		if err != nil {
			log.Panic(err)
		}
		err = ioutil.WriteFile(dirname+"/junit-report.xml", []byte(junit), 0666)
	}
	fmt.Printf("Total %d,  Passed %d, Skipped %d,  Errored %d,  Failed %d,  Bogus %d\n",
		total, totalPass, totalSkiped, totalError, totalFailed, totalBogus)

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/ianremmler/dgrl"
	"github.com/ianremmler/lit"
)

const usage = `usage:

lit [help | usage]              Show usage
lit init                        Initialize new issue tracker
lit new [<num>]                 Create num new issues (default is 1)
lit list [<sort>] <spec>        Show summary list of specified issues
lit id [<sort>] <spec>          List ids of specified issues
lit show [<sort>] <spec>        Show specified issues
lit set <key> <val> <spec>      Set value for key in specified issues
lit tag (add|del) <tag> <spec>  Add or delete tag in specified issues
lit comment <id> [<text>]       Add issue comment (open editor if no text given)
lit edit <spec>                 Edit specified issues
lit close <spec>                Close specified issues
lit reopen <spec>               Reopen specified issues

sort: (sortby|rsortby) <key>  Sort (reverse if rsortby) based on key

spec: all | <ids> | (with|without) <key> [<val>] | (less|greater) <key> <val>
	Specifies which issues to operate on
	Use 'comment' key to filter by comment contents and times`

const (
	// id, closed?, priority, assigned, tags, summary
	listFmt = "%-8.8s %-1.1s %-1.1s %-8.8s %-17.17s %s"
)

var (
	args    = os.Args[1:]
	it      = lit.New()
	listHdr = fmt.Sprintf(listFmt, "id", "c", "p", "assigned", "tags", "summary")
	cmd     string
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("lit: ")

	// append args piped in from stdin
	if stat, err := os.Stdin.Stat(); err == nil && stat.Mode()&os.ModeNamedPipe != 0 {
		if stdin, err := ioutil.ReadAll(os.Stdin); err == nil {
			stdinArgs := strings.Fields(string(stdin))
			args = append(args, stdinArgs...)
		}
	}

	if len(args) > 0 {
		cmd = args[0]
		args = args[1:]
	}
	switch cmd {
	case "", "-h", "-help", "--help", "help", "-u", "-usage", "--usage", "usage":
		usageCmd()
	case "list":
		listCmd()
	case "init":
		initCmd()
	case "new":
		newCmd()
	case "id":
		idCmd()
	case "show":
		showCmd()
	case "set":
		setCmd()
	case "tag":
		tagCmd()
	case "comment":
		commentCmd()
	case "edit":
		editCmd()
	case "close", "reopen":
		closeCmd()
	default:
		log.Fatalln(cmd + " is not a valid command")
	}
}

func usageCmd() {
	fmt.Println(usage)
}

func initCmd() {
	err := it.Init()
	checkErr(err)
}

func newCmd() {
	numIssues := 1
	if len(args) > 0 {
		num, err := strconv.ParseUint(args[0], 10, 16)
		checkErr(err)
		numIssues = int(num)
	}
	loadIssues()
	for i := 0; i < numIssues; i++ {
		id, err := it.NewIssue()
		checkErr(err)
		fmt.Println(id)
	}
	storeIssues()
}

func listCmd() {
	loadIssues()
	doSort, key, doAscend := dispOpts()
	ids := specIds()
	if doSort {
		it.Sort(ids, key, doAscend)
	}
	fmt.Println(listHdr)
	for _, id := range ids {
		issue := it.Issue(id)
		if issue != nil {
			fmt.Println(listInfo(id, issue))
		}
	}
}

func idCmd() {
	loadIssues()
	doSort, key, doAscend := dispOpts()
	ids := specIds()
	if doSort {
		it.Sort(ids, key, doAscend)
	}
	for _, id := range ids {
		if it.Issue(id) != nil {
			fmt.Println(id)
		}
	}
}

func showCmd() {
	loadIssues()
	doSort, key, doAscend := dispOpts()
	ids := specIds()
	if doSort {
		it.Sort(ids, key, doAscend)
	}
	for _, id := range ids {
		fmt.Println(it.Issue(id))
	}
}

func setCmd() {
	if len(args) < 3 {
		log.Fatalln("set: you must specify a key, value, and spec")
	}
	key, val := args[0], args[1]
	args = args[2:]
	loadIssues()
	stamp := lit.Stamp()
	for _, id := range specIds() {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("set: error finding issue %s\n", id)
			continue
		}
		ok := lit.Set(issue, key, val)
		ok = ok && lit.Set(issue, "updated", stamp)
		if !ok {
			log.Printf("set: error updating fields in issue %s\n", id)
			continue
		}
	}
	storeIssues()
}

func tagCmd() {
	if len(args) < 3 {
		log.Fatalln("tag: you must specify an operation, tag, and spec")
	}
	op, tag := args[0], args[1]
	if op != "add" && op != "del" {
		log.Fatalf("tag: is not a valid operation\n")
	}
	doAdd := (op == "add")

	args = args[2:]
	loadIssues()
	stamp := lit.Stamp()
	for _, id := range specIds() {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("tag: error finding issue %s\n", id)
			continue
		}
		ok := lit.ModifyTag(issue, tag, doAdd)
		ok = ok && lit.Set(issue, "updated", stamp)
		if !ok {
			log.Printf("tag: error updating fields in issue %s\n", id)
			continue
		}
	}
	storeIssues()
}

func editCmd() {
	if len(args) < 1 {
		log.Fatalln("edit: you must specify a spec to edit")
	}
	editor := getEditor()
	if editor == "" {
		log.Fatalln("edit: VISUAL or EDITOR environment variable must be set")
	}

	loadIssues()

	// create temp file
	tempFile, err := ioutil.TempFile("", "lit-")
	checkErr(err)
	filename := tempFile.Name()

	// load issue content into temp file
	ids := specIds()
	toEdit := dgrl.NewRoot()
	for _, id := range ids {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("edit: error finding issue %s\n", id)
			continue
		}
		if !lit.Set(issue, "updated", lit.Stamp()) {
			log.Printf("edit: error setting update time for issue %s\n", id)
			continue
		}
		toEdit.Append(issue)
	}
	err = toEdit.Write(tempFile)
	checkErr(err)
	tempFile.Close()

	// get original file state
	origStat, err := os.Stat(filename)
	checkErr(err)

	// launch editor
	ed := exec.Command(editor, filename)
	ed.Stdin, ed.Stdout, ed.Stderr = os.Stdin, os.Stdout, os.Stderr
	err = ed.Run()
	checkErr(err)

	// get updated file state, compare to original
	newStat, err := os.Stat(filename)
	checkErr(err)
	if newStat.ModTime() == origStat.ModTime() {
		log.Fatalln("edit: file unchanged")
	}

	// parse issue from temp file
	tempFile, err = os.Open(filename)
	checkErr(err)
	edited := dgrl.NewParser().Parse(tempFile)
	tempFile.Close()
	if edited == nil {
		log.Fatalln("edit: error parsing file")
	}

	// update issues if we find a match
	didUpdate := false
	for _, id := range ids {
		issue := it.Issue(id)
		if issue == nil {
			// already printed error, so don't repeat here
			continue
		}
		for _, node := range edited.Kids() {
			if node.Type() == dgrl.BranchType && strings.HasPrefix(node.Key(), id) {
				if editedIssue, ok := node.(*dgrl.Branch); ok {
					*issue = *editedIssue
					didUpdate = true
					break
				}
			}
		}
	}
	if !didUpdate {
		log.Fatalln("edit: did not update anything")
	}

	storeIssues()
}

func closeCmd() {
	if len(args) < 1 {
		log.Fatalf("%s: you must specify a spec\n", cmd)
	}
	loadIssues()
	stamp := lit.Stamp()
	for _, id := range specIds() {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("%s: error finding issue %s\n", cmd, id)
			continue
		}
		closedStamp := ""
		if cmd == "close" {
			closedStamp = stamp
		}
		ok := lit.Set(issue, "closed", closedStamp)
		ok = ok && lit.Set(issue, "updated", stamp)
		if !ok {
			log.Printf("%s: error updating fields for issue %s\n", cmd, id)
			continue
		}
	}
	storeIssues()
}

func commentCmd() {
	if len(args) < 1 {
		log.Fatalln("comment: you must specify an issue to comment on")
	}
	id := args[0]
	loadIssues()
	issue := it.Issue(id)
	if issue == nil {
		log.Fatalf("comment: error finding issue %s\n", id)
	}
	comment := ""
	if len(args) > 1 {
		comment = args[1]
	} else {
		comment = editComment()
	}
	stamp := lit.Stamp()
	commentBranch := dgrl.NewBranch(stamp)
	commentBranch.Append(dgrl.NewText(comment))
	issue.Append(commentBranch)
	if !lit.Set(issue, "updated", stamp) {
		log.Printf("comment: error setting update time for issue %s\n", id)
	}
	storeIssues()
}

func editComment() string {
	editor := getEditor()
	if editor == "" {
		log.Fatalln("comment: VISUAL or EDITOR environment variable must be set")
	}
	// create temp file
	tempFile, err := ioutil.TempFile("", "lit-")
	checkErr(err)
	filename := tempFile.Name()

	// get original file state
	origStat, err := os.Stat(filename)
	checkErr(err)

	// launch editor
	ed := exec.Command(editor, filename)
	ed.Stdin, ed.Stdout, ed.Stderr = os.Stdin, os.Stdout, os.Stderr
	err = ed.Run()
	checkErr(err)

	// get updated file state, compare to original
	newStat, err := os.Stat(filename)
	checkErr(err)
	if newStat.ModTime() == origStat.ModTime() {
		log.Fatalln("comment: file unchanged")
	}

	// read comment from file
	commentData, err := ioutil.ReadFile(filename)
	checkErr(err)
	return string(commentData)
}

func listInfo(id string, issue *dgrl.Branch) string {
	status := " "
	closed, _ := lit.Get(issue, "closed")
	if len(closed) > 0 {
		status = "*"
	}
	tags, _ := lit.Get(issue, "tags")
	priority, _ := lit.Get(issue, "priority")
	assigned, _ := lit.Get(issue, "assigned")
	summary, _ := lit.Get(issue, "summary")
	return fmt.Sprintf(listFmt, id, status, priority, assigned, tags, summary)
}

func keyval(kv []string) (string, string) {
	key, val := "", ""
	if len(kv) > 0 {
		key = kv[0]
	}
	if len(kv) > 1 {
		val = kv[1]
	}
	return key, val
}

func matchIds(kv []string, doesMatch bool) []string {
	key, val := keyval(kv)
	return it.Match(key, val, doesMatch)
}

func compareIds(kv []string, isLess bool) []string {
	key, val := keyval(kv)
	return it.Compare(key, val, isLess)
}

func dispOpts() (bool, string, bool) {
	switch {
	case len(args) == 0:
		return false, "", true
	case args[0] == "sortby" || args[0] == "rsortby":
		if len(args) < 2 {
			log.Fatalf("%s: sort requested, but no key given to sort by\n", cmd)
		}
		doSort := true
		doAscend := (args[0] == "sortby")
		key := args[1]
		args = args[2:]
		return doSort, key, doAscend
	}
	return false, "", true
}

func specIds() []string {
	ids := []string{}
	switch {
	case len(args) == 0:
		return ids
	case args[0] == "with":
		ids = matchIds(args[1:], true)
	case args[0] == "without":
		ids = matchIds(args[1:], false)
	case args[0] == "less":
		ids = compareIds(args[1:], true)
	case args[0] == "greater":
		ids = compareIds(args[1:], false)
	case args[0] == "all":
		ids = it.IssueIds()
	default:
		ids = args
	}
	return ids
}

func loadIssues() {
	err := it.Load()
	checkErr(err)
}

func storeIssues() {
	err := it.Store()
	checkErr(err)
}

func checkErr(err error) {
	if err != nil {
		log.Fatalf("%s: %s\n", cmd, err)
	}
}

func getEditor() string {
	editor := os.Getenv("VISUAL")
	if editor == "" {
		editor = os.Getenv("EDITOR")
	}
	return editor
}
<commit_msg>Update time stamps after manual edit.<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/ianremmler/dgrl"
	"github.com/ianremmler/lit"
)

const usage = `usage:

lit [help | usage]              Show usage
lit init                        Initialize new issue tracker
lit new [<num>]                 Create num new issues (default is 1)
lit list [<sort>] <spec>        Show summary list of specified issues
lit id [<sort>] <spec>          List ids of specified issues
lit show [<sort>] <spec>        Show specified issues
lit set <key> <val> <spec>      Set value for key in specified issues
lit tag (add|del) <tag> <spec>  Add or delete tag in specified issues
lit comment <id> [<text>]       Add issue comment (open editor if no text given)
lit edit <spec>                 Edit specified issues
lit close <spec>                Close specified issues
lit reopen <spec>               Reopen specified issues

sort: (sortby|rsortby) <key>  Sort (reverse if rsortby) based on key

spec: all | <ids> | (with|without) <key> [<val>] | (less|greater) <key> <val>
	Specifies which issues to operate on
	Use 'comment' key to filter by comment contents and times`

const (
	// id, closed?, priority, assigned, tags, summary
	listFmt = "%-8.8s %-1.1s %-1.1s %-8.8s %-17.17s %s"
)

var (
	args    = os.Args[1:]
	it      = lit.New()
	listHdr = fmt.Sprintf(listFmt, "id", "c", "p", "assigned", "tags", "summary")
	cmd     string
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("lit: ")

	// append args piped in from stdin
	if stat, err := os.Stdin.Stat(); err == nil && stat.Mode()&os.ModeNamedPipe != 0 {
		if stdin, err := ioutil.ReadAll(os.Stdin); err == nil {
			stdinArgs := strings.Fields(string(stdin))
			args = append(args, stdinArgs...)
		}
	}

	if len(args) > 0 {
		cmd = args[0]
		args = args[1:]
	}
	switch cmd {
	case "", "-h", "-help", "--help", "help", "-u", "-usage", "--usage", "usage":
		usageCmd()
	case "list":
		listCmd()
	case "init":
		initCmd()
	case "new":
		newCmd()
	case "id":
		idCmd()
	case "show":
		showCmd()
	case "set":
		setCmd()
	case "tag":
		tagCmd()
	case "comment":
		commentCmd()
	case "edit":
		editCmd()
	case "close", "reopen":
		closeCmd()
	default:
		log.Fatalln(cmd + " is not a valid command")
	}
}

func usageCmd() {
	fmt.Println(usage)
}

func initCmd() {
	err := it.Init()
	checkErr(err)
}

func newCmd() {
	numIssues := 1
	if len(args) > 0 {
		num, err := strconv.ParseUint(args[0], 10, 16)
		checkErr(err)
		numIssues = int(num)
	}
	loadIssues()
	for i := 0; i < numIssues; i++ {
		id, err := it.NewIssue()
		checkErr(err)
		fmt.Println(id)
	}
	storeIssues()
}

func listCmd() {
	loadIssues()
	doSort, key, doAscend := dispOpts()
	ids := specIds()
	if doSort {
		it.Sort(ids, key, doAscend)
	}
	fmt.Println(listHdr)
	for _, id := range ids {
		issue := it.Issue(id)
		if issue != nil {
			fmt.Println(listInfo(id, issue))
		}
	}
}

func idCmd() {
	loadIssues()
	doSort, key, doAscend := dispOpts()
	ids := specIds()
	if doSort {
		it.Sort(ids, key, doAscend)
	}
	for _, id := range ids {
		if it.Issue(id) != nil {
			fmt.Println(id)
		}
	}
}

func showCmd() {
	loadIssues()
	doSort, key, doAscend := dispOpts()
	ids := specIds()
	if doSort {
		it.Sort(ids, key, doAscend)
	}
	for _, id := range ids {
		fmt.Println(it.Issue(id))
	}
}

func setCmd() {
	if len(args) < 3 {
		log.Fatalln("set: you must specify a key, value, and spec")
	}
	key, val := args[0], args[1]
	args = args[2:]
	loadIssues()
	stamp := lit.Stamp()
	for _, id := range specIds() {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("set: error finding issue %s\n", id)
			continue
		}
		ok := lit.Set(issue, key, val)
		ok = ok && lit.Set(issue, "updated", stamp)
		if !ok {
			log.Printf("set: error updating fields in issue %s\n", id)
			continue
		}
	}
	storeIssues()
}

func tagCmd() {
	if len(args) < 3 {
		log.Fatalln("tag: you must specify an operation, tag, and spec")
	}
	op, tag := args[0], args[1]
	if op != "add" && op != "del" {
		log.Fatalf("tag: is not a valid operation\n")
	}
	doAdd := (op == "add")

	args = args[2:]
	loadIssues()
	stamp := lit.Stamp()
	for _, id := range specIds() {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("tag: error finding issue %s\n", id)
			continue
		}
		ok := lit.ModifyTag(issue, tag, doAdd)
		ok = ok && lit.Set(issue, "updated", stamp)
		if !ok {
			log.Printf("tag: error updating fields in issue %s\n", id)
			continue
		}
	}
	storeIssues()
}

func editCmd() {
	if len(args) < 1 {
		log.Fatalln("edit: you must specify a spec to edit")
	}
	editor := getEditor()
	if editor == "" {
		log.Fatalln("edit: VISUAL or EDITOR environment variable must be set")
	}

	loadIssues()

	// create temp file
	tempFile, err := ioutil.TempFile("", "lit-")
	checkErr(err)
	filename := tempFile.Name()

	// load issue content into temp file
	ids := specIds()
	toEdit := dgrl.NewRoot()
	for _, id := range ids {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("edit: error finding issue %s\n", id)
			continue
		}
		toEdit.Append(issue)
	}
	err = toEdit.Write(tempFile)
	checkErr(err)
	tempFile.Close()

	// get original file state
	origStat, err := os.Stat(filename)
	checkErr(err)

	// launch editor
	ed := exec.Command(editor, filename)
	ed.Stdin, ed.Stdout, ed.Stderr = os.Stdin, os.Stdout, os.Stderr
	err = ed.Run()
	checkErr(err)

	// get updated file state, compare to original
	newStat, err := os.Stat(filename)
	checkErr(err)
	if newStat.ModTime() == origStat.ModTime() {
		log.Fatalln("edit: file unchanged")
	}

	// parse issue from temp file
	tempFile, err = os.Open(filename)
	checkErr(err)
	edited := dgrl.NewParser().Parse(tempFile)
	tempFile.Close()
	if edited == nil {
		log.Fatalln("edit: error parsing file")
	}

	// update issues if we find a match
	didUpdate := false
	stamp := lit.Stamp()
	for _, id := range ids {
		issue := it.Issue(id)
		if issue == nil {
			// already printed error, so don't repeat here
			continue
		}
		for _, node := range edited.Kids() {
			if node.Type() == dgrl.BranchType && strings.HasPrefix(node.Key(), id) {
				if editedIssue, ok := node.(*dgrl.Branch); ok {
					*issue = *editedIssue
					if !lit.Set(issue, "updated", stamp) {
						log.Printf("edit: error setting update time for issue %s\n", id)
						continue
					}
					didUpdate = true
					break
				}
			}
		}
	}
	if !didUpdate {
		log.Fatalln("edit: did not update anything")
	}

	storeIssues()
}

func closeCmd() {
	if len(args) < 1 {
		log.Fatalf("%s: you must specify a spec\n", cmd)
	}
	loadIssues()
	stamp := lit.Stamp()
	for _, id := range specIds() {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("%s: error finding issue %s\n", cmd, id)
			continue
		}
		closedStamp := ""
		if cmd == "close" {
			closedStamp = stamp
		}
		ok := lit.Set(issue, "closed", closedStamp)
		ok = ok && lit.Set(issue, "updated", stamp)
		if !ok {
			log.Printf("%s: error updating fields for issue %s\n", cmd, id)
			continue
		}
	}
	storeIssues()
}

func commentCmd() {
	if len(args) < 1 {
		log.Fatalln("comment: you must specify an issue to comment on")
	}
	id := args[0]
	loadIssues()
	issue := it.Issue(id)
	if issue == nil {
		log.Fatalf("comment: error finding issue %s\n", id)
	}
	comment := ""
	if len(args) > 1 {
		comment = args[1]
	} else {
		comment = editComment()
	}
	stamp := lit.Stamp()
	commentBranch := dgrl.NewBranch(stamp)
	commentBranch.Append(dgrl.NewText(comment))
	issue.Append(commentBranch)
	if !lit.Set(issue, "updated", stamp) {
		log.Printf("comment: error setting update time for issue %s\n", id)
	}
	storeIssues()
}

func editComment() string {
	editor := getEditor()
	if editor == "" {
		log.Fatalln("comment: VISUAL or EDITOR environment variable must be set")
	}
	// create temp file
	tempFile, err := ioutil.TempFile("", "lit-")
	checkErr(err)
	filename := tempFile.Name()

	// get original file state
	origStat, err := os.Stat(filename)
	checkErr(err)

	// launch editor
	ed := exec.Command(editor, filename)
	ed.Stdin, ed.Stdout, ed.Stderr = os.Stdin, os.Stdout, os.Stderr
	err = ed.Run()
	checkErr(err)

	// get updated file state, compare to original
	newStat, err := os.Stat(filename)
	checkErr(err)
	if newStat.ModTime() == origStat.ModTime() {
		log.Fatalln("comment: file unchanged")
	}

	// read comment from file
	commentData, err := ioutil.ReadFile(filename)
	checkErr(err)
	return string(commentData)
}

func listInfo(id string, issue *dgrl.Branch) string {
	status := " "
	closed, _ := lit.Get(issue, "closed")
	if len(closed) > 0 {
		status = "*"
	}
	tags, _ := lit.Get(issue, "tags")
	priority, _ := lit.Get(issue, "priority")
	assigned, _ := lit.Get(issue, "assigned")
	summary, _ := lit.Get(issue, "summary")
	return fmt.Sprintf(listFmt, id, status, priority, assigned, tags, summary)
}

func keyval(kv []string) (string, string) {
	key, val := "", ""
	if len(kv) > 0 {
		key = kv[0]
	}
	if len(kv) > 1 {
		val = kv[1]
	}
	return key, val
}

func matchIds(kv []string, doesMatch bool) []string {
	key, val := keyval(kv)
	return it.Match(key, val, doesMatch)
}

func compareIds(kv []string, isLess bool) []string {
	key, val := keyval(kv)
	return it.Compare(key, val, isLess)
}

func dispOpts() (bool, string, bool) {
	switch {
	case len(args) == 0:
		return false, "", true
	case args[0] == "sortby" || args[0] == "rsortby":
		if len(args) < 2 {
			log.Fatalf("%s: sort requested, but no key given to sort by\n", cmd)
		}
		doSort := true
		doAscend := (args[0] == "sortby")
		key := args[1]
		args = args[2:]
		return doSort, key, doAscend
	}
	return false, "", true
}

func specIds() []string {
	ids := []string{}
	switch {
	case len(args) == 0:
		return ids
	case args[0] == "with":
		ids = matchIds(args[1:], true)
	case args[0] == "without":
		ids = matchIds(args[1:], false)
	case args[0] == "less":
		ids = compareIds(args[1:], true)
	case args[0] == "greater":
		ids = compareIds(args[1:], false)
	case args[0] == "all":
		ids = it.IssueIds()
	default:
		ids = args
	}
	return ids
}

func loadIssues() {
	err := it.Load()
	checkErr(err)
}

func storeIssues() {
	err := it.Store()
	checkErr(err)
}

func checkErr(err error) {
	if err != nil {
		log.Fatalf("%s: %s\n", cmd, err)
	}
}

func getEditor() string {
	editor := os.Getenv("VISUAL")
	if editor == "" {
		editor = os.Getenv("EDITOR")
	}
	return editor
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Brian Hetro <whee@smaertness.net>
// Use of this source code is governed by the ISC
// license which can be found in the LICENSE file.

package main

import (
	"fmt"
	"io"
	"os"

	docopt "github.com/docopt/docopt-go"
	"github.com/whee/rp"
)

type config struct {
	channels    []string
	read        bool
	write       bool
	passthrough bool
	rpConfig    rp.Config
}

func main() {
	usage := `Redis Pipe.

Usage:
	rp      read  <name>...
	rp [-p] write <name>...
	rp      -n <network> -a <address> read  <name>...
	rp [-p] -n <network> -a <address> write <name>...

Options:
  -p, --passthrough  Pass written data to standard output.
  -n, --network <network>  Network of the Redis server [default: tcp]
  -a, --address <address>  Address of the Redis server [default: :6379]`

	arguments, err := docopt.Parse(usage, nil, true, "Redis Pipe 0.9", false)
	if err != nil {
		panic(err)
	}
	conf := config{
		channels:    arguments["<name>"].([]string),
		read:        arguments["read"].(bool),
		write:       arguments["write"].(bool),
		passthrough: arguments["--passthrough"].(bool),
		rpConfig: rp.Config{
			Network: arguments["--network"].(string),
			Address: arguments["--address"].(string)},
	}
	_, err = conf.do()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	os.Exit(0)
}

func (c *config) do() (n int64, err error) {
	if c.write {
		n, err = writeTo(c)
	} else if c.read {
		n, err = readFrom(c)
	}
	return
}

type ptWriter struct {
	w io.Writer
}

func (w ptWriter) Write(p []byte) (n int, err error) {
	n, err = os.Stdout.Write(p)
	if err != nil {
		return
	}
	return w.w.Write(p)
}

func writeTo(c *config) (int64, error) {
	t, err := rp.NewWriter(&c.rpConfig, c.channels...)
	if err != nil {
		return 0, err
	}
	defer t.Close()

	var w io.Writer
	if c.passthrough {
		w = ptWriter{t}
	} else {
		w = t
	}
	return io.Copy(w, os.Stdin)
}

func readFrom(c *config) (int64, error) {
	t, err := rp.NewReader(&c.rpConfig, c.channels...)
	if err != nil {
		return 0, err
	}
	defer t.Close()
	return io.Copy(os.Stdout, t)
}
<commit_msg>Clean up usage formatting.<commit_after>// Copyright (c) 2014 Brian Hetro <whee@smaertness.net>
// Use of this source code is governed by the ISC
// license which can be found in the LICENSE file.

package main

import (
	"fmt"
	"io"
	"os"

	docopt "github.com/docopt/docopt-go"
	"github.com/whee/rp"
)

type config struct {
	channels    []string
	read        bool
	write       bool
	passthrough bool
	rpConfig    rp.Config
}

func main() {
	usage := `Redis Pipe.

Usage:
	rp      read  <name>...
	rp [-p] write <name>...
	rp      -n <network> -a <address> read  <name>...
	rp [-p] -n <network> -a <address> write <name>...

Options:
	-p, --passthrough  Pass written data to standard output.
	-n, --network <network>  Network of the Redis server [default: tcp]
	-a, --address <address>  Address of the Redis server [default: :6379]`

	arguments, err := docopt.Parse(usage, nil, true, "Redis Pipe 0.9", false)
	if err != nil {
		panic(err)
	}
	conf := config{
		channels:    arguments["<name>"].([]string),
		read:        arguments["read"].(bool),
		write:       arguments["write"].(bool),
		passthrough: arguments["--passthrough"].(bool),
		rpConfig: rp.Config{
			Network: arguments["--network"].(string),
			Address: arguments["--address"].(string)},
	}
	_, err = conf.do()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	os.Exit(0)
}

func (c *config) do() (n int64, err error) {
	if c.write {
		n, err = writeTo(c)
	} else if c.read {
		n, err = readFrom(c)
	}
	return
}

type ptWriter struct {
	w io.Writer
}

func (w ptWriter) Write(p []byte) (n int, err error) {
	n, err = os.Stdout.Write(p)
	if err != nil {
		return
	}
	return w.w.Write(p)
}

func writeTo(c *config) (int64, error) {
	t, err := rp.NewWriter(&c.rpConfig, c.channels...)
	if err != nil {
		return 0, err
	}
	defer t.Close()

	var w io.Writer
	if c.passthrough {
		w = ptWriter{t}
	} else {
		w = t
	}
	return io.Copy(w, os.Stdin)
}

func readFrom(c *config) (int64, error) {
	t, err := rp.NewReader(&c.rpConfig, c.channels...)
	if err != nil {
		return 0, err
	}
	defer t.Close()
	return io.Copy(os.Stdout, t)
}
<|endoftext|>


<commit_before>package run

import (
	"fmt"
	"github.com/ohsu-comp-bio/funnel/cmd/client"
	"github.com/ohsu-comp-bio/funnel/logger"
	"github.com/ohsu-comp-bio/funnel/proto/tes"
	"github.com/spf13/cobra"
	"os"
)

var log = logger.New("run")

// TODO figure out a nice default for name
var name = "Funnel Run Task"
var workdir = "/opt/funnel"
var server = "http://localhost:8000"
var image, project, description, stdin, stdout, stderr string
var printTask, preemptible, wait bool
var cliInputs, cliInputDirs, cliOutputs, cliOutputDirs, cliEnvVars, tags, volumes []string

// TODO validate resources
var cpu int
var ram float64
var disk float64
var zones []string

// TODO with input contents, script could be loaded from file
// TODO load vars from file
// TODO is there a case for glob inputs?

var example = `
    funnel run
    'bowtie2 -f $factor -x $other -p1 $pair1 -p2 $pair2 -o $alignments'
    --image opengenomics/bowtie2:latest
    --name 'Bowtie2 test'
    --description 'Testings an example of using 'funnel run' for a bowtie2 command'
    --in pair1=file://~/pair1.fastq
    --in pair2=file://~/pair2.fastq
    --out alignments=gs://bkt/bowtie/alignments.bam
    --env factor=5
    --vol /tmp
    --vol /opt
    --cpu 8
    --ram 32
    --disk 100
`

// Cmd represents the run command
var Cmd = &cobra.Command{
	Use:     "run [flags] --image IMAGE CMD",
	Short:   "Run a task.\n",
	Long:    ``,
	Example: example,
	Run:     run,
}

func init() {
	f := Cmd.Flags()
	f.StringVarP(&name, "name", "n", name, "Task name")
	f.StringVar(&description, "description", description, "Task description")
	f.StringVar(&project, "project", project, "Project")
	f.StringVarP(&workdir, "workdir", "w", workdir, "Set the containter working directory")
	f.StringVarP(&image, "container", "c", image, "Specify the containter image")
	f.StringSliceVarP(&cliInputs, "in", "i", cliInputs, "A key-value map of input files")
	f.StringSliceVarP(&cliInputDirs, "in-dir", "I", cliInputDirs, "A key-value map of input directories")
	f.StringSliceVarP(&cliOutputs, "out", "o", cliOutputs, "A key-value map of output files")
	f.StringSliceVarP(&cliOutputDirs, "out-dir", "O", cliOutputDirs, "A key-value map of output directories")
	f.StringSliceVarP(&cliEnvVars, "env", "e", cliEnvVars, "A key-value map of enviromental variables")
	f.StringVar(&stdin, "stdin", stdin, "File to pass via stdin to the command")
	f.StringVar(&stdout, "stdout", stdout, "File to write the stdout of the command")
	f.StringVar(&stderr, "stderr", stderr, "File to write the stderr of the command")
	f.StringSliceVar(&volumes, "vol", volumes, "Volumes to be defined on the container")
	f.StringSliceVar(&tags, "tag", tags, "A key-value map of arbitrary tags")
	f.IntVar(&cpu, "cpu", cpu, "Number of CPUs requested")
	f.Float64Var(&ram, "ram", ram, "Amount of RAM requested (in GB)")
	f.Float64Var(&disk, "disk", disk, "Amount of disk space requested (in GB)")
	f.BoolVar(&preemptible, "preemptible", preemptible, "Allow task to be scheduled on preemptible workers")
	f.StringSliceVar(&zones, "zone", zones, "Require task be scheduled in certain zones")
	// TODO
	//f.StringVar(&varsFile, "vars-file", varsFile, "Read vars from file")
	//f.StringVar(&cmdFile, "cmd-file", cmdFile, "Read cmd template from file")
	f.StringVarP(&server, "server", "S", server, "Address of Funnel server")
	f.BoolVarP(&printTask, "print", "p", printTask, "Print the task, instead of running it")
	f.BoolVar(&wait, "wait", wait, "Wait for the task to finish before exiting")
}

func run(cmd *cobra.Command, args []string) {

	if len(args) < 1 {
		fmt.Printf("ERROR - You must specify a command to run\n\n")
		cmd.Usage()
		os.Exit(2)
		return
	}

	if len(args) > 1 {
		fmt.Printf("ERROR - extra arg(s) %s\n\n", args[1:])
		fmt.Printf("--in, --out and --env args should have the form 'KEY=VALUE' not 'KEY VALUE'\n\n")
		cmd.Usage()
		os.Exit(2)
		return
	}

	if image == "" {
		fmt.Printf("ERROR - You must specify an image to run your command in\n\n")
		cmd.Usage()
		os.Exit(2)
		return
	}

	// TODO validate cmd
	rawcmd := args[0]
	executorCmd := []string{"bash", "-c", rawcmd}

	// Get template variables from the command line.
	// TODO variables file.
	inputFileMap, err := parseCliVars(cliInputs)
	checkErr(err)
	inputDirMap, err := parseCliVars(cliInputDirs)
	checkErr(err)
	outputFileMap, err := parseCliVars(cliOutputs)
	checkErr(err)
	outputDirMap, err := parseCliVars(cliOutputDirs)
	checkErr(err)
	envVarMap, err := parseCliVars(cliEnvVars)
	checkErr(err)
	tagsMap, err := parseCliVars(tags)
	checkErr(err)

	// check for key collisions
	err = compareKeys(inputFileMap, inputDirMap, outputFileMap, outputDirMap, envVarMap)
	checkErr(err)

	// Create map of enviromental variables to be passed to the executor
	inputEnvVars, err := fileMapToEnvVars(inputFileMap, "/opt/funnel/inputs/")
	checkErr(err)
	inputDirEnvVars, err := fileMapToEnvVars(inputDirMap, "/opt/funnel/inputs/")
	checkErr(err)
	outputEnvVars, err := fileMapToEnvVars(outputFileMap, "/opt/funnel/outputs/")
	checkErr(err)
	outputDirEnvVars, err := fileMapToEnvVars(outputDirMap, "/opt/funnel/outputs/")
	checkErr(err)
	environ, err := mergeVars(inputEnvVars, inputDirEnvVars, outputEnvVars, outputDirEnvVars, envVarMap)
	checkErr(err)

	// Build task input parameters
	inputs, err := createTaskParams(inputFileMap, "/opt/funnel/inputs/", tes.FileType_FILE)
	checkErr(err)
	inputDirs, err := createTaskParams(inputDirMap, "/opt/funnel/inputs/", tes.FileType_DIRECTORY)
	checkErr(err)
	inputs = append(inputs, inputDirs...)

	// Build task output parameters
	outputs, err := createTaskParams(outputFileMap, "/opt/funnel/outputs/", tes.FileType_FILE)
	checkErr(err)
	outputDirs, err := createTaskParams(outputDirMap, "/opt/funnel/outputs/", tes.FileType_DIRECTORY)
	checkErr(err)
	outputs = append(outputs, outputDirs...)

	stdinPath := ""
	if stdin != "" {
		stdinPath = stdin
	}

	// Build the task message
	task := &tes.Task{
		Name:        name,
		Project:     project,
		Description: description,
		Inputs:      inputs,
		Outputs:     outputs,
		Resources: &tes.Resources{
			CpuCores:    uint32(cpu),
			RamGb:       ram,
			SizeGb:      disk,
			Zones:       zones,
			Preemptible: preemptible,
		},
		Executors: []*tes.Executor{
			{
				ImageName: image,
				Cmd:       executorCmd,
				Environ:   environ,
				Workdir:   workdir,
				Stdin:     stdinPath,
				Stdout:    "/opt/funnel/outputs/stdout",
				Stderr:    "/opt/funnel/outputs/stderr",
				// TODO no ports
				Ports: nil,
			},
		},
		Volumes: volumes,
		Tags:    tagsMap,
	}

	cli := client.NewClient(server)
	// Marshal message to JSON
	taskJSON, merr := cli.Marshaler.MarshalToString(task)
	checkErr(merr)

	if printTask {
		fmt.Println(taskJSON)
		return
	}

	resp, rerr := cli.CreateTask([]byte(taskJSON))
	checkErr(rerr)

	taskID := resp.Id
	fmt.Println(taskID)

	if wait {
		werr := cli.WaitForTask(taskID)
		checkErr(werr)
	}
}

func checkErr(err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(3)
	}
}
<commit_msg>fix usage docs<commit_after>package run

import (
	"fmt"
	"github.com/ohsu-comp-bio/funnel/cmd/client"
	"github.com/ohsu-comp-bio/funnel/logger"
	"github.com/ohsu-comp-bio/funnel/proto/tes"
	"github.com/spf13/cobra"
	"os"
)

var log = logger.New("run")

// TODO figure out a nice default for name
var name = "Funnel Run Task"
var workdir = "/opt/funnel"
var server = "http://localhost:8000"
var image, project, description, stdin, stdout, stderr string
var printTask, preemptible, wait bool
var cliInputs, cliInputDirs, cliOutputs, cliOutputDirs, cliEnvVars, tags, volumes []string

// TODO validate resources
var cpu int
var ram float64
var disk float64
var zones []string

// TODO with input contents, script could be loaded from file
// TODO load vars from file
// TODO is there a case for glob inputs?

var example = `
    funnel run
    'bowtie2 -f $factor -x $other -p1 $pair1 -p2 $pair2 -o $alignments'
    --container opengenomics/bowtie2:latest
    --name 'Bowtie2 test'
    --description 'Testings an example of using 'funnel run' for a bowtie2 command'
    --in pair1=file://~/pair1.fastq
    --in pair2=file://~/pair2.fastq
    --out alignments=gs://bkt/bowtie/alignments.bam
    --env factor=5
    --vol /tmp
    --vol /opt
    --cpu 8
    --ram 32
    --disk 100
`

// Cmd represents the run command
var Cmd = &cobra.Command{
	Use:     "run [flags] --container IMAGE CMD",
	Short:   "Run a task.\n",
	Long:    ``,
	Example: example,
	Run:     run,
}

func init() {
	f := Cmd.Flags()
	f.StringVarP(&name, "name", "n", name, "Task name")
	f.StringVar(&description, "description", description, "Task description")
	f.StringVar(&project, "project", project, "Project")
	f.StringVarP(&workdir, "workdir", "w", workdir, "Set the containter working directory")
	f.StringVarP(&image, "container", "c", image, "Specify the containter image")
	f.StringSliceVarP(&cliInputs, "in", "i", cliInputs, "A key-value map of input files")
	f.StringSliceVarP(&cliInputDirs, "in-dir", "I", cliInputDirs, "A key-value map of input directories")
	f.StringSliceVarP(&cliOutputs, "out", "o", cliOutputs, "A key-value map of output files")
	f.StringSliceVarP(&cliOutputDirs, "out-dir", "O", cliOutputDirs, "A key-value map of output directories")
	f.StringSliceVarP(&cliEnvVars, "env", "e", cliEnvVars, "A key-value map of enviromental variables")
	f.StringVar(&stdin, "stdin", stdin, "File to pass via stdin to the command")
	f.StringVar(&stdout, "stdout", stdout, "File to write the stdout of the command")
	f.StringVar(&stderr, "stderr", stderr, "File to write the stderr of the command")
	f.StringSliceVar(&volumes, "vol", volumes, "Volumes to be defined on the container")
	f.StringSliceVar(&tags, "tag", tags, "A key-value map of arbitrary tags")
	f.IntVar(&cpu, "cpu", cpu, "Number of CPUs requested")
	f.Float64Var(&ram, "ram", ram, "Amount of RAM requested (in GB)")
	f.Float64Var(&disk, "disk", disk, "Amount of disk space requested (in GB)")
	f.BoolVar(&preemptible, "preemptible", preemptible, "Allow task to be scheduled on preemptible workers")
	f.StringSliceVar(&zones, "zone", zones, "Require task be scheduled in certain zones")
	// TODO
	//f.StringVar(&varsFile, "vars-file", varsFile, "Read vars from file")
	//f.StringVar(&cmdFile, "cmd-file", cmdFile, "Read cmd template from file")
	f.StringVarP(&server, "server", "S", server, "Address of Funnel server")
	f.BoolVarP(&printTask, "print", "p", printTask, "Print the task, instead of running it")
	f.BoolVar(&wait, "wait", wait, "Wait for the task to finish before exiting")
}

func run(cmd *cobra.Command, args []string) {

	if len(args) < 1 {
		fmt.Printf("ERROR - You must specify a command to run\n\n")
		cmd.Usage()
		os.Exit(2)
		return
	}

	if len(args) > 1 {
		fmt.Printf("ERROR - extra arg(s) %s\n\n", args[1:])
		fmt.Printf("--in, --out and --env args should have the form 'KEY=VALUE' not 'KEY VALUE'\n\n")
		cmd.Usage()
		os.Exit(2)
		return
	}

	if image == "" {
		fmt.Printf("ERROR - You must specify an image to run your command in\n\n")
		cmd.Usage()
		os.Exit(2)
		return
	}

	// TODO validate cmd
	rawcmd := args[0]
	executorCmd := []string{"bash", "-c", rawcmd}

	// Get template variables from the command line.
	// TODO variables file.
	inputFileMap, err := parseCliVars(cliInputs)
	checkErr(err)
	inputDirMap, err := parseCliVars(cliInputDirs)
	checkErr(err)
	outputFileMap, err := parseCliVars(cliOutputs)
	checkErr(err)
	outputDirMap, err := parseCliVars(cliOutputDirs)
	checkErr(err)
	envVarMap, err := parseCliVars(cliEnvVars)
	checkErr(err)
	tagsMap, err := parseCliVars(tags)
	checkErr(err)

	// check for key collisions
	err = compareKeys(inputFileMap, inputDirMap, outputFileMap, outputDirMap, envVarMap)
	checkErr(err)

	// Create map of enviromental variables to be passed to the executor
	inputEnvVars, err := fileMapToEnvVars(inputFileMap, "/opt/funnel/inputs/")
	checkErr(err)
	inputDirEnvVars, err := fileMapToEnvVars(inputDirMap, "/opt/funnel/inputs/")
	checkErr(err)
	outputEnvVars, err := fileMapToEnvVars(outputFileMap, "/opt/funnel/outputs/")
	checkErr(err)
	outputDirEnvVars, err := fileMapToEnvVars(outputDirMap, "/opt/funnel/outputs/")
	checkErr(err)
	environ, err := mergeVars(inputEnvVars, inputDirEnvVars, outputEnvVars, outputDirEnvVars, envVarMap)
	checkErr(err)

	// Build task input parameters
	inputs, err := createTaskParams(inputFileMap, "/opt/funnel/inputs/", tes.FileType_FILE)
	checkErr(err)
	inputDirs, err := createTaskParams(inputDirMap, "/opt/funnel/inputs/", tes.FileType_DIRECTORY)
	checkErr(err)
	inputs = append(inputs, inputDirs...)

	// Build task output parameters
	outputs, err := createTaskParams(outputFileMap, "/opt/funnel/outputs/", tes.FileType_FILE)
	checkErr(err)
	outputDirs, err := createTaskParams(outputDirMap, "/opt/funnel/outputs/", tes.FileType_DIRECTORY)
	checkErr(err)
	outputs = append(outputs, outputDirs...)

	stdinPath := ""
	if stdin != "" {
		stdinPath = stdin
	}

	// Build the task message
	task := &tes.Task{
		Name:        name,
		Project:     project,
		Description: description,
		Inputs:      inputs,
		Outputs:     outputs,
		Resources: &tes.Resources{
			CpuCores:    uint32(cpu),
			RamGb:       ram,
			SizeGb:      disk,
			Zones:       zones,
			Preemptible: preemptible,
		},
		Executors: []*tes.Executor{
			{
				ImageName: image,
				Cmd:       executorCmd,
				Environ:   environ,
				Workdir:   workdir,
				Stdin:     stdinPath,
				Stdout:    "/opt/funnel/outputs/stdout",
				Stderr:    "/opt/funnel/outputs/stderr",
				// TODO no ports
				Ports: nil,
			},
		},
		Volumes: volumes,
		Tags:    tagsMap,
	}

	cli := client.NewClient(server)
	// Marshal message to JSON
	taskJSON, merr := cli.Marshaler.MarshalToString(task)
	checkErr(merr)

	if printTask {
		fmt.Println(taskJSON)
		return
	}

	resp, rerr := cli.CreateTask([]byte(taskJSON))
	checkErr(rerr)

	taskID := resp.Id
	fmt.Println(taskID)

	if wait {
		werr := cli.WaitForTask(taskID)
		checkErr(werr)
	}
}

func checkErr(err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(3)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Boise State University All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"flag"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"sync/atomic"
	"time"
)

var (
	insecure  bool
	tmpnbid   string
	tmpnbhost string
	tick      time.Duration
	phonehome uint32
)

func checkin() {
	ticker := time.NewTicker(tick)
	for {
		select {
		case <-ticker.C:
			if !atomic.CompareAndSwapUint32(&phonehome, 1, 0) {
				continue
			}
			u := url.URL{
				Host: tmpnbhost,
				Path: path.Join("/", "book", tmpnbid),
			}
			if insecure {
				u.Scheme = "http"
			} else {
				u.Scheme = "https"
			}
			log.Printf("checking in to %s at %s", u.String(), time.Now())
			ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
			req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
			if err != nil {
				log.Print(err)
				continue
			}
			resp, err := http.DefaultClient.Do(req)
			if err != nil {
				log.Print(err)
				continue
			}
			cancel()
			resp.Body.Close()
			if resp.StatusCode != 200 {
				log.Print(resp.Status)
			}
		}
	}
}

type writer struct{}

func (w writer) Write(p []byte) (int, error) {
	n, err := os.Stdout.Write(p)
	if n > 0 {
		atomic.CompareAndSwapUint32(&phonehome, 0, 1)
	}
	return n, err
}

func main() {
	flag.BoolVar(&insecure, "insecure", false, "use http instead of https")
	flagLog := flag.String("log", "", "log file for verbose output")
	flag.DurationVar(&tick, "tick", time.Minute, "time between check in")
	flag.Parse()
	var (
		fout io.WriteCloser
		err  error
	)
	switch *flagLog {
	case "":
		fout, _ = os.Create(os.DevNull)
	default:
		fout, err = os.Create(*flagLog)
		if err != nil {
			log.Fatal(err)
		}
	}
	defer fout.Close()

	tmpnbid = os.Getenv("TMPNB_ID")
	tmpnbhost = os.Getenv("TMPNB_HOST")
	shell := os.Getenv("SHELL")
	if tmpnbid == "" || tmpnbhost == "" {
		log.Fatalf("TMPNB_ID or TMPNB_HOST not set, just use %s, not tmpnbsh", shell)
	}

	if shell == "" {
		shell = "bash"
	}

	log.SetOutput(fout)
	go checkin()

	sh := exec.Command(shell, "-i")
	sh.Env = []string{
		"TMPNB_ID=" + tmpnbid,
		"TMPNB_HOST=" + tmpnbhost,
		"TERM=" + os.Getenv("TERM"),
	}
	var w writer
	sh.Stdin = os.Stdin
	sh.Stdout = w
	//sh.Stdout = os.Stdout
	sh.Stderr = os.Stderr
	err = sh.Run()
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>add appropriate cookie for sh<commit_after>// Copyright (c) 2017, Boise State University All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"flag"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"sync/atomic"
	"time"
)

var (
	insecure  bool
	tmpnbid   string
	tmpnbhost string
	tick      time.Duration
	phonehome uint32
)

func checkin() {
	ticker := time.NewTicker(tick)
	for {
		select {
		case <-ticker.C:
			if !atomic.CompareAndSwapUint32(&phonehome, 1, 0) {
				continue
			}
			u := url.URL{
				Host: tmpnbhost,
				Path: path.Join("/", "book", tmpnbid),
			}
			if insecure {
				u.Scheme = "http"
			} else {
				u.Scheme = "https"
			}
			log.Printf("checking in to %s at %s", u.String(), time.Now())
			ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
			req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
			if err != nil {
				log.Print(err)
				continue
			}
			req.AddCookie(&http.Cookie{"bsuJupyter", os.Getenv("TMPNB_SESSION")})
			resp, err := http.DefaultClient.Do(req)
			if err != nil {
				log.Print(err)
				continue
			}
			cancel()
			resp.Body.Close()
			if resp.StatusCode != 200 {
				log.Print(resp.Status)
			}
		}
	}
}

type writer struct{}

func (w writer) Write(p []byte) (int, error) {
	n, err := os.Stdout.Write(p)
	if n > 0 {
		atomic.CompareAndSwapUint32(&phonehome, 0, 1)
	}
	return n, err
}

func main() {
	flag.BoolVar(&insecure, "insecure", false, "use http instead of https")
	flagLog := flag.String("log", "", "log file for verbose output")
	flag.DurationVar(&tick, "tick", time.Minute, "time between check in")
	flag.Parse()
	var (
		fout io.WriteCloser
		err  error
	)
	switch *flagLog {
	case "":
		fout, _ = os.Create(os.DevNull)
	default:
		fout, err = os.Create(*flagLog)
		if err != nil {
			log.Fatal(err)
		}
	}
	defer fout.Close()

	tmpnbid = os.Getenv("TMPNB_ID")
	tmpnbhost = os.Getenv("TMPNB_HOST")
	shell := os.Getenv("SHELL")
	if tmpnbid == "" || tmpnbhost == "" {
		log.Fatalf("TMPNB_ID or TMPNB_HOST not set, just use %s, not tmpnbsh", shell)
	}

	if shell == "" {
		shell = "bash"
	}

	log.SetOutput(fout)
	go checkin()

	sh := exec.Command(shell, "-i")
	sh.Env = []string{
		"TMPNB_ID=" + tmpnbid,
		"TMPNB_HOST=" + tmpnbhost,
		"TERM=" + os.Getenv("TERM"),
	}
	var w writer
	sh.Stdin = os.Stdin
	sh.Stdout = w
	//sh.Stdout = os.Stdout
	sh.Stderr = os.Stderr
	err = sh.Run()
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Copyright © 2017 uMov.me Team <devteam-umovme@googlegroups.com>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package cmd

import (
	"fmt"

	"github.com/spf13/cobra"
)

const version = "0.7.1"

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Displays a current version",
	Long:  `This option is used to display a current version.`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Printf("dbview v%s\n", version)
	},
}

func init() {
	RootCmd.AddCommand(versionCmd)
}
<commit_msg>ajuste do version<commit_after>// Copyright © 2017 uMov.me Team <devteam-umovme@googlegroups.com>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package cmd

import (
	"fmt"

	"github.com/spf13/cobra"
)

const version = "0.8.0"

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Displays a current version",
	Long:  `This option is used to display a current version.`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Printf("dbview v%s\n", version)
	},
}

func init() {
	RootCmd.AddCommand(versionCmd)
}
<|endoftext|>


<commit_before>// colorstring provides functions for colorizing strings for terminal
// output.
package colorstring

import (
	"bytes"
	"fmt"
	"regexp"
)

// Color colorizes your strings using the default settings.
//
// Strings given to Color should use the syntax `[color]` to specify the
// color for text following. For example: `[blue]Hello` will return "Hello"
// in blue. See DefaultColors for all the supported colors and attributes.
//
// If an unrecognized color is given, it is ignored and assumed to be part
// of the string. For example: `[hi]world` will result in "[hi]world".
//
// A color reset is appended to the end of every string. This will reset
// the color of following strings when you output this text to the same
// terminal session.
//
// If you want to customize any of this behavior, use the Colorize struct.
func Color(v string) string {
	return def.Color(v)
}

// Colorize colorizes your strings, giving you the ability to customize
// some of the colorization process.
//
// The options in Colorize can be set to customize colorization. If you're
// only interested in the defaults, just use the top Color function directly,
// which creates a default Colorize.
type Colorize struct {
	// Colors maps a color string to the code for that color. The code
	// is a string so that you can use more complex colors to set foreground,
	// background, attributes, etc. For example, "boldblue" might be
	// "1;34"
	Colors map[string]string

	// If true, color attributes will be ignored. This is useful if you're
	// outputting to a location that doesn't support colors and you just
	// want the strings returned.
	Disable bool

	// Reset, if true, will reset the color after each colorization by
	// adding a reset code at the end.
	Reset bool
}

// Color colorizes a string according to the settings setup in the struct.
//
// For more details on the syntax, see the top-level Color function.
func (c *Colorize) Color(v string) string {
	matches := parseRe.FindAllStringIndex(v, -1)
	if len(matches) == 0 {
		return v
	}

	result := new(bytes.Buffer)
	if matches[0][0] > 0 {
		if _, err := result.WriteString(v[:matches[0][0]]); err != nil {
			panic(err)
		}
	}

	colored := false
	var m []int
	for _, nm := range matches {
		// Write the text in between this match and the last
		if len(m) > 0 {
			result.WriteString(v[m[1]:nm[0]])
		}
		m = nm

		// If we're disabled, just ignore the color code information
		if c.Disable {
			continue
		}

		var replace string
		if code, ok := c.Colors[v[m[0]+1:m[1]-1]]; ok {
			colored = true
			replace = fmt.Sprintf("\033[%sm", code)
		} else {
			replace = v[m[0]:m[1]]
		}

		result.WriteString(replace)
	}
	result.WriteString(v[m[1]:])

	if colored && c.Reset {
		// Write the clear byte at the end
		result.WriteString("\033[0m")
	}

	return result.String()
}

// DefaultColors are the default colors used when colorizing.
var DefaultColors map[string]string

func init() {
	DefaultColors = map[string]string{
		"blue": "34",
	}

	def = Colorize{
		Colors: DefaultColors,
		Reset:  true,
	}
}

var def Colorize
var parseRe = regexp.MustCompile(`(?i)\[[a-z0-9_-]+\]`)
<commit_msg>Fill out the default colors<commit_after>// colorstring provides functions for colorizing strings for terminal
// output.
package colorstring

import (
	"bytes"
	"fmt"
	"regexp"
)

// Color colorizes your strings using the default settings.
//
// Strings given to Color should use the syntax `[color]` to specify the
// color for text following. For example: `[blue]Hello` will return "Hello"
// in blue. See DefaultColors for all the supported colors and attributes.
//
// If an unrecognized color is given, it is ignored and assumed to be part
// of the string. For example: `[hi]world` will result in "[hi]world".
//
// A color reset is appended to the end of every string. This will reset
// the color of following strings when you output this text to the same
// terminal session.
//
// If you want to customize any of this behavior, use the Colorize struct.
func Color(v string) string {
	return def.Color(v)
}

// Colorize colorizes your strings, giving you the ability to customize
// some of the colorization process.
//
// The options in Colorize can be set to customize colorization. If you're
// only interested in the defaults, just use the top Color function directly,
// which creates a default Colorize.
type Colorize struct {
	// Colors maps a color string to the code for that color. The code
	// is a string so that you can use more complex colors to set foreground,
	// background, attributes, etc. For example, "boldblue" might be
	// "1;34"
	Colors map[string]string

	// If true, color attributes will be ignored. This is useful if you're
	// outputting to a location that doesn't support colors and you just
	// want the strings returned.
	Disable bool

	// Reset, if true, will reset the color after each colorization by
	// adding a reset code at the end.
	Reset bool
}

// Color colorizes a string according to the settings setup in the struct.
//
// For more details on the syntax, see the top-level Color function.
func (c *Colorize) Color(v string) string {
	matches := parseRe.FindAllStringIndex(v, -1)
	if len(matches) == 0 {
		return v
	}

	result := new(bytes.Buffer)
	if matches[0][0] > 0 {
		if _, err := result.WriteString(v[:matches[0][0]]); err != nil {
			panic(err)
		}
	}

	colored := false
	var m []int
	for _, nm := range matches {
		// Write the text in between this match and the last
		if len(m) > 0 {
			result.WriteString(v[m[1]:nm[0]])
		}
		m = nm

		// If we're disabled, just ignore the color code information
		if c.Disable {
			continue
		}

		var replace string
		if code, ok := c.Colors[v[m[0]+1:m[1]-1]]; ok {
			colored = true
			replace = fmt.Sprintf("\033[%sm", code)
		} else {
			replace = v[m[0]:m[1]]
		}

		result.WriteString(replace)
	}
	result.WriteString(v[m[1]:])

	if colored && c.Reset {
		// Write the clear byte at the end
		result.WriteString("\033[0m")
	}

	return result.String()
}

// DefaultColors are the default colors used when colorizing.
//
// If the color is surrounded in underscores, such as "_blue_", then that
// color will be used for the background color.
var DefaultColors map[string]string

func init() {
	DefaultColors = map[string]string{
		// Default foreground/background colors
		"default":   "39",
		"_default_": "49",

		// Foreground colors
		"black":         "30",
		"red":           "31",
		"green":         "32",
		"yellow":        "33",
		"blue":          "34",
		"magenta":       "35",
		"cyan":          "36",
		"light_gray":    "37",
		"dark_gray":     "90",
		"light_red":     "91",
		"light_green":   "92",
		"light_yellow":  "93",
		"light_blue":    "94",
		"light_magenta": "95",
		"light_cyan":    "96",
		"white":         "97",

		// Background colors
		"_black_":         "40",
		"_red_":           "41",
		"_green_":         "42",
		"_yellow_":        "43",
		"_blue_":          "44",
		"_magenta_":       "45",
		"_cyan_":          "46",
		"_light_gray_":    "47",
		"_dark_gray_":     "100",
		"_light_red_":     "101",
		"_light_green_":   "102",
		"_light_yellow_":  "103",
		"_light_blue_":    "104",
		"_light_magenta_": "105",
		"_light_cyan_":    "106",
		"_white_":         "107",

		// Attributes
		"bold":       "1",
		"dim":        "2",
		"underline":  "4",
		"blink_slow": "5",
		"blink_fast": "6",
		"invert":     "7",
		"hidden":     "8",
	}

	def = Colorize{
		Colors: DefaultColors,
		Reset:  true,
	}
}

var def Colorize
var parseRe = regexp.MustCompile(`(?i)\[[a-z0-9_-]+\]`)
<|endoftext|>


<commit_before>package command

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/jobspec"
	"github.com/hashicorp/nomad/nomad/structs"
)

var (
	// enforceIndexRegex is a regular expression which extracts the enforcement error
	enforceIndexRegex = regexp.MustCompile(`\((Enforcing job modify index.*)\)`)
)

type RunCommand struct {
	Meta

	// The fields below can be overwritten for tests
	testStdin io.Reader
}

func (c *RunCommand) Help() string {
	helpText := `
Usage: nomad run [options] <path>

  Starts running a new job or updates an existing job using
  the specification located at <path>. This is the main command
  used to interact with Nomad.

  If the supplied path is "-", the jobfile is read from stdin. Otherwise
  it is read from the file at the supplied path.

  Upon successful job submission, this command will immediately
  enter an interactive monitor. This is useful to watch Nomad's
  internals make scheduling decisions and place the submitted work
  onto nodes. The monitor will end once job placement is done. It
  is safe to exit the monitor early using ctrl+c.

  On successful job submission and scheduling, exit code 0 will be
  returned. If there are job placement issues encountered
  (unsatisfiable constraints, resource exhaustion, etc), then the
  exit code will be 2. Any other errors, including client connection
  issues or internal errors, are indicated by exit code 1.

  If the job has specified the region, the -region flag and NOMAD_REGION
  environment variable are overridden and the the job's region is used.

General Options:

  ` + generalOptionsUsage() + `

Run Options:

  -check-index
	If set, the job is only registered or updated if the the passed
	job modify index matches the server side version. If a check-index value of
	zero is passed, the job is only registered if it does not yet exist. If a
	non-zero value is passed, it ensures that the job is being updated from a
	known state. The use of this flag is most common in conjunction with plan
	command.

  -detach
	Return immediately instead of entering monitor mode. After job submission,
	the evaluation ID will be printed to the screen, which can be used to
	examine the evaluation using the eval-status command.

  -verbose
    Display full information.

  -output
    Output the JSON that would be submitted to the HTTP API without submitting
    the job.
`
	return strings.TrimSpace(helpText)
}

func (c *RunCommand) Synopsis() string {
	return "Run a new job or update an existing job"
}

func (c *RunCommand) Run(args []string) int {
	var detach, verbose, output bool
	var checkIndexStr string

	flags := c.Meta.FlagSet("run", FlagSetClient)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	flags.BoolVar(&detach, "detach", false, "")
	flags.BoolVar(&verbose, "verbose", false, "")
	flags.BoolVar(&output, "output", false, "")
	flags.StringVar(&checkIndexStr, "check-index", "", "")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	// Truncate the id unless full length is requested
	length := shortId
	if verbose {
		length = fullId
	}

	// Check that we got exactly one node
	args = flags.Args()
	if len(args) != 1 {
		c.Ui.Error(c.Help())
		return 1
	}

	// Read the Jobfile
	path := args[0]

	var f io.Reader
	switch path {
	case "-":
		if c.testStdin != nil {
			f = c.testStdin
		} else {
			f = os.Stdin
		}
	default:
		file, err := os.Open(path)
		defer file.Close()
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error opening file %q: %v", path, err))
			return 1
		}
		f = file
	}

	// Parse the JobFile
	job, err := jobspec.Parse(f)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error parsing job file %s: %v", f, err))
		return 1
	}

	// Initialize any fields that need to be.
	job.Canonicalize()

	// Check that the job is valid
	if err := job.Validate(); err != nil {
		c.Ui.Error(fmt.Sprintf("Error validating job: %v", err))
		return 1
	}

	// Check if the job is periodic.
	periodic := job.IsPeriodic()

	// Convert it to something we can use
	apiJob, err := convertStructJob(job)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error converting job: %s", err))
		return 1
	}

	if output {
		req := api.RegisterJobRequest{Job: apiJob}
		buf, err := json.MarshalIndent(req, "", "    ")
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error converting job: %s", err))
			return 1
		}

		c.Ui.Output(string(buf))
		return 0
	}

	// Get the HTTP client
	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	// Force the region to be that of the job.
	if r := job.Region; r != "" {
		client.SetRegion(r)
	}

	// Parse the check-index
	checkIndex, enforce, err := parseCheckIndex(checkIndexStr)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error parsing check-index value %q: %v", checkIndexStr, err))
		return 1
	}

	// Submit the job
	var evalID string
	if enforce {
		evalID, _, err = client.Jobs().EnforceRegister(apiJob, checkIndex, nil)
	} else {
		evalID, _, err = client.Jobs().Register(apiJob, nil)
	}
	if err != nil {
		if strings.Contains(err.Error(), api.RegisterEnforceIndexErrPrefix) {
			// Format the error specially if the error is due to index
			// enforcement
			matches := enforceIndexRegex.FindStringSubmatch(err.Error())
			if len(matches) == 2 {
				c.Ui.Error(matches[1]) // The matched group
				c.Ui.Error("Job not updated")
				return 1
			}
		}

		c.Ui.Error(fmt.Sprintf("Error submitting job: %s", err))
		return 1
	}

	// Check if we should enter monitor mode
	if detach || periodic {
		c.Ui.Output("Job registration successful")
		if periodic {
			now := time.Now().UTC()
			next := job.Periodic.Next(now)
			c.Ui.Output(fmt.Sprintf("Approximate next launch time: %s (%s from now)",
				formatTime(next), formatTimeDifference(now, next, time.Second)))
		} else {
			c.Ui.Output("Evaluation ID: " + evalID)
		}

		return 0
	}

	// Detach was not specified, so start monitoring
	mon := newMonitor(c.Ui, client, length)
	return mon.monitor(evalID, false)

}

// parseCheckIndex parses the check-index flag and returns the index, whether it
// was set and potentially an error during parsing.
func parseCheckIndex(input string) (uint64, bool, error) {
	if input == "" {
		return 0, false, nil
	}

	u, err := strconv.ParseUint(input, 10, 64)
	return u, true, err
}

// convertStructJob is used to take a *structs.Job and convert it to an *api.Job.
// This function is just a hammer and probably needs to be revisited.
func convertStructJob(in *structs.Job) (*api.Job, error) {
	gob.Register([]map[string]interface{}{})
	gob.Register([]interface{}{})
	var apiJob *api.Job
	buf := new(bytes.Buffer)
	if err := gob.NewEncoder(buf).Encode(in); err != nil {
		return nil, err
	}
	if err := gob.NewDecoder(buf).Decode(&apiJob); err != nil {
		return nil, err
	}
	return apiJob, nil
}
<commit_msg>Fix nomad run parse error<commit_after>package command

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/jobspec"
	"github.com/hashicorp/nomad/nomad/structs"
)

var (
	// enforceIndexRegex is a regular expression which extracts the enforcement error
	enforceIndexRegex = regexp.MustCompile(`\((Enforcing job modify index.*)\)`)
)

type RunCommand struct {
	Meta

	// The fields below can be overwritten for tests
	testStdin io.Reader
}

func (c *RunCommand) Help() string {
	helpText := `
Usage: nomad run [options] <path>

  Starts running a new job or updates an existing job using
  the specification located at <path>. This is the main command
  used to interact with Nomad.

  If the supplied path is "-", the jobfile is read from stdin. Otherwise
  it is read from the file at the supplied path.

  Upon successful job submission, this command will immediately
  enter an interactive monitor. This is useful to watch Nomad's
  internals make scheduling decisions and place the submitted work
  onto nodes. The monitor will end once job placement is done. It
  is safe to exit the monitor early using ctrl+c.

  On successful job submission and scheduling, exit code 0 will be
  returned. If there are job placement issues encountered
  (unsatisfiable constraints, resource exhaustion, etc), then the
  exit code will be 2. Any other errors, including client connection
  issues or internal errors, are indicated by exit code 1.

  If the job has specified the region, the -region flag and NOMAD_REGION
  environment variable are overridden and the the job's region is used.

General Options:

  ` + generalOptionsUsage() + `

Run Options:

  -check-index
	If set, the job is only registered or updated if the the passed
	job modify index matches the server side version. If a check-index value of
	zero is passed, the job is only registered if it does not yet exist. If a
	non-zero value is passed, it ensures that the job is being updated from a
	known state. The use of this flag is most common in conjunction with plan
	command.

  -detach
	Return immediately instead of entering monitor mode. After job submission,
	the evaluation ID will be printed to the screen, which can be used to
	examine the evaluation using the eval-status command.

  -verbose
    Display full information.

  -output
    Output the JSON that would be submitted to the HTTP API without submitting
    the job.
`
	return strings.TrimSpace(helpText)
}

func (c *RunCommand) Synopsis() string {
	return "Run a new job or update an existing job"
}

func (c *RunCommand) Run(args []string) int {
	var detach, verbose, output bool
	var checkIndexStr string

	flags := c.Meta.FlagSet("run", FlagSetClient)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	flags.BoolVar(&detach, "detach", false, "")
	flags.BoolVar(&verbose, "verbose", false, "")
	flags.BoolVar(&output, "output", false, "")
	flags.StringVar(&checkIndexStr, "check-index", "", "")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	// Truncate the id unless full length is requested
	length := shortId
	if verbose {
		length = fullId
	}

	// Check that we got exactly one argument
	args = flags.Args()
	if len(args) != 1 {
		c.Ui.Error(c.Help())
		return 1
	}

	// Read the Jobfile
	path := args[0]

	var f io.Reader
	switch path {
	case "-":
		if c.testStdin != nil {
			f = c.testStdin
		} else {
			f = os.Stdin
		}
	default:
		file, err := os.Open(path)
		defer file.Close()
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error opening file %q: %v", path, err))
			return 1
		}
		f = file
	}

	// Parse the JobFile
	job, err := jobspec.Parse(f)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error parsing job file %s: %v", path, err))
		return 1
	}

	// Initialize any fields that need to be.
	job.Canonicalize()

	// Check that the job is valid
	if err := job.Validate(); err != nil {
		c.Ui.Error(fmt.Sprintf("Error validating job: %v", err))
		return 1
	}

	// Check if the job is periodic.
	periodic := job.IsPeriodic()

	// Convert it to something we can use
	apiJob, err := convertStructJob(job)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error converting job: %s", err))
		return 1
	}

	if output {
		req := api.RegisterJobRequest{Job: apiJob}
		buf, err := json.MarshalIndent(req, "", "    ")
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error converting job: %s", err))
			return 1
		}

		c.Ui.Output(string(buf))
		return 0
	}

	// Get the HTTP client
	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	// Force the region to be that of the job.
	if r := job.Region; r != "" {
		client.SetRegion(r)
	}

	// Parse the check-index
	checkIndex, enforce, err := parseCheckIndex(checkIndexStr)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error parsing check-index value %q: %v", checkIndexStr, err))
		return 1
	}

	// Submit the job
	var evalID string
	if enforce {
		evalID, _, err = client.Jobs().EnforceRegister(apiJob, checkIndex, nil)
	} else {
		evalID, _, err = client.Jobs().Register(apiJob, nil)
	}
	if err != nil {
		if strings.Contains(err.Error(), api.RegisterEnforceIndexErrPrefix) {
			// Format the error specially if the error is due to index
			// enforcement
			matches := enforceIndexRegex.FindStringSubmatch(err.Error())
			if len(matches) == 2 {
				c.Ui.Error(matches[1]) // The matched group
				c.Ui.Error("Job not updated")
				return 1
			}
		}

		c.Ui.Error(fmt.Sprintf("Error submitting job: %s", err))
		return 1
	}

	// Check if we should enter monitor mode
	if detach || periodic {
		c.Ui.Output("Job registration successful")
		if periodic {
			now := time.Now().UTC()
			next := job.Periodic.Next(now)
			c.Ui.Output(fmt.Sprintf("Approximate next launch time: %s (%s from now)",
				formatTime(next), formatTimeDifference(now, next, time.Second)))
		} else {
			c.Ui.Output("Evaluation ID: " + evalID)
		}

		return 0
	}

	// Detach was not specified, so start monitoring
	mon := newMonitor(c.Ui, client, length)
	return mon.monitor(evalID, false)

}

// parseCheckIndex parses the check-index flag and returns the index, whether it
// was set and potentially an error during parsing.
func parseCheckIndex(input string) (uint64, bool, error) {
	if input == "" {
		return 0, false, nil
	}

	u, err := strconv.ParseUint(input, 10, 64)
	return u, true, err
}

// convertStructJob is used to take a *structs.Job and convert it to an *api.Job.
// This function is just a hammer and probably needs to be revisited.
func convertStructJob(in *structs.Job) (*api.Job, error) {
	gob.Register([]map[string]interface{}{})
	gob.Register([]interface{}{})
	var apiJob *api.Job
	buf := new(bytes.Buffer)
	if err := gob.NewEncoder(buf).Encode(in); err != nil {
		return nil, err
	}
	if err := gob.NewDecoder(buf).Decode(&apiJob); err != nil {
		return nil, err
	}
	return apiJob, nil
}
<|endoftext|>


<commit_before>package common

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"log"
	"math/big"
	"time"
)

func GenerateClientCert(userName string, daysValid int) (_ []byte, err error) {
	pageName := "Add user:generateClientCert()"

	// Use a template approach, similar to:
	//   https://github.com/driskell/log-courier/blob/master/lc-tlscert/lc-tlscert.go
	nowTime := time.Now()
	newCert := x509.Certificate{
		Subject: pkix.Name{
			Organization: []string{"DB Browser for SQLite"},
			CommonName:   fmt.Sprintf("%s : db4s.dbhub.io", userName),
		},
		BasicConstraintsValid: true,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
		IsCA:                  false,
		NotAfter:              nowTime.AddDate(0, 0, daysValid),
		NotBefore:             nowTime,
	}

	// TODO: Check this serial number isn't already in use, just to be safe
	serialNumLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	newCert.SerialNumber, err = rand.Int(rand.Reader, serialNumLimit)
	if err != nil {
		log.Printf("%s: Error when generating serial number: %v\n", pageName, err)
		return nil, err
	}

	// Load the certificate used for signing (the intermediate certificate)
	certFile, err := ioutil.ReadFile(SigningCert())
	if err != nil {
		log.Printf("%s: Error opening intermediate certificate file: %v\n", pageName, err)
		return
	}
	certPEM, _ := pem.Decode(certFile)
	if certPEM == nil {
		log.Printf("%s: Error when PEM decoding the intermediate certificate file\n", pageName)
		return
	}
	intCert, err := x509.ParseCertificate(certPEM.Bytes)
	if err != nil {
		log.Printf("%s: Error when parsing decoded intermediate certificate data: %v\n", pageName, err)
		return
	}

	// Load the private key for the intermediate certificate
	intKeyFile, err := ioutil.ReadFile(SigningCertKey())
	if err != nil {
		log.Printf("%s: Error opening intermediate certificate key: %v\n", pageName, err)
		return
	}
	intKeyPEM, _ := pem.Decode(intKeyFile)
	if certPEM == nil {
		log.Printf("%s: Error when PEM decoding the intermediate key file\n", pageName)
		return
	}
	intKey, err := x509.ParsePKCS1PrivateKey(intKeyPEM.Bytes)
	if err != nil {
		log.Printf("%s: Error when parsing intermediate certificate key: %v\n", pageName, err)
		return
	}

	// Generate a public key to sign the new certificate with
	clientKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		log.Printf("%s: Failed to public key for signing: %v\n", pageName, err)
		return
	}

	// Generate the new certificate
	clientCert, err := x509.CreateCertificate(rand.Reader, &newCert, intCert, &clientKey.PublicKey, intKey)
	if err != nil {
		log.Printf("%s: Failed to create certificate: %v\n", pageName, err)
		return
	}

	// Convert the new certificate into PEM format
	buf := &bytes.Buffer{}
	err = pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: clientCert})
	if err != nil {
		log.Printf("%s: Failed to PEM encode certificate: %v\n", pageName, err)
		return
	}

	// Convert the private key for the certificate into PEM format
	buf2 := &bytes.Buffer{}
	err = pem.Encode(buf2, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(clientKey)})
	if err != nil {
		log.Printf("%s: Failed to PEM encode private key: %v\n", pageName, err)
		return
	}

	// Concatenate the newly generated certificate and its key
	_, err = buf.ReadFrom(buf2)
	if err != nil {
		log.Printf("%s: Failed to concatenate the PEM blocks: %v\n", pageName, err)
		return
	}

	log.Printf("New client cert generated for user '%s'", userName)

	return buf.Bytes(), nil
}
<commit_msg>Update Common Name (CN) information for client certificates<commit_after>package common

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"log"
	"math/big"
	"time"
)

func GenerateClientCert(userName string, daysValid int) (_ []byte, err error) {
	pageName := "Add user:generateClientCert()"

	// Use a template approach, similar to:
	//   https://github.com/driskell/log-courier/blob/master/lc-tlscert/lc-tlscert.go
	nowTime := time.Now()
	emailAddress := fmt.Sprintf("%s@db4s.dbhub.io", userName)
	oidEmailAddress := asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}
	newCert := x509.Certificate{
		Subject: pkix.Name{
			Organization: []string{"DB Browser for SQLite"},
			CommonName:   "db4s.dbhub.io",
			ExtraNames:   []pkix.AttributeTypeAndValue{{Type: oidEmailAddress, Value: emailAddress}},
		},
		BasicConstraintsValid: true,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
		IsCA:                  false,
		NotAfter:              nowTime.AddDate(0, 0, daysValid),
		NotBefore:             nowTime,
	}

	// TODO: Check this serial number isn't already in use, just to be safe
	serialNumLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	newCert.SerialNumber, err = rand.Int(rand.Reader, serialNumLimit)
	if err != nil {
		log.Printf("%s: Error when generating serial number: %v\n", pageName, err)
		return nil, err
	}

	// Load the certificate used for signing (the intermediate certificate)
	certFile, err := ioutil.ReadFile(SigningCert())
	if err != nil {
		log.Printf("%s: Error opening intermediate certificate file: %v\n", pageName, err)
		return
	}
	certPEM, _ := pem.Decode(certFile)
	if certPEM == nil {
		log.Printf("%s: Error when PEM decoding the intermediate certificate file\n", pageName)
		return
	}
	intCert, err := x509.ParseCertificate(certPEM.Bytes)
	if err != nil {
		log.Printf("%s: Error when parsing decoded intermediate certificate data: %v\n", pageName, err)
		return
	}

	// Load the private key for the intermediate certificate
	intKeyFile, err := ioutil.ReadFile(SigningCertKey())
	if err != nil {
		log.Printf("%s: Error opening intermediate certificate key: %v\n", pageName, err)
		return
	}
	intKeyPEM, _ := pem.Decode(intKeyFile)
	if certPEM == nil {
		log.Printf("%s: Error when PEM decoding the intermediate key file\n", pageName)
		return
	}
	intKey, err := x509.ParsePKCS1PrivateKey(intKeyPEM.Bytes)
	if err != nil {
		log.Printf("%s: Error when parsing intermediate certificate key: %v\n", pageName, err)
		return
	}

	// Generate a public key to sign the new certificate with
	clientKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		log.Printf("%s: Failed to public key for signing: %v\n", pageName, err)
		return
	}

	// Generate the new certificate
	clientCert, err := x509.CreateCertificate(rand.Reader, &newCert, intCert, &clientKey.PublicKey, intKey)
	if err != nil {
		log.Printf("%s: Failed to create certificate: %v\n", pageName, err)
		return
	}

	// Convert the new certificate into PEM format
	buf := &bytes.Buffer{}
	err = pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: clientCert})
	if err != nil {
		log.Printf("%s: Failed to PEM encode certificate: %v\n", pageName, err)
		return
	}

	// Convert the private key for the certificate into PEM format
	buf2 := &bytes.Buffer{}
	err = pem.Encode(buf2, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(clientKey)})
	if err != nil {
		log.Printf("%s: Failed to PEM encode private key: %v\n", pageName, err)
		return
	}

	// Concatenate the newly generated certificate and its key
	_, err = buf.ReadFrom(buf2)
	if err != nil {
		log.Printf("%s: Failed to concatenate the PEM blocks: %v\n", pageName, err)
		return
	}

	log.Printf("New client cert generated for user '%s'", userName)

	return buf.Bytes(), nil
}
<|endoftext|>