<commit_before>// Copyright 2015 Florian Orben. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

//Package prettybenchmarks formats your go benchmarks into nice looking sorted tables
//
//Prettybenchmarks
//
//Works with and without -benchmem flag
//
//If you provide a time interval (either ns, µs (or us), ms, s), each benchmark's runtime will be
//converted to that interval. If left blank, a suitable value will automatically be chosen
//
//    go test -bench=YOUR_PKG [-benchmem] | pb [timeinterval]
//Example
//    go test -bench=. -benchmem | pb ms
//
package prettybenchmarks

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/apcera/termtables"
)

const (
	fmtInt     = "#,###."
	fmtFloat   = "#,###.###"
	fmtFloatNS = "#,###."
)

type (
	benchmark struct {
		info    *benchmarkInfo
		results *results
	}
	benchmarkInfo struct {
		hasFnIterations bool
		benchmemUsed    bool
		suggestedTiming string
	}
	results map[string][]*result
	result  struct {
		Name         string
		FnIterations int
		Runs         int
		Speed        float64
		Bps          int
		Aps          int
	}
)

type sortByFnIterations []*result

func (b sortByFnIterations) Len() int           { return len(b) }
func (b sortByFnIterations) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b sortByFnIterations) Less(i, j int) bool { return b[i].FnIterations < b[j].FnIterations }

var (
	regExByWhitespace = regexp.MustCompile(`\s+`)
	regExByRuns       = regexp.MustCompile(`-\d+$`)
	regExByIterations = regexp.MustCompile(`(?i:)(^Benchmark_?)`)
	regExIsBenchmark  = regExByIterations
	linePassed        = "PASS"
	lineSkipped       = "SKIP"
	lineFail          = "FAIL"
)

var (
	lines           [][]byte
	unparsableLines []string
	table           *termtables.Table
	bench           *benchmark
	timing          string
)

func init() {
	setTiming()
}

//Main is the entry point to parse benchmarks
//not intended for use in libraries, but has to be exported to ensure the tool can be called via 'pb'
func Main() {

	reader := bufio.NewReader(os.Stdin)
	quit := make(chan bool)
	go loading(quit)

	for {
		text, err := reader.ReadBytes('\n')
		if err != nil {
			if err != io.EOF {
				panic(err)
			}
			break
		}
		lines = append(lines, text)
	}
	close(quit)

	if len(lines) == 0 {
		os.Exit(0)
	}

	bench = newBenchmark(lines)

	table = termtables.CreateTable()
	table.Style.Alignment = termtables.AlignRight
	addTableHeader(table)
	addTableBody(table)

	fmt.Print("\r \n")
	fmt.Println(table.Render())
	fmt.Println(footer())
}

func newBenchmark(l [][]byte) *benchmark {
	results := newResults(l)
	return &benchmark{
		info:    newBenchmarkInfo(results),
		results: results,
	}
}

func newResults(l [][]byte) *results {
	benchMap := make(results)

	for _, l := range l {
		bl, err := newResult(l)
		if err != nil {
			unparsableLines = append(unparsableLines, err.Error())
			continue
		}
		if _, ok := benchMap[bl.Name]; !ok {
			benchMap[bl.Name] = make([]*result, 0)
		}
		benchMap[bl.Name] = append(benchMap[bl.Name], bl)

	}

	for _, r := range benchMap {
		sort.Sort(sortByFnIterations(r))
	}

	return &benchMap
}

func newResult(b []byte) (*result, error) {
	var (
		name   string
		fnIter int
		bps    int
		aps    int
		err    error
		iter   int
		speed  float64
	)

	s := string(b)
	parts := regExByWhitespace.Split(s, -1)
	if len(parts) < 4 || !regExIsBenchmark.MatchString(parts[0]) {
		return nil, fmt.Errorf("%s", s)
	}

	nameRuns := regExByRuns.ReplaceAllString(parts[0], "")
	nameIterations := regExByIterations.ReplaceAllString(nameRuns, "")
	lastIndex := strings.LastIndex(nameIterations, "_")

	if lastIndex > -1 {
		name = nameIterations[:lastIndex]
		fnIter, _ = strconv.Atoi(nameIterations[lastIndex+1:])
	} else {
		name = nameIterations
		fnIter = -1
	}

	iter, err = strconv.Atoi(parts[1])
	if err != nil {
		iter = -1
	}
	speed, err = strconv.ParseFloat(parts[2], 64)
	if err != nil {
		speed = -1
	}

	if len(parts) > 5 {
		bps, err = strconv.Atoi(parts[4])
		if err != nil {
			bps = -1
		}
		aps, err = strconv.Atoi(parts[6])
		if err != nil {
			aps = -1
		}
	} else {

		//without benchmem
		bps = -1
		aps = -1
	}

	return &result{
		Name:         name,
		FnIterations: fnIter,
		Runs:         iter,
		Speed:        speed,
		Bps:          bps,
		Aps:          aps,
	}, nil
}

func newBenchmarkInfo(r *results) *benchmarkInfo {
	var (
		hasFnIter    bool
		benchmemUsed bool
		wg           sync.WaitGroup
	)

	wg.Add(3)
	go func(r *results) {
		timing = getSuggestedTiming(r)
		wg.Done()
	}(r)
	go func(r *results) {
		hasFnIter = hasFnIterations(r)
		wg.Done()
	}(r)
	go func(r *results) {
		benchmemUsed = isBenchmem(r)
		wg.Done()
	}(r)
	wg.Wait()

	switch timing {
	case "ns":
		//ns is default, dont't do anything
	case "µs":
		updateSpeedVals(r, float64(1e3))
	case "ms":
		updateSpeedVals(r, float64(1e6))
	case "s":
		updateSpeedVals(r, float64(1e9))
	}

	return &benchmarkInfo{hasFnIter, benchmemUsed, timing}
}

func getSuggestedTiming(r *results) string {
	var (
		slowest         float64
		suggestedTiming string
	)

	for _, bl := range *r {
		for _, l := range bl {
			if slowest < l.Speed {
				slowest = l.Speed
			}
		}
	}

	if timing == "" {
		switch {
		case slowest <= 1e3:
			suggestedTiming = "ns"
		case slowest > 1e3 && slowest <= 1e6:
			suggestedTiming = "µs"
		case slowest > 1e6 && slowest <= 1e9:
			suggestedTiming = "ms"
		case slowest > 1e9:
			suggestedTiming = "s"
		}
	} else {
		suggestedTiming = timing
	}

	return suggestedTiming
}

func isBenchmem(r *results) bool {
	var benchmemUsed bool

	for _, bl := range *r {
		for _, l := range bl {
			if l.Aps > -1 && l.Bps > -1 {
				benchmemUsed = true
			}
		}
	}

	return benchmemUsed
}

func hasFnIterations(r *results) bool {
	var hasFnIterations bool

	for _, bl := range *r {
		for _, l := range bl {
			if l.FnIterations > -1 {
				hasFnIterations = true
			}
		}
	}

	return hasFnIterations
}

func updateSpeedVals(r *results, f float64) {
	for _, bl := range *r {
		for _, l := range bl {
			l.Speed = l.Speed / f
		}
	}
}

func footer() string {
	var footer []byte

	footer = append(footer, []byte{10}...)
	footer = append(footer, []byte((bold("Summary:"))+"\n")...)
	footer = append(footer, []byte((bold("+------+"))+"\n")...)

	for _, line := range unparsableLines {
		tmp := strings.TrimSpace(line)
		switch {
		case tmp == linePassed:
			footer = append(footer, []byte(green(bold(tmp))+"\n")...)
		case tmp == lineSkipped:
			footer = append(footer, []byte(gray(bold(tmp))+"\n")...)
		case tmp == lineFail:
			footer = append(footer, []byte(red(bold(tmp))+"\n")...)
		default:
			footer = append(footer, []byte(tmp+"\n")...)
		}
	}

	return string(footer)
}

func addTableHeader(t *termtables.Table) {
	var lenLongestName int
	for name := range *bench.results {
		if tmpLen := len(name); tmpLen > lenLongestName {
			lenLongestName = tmpLen
		}
	}

	// add padding to first col since alignment in header columns does not work
	// padding of longest name + len("name") + 1 padding right
	nameCol := make([]byte, 0, lenLongestName+4+1)
	nameCol = append(nameCol, []byte("Name")...)
	for i := 0; i < lenLongestName; i++ {
		nameCol = append(nameCol, byte(32))
	}

	if bench.info.benchmemUsed {
		if bench.info.hasFnIterations {
			t.AddHeaders(bold(string(nameCol)), bold("Iterations"), bold("Runs"), bold(bench.info.suggestedTiming+"/op"), bold("B/op"), bold("allocations/op"))
		} else {
			t.AddHeaders(bold(string(nameCol)), bold("Runs"), bold(bench.info.suggestedTiming+"/op"), bold("B/op"), bold("allocations/op"))
		}
	} else {
		if bench.info.hasFnIterations {
			t.AddHeaders(bold(string(nameCol)), bold("Iterations"), bold("Runs"), bold(bench.info.suggestedTiming+"/op"))
		} else {
			t.AddHeaders(bold(string(nameCol)), bold("Runs"), bold(bench.info.suggestedTiming+"/op"))
		}
	}
}

func addTableBody(t *termtables.Table) {
	floatFmt := fmtFloat
	if bench.info.suggestedTiming == "ns" {
		floatFmt = fmtFloatNS
	}

	i := len(*bench.results)
	sorted := make([]string, 0, i)
	for name := range *bench.results {
		sorted = append(sorted, name)
	}
	sort.Sort(sort.StringSlice(sorted))

	for _, benchName := range sorted {
		results := (*bench.results)[benchName]

		for j, b := range results {
			var name string
			if j == 0 {
				name = bold(b.Name)
			}

			if bench.info.benchmemUsed {
				if bench.info.hasFnIterations {
					fnIterations := strconv.Itoa(b.FnIterations)

					if fnIterations == "-1" {
						fnIterations = ""
					} else {
						i, err := strconv.Atoi(fnIterations)
						if err != nil {
							fnIterations = ""
						} else {
							fnIterations = RenderInteger(fmtInt, i)
						}
					}

					t.AddRow(name, fnIterations, RenderInteger(fmtInt, b.Runs), RenderFloat(floatFmt, b.Speed), RenderInteger(fmtInt, b.Bps), RenderInteger(fmtInt, b.Aps))
				} else {
					t.AddRow(name, RenderInteger(fmtInt, b.Runs), RenderFloat(floatFmt, b.Speed), RenderInteger(fmtInt, b.Bps), RenderInteger(fmtInt, b.Aps))
				}
			} else {
				if bench.info.hasFnIterations {
					fnIterations := strconv.Itoa(b.FnIterations)

					if fnIterations == "-1" {
						fnIterations = ""
					} else {
						i, err := strconv.Atoi(fnIterations)
						if err != nil {
							fnIterations = ""
						} else {
							fnIterations = RenderInteger(fmtInt, i)
						}
					}

					t.AddRow(name, fnIterations, RenderInteger(fmtInt, b.Runs), RenderFloat(floatFmt, b.Speed))
				} else {
					t.AddRow(name, RenderInteger(fmtInt, b.Runs), RenderFloat(floatFmt, b.Speed))
				}
			}
		}

		i--
		if i > 0 {
			t.AddSeparator()
		}
	}

	t.SetAlign(termtables.AlignLeft, 1)
}

func setTiming() {
	flag.Parse()
	args := flag.Args()

	if len(args) > 0 {
		if lowerArg := strings.ToLower(args[0]); lowerArg == "ns" || lowerArg == "us" || lowerArg == "µs" || lowerArg == "ms" || lowerArg == "s" {
			if lowerArg == "us" {
				lowerArg = "µs"
			}
			timing = lowerArg
		}
	}
}

func loading(q chan bool) {
	states := []string{"|", "/", "-", "\\", "|", "/", "–", "\\"}
	current := 0

	for {
		select {
		case <-time.Tick(150 * time.Millisecond):
			fmt.Printf("\r%s", states[current])
			if current == len(states)-1 {
				current = 0
			} else {
				current++
			}
		case <-q:
			break
		}
	}
}

func bold(s string) string {
	return fmt.Sprintf("\033[1m%s\033[0m", s)
}

func green(s string) string {
	return fmt.Sprintf("\033[32m%s\033[0m", s)
}

func red(s string) string {
	return fmt.Sprintf("\033[31m%s\033[0m", s)
}

func gray(s string) string {
	return fmt.Sprintf("\033[90m%s\033[0m", s)
}
<commit_msg>cleanup code style, add missing spaces<commit_after>// Copyright 2015 Florian Orben. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

// Package prettybenchmarks formats your go benchmarks into nice looking sorted tables
//
// Prettybenchmarks
//
// Works with and without -benchmem flag
//
// If you provide a time interval (either ns, µs (or us), ms, s), each benchmark's runtime will be
// converted to that interval. If left blank, a suitable value will automatically be chosen
//
//    go test -bench=YOUR_PKG [-benchmem] | pb [timeinterval]
// Example
//    go test -bench=. -benchmem | pb ms
//
package prettybenchmarks

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/apcera/termtables"
)

const (
	fmtInt     = "#,###."
	fmtFloat   = "#,###.###"
	fmtFloatNS = "#,###."
)

type (
	benchmark struct {
		info    *benchmarkInfo
		results *results
	}
	benchmarkInfo struct {
		hasFnIterations bool
		benchmemUsed    bool
		suggestedTiming string
	}
	results map[string][]*result
	result  struct {
		Name         string
		FnIterations int
		Runs         int
		Speed        float64
		Bps          int
		Aps          int
	}
)

type sortByFnIterations []*result

func (b sortByFnIterations) Len() int           { return len(b) }
func (b sortByFnIterations) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b sortByFnIterations) Less(i, j int) bool { return b[i].FnIterations < b[j].FnIterations }

var (
	regExByWhitespace = regexp.MustCompile(`\s+`)
	regExByRuns       = regexp.MustCompile(`-\d+$`)
	regExByIterations = regexp.MustCompile(`(?i:)(^Benchmark_?)`)
	regExIsBenchmark  = regExByIterations
	linePassed        = "PASS"
	lineSkipped       = "SKIP"
	lineFail          = "FAIL"
)

var (
	lines           [][]byte
	unparsableLines []string
	table           *termtables.Table
	bench           *benchmark
	timing          string
)

func init() {
	setTiming()
}

// Main is the entry point to parse benchmarks
// not intended for use in libraries, but has to be exported to ensure the tool can be called via 'pb'
func Main() {
	reader := bufio.NewReader(os.Stdin)
	quit := make(chan bool)

	go loading(quit)

	for {
		text, err := reader.ReadBytes('\n')

		if err != nil {
			if err != io.EOF {
				panic(err)
			}
			break
		}

		lines = append(lines, text)
	}

	close(quit)

	if len(lines) == 0 {
		os.Exit(0)
	}

	bench = newBenchmark(lines)

	table = termtables.CreateTable()
	table.Style.Alignment = termtables.AlignRight
	addTableHeader(table)
	addTableBody(table)

	fmt.Print("\r \n")
	fmt.Println(table.Render())
	fmt.Println(footer())
}

func newBenchmark(l [][]byte) *benchmark {
	results := newResults(l)

	return &benchmark{
		info:    newBenchmarkInfo(results),
		results: results,
	}
}

func newResults(l [][]byte) *results {
	benchMap := make(results)

	for _, l := range l {
		bl, err := newResult(l)

		if err != nil {
			unparsableLines = append(unparsableLines, err.Error())
			continue
		}

		if _, ok := benchMap[bl.Name]; !ok {
			benchMap[bl.Name] = make([]*result, 0)
		}

		benchMap[bl.Name] = append(benchMap[bl.Name], bl)
	}

	for _, r := range benchMap {
		sort.Sort(sortByFnIterations(r))
	}

	return &benchMap
}

func newResult(b []byte) (*result, error) {
	var (
		name   string
		fnIter int
		bps    int
		aps    int
		err    error
		iter   int
		speed  float64
	)

	s := string(b)
	parts := regExByWhitespace.Split(s, -1)

	if len(parts) < 4 || !regExIsBenchmark.MatchString(parts[0]) {
		return nil, fmt.Errorf("%s", s)
	}

	nameRuns := regExByRuns.ReplaceAllString(parts[0], "")
	nameIterations := regExByIterations.ReplaceAllString(nameRuns, "")
	lastIndex := strings.LastIndex(nameIterations, "_")

	if lastIndex > -1 {
		name = nameIterations[:lastIndex]
		fnIter, _ = strconv.Atoi(nameIterations[lastIndex+1:])
	} else {
		name = nameIterations
		fnIter = -1
	}

	iter, err = strconv.Atoi(parts[1])

	if err != nil {
		iter = -1
	}

	speed, err = strconv.ParseFloat(parts[2], 64)

	if err != nil {
		speed = -1
	}

	if len(parts) > 5 {
		bps, err = strconv.Atoi(parts[4])

		if err != nil {
			bps = -1
		}
		aps, err = strconv.Atoi(parts[6])

		if err != nil {
			aps = -1
		}
	} else {
		//without benchmem
		bps = -1
		aps = -1
	}

	return &result{
		Name:         name,
		FnIterations: fnIter,
		Runs:         iter,
		Speed:        speed,
		Bps:          bps,
		Aps:          aps,
	}, nil
}

func newBenchmarkInfo(r *results) *benchmarkInfo {
	var (
		hasFnIter    bool
		benchmemUsed bool
		wg           sync.WaitGroup
	)

	wg.Add(3)

	go func(r *results) {
		timing = getSuggestedTiming(r)
		wg.Done()
	}(r)

	go func(r *results) {
		hasFnIter = hasFnIterations(r)
		wg.Done()
	}(r)

	go func(r *results) {
		benchmemUsed = isBenchmem(r)
		wg.Done()
	}(r)

	wg.Wait()

	switch timing {
	case "ns":
		// ns is default, dont't do anything
	case "µs":
		updateSpeedVals(r, float64(1e3))
	case "ms":
		updateSpeedVals(r, float64(1e6))
	case "s":
		updateSpeedVals(r, float64(1e9))
	}

	return &benchmarkInfo{hasFnIter, benchmemUsed, timing}
}

func getSuggestedTiming(r *results) string {
	var (
		slowest         float64
		suggestedTiming string
	)

	for _, bl := range *r {
		for _, l := range bl {
			if slowest < l.Speed {
				slowest = l.Speed
			}
		}
	}

	if timing == "" {
		switch {
		case slowest <= 1e3:
			suggestedTiming = "ns"
		case slowest > 1e3 && slowest <= 1e6:
			suggestedTiming = "µs"
		case slowest > 1e6 && slowest <= 1e9:
			suggestedTiming = "ms"
		case slowest > 1e9:
			suggestedTiming = "s"
		}
	} else {
		suggestedTiming = timing
	}

	return suggestedTiming
}

func isBenchmem(r *results) bool {
	var benchmemUsed bool

	for _, bl := range *r {
		for _, l := range bl {
			if l.Aps > -1 && l.Bps > -1 {
				benchmemUsed = true
			}
		}
	}

	return benchmemUsed
}

func hasFnIterations(r *results) bool {
	var hasFnIterations bool

	for _, bl := range *r {
		for _, l := range bl {
			if l.FnIterations > -1 {
				hasFnIterations = true
			}
		}
	}

	return hasFnIterations
}

func updateSpeedVals(r *results, f float64) {
	for _, bl := range *r {
		for _, l := range bl {
			l.Speed = l.Speed / f
		}
	}
}

func footer() string {
	var footer []byte

	footer = append(footer, []byte{10}...)
	footer = append(footer, []byte((bold("Summary:"))+"\n")...)
	footer = append(footer, []byte((bold("+------+"))+"\n")...)

	for _, line := range unparsableLines {
		tmp := strings.TrimSpace(line)
		switch {
		case tmp == linePassed:
			footer = append(footer, []byte(green(bold(tmp))+"\n")...)
		case tmp == lineSkipped:
			footer = append(footer, []byte(gray(bold(tmp))+"\n")...)
		case tmp == lineFail:
			footer = append(footer, []byte(red(bold(tmp))+"\n")...)
		default:
			footer = append(footer, []byte(tmp+"\n")...)
		}
	}

	return string(footer)
}

func addTableHeader(t *termtables.Table) {
	var lenLongestName int
	for name := range *bench.results {
		if tmpLen := len(name); tmpLen > lenLongestName {
			lenLongestName = tmpLen
		}
	}

	// add padding to first col since alignment in header columns does not work
	// padding of longest name + len("name") + 1 padding right
	nameCol := make([]byte, 0, lenLongestName+4+1)
	nameCol = append(nameCol, []byte("Name")...)
	for i := 0; i < lenLongestName; i++ {
		nameCol = append(nameCol, byte(32))
	}

	if bench.info.benchmemUsed {
		if bench.info.hasFnIterations {
			t.AddHeaders(bold(string(nameCol)), bold("Iterations"), bold("Runs"), bold(bench.info.suggestedTiming+"/op"), bold("B/op"), bold("allocations/op"))
		} else {
			t.AddHeaders(bold(string(nameCol)), bold("Runs"), bold(bench.info.suggestedTiming+"/op"), bold("B/op"), bold("allocations/op"))
		}
	} else {
		if bench.info.hasFnIterations {
			t.AddHeaders(bold(string(nameCol)), bold("Iterations"), bold("Runs"), bold(bench.info.suggestedTiming+"/op"))
		} else {
			t.AddHeaders(bold(string(nameCol)), bold("Runs"), bold(bench.info.suggestedTiming+"/op"))
		}
	}
}

func addTableBody(t *termtables.Table) {
	floatFmt := fmtFloat

	if bench.info.suggestedTiming == "ns" {
		floatFmt = fmtFloatNS
	}

	i := len(*bench.results)
	sorted := make([]string, 0, i)

	for name := range *bench.results {
		sorted = append(sorted, name)
	}

	sort.Sort(sort.StringSlice(sorted))

	for _, benchName := range sorted {
		results := (*bench.results)[benchName]

		for j, b := range results {
			var name string

			if j == 0 {
				name = bold(b.Name)
			}

			if bench.info.benchmemUsed {
				if bench.info.hasFnIterations {
					fnIterations := strconv.Itoa(b.FnIterations)

					if fnIterations == "-1" {
						fnIterations = ""
					} else {
						i, err := strconv.Atoi(fnIterations)
						if err != nil {
							fnIterations = ""
						} else {
							fnIterations = RenderInteger(fmtInt, i)
						}
					}

					t.AddRow(name, fnIterations, RenderInteger(fmtInt, b.Runs), RenderFloat(floatFmt, b.Speed), RenderInteger(fmtInt, b.Bps), RenderInteger(fmtInt, b.Aps))
				} else {
					t.AddRow(name, RenderInteger(fmtInt, b.Runs), RenderFloat(floatFmt, b.Speed), RenderInteger(fmtInt, b.Bps), RenderInteger(fmtInt, b.Aps))
				}
			} else {
				if bench.info.hasFnIterations {
					fnIterations := strconv.Itoa(b.FnIterations)

					if fnIterations == "-1" {
						fnIterations = ""
					} else {
						i, err := strconv.Atoi(fnIterations)
						if err != nil {
							fnIterations = ""
						} else {
							fnIterations = RenderInteger(fmtInt, i)
						}
					}

					t.AddRow(name, fnIterations, RenderInteger(fmtInt, b.Runs), RenderFloat(floatFmt, b.Speed))
				} else {
					t.AddRow(name, RenderInteger(fmtInt, b.Runs), RenderFloat(floatFmt, b.Speed))
				}
			}
		}

		i--
		if i > 0 {
			t.AddSeparator()
		}
	}

	t.SetAlign(termtables.AlignLeft, 1)
}

func setTiming() {
	flag.Parse()
	args := flag.Args()

	if len(args) > 0 {
		if lowerArg := strings.ToLower(args[0]); lowerArg == "ns" || lowerArg == "us" || lowerArg == "µs" || lowerArg == "ms" || lowerArg == "s" {
			if lowerArg == "us" {
				lowerArg = "µs"
			}

			timing = lowerArg
		}
	}
}

func loading(q chan bool) {
	states := []string{"|", "/", "-", "\\", "|", "/", "–", "\\"}
	current := 0

	for {
		select {
		case <-time.Tick(150 * time.Millisecond):
			fmt.Printf("\r%s", states[current])

			if current == len(states)-1 {
				current = 0
			} else {
				current++
			}
		case <-q:
			break
		}
	}
}

func bold(s string) string {
	return fmt.Sprintf("\033[1m%s\033[0m", s)
}

func green(s string) string {
	return fmt.Sprintf("\033[32m%s\033[0m", s)
}

func red(s string) string {
	return fmt.Sprintf("\033[31m%s\033[0m", s)
}

func gray(s string) string {
	return fmt.Sprintf("\033[90m%s\033[0m", s)
}
<|endoftext|>


<commit_before>package dcnet

import (
	"github.com/lbarman/prifi/prifi-lib/config"
	"gopkg.in/dedis/crypto.v0/abstract"
	"gopkg.in/dedis/onet.v1/log"
	"strconv"
)

// Relay, Trustee or Client
type DCNET_ENTITY int

const (
	// Define this DCNET entity as a client
	DCNET_CLIENT DCNET_ENTITY = iota

	// Define this DCNET entity as a trustee
	DCNET_TRUSTEE

	// Define this DCNET entity as a relay
	DCNET_RELAY
)

// A struct with all methods to encode and decode dc-net messages
type DCNetEntity struct {
	//Global for all nodes
	EntityID                      int
	Entity                        DCNET_ENTITY
	EquivocationProtectionEnabled bool
	DCNetMessageSize              int
	DCNetContentSize              int

	cryptoSuite  abstract.Suite
	sharedKeys   []abstract.Cipher // keys shared with other DC-net members
	sharedPRNGs  []abstract.Cipher // PRNGs shared with other DC-net members (seeded with sharedKeys)
	currentRound int32

	//Used by the relay
	DCNetRoundDecoder *DCNetRoundDecoder //nil if unused

	//Equivocation protection
	equivocationProtection    *EquivocationProtection //nil if unused
	equivocationContribLength int                     //0 if equivocation protection is disabled
}

// DCNetRoundDecoder is used by the relay to decode the dcnet ciphers
type DCNetRoundDecoder struct {
	currentRoundBeingDecoded int32
	xorBuffer                []byte
	equivTrusteeContribs     [][]byte
	equivClientContribs      [][]byte
}

// Used by clients, trustees
func NewDCNetEntity(
	entityID int,
	entity DCNET_ENTITY,
	DCNetMessageSize int,
	equivocationProtection bool,
	sharedKeys []abstract.Cipher) *DCNetEntity {

	e := new(DCNetEntity)
	e.EntityID = entityID
	e.Entity = entity
	e.DCNetMessageSize = DCNetMessageSize
	e.EquivocationProtectionEnabled = equivocationProtection
	e.DCNetRoundDecoder = nil
	e.currentRound = 0

	if equivocationProtection {
		e.equivocationProtection = NewEquivocation()
	}

	e.cryptoSuite = config.CryptoSuite

	// if the node participates in the DC-net
	if entity != DCNET_RELAY {
		e.sharedKeys = sharedKeys

		// Use the provided shared secrets to seed a pseudorandom DC-nets ciphers shared with each peer.
		keySize := e.cryptoSuite.Cipher(nil).KeySize()
		e.sharedPRNGs = make([]abstract.Cipher, len(sharedKeys))
		for i := range sharedKeys {
			key := make([]byte, keySize)
			sharedKeys[i].Partial(key, key, nil)
			e.sharedPRNGs[i] = e.cryptoSuite.Cipher(key)
		}
	} else {
		e.sharedKeys = make([]abstract.Cipher, 0)
		e.sharedPRNGs = make([]abstract.Cipher, 0)
	}

	// if the equivocation protection is enabled
	if equivocationProtection {
		e.equivocationProtection = NewEquivocation()
		zero := e.equivocationProtection.suite.Scalar().Zero()
		one := e.equivocationProtection.suite.Scalar().One()
		minusOne := e.equivocationProtection.suite.Scalar().Sub(zero, one) //max value
		e.equivocationContribLength = len(minusOne.Bytes())
	}

	e.DCNetContentSize = DCNetMessageSize

	// make sure we can still encode stuff !
	if e.DCNetContentSize <= 0 {
		panic("Payload length is" + strconv.Itoa(e.DCNetContentSize))
	}

	return e
}

// Tells the owner of the slot how much he can embedded (=DCNetContentSize)
func (e *DCNetEntity) GetPayloadSize() int {
	return e.DCNetContentSize
}

// Encodes "Payload" in the correct round. Will skip PRNG material if the round is in the future,
// and crash if the round is in the past or the Payload is too long
func (e *DCNetEntity) TrusteeEncodeForRound(roundID int32) []byte {
	return e.EncodeForRound(roundID, false, nil)
}

// Encodes "Payload" in the correct round. Will skip PRNG material if the round is in the future,
// and crash if the round is in the past or the Payload is too long
func (e *DCNetEntity) EncodeForRound(roundID int32, slotOwner bool, payload []byte) []byte {
	if len(payload) > e.DCNetContentSize {
		panic("DCNet: cannot encode Payload of length " + strconv.Itoa(int(len(payload))) + " max length is " + strconv.Itoa(len(payload)))
	}

	if roundID < e.currentRound {
		panic("DCNet: asked to encode for round " + strconv.Itoa(int(roundID)) + " but we are at  round " + strconv.Itoa(int(e.currentRound)))
	}

	for e.currentRound < roundID {
		//discard crypto material
		log.Lvl4("DCNet: Discarding round", e.currentRound)

		// consume the PRNGs
		for i := range e.sharedPRNGs {
			dummy := make([]byte, e.DCNetContentSize)
			e.sharedPRNGs[i].XORKeyStream(dummy, dummy)
		}

		e.currentRound++
	}

	var c *DCNetCipher
	if e.Entity == DCNET_CLIENT {
		c = e.clientEncode(slotOwner, payload)
	} else {
		c = e.trusteeEncode()
	}
	e.currentRound++

	return c.ToBytes()
}

// Adds `newdata` into the sponge representing the received downstream data
func (e *DCNetEntity) UpdateReceivedMessageHistory(newData []byte) {
	if e.EquivocationProtectionEnabled {
		e.equivocationProtection.UpdateHistory(newData)
	}
}

func (e *DCNetEntity) clientEncode(slotOwner bool, payload []byte) *DCNetCipher {
	c := new(DCNetCipher)

	if payload == nil {
		payload = make([]byte, e.DCNetContentSize)
	} else {
		// deep clone and pad
		payload2 := make([]byte, e.GetPayloadSize())
		copy(payload2[0:len(payload)], payload)
		payload = payload2
	}
	c.Payload = payload

	// prepare the pads
	p_ij := make([][]byte, len(e.sharedPRNGs))
	for i := range p_ij {
		p_ij[i] = make([]byte, e.DCNetContentSize)
		e.sharedPRNGs[i].XORKeyStream(p_ij[i], p_ij[i])
	}

	// if the equivocation protection is enabled, encrypt the Payload, and add the tag
	if e.EquivocationProtectionEnabled {
		payload, sigma_j := e.equivocationProtection.ClientEncryptPayload(slotOwner, payload, p_ij)
		c.Payload = payload // replace the Payload with the encrypted version
		c.EquivocationProtectionTag = sigma_j
	}

	// DC-net encrypt the Payload
	for i := range p_ij {
		for k := range c.Payload {
			c.Payload[k] ^= p_ij[i][k] // XORs in the pads
		}
	}

	return c
}

func (e *DCNetEntity) trusteeEncode() *DCNetCipher {
	c := new(DCNetCipher)

	c.Payload = make([]byte, e.DCNetContentSize)

	// prepare the pads
	p_ij := make([][]byte, len(e.sharedPRNGs))
	for i := range p_ij {
		p_ij[i] = make([]byte, e.DCNetContentSize)
		e.sharedPRNGs[i].XORKeyStream(p_ij[i], p_ij[i])
	}

	// DC-net encrypt the Payload
	for i := range p_ij {
		for k := range c.Payload {
			c.Payload[k] ^= p_ij[i][k] // XORs in the pads
		}
	}

	// if the equivocation protection is enabled, encrypt the Payload, and add the tag
	if e.EquivocationProtectionEnabled {
		sigma_j := e.equivocationProtection.TrusteeGetContribution(p_ij)
		c.EquivocationProtectionTag = sigma_j
	}

	return c
}

// Used by the relay to start decoding a round
func (e *DCNetEntity) DecodeStart(roundID int32) {
	e.DCNetRoundDecoder = new(DCNetRoundDecoder)
	e.DCNetRoundDecoder.currentRoundBeingDecoded = roundID
	e.DCNetRoundDecoder.xorBuffer = make([]byte, e.DCNetContentSize)
	e.DCNetRoundDecoder.equivClientContribs = make([][]byte, 0)
	e.DCNetRoundDecoder.equivTrusteeContribs = make([][]byte, 0)
}

// called by the relay to decode a client contribution
func (e *DCNetEntity) DecodeClient(roundID int32, slice []byte) {

	dcNetCipher := DCNetCipherFromBytes(slice)

	if roundID != e.DCNetRoundDecoder.currentRoundBeingDecoded {
		panic("Cannot DecodeClient for round" +
			strconv.Itoa(int(roundID)) + ", we are in round " + strconv.Itoa(int(e.DCNetRoundDecoder.currentRoundBeingDecoded)))
	}

	for i := range dcNetCipher.Payload {
		e.DCNetRoundDecoder.xorBuffer[i] ^= dcNetCipher.Payload[i]
	}

	if e.EquivocationProtectionEnabled {
		e.DCNetRoundDecoder.equivClientContribs = append(e.DCNetRoundDecoder.equivClientContribs, dcNetCipher.EquivocationProtectionTag)
	}
}

// called by the relay to decode a client contribution
func (e *DCNetEntity) DecodeTrustee(roundID int32, slice []byte) {

	dcNetCipher := DCNetCipherFromBytes(slice)

	if roundID != e.DCNetRoundDecoder.currentRoundBeingDecoded {
		panic("Cannot DecodeClient for round" +
			strconv.Itoa(int(roundID)) + ", we are in round " + strconv.Itoa(int(e.DCNetRoundDecoder.currentRoundBeingDecoded)))
	}

	for i := range dcNetCipher.Payload {
		e.DCNetRoundDecoder.xorBuffer[i] ^= dcNetCipher.Payload[i]
	}

	if e.EquivocationProtectionEnabled {
		e.DCNetRoundDecoder.equivTrusteeContribs = append(e.DCNetRoundDecoder.equivTrusteeContribs, dcNetCipher.EquivocationProtectionTag)
	}
}

// Called on the relay to decode the cell, after having stored the cryptographic materials
func (e *DCNetEntity) DecodeCell() []byte {
	//No Equivocation -> just XOR
	d := e.DCNetRoundDecoder

	decoded := d.xorBuffer
	if e.EquivocationProtectionEnabled {
		decoded = e.equivocationProtection.RelayDecode(d.xorBuffer, d.equivTrusteeContribs, d.equivClientContribs)
	}

	return decoded
}
<commit_msg>Rename things in DCNet<commit_after>package dcnet

import (
	"github.com/lbarman/prifi/prifi-lib/config"
	"gopkg.in/dedis/crypto.v0/abstract"
	"gopkg.in/dedis/onet.v1/log"
	"strconv"
)

// Relay, Trustee or Client
type DCNET_ENTITY int

const (
	// Define this DCNET entity as a client
	DCNET_CLIENT DCNET_ENTITY = iota

	// Define this DCNET entity as a trustee
	DCNET_TRUSTEE

	// Define this DCNET entity as a relay
	DCNET_RELAY
)

// A struct with all methods to encode and decode dc-net messages
type DCNetEntity struct {
	//Global for all nodes
	EntityID                      int
	Entity                        DCNET_ENTITY
	EquivocationProtectionEnabled bool
	DCNetPayloadSize              int

	cryptoSuite  abstract.Suite
	sharedKeys   []abstract.Cipher // keys shared with other DC-net members
	sharedPRNGs  []abstract.Cipher // PRNGs shared with other DC-net members (seeded with sharedKeys)
	currentRound int32

	//Used by the relay
	DCNetRoundDecoder *DCNetRoundDecoder //nil if unused

	//Equivocation protection
	equivocationProtection    *EquivocationProtection //nil if unused
	equivocationContribLength int                     //0 if equivocation protection is disabled
}

// DCNetRoundDecoder is used by the relay to decode the dcnet ciphers
type DCNetRoundDecoder struct {
	currentRoundBeingDecoded int32
	xorBuffer                []byte
	equivTrusteeContribs     [][]byte
	equivClientContribs      [][]byte
}

// Used by clients, trustees
func NewDCNetEntity(
	entityID int,
	entity DCNET_ENTITY,
	PayloadSize int,
	equivocationProtection bool,
	sharedKeys []abstract.Cipher) *DCNetEntity {

	e := new(DCNetEntity)
	e.EntityID = entityID
	e.Entity = entity
	e.DCNetPayloadSize = PayloadSize
	e.EquivocationProtectionEnabled = equivocationProtection
	e.DCNetRoundDecoder = nil
	e.currentRound = 0

	if equivocationProtection {
		e.equivocationProtection = NewEquivocation()
	}

	e.cryptoSuite = config.CryptoSuite

	// if the node participates in the DC-net
	if entity != DCNET_RELAY {
		e.sharedKeys = sharedKeys

		// Use the provided shared secrets to seed a pseudorandom DC-nets ciphers shared with each peer.
		keySize := e.cryptoSuite.Cipher(nil).KeySize()
		e.sharedPRNGs = make([]abstract.Cipher, len(sharedKeys))
		for i := range sharedKeys {
			key := make([]byte, keySize)
			sharedKeys[i].Partial(key, key, nil)
			e.sharedPRNGs[i] = e.cryptoSuite.Cipher(key)
		}
	} else {
		e.sharedKeys = make([]abstract.Cipher, 0)
		e.sharedPRNGs = make([]abstract.Cipher, 0)
	}

	// if the equivocation protection is enabled
	if equivocationProtection {
		e.equivocationProtection = NewEquivocation()
		zero := e.equivocationProtection.suite.Scalar().Zero()
		one := e.equivocationProtection.suite.Scalar().One()
		minusOne := e.equivocationProtection.suite.Scalar().Sub(zero, one) //max value
		e.equivocationContribLength = len(minusOne.Bytes())
	}

	// make sure we can still encode stuff !
	if e.DCNetPayloadSize <= 0 {
		panic("Payload length is" + strconv.Itoa(e.DCNetPayloadSize))
	}

	return e
}

// Tells the owner of the slot how much he can embedded (=DCNetPayloadSize)
func (e *DCNetEntity) GetPayloadSize() int {
	return e.DCNetPayloadSize
}

// Encodes "Payload" in the correct round. Will skip PRNG material if the round is in the future,
// and crash if the round is in the past or the Payload is too long
func (e *DCNetEntity) TrusteeEncodeForRound(roundID int32) []byte {
	return e.EncodeForRound(roundID, false, nil)
}

// Encodes "Payload" in the correct round. Will skip PRNG material if the round is in the future,
// and crash if the round is in the past or the Payload is too long
func (e *DCNetEntity) EncodeForRound(roundID int32, slotOwner bool, payload []byte) []byte {
	if len(payload) > e.DCNetPayloadSize {
		panic("DCNet: cannot encode Payload of length " + strconv.Itoa(int(len(payload))) + " max length is " + strconv.Itoa(len(payload)))
	}

	if roundID < e.currentRound {
		panic("DCNet: asked to encode for round " + strconv.Itoa(int(roundID)) + " but we are at  round " + strconv.Itoa(int(e.currentRound)))
	}

	for e.currentRound < roundID {
		//discard crypto material
		log.Lvl4("DCNet: Discarding round", e.currentRound)

		// consume the PRNGs
		for i := range e.sharedPRNGs {
			dummy := make([]byte, e.DCNetPayloadSize)
			e.sharedPRNGs[i].XORKeyStream(dummy, dummy)
		}

		e.currentRound++
	}

	var c *DCNetCipher
	if e.Entity == DCNET_CLIENT {
		c = e.clientEncode(slotOwner, payload)
	} else {
		c = e.trusteeEncode()
	}
	e.currentRound++

	return c.ToBytes()
}

// Adds `newdata` into the sponge representing the received downstream data
func (e *DCNetEntity) UpdateReceivedMessageHistory(newData []byte) {
	if e.EquivocationProtectionEnabled {
		e.equivocationProtection.UpdateHistory(newData)
	}
}

func (e *DCNetEntity) clientEncode(slotOwner bool, payload []byte) *DCNetCipher {
	c := new(DCNetCipher)

	if payload == nil {
		payload = make([]byte, e.DCNetPayloadSize)
	} else {
		// deep clone and pad
		payload2 := make([]byte, e.GetPayloadSize())
		copy(payload2[0:len(payload)], payload)
		payload = payload2
	}
	c.Payload = payload

	// prepare the pads
	p_ij := make([][]byte, len(e.sharedPRNGs))
	for i := range p_ij {
		p_ij[i] = make([]byte, e.DCNetPayloadSize)
		e.sharedPRNGs[i].XORKeyStream(p_ij[i], p_ij[i])
	}

	// if the equivocation protection is enabled, encrypt the Payload, and add the tag
	if e.EquivocationProtectionEnabled {
		payload, sigma_j := e.equivocationProtection.ClientEncryptPayload(slotOwner, payload, p_ij)
		c.Payload = payload // replace the Payload with the encrypted version
		c.EquivocationProtectionTag = sigma_j
	}

	// DC-net encrypt the Payload
	for i := range p_ij {
		for k := range c.Payload {
			c.Payload[k] ^= p_ij[i][k] // XORs in the pads
		}
	}

	return c
}

func (e *DCNetEntity) trusteeEncode() *DCNetCipher {
	c := new(DCNetCipher)

	c.Payload = make([]byte, e.DCNetPayloadSize)

	// prepare the pads
	p_ij := make([][]byte, len(e.sharedPRNGs))
	for i := range p_ij {
		p_ij[i] = make([]byte, e.DCNetPayloadSize)
		e.sharedPRNGs[i].XORKeyStream(p_ij[i], p_ij[i])
	}

	// DC-net encrypt the Payload
	for i := range p_ij {
		for k := range c.Payload {
			c.Payload[k] ^= p_ij[i][k] // XORs in the pads
		}
	}

	// if the equivocation protection is enabled, encrypt the Payload, and add the tag
	if e.EquivocationProtectionEnabled {
		sigma_j := e.equivocationProtection.TrusteeGetContribution(p_ij)
		c.EquivocationProtectionTag = sigma_j
	}

	return c
}

// Used by the relay to start decoding a round
func (e *DCNetEntity) DecodeStart(roundID int32) {
	e.DCNetRoundDecoder = new(DCNetRoundDecoder)
	e.DCNetRoundDecoder.currentRoundBeingDecoded = roundID
	e.DCNetRoundDecoder.xorBuffer = make([]byte, e.DCNetPayloadSize)
	e.DCNetRoundDecoder.equivClientContribs = make([][]byte, 0)
	e.DCNetRoundDecoder.equivTrusteeContribs = make([][]byte, 0)
}

// called by the relay to decode a client contribution
func (e *DCNetEntity) DecodeClient(roundID int32, slice []byte) {

	dcNetCipher := DCNetCipherFromBytes(slice)

	if roundID != e.DCNetRoundDecoder.currentRoundBeingDecoded {
		panic("Cannot DecodeClient for round" +
			strconv.Itoa(int(roundID)) + ", we are in round " + strconv.Itoa(int(e.DCNetRoundDecoder.currentRoundBeingDecoded)))
	}

	for i := range dcNetCipher.Payload {
		e.DCNetRoundDecoder.xorBuffer[i] ^= dcNetCipher.Payload[i]
	}

	if e.EquivocationProtectionEnabled {
		e.DCNetRoundDecoder.equivClientContribs = append(e.DCNetRoundDecoder.equivClientContribs, dcNetCipher.EquivocationProtectionTag)
	}
}

// called by the relay to decode a client contribution
func (e *DCNetEntity) DecodeTrustee(roundID int32, slice []byte) {

	dcNetCipher := DCNetCipherFromBytes(slice)

	if roundID != e.DCNetRoundDecoder.currentRoundBeingDecoded {
		panic("Cannot DecodeClient for round" +
			strconv.Itoa(int(roundID)) + ", we are in round " + strconv.Itoa(int(e.DCNetRoundDecoder.currentRoundBeingDecoded)))
	}

	for i := range dcNetCipher.Payload {
		e.DCNetRoundDecoder.xorBuffer[i] ^= dcNetCipher.Payload[i]
	}

	if e.EquivocationProtectionEnabled {
		e.DCNetRoundDecoder.equivTrusteeContribs = append(e.DCNetRoundDecoder.equivTrusteeContribs, dcNetCipher.EquivocationProtectionTag)
	}
}

// Called on the relay to decode the cell, after having stored the cryptographic materials
func (e *DCNetEntity) DecodeCell() []byte {
	//No Equivocation -> just XOR
	d := e.DCNetRoundDecoder

	decoded := d.xorBuffer
	if e.EquivocationProtectionEnabled {
		decoded = e.equivocationProtection.RelayDecode(d.xorBuffer, d.equivTrusteeContribs, d.equivClientContribs)
	}

	return decoded
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package util

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

// CompileProtos regenerates all of the generated source code for the Showcase
// API including the generated messages, gRPC services, go gapic clients,
// and the generated CLI. This must be ran from the root directory
// of the gapic-showcase repository.
func CompileProtos(version string) {
	// Check if protoc is installed.
	if err := exec.Command("protoc", "--version").Run(); err != nil {
		log.Fatal("Error: 'protoc' is expected to be installed on the path.")
	}

	// Setup paths
	pwd, err := os.Getwd()
	if err != nil {
		log.Fatalf("Error: unable to get working dir: %+v", err)
	}
	gosrc, err := filepath.Abs(
		filepath.Join(pwd, "..", "..", ".."),
	)
	if err != nil || !strings.HasSuffix(gosrc, filepath.Join("go", "src")) {
		log.Fatal("Error: could not find the go src path from the current working dir.")
	}

	protos := filepath.Join("schema", "google", "showcase", version, "*.proto")
	files, err := filepath.Glob(protos)
	if err != nil {
		log.Fatal("Error: failed to find protos in " + protos)
	}

	// Run protoc
	command := []string{
		"protoc",
		"--proto_path=schema/api-common-protos",
		"--proto_path=schema",
		"--go_cli_out=" + filepath.Join("cmd", "gapic-showcase"),
		"--go_cli_opt=root=gapic-showcase",
		"--go_cli_opt=gapic=github.com/googleapis/gapic-showcase/client",
		"--go_cli_opt=fmt=false",
		"--go_gapic_out=" + gosrc,
		"--go_gapic_opt=go-gapic-package=github.com/googleapis/gapic-showcase/client;client",
		"--go_gapic_opt=grpc-service-config=schema/google/showcase/v1beta1/showcase_grpc_service_config.json",
		"--go_out=plugins=grpc:" + gosrc,
	}
	Execute(append(command, files...)...)

	// Fix some generated errors.
	fixes := []struct {
		file string
		fix  string
	}{
		{
			"cmd/gapic-showcase/verify-test.go",
			"/ByteSliceVar/d",
		},
		{
			"cmd/gapic-showcase/wait.go",
			"s/EndEnd_time/EndEndTime/g",
		},
	}
	command = []string{
		"sed",
		"-i.bak",
	}
	for _, f := range fixes {
		Execute(append(command, f.fix, f.file)...)

		// Remove the backup file.
		Execute("rm", fmt.Sprintf("%s.bak", f.file))
	}

	// Format generated output
	Execute("go", "fmt", "./...")
}
<commit_msg>chore: update compile_protos to not be GOPATH based (#252)<commit_after>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package util

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
)

// CompileProtos regenerates all of the generated source code for the Showcase
// API including the generated messages, gRPC services, go gapic clients,
// and the generated CLI. This must be ran from the root directory
// of the gapic-showcase repository.
func CompileProtos(version string) {
	// Check if protoc is installed.
	if err := exec.Command("protoc", "--version").Run(); err != nil {
		log.Fatal("Error: 'protoc' is expected to be installed on the path.")
	}

	// Setup paths
	pwd, err := os.Getwd()
	if err != nil {
		log.Fatalf("Error: unable to get working dir: %+v", err)
	}

	outDir, err := ioutil.TempDir(os.TempDir(), "gapic-showcase")
	if err != nil {
		log.Fatalf("Error: unable to create a temporary dir: %+v\n", err)
	}
	defer os.RemoveAll(outDir)

	protos := filepath.Join("schema", "google", "showcase", version, "*.proto")
	files, err := filepath.Glob(protos)
	if err != nil {
		log.Fatal("Error: failed to find protos in " + protos)
	}

	// Run protoc
	command := []string{
		"protoc",
		"--proto_path=schema/api-common-protos",
		"--proto_path=schema",
		"--go_cli_out=" + filepath.Join("cmd", "gapic-showcase"),
		"--go_cli_opt=root=gapic-showcase",
		"--go_cli_opt=gapic=github.com/googleapis/gapic-showcase/client",
		"--go_cli_opt=fmt=false",
		"--go_gapic_out=" + outDir,
		"--go_gapic_opt=go-gapic-package=github.com/googleapis/gapic-showcase/client;client",
		"--go_gapic_opt=grpc-service-config=schema/google/showcase/v1beta1/showcase_grpc_service_config.json",
		"--go_out=plugins=grpc:" + outDir,
	}
	Execute(append(command, files...)...)

	// Copy generated code back into repo.
	tempClient := filepath.Join(outDir, "github.com", "googleapis", "gapic-showcase", "client")
	tempServer := filepath.Join(outDir, "github.com", "googleapis", "gapic-showcase", "server")
	command = []string{
		"cp",
		"-r",
		tempClient,
		tempServer,
		pwd,
	}
	Execute(command...)

	// Fix some generated errors.
	fixes := []struct {
		file string
		fix  string
	}{
		{
			"cmd/gapic-showcase/verify-test.go",
			"/ByteSliceVar/d",
		},
		{
			"cmd/gapic-showcase/wait.go",
			"s/EndEnd_time/EndEndTime/g",
		},
	}
	command = []string{
		"sed",
		"-i.bak",
	}
	for _, f := range fixes {
		Execute(append(command, f.fix, f.file)...)

		// Remove the backup file.
		Execute("rm", fmt.Sprintf("%s.bak", f.file))
	}

	// Format generated output
	Execute("go", "fmt", "./...")
}
<|endoftext|>


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

package sys

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/e-XpertSolutions/f5-rest-client/f5"
)

// FileSSLCertConfigList holds a list of FileSSLCert configuration.
type FileSSLCertConfigList struct {
	Items    []FileSSLCertConfig `json:"items"`
	Kind     string              `json:"kind"`
	SelfLink string              `json:"selflink"`
}

// FileSSLCertConfig holds the configuration of a single FileSSLCert.
type FileSSLCertConfig struct {
	BundleCertificatesReference struct {
		IsSubcollection bool   `json:"isSubcollection"`
		Link            string `json:"link"`
	} `json:"bundleCertificatesReference"`
	CertificateKeyCurveName string `json:"certificateKeyCurveName"`
	CertificateKeySize      int    `json:"certificateKeySize"`
	Checksum                string `json:"checksum"`
	CreateTime              string `json:"createTime"`
	CreatedBy               string `json:"createdBy"`
	ExpirationDate          int64  `json:"expirationDate"`
	ExpirationString        string `json:"expirationString"`
	FullPath                string `json:"fullPath"`
	Generation              int    `json:"generation"`
	IsBundle                string `json:"isBundle"`
	Issuer                  string `json:"issuer"`
	KeyType                 string `json:"keyType"`
	Kind                    string `json:"kind"`
	LastUpdateTime          string `json:"lastUpdateTime"`
	Mode                    int    `json:"mode"`
	Name                    string `json:"name"`
	Partition               string `json:"partition"`
	Revision                int    `json:"revision"`
	SelfLink                string `json:"selfLink"`
	SerialNumber            string `json:"serialNumber"`
	Size                    int    `json:"size"`
	Subject                 string `json:"subject"`
	SystemPath              string `json:"systemPath"`
	UpdatedBy               string `json:"updatedBy"`
	Version                 int    `json:"version"`
}

// FileSSLCertEndpoint represents the REST resource for managing FileSSLCert.
const FileSSLCertEndpoint = "/file/ssl-cert"

// FileSSLCertResource provides an API to manage FileSSLCert configurations.
type FileSSLCertResource struct {
	c f5.Client
}

// ListAll  lists all the FileSSLCert configurations.
func (r *FileSSLCertResource) ListAll() (*FileSSLCertConfigList, error) {
	var list FileSSLCertConfigList
	if err := r.c.ReadQuery(BasePath+FileSSLCertEndpoint, &list); err != nil {
		return nil, err
	}
	return &list, nil
}

// ListExpired lists all expired certificates.
func (r *FileSSLCertResource) ListExpired() (*FileSSLCertConfigList, error) {
	var list FileSSLCertConfigList
	if err := r.c.ReadQuery(BasePath+FileSSLCertEndpoint, &list); err != nil {
		return nil, err
	}

	var expiredList []FileSSLCertConfig

	for _, cert := range list.Items {
		if time.Now().After(time.Unix(cert.ExpirationDate, 0)) {
			expiredList = append(expiredList, cert)
		}
	}

	expConfigList := FileSSLCertConfigList{
		Items:    expiredList,
		Kind:     list.Kind,
		SelfLink: list.SelfLink,
	}

	return &expConfigList, nil
}

// Get a single FileSSLCert configuration identified by id.
func (r *FileSSLCertResource) Get(id string) (*FileSSLCertConfig, error) {
	var item FileSSLCertConfig
	if err := r.c.ReadQuery(BasePath+FileSSLCertEndpoint, &item); err != nil {
		return nil, err
	}
	return &item, nil
}

// Create a new FileSSLCert configuration.
func (r *FileSSLCertResource) Create(name, path string) error {
	info, err := os.Stat(path)
	if err != nil {
		return fmt.Errorf("failed to gather information about '%s': %v", path, err)
	}
	f, err := os.Open(path)
	if err != nil {
		return fmt.Errorf("failed to read file from path: %v", err)
	}
	defer f.Close()

	req, err := r.c.MakeUploadRequest(f5.UploadRESTPath+"/"+filepath.Base(path), f, info.Size())
	if err != nil {
		return fmt.Errorf("failed to create upload request: %v", err)
	}
	resp, err := r.c.Do(req)
	if err != nil {
		return fmt.Errorf("failed to upload file '%s': %v", path, err)
	}
	defer resp.Body.Close()

	bs, _ := ioutil.ReadAll(resp.Body)
	log.Print("DEBUG resp=", string(bs))

	data := map[string]string{
		"name":        name,
		"source-path": "file://localhost/var/config/rest/downloads/" + filepath.Base(path),
	}
	if err := r.c.ModQuery("POST", BasePath+FileSSLCertEndpoint, data); err != nil {
		return fmt.Errorf("failed to create FileSSLCert configuration: %v", err)
	}

	return nil
}

// Edit a FileSSLCert configuration identified by id.
func (r *FileSSLCertResource) Edit(id, path string) error {
	info, err := os.Stat(path)
	if err != nil {
		return fmt.Errorf("failed to gather information about '%s': %v", path, err)
	}
	f, err := os.Open(path)
	if err != nil {
		return fmt.Errorf("failed to read file from path: %v", err)
	}
	defer f.Close()

	req, err := r.c.MakeUploadRequest(f5.UploadRESTPath+"/"+filepath.Base(path), f, info.Size())
	if err != nil {
		return fmt.Errorf("failed to create upload request: %v", err)
	}
	resp, err := r.c.Do(req)
	if err != nil {
		return fmt.Errorf("failed to upload file '%s': %v", path, err)
	}
	defer resp.Body.Close()

	bs, _ := ioutil.ReadAll(resp.Body)
	log.Print("DEBUG resp=", string(bs))

	data := map[string]string{
		"source-path": "file://localhost/var/config/rest/downloads/" + filepath.Base(path),
	}
	if err := r.c.ModQuery("PUT", BasePath+FileSSLCertEndpoint+"/"+id, data); err != nil {
		return fmt.Errorf("failed to create FileSSLCert configuration: %v", err)
	}

	return nil
}

// Delete a single FileSSLCert configuration identified by id.
func (r *FileSSLCertResource) Delete(id string) error {
	if err := r.c.ModQuery("DELETE", BasePath+FileSSLCertEndpoint+"/"+id, nil); err != nil {
		return err
	}
	return nil
}
<commit_msg>f5/sys/ssl: Add a function to list Expiring Certificates<commit_after>// Copyright e-Xpert Solutions SA. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sys

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/e-XpertSolutions/f5-rest-client/f5"
)

// FileSSLCertConfigList holds a list of FileSSLCert configuration.
type FileSSLCertConfigList struct {
	Items    []FileSSLCertConfig `json:"items"`
	Kind     string              `json:"kind"`
	SelfLink string              `json:"selflink"`
}

// FileSSLCertConfig holds the configuration of a single FileSSLCert.
type FileSSLCertConfig struct {
	BundleCertificatesReference struct {
		IsSubcollection bool   `json:"isSubcollection"`
		Link            string `json:"link"`
	} `json:"bundleCertificatesReference"`
	CertificateKeyCurveName string `json:"certificateKeyCurveName"`
	CertificateKeySize      int    `json:"certificateKeySize"`
	Checksum                string `json:"checksum"`
	CreateTime              string `json:"createTime"`
	CreatedBy               string `json:"createdBy"`
	ExpirationDate          int64  `json:"expirationDate"`
	ExpirationString        string `json:"expirationString"`
	FullPath                string `json:"fullPath"`
	Generation              int    `json:"generation"`
	IsBundle                string `json:"isBundle"`
	Issuer                  string `json:"issuer"`
	KeyType                 string `json:"keyType"`
	Kind                    string `json:"kind"`
	LastUpdateTime          string `json:"lastUpdateTime"`
	Mode                    int    `json:"mode"`
	Name                    string `json:"name"`
	Partition               string `json:"partition"`
	Revision                int    `json:"revision"`
	SelfLink                string `json:"selfLink"`
	SerialNumber            string `json:"serialNumber"`
	Size                    int    `json:"size"`
	Subject                 string `json:"subject"`
	SystemPath              string `json:"systemPath"`
	UpdatedBy               string `json:"updatedBy"`
	Version                 int    `json:"version"`
}

// FileSSLCertEndpoint represents the REST resource for managing FileSSLCert.
const FileSSLCertEndpoint = "/file/ssl-cert"

// FileSSLCertResource provides an API to manage FileSSLCert configurations.
type FileSSLCertResource struct {
	c f5.Client
}

// ListAll  lists all the FileSSLCert configurations.
func (r *FileSSLCertResource) ListAll() (*FileSSLCertConfigList, error) {
	var list FileSSLCertConfigList
	if err := r.c.ReadQuery(BasePath+FileSSLCertEndpoint, &list); err != nil {
		return nil, err
	}
	return &list, nil
}

// ListExpired lists all expired certificates.
func (r *FileSSLCertResource) ListExpired() (*FileSSLCertConfigList, error) {
	var list FileSSLCertConfigList
	if err := r.c.ReadQuery(BasePath+FileSSLCertEndpoint, &list); err != nil {
		return nil, err
	}

	var expiredList []FileSSLCertConfig

	for _, cert := range list.Items {
		if time.Now().After(time.Unix(cert.ExpirationDate, 0)) {
			expiredList = append(expiredList, cert)
		}
	}

	expConfigList := FileSSLCertConfigList{
		Items:    expiredList,
		Kind:     list.Kind,
		SelfLink: list.SelfLink,
	}

	return &expConfigList, nil
}

// ListExpiring lists all expiring certificates.
func (r *FileSSLCertResource) ListExpiring(sec int64) (*FileSSLCertConfigList, error) {
	var list FileSSLCertConfigList
	if err := r.c.ReadQuery(BasePath+FileSSLCertEndpoint, &list); err != nil {
		return nil, err
	}

	var expiringList []FileSSLCertConfig

	for _, cert := range list.Items {
		if time.Now().After(time.Unix(cert.ExpirationDate-sec, 0)) && time.Now().Before(time.Unix(cert.ExpirationDate, 0)) {
			expiringList = append(expiringList, cert)
		}
	}

	expConfigList := FileSSLCertConfigList{
		Items:    expiringList,
		Kind:     list.Kind,
		SelfLink: list.SelfLink,
	}

	return &expConfigList, nil
}

// Get a single FileSSLCert configuration identified by id.
func (r *FileSSLCertResource) Get(id string) (*FileSSLCertConfig, error) {
	var item FileSSLCertConfig
	if err := r.c.ReadQuery(BasePath+FileSSLCertEndpoint, &item); err != nil {
		return nil, err
	}
	return &item, nil
}

// Create a new FileSSLCert configuration.
func (r *FileSSLCertResource) Create(name, path string) error {
	info, err := os.Stat(path)
	if err != nil {
		return fmt.Errorf("failed to gather information about '%s': %v", path, err)
	}
	f, err := os.Open(path)
	if err != nil {
		return fmt.Errorf("failed to read file from path: %v", err)
	}
	defer f.Close()

	req, err := r.c.MakeUploadRequest(f5.UploadRESTPath+"/"+filepath.Base(path), f, info.Size())
	if err != nil {
		return fmt.Errorf("failed to create upload request: %v", err)
	}
	resp, err := r.c.Do(req)
	if err != nil {
		return fmt.Errorf("failed to upload file '%s': %v", path, err)
	}
	defer resp.Body.Close()

	bs, _ := ioutil.ReadAll(resp.Body)
	log.Print("DEBUG resp=", string(bs))

	data := map[string]string{
		"name":        name,
		"source-path": "file://localhost/var/config/rest/downloads/" + filepath.Base(path),
	}
	if err := r.c.ModQuery("POST", BasePath+FileSSLCertEndpoint, data); err != nil {
		return fmt.Errorf("failed to create FileSSLCert configuration: %v", err)
	}

	return nil
}

// Edit a FileSSLCert configuration identified by id.
func (r *FileSSLCertResource) Edit(id, path string) error {
	info, err := os.Stat(path)
	if err != nil {
		return fmt.Errorf("failed to gather information about '%s': %v", path, err)
	}
	f, err := os.Open(path)
	if err != nil {
		return fmt.Errorf("failed to read file from path: %v", err)
	}
	defer f.Close()

	req, err := r.c.MakeUploadRequest(f5.UploadRESTPath+"/"+filepath.Base(path), f, info.Size())
	if err != nil {
		return fmt.Errorf("failed to create upload request: %v", err)
	}
	resp, err := r.c.Do(req)
	if err != nil {
		return fmt.Errorf("failed to upload file '%s': %v", path, err)
	}
	defer resp.Body.Close()

	bs, _ := ioutil.ReadAll(resp.Body)
	log.Print("DEBUG resp=", string(bs))

	data := map[string]string{
		"source-path": "file://localhost/var/config/rest/downloads/" + filepath.Base(path),
	}
	if err := r.c.ModQuery("PUT", BasePath+FileSSLCertEndpoint+"/"+id, data); err != nil {
		return fmt.Errorf("failed to create FileSSLCert configuration: %v", err)
	}

	return nil
}

// Delete a single FileSSLCert configuration identified by id.
func (r *FileSSLCertResource) Delete(id string) error {
	if err := r.c.ModQuery("DELETE", BasePath+FileSSLCertEndpoint+"/"+id, nil); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"flag"
	"fmt"
	"github.com/github/git-media/gitmedia"
	"github.com/spf13/cobra"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime/debug"
	"strings"
	"time"
)

var (
	Debugging    = false
	ErrorBuffer  = &bytes.Buffer{}
	ErrorWriter  = io.MultiWriter(os.Stderr, ErrorBuffer)
	OutputWriter = io.MultiWriter(os.Stdout, ErrorBuffer)
	commands     = make(map[string]func(*Command) RunnableCommand)
	RootCmd      = &cobra.Command{
		Use:   "git-media",
		Short: "Git Media provides large file support to Git.",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("Git Media, yo")
		},
	}
)

// Error prints a formatted message to Stderr.  It also gets printed to the
// panic log if one is created for this command.
func Error(format string, args ...interface{}) {
	line := fmt.Sprintf(format, args...)
	fmt.Fprintln(ErrorWriter, line)
}

// Print prints a formatted message to Stdout.  It also gets printed to the
// panic log if one is created for this command.
func Print(format string, args ...interface{}) {
	line := fmt.Sprintf(format, args...)
	fmt.Fprintln(OutputWriter, line)
}

// Exit prints a formatted message and exits.
func Exit(format string, args ...interface{}) {
	Error(format, args...)
	os.Exit(2)
}

// Debug prints a formatted message if debugging is enabled.  The formatted
// message also shows up in the panic log, if created.
func Debug(format string, args ...interface{}) {
	if !Debugging {
		return
	}
	log.Printf(format, args...)
}

// Panic prints a formatted message, and writes a stack trace for the error to
// a log file before exiting.
func Panic(err error, format string, args ...interface{}) {
	Error(format, args...)
	file := handlePanic(err)

	if len(file) > 0 {
		fmt.Fprintf(os.Stderr, "\nErrors logged to %s.\nUse `git media logs last` to view the log.\n", file)
	}
	os.Exit(2)
}

func Run() {
	if err := RootCmd.Execute(); err == nil {
		return
	}

	runcmd := true
	subname := SubCommand(1)

	if subname == "help" {
		runcmd = false
		subname = SubCommand(2)
	}

	cmd := NewCommand(filepath.Base(os.Args[0]), subname)
	cmdcb, ok := commands[subname]
	if ok {
		subcmd := cmdcb(cmd)
		subcmd.Setup()

		if runcmd {
			subcmd.Parse()
			subcmd.Run()
		} else {
			subcmd.Usage()
		}
	} else {
		missingCommand(cmd, subname)
	}
}

func SubCommand(pos int) string {
	if len(os.Args) < (pos + 1) {
		return "version"
	} else {
		return os.Args[pos]
	}
}

func NewCommand(name, subname string) *Command {
	var args []string
	if len(os.Args) > 1 {
		args = os.Args[2:]
	}

	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	setupDebugging(fs)
	fs.SetOutput(ErrorWriter)

	return &Command{name, subname, fs, args, args}
}

func PipeMediaCommand(name string, args ...string) error {
	return PipeCommand("bin/"+name, args...)
}

func PipeCommand(name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

type RunnableCommand interface {
	Setup()
	Parse()
	Run()
	Usage()
}

type Command struct {
	Name        string
	SubCommand  string
	FlagSet     *flag.FlagSet
	Args        []string
	SubCommands []string
}

func (c *Command) Usage() {
	Print("usage: %s %s", c.Name, c.SubCommand)
	c.FlagSet.PrintDefaults()
}

func (c *Command) Parse() {
	c.FlagSet.Parse(c.Args)
	c.SubCommands = c.FlagSet.Args()
}

func (c *Command) Setup() {}
func (c *Command) Run()   {}

func registerCommand(name string, cmdcb func(*Command) RunnableCommand) {
	commands[name] = cmdcb
}

func missingCommand(cmd *Command, subname string) {
	Error("%s: '%s' is not a %s command.  See %s help.",
		cmd.Name, subname, cmd.Name, cmd.Name)
}

func setupDebugging(flagset *flag.FlagSet) {
	if flagset == nil {
		flag.BoolVar(&Debugging, "debug", false, "Turns debugging on")
	} else {
		flagset.BoolVar(&Debugging, "debug", false, "Turns debugging on")
	}
}

func handlePanic(err error) string {
	if err == nil {
		return ""
	}

	Debug(err.Error())
	logFile, logErr := logPanic(err)
	if logErr != nil {
		fmt.Fprintf(os.Stderr, "Unable to log panic to %s - %s\n\n", gitmedia.LocalLogDir, err)
		logEnv(os.Stderr)
	}

	return logFile
}

func logEnv(w io.Writer) {
	for _, env := range gitmedia.Environ() {
		fmt.Fprintln(w, env)
	}
}

func logPanic(loggedError error) (string, error) {
	if err := os.MkdirAll(gitmedia.LocalLogDir, 0744); err != nil {
		return "", err
	}

	now := time.Now()
	name := now.Format("2006-01-02T15:04:05.999999999")
	full := filepath.Join(gitmedia.LocalLogDir, name+".log")

	file, err := os.Create(full)
	if err != nil {
		return "", err
	}

	defer file.Close()

	fmt.Fprintf(file, "> %s", filepath.Base(os.Args[0]))
	if len(os.Args) > 0 {
		fmt.Fprintf(file, " %s", strings.Join(os.Args[1:], " "))
	}
	fmt.Fprint(file, "\n")

	logEnv(file)
	fmt.Fprint(file, "\n")

	file.Write(ErrorBuffer.Bytes())
	fmt.Fprint(file, "\n")

	fmt.Fprintln(file, loggedError.Error())
	file.Write(debug.Stack())

	return full, nil
}

func init() {
	log.SetOutput(ErrorWriter)
}
<commit_msg>ンンンン ン<commit_after>package commands

import (
	"bytes"
	"flag"
	"fmt"
	"github.com/github/git-media/gitmedia"
	"github.com/spf13/cobra"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime/debug"
	"strings"
	"time"
)

var (
	Debugging    = false
	ErrorBuffer  = &bytes.Buffer{}
	ErrorWriter  = io.MultiWriter(os.Stderr, ErrorBuffer)
	OutputWriter = io.MultiWriter(os.Stdout, ErrorBuffer)
	commands     = make(map[string]func(*Command) RunnableCommand)
	RootCmd      = &cobra.Command{
		Use:   "git-media",
		Short: "Git Media provides large file support to Git.",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("Git Media, yo")
		},
	}
)

// Error prints a formatted message to Stderr.  It also gets printed to the
// panic log if one is created for this command.
func Error(format string, args ...interface{}) {
	line := fmt.Sprintf(format, args...)
	fmt.Fprintln(ErrorWriter, line)
}

// Print prints a formatted message to Stdout.  It also gets printed to the
// panic log if one is created for this command.
func Print(format string, args ...interface{}) {
	line := fmt.Sprintf(format, args...)
	fmt.Fprintln(OutputWriter, line)
}

// Exit prints a formatted message and exits.
func Exit(format string, args ...interface{}) {
	Error(format, args...)
	os.Exit(2)
}

// Debug prints a formatted message if debugging is enabled.  The formatted
// message also shows up in the panic log, if created.
func Debug(format string, args ...interface{}) {
	if !Debugging {
		return
	}
	log.Printf(format, args...)
}

// Panic prints a formatted message, and writes a stack trace for the error to
// a log file before exiting.
func Panic(err error, format string, args ...interface{}) {
	Error(format, args...)
	file := handlePanic(err)

	if len(file) > 0 {
		fmt.Fprintf(os.Stderr, "\nErrors logged to %s.\nUse `git media logs last` to view the log.\n", file)
	}
	os.Exit(2)
}

func Run() {
	RootCmd.Execute()
}

func NewCommand(name, subname string) *Command {
	var args []string
	if len(os.Args) > 1 {
		args = os.Args[2:]
	}

	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	setupDebugging(fs)
	fs.SetOutput(ErrorWriter)

	return &Command{name, subname, fs, args, args}
}

func PipeMediaCommand(name string, args ...string) error {
	return PipeCommand("bin/"+name, args...)
}

func PipeCommand(name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

type RunnableCommand interface {
	Setup()
	Parse()
	Run()
	Usage()
}

type Command struct {
	Name        string
	SubCommand  string
	FlagSet     *flag.FlagSet
	Args        []string
	SubCommands []string
}

func (c *Command) Usage() {
	Print("usage: %s %s", c.Name, c.SubCommand)
	c.FlagSet.PrintDefaults()
}

func (c *Command) Parse() {
	c.FlagSet.Parse(c.Args)
	c.SubCommands = c.FlagSet.Args()
}

func (c *Command) Setup() {}
func (c *Command) Run()   {}

func registerCommand(name string, cmdcb func(*Command) RunnableCommand) {
	commands[name] = cmdcb
}

func missingCommand(cmd *Command, subname string) {
	Error("%s: '%s' is not a %s command.  See %s help.",
		cmd.Name, subname, cmd.Name, cmd.Name)
}

func setupDebugging(flagset *flag.FlagSet) {
	if flagset == nil {
		flag.BoolVar(&Debugging, "debug", false, "Turns debugging on")
	} else {
		flagset.BoolVar(&Debugging, "debug", false, "Turns debugging on")
	}
}

func handlePanic(err error) string {
	if err == nil {
		return ""
	}

	Debug(err.Error())
	logFile, logErr := logPanic(err)
	if logErr != nil {
		fmt.Fprintf(os.Stderr, "Unable to log panic to %s - %s\n\n", gitmedia.LocalLogDir, err)
		logEnv(os.Stderr)
	}

	return logFile
}

func logEnv(w io.Writer) {
	for _, env := range gitmedia.Environ() {
		fmt.Fprintln(w, env)
	}
}

func logPanic(loggedError error) (string, error) {
	if err := os.MkdirAll(gitmedia.LocalLogDir, 0744); err != nil {
		return "", err
	}

	now := time.Now()
	name := now.Format("2006-01-02T15:04:05.999999999")
	full := filepath.Join(gitmedia.LocalLogDir, name+".log")

	file, err := os.Create(full)
	if err != nil {
		return "", err
	}

	defer file.Close()

	fmt.Fprintf(file, "> %s", filepath.Base(os.Args[0]))
	if len(os.Args) > 0 {
		fmt.Fprintf(file, " %s", strings.Join(os.Args[1:], " "))
	}
	fmt.Fprint(file, "\n")

	logEnv(file)
	fmt.Fprint(file, "\n")

	file.Write(ErrorBuffer.Bytes())
	fmt.Fprint(file, "\n")

	fmt.Fprintln(file, loggedError.Error())
	file.Write(debug.Stack())

	return full, nil
}

func init() {
	log.SetOutput(ErrorWriter)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/base64"
	"fmt"
	"strconv"

	// "golang.org/x/net/context"

	pubsub "google.golang.org/api/pubsub/v1"

	log "github.com/Sirupsen/logrus"
)

type (
	Publisher interface {
		Publish(topic string, msg *pubsub.PubsubMessage) (*pubsub.PublishResponse, error)
	}

	pubsubPublisher struct {
		topicsService *pubsub.ProjectsTopicsService
	}
)

func (pp *pubsubPublisher) Publish(topic string, msg *pubsub.PubsubMessage) (*pubsub.PublishResponse, error) {
	req := &pubsub.PublishRequest{
		Messages: []*pubsub.PubsubMessage{msg},
	}
	return pp.topicsService.Publish(topic, req).Do()
}

type Progress int

const (
	PREPARING Progress = 1 + iota
	WORKING
	RETRYING
	INVALID_JOB
	COMPLETED
)

type ProgressConfig struct {
	Topic    string `json:"topic"`
	LogLevel string `json:"log_level"`
}

func (c *ProgressConfig) setup() {
	if c.LogLevel == "" {
		c.LogLevel = log.InfoLevel.String()
	}
}

type ProgressNotification struct {
	config    *ProgressConfig
	publisher Publisher
	logLevel  log.Level
}

func (pn *ProgressNotification) wrap(msg_id string, step JobStep, f func() error) func() error {
	return func() error {
		pn.notify(msg_id, step, STARTING)
		err := f()
		if err != nil {
			pn.notifyWithMessage(msg_id, step, FAILURE, err.Error())
			return err
		}
		pn.notify(msg_id, step, SUCCESS)
		return nil
	}
}

func (pn *ProgressNotification) notify(job_msg_id string, step JobStep, st JobStepStatus) error {
	msg := fmt.Sprintf("%v %v", step, st)
	return pn.notifyWithMessage(job_msg_id, step, st, msg)
}

func (pn *ProgressNotification) notifyWithMessage(job_msg_id string, step JobStep, st JobStepStatus, msg string) error {
	return pn.notifyProgress(job_msg_id, step.progressFor(st), step.completed(st), step.logLevelFor(st), msg)
}

func (pn *ProgressNotification) notifyProgress(job_msg_id string, progress Progress, completed bool, level log.Level, data string) error {

	opts := map[string]string{
		"progress":       strconv.Itoa(int(progress)),
		"completed":      strconv.FormatBool(completed),
		"job_message_id": job_msg_id,
		"level":          level.String(),
	}
	logAttrs := log.Fields{}
	for k, v := range opts {
		logAttrs[k] = v
	}
	log.WithFields(logAttrs).Debugln("Publishing notification")
	m := &pubsub.PubsubMessage{Data: base64.StdEncoding.EncodeToString([]byte(data)), Attributes: opts}
	_, err := pn.publisher.Publish(pn.config.Topic, m)
	if err != nil {
		logAttrs["error"] = err
		log.WithFields(logAttrs).Debugln("Failed to publish notification")
		return err
	}
	return nil
}
<commit_msg>:+1: Don't publish the message with higher log level than log level in config<commit_after>package main

import (
	"encoding/base64"
	"fmt"
	"strconv"

	// "golang.org/x/net/context"

	pubsub "google.golang.org/api/pubsub/v1"

	log "github.com/Sirupsen/logrus"
)

type (
	Publisher interface {
		Publish(topic string, msg *pubsub.PubsubMessage) (*pubsub.PublishResponse, error)
	}

	pubsubPublisher struct {
		topicsService *pubsub.ProjectsTopicsService
	}
)

func (pp *pubsubPublisher) Publish(topic string, msg *pubsub.PubsubMessage) (*pubsub.PublishResponse, error) {
	req := &pubsub.PublishRequest{
		Messages: []*pubsub.PubsubMessage{msg},
	}
	return pp.topicsService.Publish(topic, req).Do()
}

type Progress int

const (
	PREPARING Progress = 1 + iota
	WORKING
	RETRYING
	INVALID_JOB
	COMPLETED
)

type ProgressConfig struct {
	Topic    string `json:"topic"`
	LogLevel string `json:"log_level"`
}

func (c *ProgressConfig) setup() {
	if c.LogLevel == "" {
		c.LogLevel = log.InfoLevel.String()
	}
}

type ProgressNotification struct {
	config    *ProgressConfig
	publisher Publisher
	logLevel  log.Level
}

func (pn *ProgressNotification) wrap(msg_id string, step JobStep, f func() error) func() error {
	return func() error {
		pn.notify(msg_id, step, STARTING)
		err := f()
		if err != nil {
			pn.notifyWithMessage(msg_id, step, FAILURE, err.Error())
			return err
		}
		pn.notify(msg_id, step, SUCCESS)
		return nil
	}
}

func (pn *ProgressNotification) notify(job_msg_id string, step JobStep, st JobStepStatus) error {
	msg := fmt.Sprintf("%v %v", step, st)
	return pn.notifyWithMessage(job_msg_id, step, st, msg)
}

func (pn *ProgressNotification) notifyWithMessage(job_msg_id string, step JobStep, st JobStepStatus, msg string) error {
	return pn.notifyProgress(job_msg_id, step.progressFor(st), step.completed(st), step.logLevelFor(st), msg)
}

func (pn *ProgressNotification) notifyProgress(job_msg_id string, progress Progress, completed bool, level log.Level, data string) error {
	// https://godoc.org/github.com/sirupsen/logrus#Level
	// log.InfoLevel < log.DebugLevel => true
	if pn.logLevel < level {
		return nil
	}
	opts := map[string]string{
		"progress":       strconv.Itoa(int(progress)),
		"completed":      strconv.FormatBool(completed),
		"job_message_id": job_msg_id,
		"level":          level.String(),
	}
	logAttrs := log.Fields{}
	for k, v := range opts {
		logAttrs[k] = v
	}
	log.WithFields(logAttrs).Debugln("Publishing notification")
	m := &pubsub.PubsubMessage{Data: base64.StdEncoding.EncodeToString([]byte(data)), Attributes: opts}
	_, err := pn.publisher.Publish(pn.config.Topic, m)
	if err != nil {
		logAttrs["error"] = err
		log.WithFields(logAttrs).Debugln("Failed to publish notification")
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"strings"
)

// ErrorType signfies a category of errors
type ErrorType uint

// ErrorTypes convey what category of error ocurred
const (
	ErrNormal ErrorType = iota // general errors
	ErrClient                  // error was caused by the client, (e.g. invalid CLI usage)
	// TODO: add more types of errors for better error-specific handling
)

// Error is a struct for marshalling errors
type Error struct {
	Message string
	Code    ErrorType
}

func (e Error) Error() string {
	return e.Message
}

// EncodingType defines a supported encoding
type EncodingType string

// Supported EncodingType constants.
const (
	JSON = "json"
	XML  = "xml"
	Text = "text"
	// TODO: support more encoding types
)

var marshallers = map[EncodingType]Marshaller{
	JSON: func(res Response) ([]byte, error) {
		if res.Error() != nil {
			return json.Marshal(res.Error())
		}
		return json.Marshal(res.Output())
	},
	XML: func(res Response) ([]byte, error) {
		if res.Error() != nil {
			return xml.Marshal(res.Error())
		}
		return xml.Marshal(res.Output())
	},
	Text: func(res Response) ([]byte, error) {
		format := res.Request().Command().Format
		if format == nil {
			return nil, ErrNoFormatter
		}

		bytes, err := format(res)
		if err != nil {
			return nil, err
		}
		return bytes, nil
	},
}

// Response is the result of a command request. Handlers write to the response,
// setting Error or Value. Response is returned to the client.
type Response interface {
	Request() Request

	// Set/Return the response Error
	SetError(err error, code ErrorType)
	Error() *Error

	// Sets/Returns the response value
	SetOutput(interface{})
	Output() interface{}

	// Marshal marshals out the response into a buffer. It uses the EncodingType
	// on the Request to chose a Marshaller (Codec).
	Marshal() ([]byte, error)

	// Gets a io.Reader that reads the marshalled output
	Reader() (io.Reader, error)
}

type response struct {
	req   Request
	err   *Error
	value interface{}
	out   io.Reader
}

func (r *response) Request() Request {
	return r.req
}

func (r *response) Output() interface{} {
	return r.value
}

func (r *response) SetOutput(v interface{}) {
	r.value = v
}

func (r *response) Error() *Error {
	return r.err
}

func (r *response) SetError(err error, code ErrorType) {
	r.err = &Error{Message: err.Error(), Code: code}
}

func (r *response) Marshal() ([]byte, error) {
	if r.err == nil && r.value == nil {
		return []byte{}, nil
	}

	enc, ok := r.req.Option(EncShort)
	if !ok || enc.(string) == "" {
		return nil, fmt.Errorf("No encoding type was specified")
	}
	encType := EncodingType(strings.ToLower(enc.(string)))

	marshaller, ok := marshallers[encType]
	if !ok {
		return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc)
	}

	return marshaller(r)
}

// Reader returns an `io.Reader` representing marshalled output of this Response
// Note that multiple calls to this will return a reference to the same io.Reader
func (r *response) Reader() (io.Reader, error) {
	// if command set value to a io.Reader, use that as our reader
	if r.out == nil {
		if out, ok := r.value.(io.Reader); ok {
			r.out = out
		}
	}

	if r.out == nil {
		// no reader set, so marshal the error or value
		marshalled, err := r.Marshal()
		if err != nil {
			return nil, err
		}

		// create a Reader from the marshalled data
		r.out = bytes.NewReader(marshalled)
	}

	return r.out, nil
}

// NewResponse returns a response to match given Request
func NewResponse(req Request) Response {
	return &response{req: req}
}
<commit_msg>commands: Safer type coercion when choosing marshaller<commit_after>package commands

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"strings"
)

// ErrorType signfies a category of errors
type ErrorType uint

// ErrorTypes convey what category of error ocurred
const (
	ErrNormal ErrorType = iota // general errors
	ErrClient                  // error was caused by the client, (e.g. invalid CLI usage)
	// TODO: add more types of errors for better error-specific handling
)

// Error is a struct for marshalling errors
type Error struct {
	Message string
	Code    ErrorType
}

func (e Error) Error() string {
	return e.Message
}

// EncodingType defines a supported encoding
type EncodingType string

// Supported EncodingType constants.
const (
	JSON = "json"
	XML  = "xml"
	Text = "text"
	// TODO: support more encoding types
)

var marshallers = map[EncodingType]Marshaller{
	JSON: func(res Response) ([]byte, error) {
		if res.Error() != nil {
			return json.Marshal(res.Error())
		}
		return json.Marshal(res.Output())
	},
	XML: func(res Response) ([]byte, error) {
		if res.Error() != nil {
			return xml.Marshal(res.Error())
		}
		return xml.Marshal(res.Output())
	},
	Text: func(res Response) ([]byte, error) {
		format := res.Request().Command().Format
		if format == nil {
			return nil, ErrNoFormatter
		}

		bytes, err := format(res)
		if err != nil {
			return nil, err
		}
		return bytes, nil
	},
}

// Response is the result of a command request. Handlers write to the response,
// setting Error or Value. Response is returned to the client.
type Response interface {
	Request() Request

	// Set/Return the response Error
	SetError(err error, code ErrorType)
	Error() *Error

	// Sets/Returns the response value
	SetOutput(interface{})
	Output() interface{}

	// Marshal marshals out the response into a buffer. It uses the EncodingType
	// on the Request to chose a Marshaller (Codec).
	Marshal() ([]byte, error)

	// Gets a io.Reader that reads the marshalled output
	Reader() (io.Reader, error)
}

type response struct {
	req   Request
	err   *Error
	value interface{}
	out   io.Reader
}

func (r *response) Request() Request {
	return r.req
}

func (r *response) Output() interface{} {
	return r.value
}

func (r *response) SetOutput(v interface{}) {
	r.value = v
}

func (r *response) Error() *Error {
	return r.err
}

func (r *response) SetError(err error, code ErrorType) {
	r.err = &Error{Message: err.Error(), Code: code}
}

func (r *response) Marshal() ([]byte, error) {
	if r.err == nil && r.value == nil {
		return []byte{}, nil
	}

	enc, found := r.req.Option(EncShort)
	encStr, ok := enc.(string)
	if !found || !ok || encStr == "" {
		return nil, fmt.Errorf("No encoding type was specified")
	}
	encType := EncodingType(strings.ToLower(encStr))

	marshaller, ok := marshallers[encType]
	if !ok {
		return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc)
	}

	return marshaller(r)
}

// Reader returns an `io.Reader` representing marshalled output of this Response
// Note that multiple calls to this will return a reference to the same io.Reader
func (r *response) Reader() (io.Reader, error) {
	// if command set value to a io.Reader, use that as our reader
	if r.out == nil {
		if out, ok := r.value.(io.Reader); ok {
			r.out = out
		}
	}

	if r.out == nil {
		// no reader set, so marshal the error or value
		marshalled, err := r.Marshal()
		if err != nil {
			return nil, err
		}

		// create a Reader from the marshalled data
		r.out = bytes.NewReader(marshalled)
	}

	return r.out, nil
}

// NewResponse returns a response to match given Request
func NewResponse(req Request) Response {
	return &response{req: req}
}
<|endoftext|>


<commit_before>// Zero-downtime restarts in Go.

/*************************************************
*
*  modified version of goagain to support multiple listeners
*
*  - arslan
*************************************************/

package goagain

import (
	"errors"
	"fmt"
	"log"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"reflect"
	"syscall"
)

// Export an error equivalent to net.errClosing for use with Accept during
// a graceful exit.
var ErrClosing = errors.New("use of closed network connection")

// Block this goroutine awaiting signals.  With the exception of SIGTERM
// taking the place of SIGQUIT, signals are handled exactly as in Nginx
// and Unicorn: <http://unicorn.bogomips.org/SIGNALS.html>.
func AwaitSignals(listeners map[string]net.Listener) error {
	ch := make(chan os.Signal, 2)
	signal.Notify(ch, syscall.SIGTERM, syscall.SIGUSR2)
	for {
		sig := <-ch
		log.Println(sig.String())
		switch sig {

		// TODO SIGHUP should reload configuration.

		// SIGQUIT should exit gracefully.  However, Go doesn't seem
		// to like handling SIGQUIT (or any signal which dumps core by
		// default) at all so SIGTERM takes its place.  How graceful
		// this exit is depends on what the program does after this
		// function returns control.
		case syscall.SIGTERM:
			fmt.Printf("stopping listeners\n")
			return nil

		// TODO SIGUSR1 should reopen logs.

		// SIGUSR2 begins the process of restarting without dropping
		// the listener passed to this function.
		case syscall.SIGUSR2:
			log.Printf("relaunching listeners\n")
			for addr, _ := range listeners {
				log.Printf("\t%s\n", addr)
			}

			err := Relaunch(listeners)
			if nil != err {
				return err
			}

		}
	}
	return nil // It'll never get here.
}

// Convert and validate the GOAGAIN_FD, GOAGAIN_NAME, and GOAGAIN_PPID
// environment variables.  If all three are present and in order, this
// is a child process that may pick up where the parent left off.
func GetEnvs(addr string) (l net.Listener, ppid int, err error) {
	var fd uintptr
	_, err = fmt.Sscan(os.Getenv("GOAGAIN_FD"+addr), &fd)
	if nil != err {
		return
	}
	var i net.Listener
	i, err = net.FileListener(os.NewFile(fd, os.Getenv("GOAGAIN_NAME"+addr)))
	if nil != err {
		return
	}
	switch i.(type) {
	case *net.TCPListener:
		l = i.(*net.TCPListener)
	case *net.UnixListener:
		l = i.(*net.UnixListener)
	default:
		err = errors.New(fmt.Sprintf(
			"file descriptor is %T not *net.TCPListener or *net.UnixListener",
			i,
		))
		return
	}

	if err = syscall.Close(int(fd)); nil != err {
		return
	}
	_, err = fmt.Sscan(os.Getenv("GOAGAIN_PPID"), &ppid)
	if nil != err {
		return
	}
	if syscall.Getppid() != ppid {
		err = errors.New(fmt.Sprintf(
			"GOAGAIN_PPID is %d but parent is %d",
			ppid,
			syscall.Getppid(),
		))
		return
	}
	return
}

// Send SIGQUIT (but really SIGTERM since Go can't handle SIGQUIT) to the
// given ppid in order to complete the handoff to the child process.
func KillParent(ppid int) error {
	err := syscall.Kill(ppid, syscall.SIGTERM)
	if nil != err {
		return err
	}
	return nil
}

// Re-exec this image without dropping the listener passed to this function.
// func Relaunch(l net.Listener, envs []string) error {
func Relaunch(listeners map[string]net.Listener) error {
	files := make([]*os.File, 15)
	files[syscall.Stdin] = os.Stdin
	files[syscall.Stdout] = os.Stdout
	files[syscall.Stderr] = os.Stderr
	argv0, err := exec.LookPath(os.Args[0])
	if nil != err {
		return err
	}

	wd, err := os.Getwd()
	if nil != err {
		return err
	}

	for addr, l := range listeners {
		a := reflect.ValueOf(l)
		v := a.Elem().FieldByName("fd").Elem()
		fd := uintptr(v.FieldByName("sysfd").Int())
		if err := os.Setenv("GOAGAIN_FD"+addr, fmt.Sprint(fd)); nil != err {
			return err
		}

		if err := os.Setenv("GOAGAIN_NAME"+addr, fmt.Sprintf("tcp:%s->", l.Addr().String())); nil != err {
			return err
		}

		files[fd] = os.NewFile(fd, string(v.FieldByName("sysfile").String()))
	}

	if err := os.Setenv("GOAGAIN_PPID", fmt.Sprint(syscall.Getpid())); nil != err {
		return err
	}

	p, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
		Dir:   wd,
		Env:   os.Environ(),
		Files: files,
		Sys:   &syscall.SysProcAttr{},
	})
	if nil != err {
		return err
	}
	log.Printf("spawned child %d\n", p.Pid)
	return nil
}
<commit_msg>kontrolproxy: increase Files limit, it seems every file descriptor is taking two from up to 7-8<commit_after>// Zero-downtime restarts in Go.

/*************************************************
*
*  modified version of goagain to support multiple listeners
*
*  - arslan
*************************************************/

package goagain

import (
	"errors"
	"fmt"
	"log"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"reflect"
	"syscall"
)

// Export an error equivalent to net.errClosing for use with Accept during
// a graceful exit.
var ErrClosing = errors.New("use of closed network connection")

// Block this goroutine awaiting signals.  With the exception of SIGTERM
// taking the place of SIGQUIT, signals are handled exactly as in Nginx
// and Unicorn: <http://unicorn.bogomips.org/SIGNALS.html>.
func AwaitSignals(listeners map[string]net.Listener) error {
	ch := make(chan os.Signal, 2)
	signal.Notify(ch, syscall.SIGTERM, syscall.SIGUSR2)
	for {
		sig := <-ch
		log.Println(sig.String())
		switch sig {

		// TODO SIGHUP should reload configuration.

		// SIGQUIT should exit gracefully.  However, Go doesn't seem
		// to like handling SIGQUIT (or any signal which dumps core by
		// default) at all so SIGTERM takes its place.  How graceful
		// this exit is depends on what the program does after this
		// function returns control.
		case syscall.SIGTERM:
			fmt.Printf("stopping listeners\n")
			return nil

		// TODO SIGUSR1 should reopen logs.

		// SIGUSR2 begins the process of restarting without dropping
		// the listener passed to this function.
		case syscall.SIGUSR2:
			log.Printf("relaunching listeners\n")
			for addr, _ := range listeners {
				log.Printf("\t%s\n", addr)
			}

			err := Relaunch(listeners)
			if nil != err {
				return err
			}

		}
	}
	return nil // It'll never get here.
}

// Convert and validate the GOAGAIN_FD, GOAGAIN_NAME, and GOAGAIN_PPID
// environment variables.  If all three are present and in order, this
// is a child process that may pick up where the parent left off.
func GetEnvs(addr string) (l net.Listener, ppid int, err error) {
	var fd uintptr
	_, err = fmt.Sscan(os.Getenv("GOAGAIN_FD"+addr), &fd)
	if nil != err {
		return
	}
	var i net.Listener
	i, err = net.FileListener(os.NewFile(fd, os.Getenv("GOAGAIN_NAME"+addr)))
	if nil != err {
		return
	}
	switch i.(type) {
	case *net.TCPListener:
		l = i.(*net.TCPListener)
	case *net.UnixListener:
		l = i.(*net.UnixListener)
	default:
		err = errors.New(fmt.Sprintf(
			"file descriptor is %T not *net.TCPListener or *net.UnixListener",
			i,
		))
		return
	}

	if err = syscall.Close(int(fd)); nil != err {
		return
	}
	_, err = fmt.Sscan(os.Getenv("GOAGAIN_PPID"), &ppid)
	if nil != err {
		return
	}
	if syscall.Getppid() != ppid {
		err = errors.New(fmt.Sprintf(
			"GOAGAIN_PPID is %d but parent is %d",
			ppid,
			syscall.Getppid(),
		))
		return
	}
	return
}

// Send SIGQUIT (but really SIGTERM since Go can't handle SIGQUIT) to the
// given ppid in order to complete the handoff to the child process.
func KillParent(ppid int) error {
	err := syscall.Kill(ppid, syscall.SIGTERM)
	if nil != err {
		return err
	}
	return nil
}

// Re-exec this image without dropping the listener passed to this function.
// func Relaunch(l net.Listener, envs []string) error {
func Relaunch(listeners map[string]net.Listener) error {
	files := make([]*os.File, 20)
	files[syscall.Stdin] = os.Stdin
	files[syscall.Stdout] = os.Stdout
	files[syscall.Stderr] = os.Stderr
	argv0, err := exec.LookPath(os.Args[0])
	if nil != err {
		return err
	}

	wd, err := os.Getwd()
	if nil != err {
		return err
	}

	for addr, l := range listeners {
		a := reflect.ValueOf(l)
		v := a.Elem().FieldByName("fd").Elem()
		fd := uintptr(v.FieldByName("sysfd").Int())
		if err := os.Setenv("GOAGAIN_FD"+addr, fmt.Sprint(fd)); nil != err {
			return err
		}

		if err := os.Setenv("GOAGAIN_NAME"+addr, fmt.Sprintf("tcp:%s->", l.Addr().String())); nil != err {
			return err
		}

		files[fd] = os.NewFile(fd, string(v.FieldByName("sysfile").String()))
	}

	if err := os.Setenv("GOAGAIN_PPID", fmt.Sprint(syscall.Getpid())); nil != err {
		return err
	}

	p, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
		Dir:   wd,
		Env:   os.Environ(),
		Files: files,
		Sys:   &syscall.SysProcAttr{},
	})
	if nil != err {
		return err
	}
	log.Printf("spawned child %d\n", p.Pid)
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package commands

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"

	"github.com/bogem/nehm/track"
	"github.com/bogem/nehm/trackprocessor"
	"github.com/bogem/nehm/ui"
	"github.com/fatih/color"
)

type TracksMenu struct {
	GetTracks      func(offset uint) []track.Track
	Limit          uint
	Offset         uint
	TrackProcessor trackprocessor.TrackProcessor

	downloadQueue []track.Track
}

func (tm TracksMenu) Show() {
	oldOffset := tm.Offset
	ui.Say("Getting information about tracks")
	tracks := tm.GetTracks(tm.Offset)
	for {
		if oldOffset != tm.Offset {
			tracks = tm.GetTracks(tm.Offset)
		}
		oldOffset = tm.Offset
		trackItems := tm.formTrackItems(tracks)
		clearScreen()
		tm.showMenu(trackItems)
	}
}

var trackItems []ui.MenuItem

func (tm *TracksMenu) formTrackItems(tracks []track.Track) []ui.MenuItem {
	if trackItems == nil {
		trackItems = make([]ui.MenuItem, 0, tm.Limit)
	}
	trackItems = trackItems[:0]
	for i, t := range tracks {
		desc := fmt.Sprintf("%v (%v)", t.Fullname(), t.Duration())

		var trackItem ui.MenuItem
		if contains(tm.downloadQueue, t) {
			trackItem = ui.MenuItem{
				Index: color.GreenString("A"),
				Desc:  desc,
			}
		} else {
			tDup := t
			trackItem = ui.MenuItem{
				Index: strconv.Itoa(i + 1),
				Desc:  desc,
				Run:   func() { tm.AddToDownloadQueue(tDup) },
			}
		}
		trackItems = append(trackItems, trackItem)
	}
	return trackItems
}

func (tm *TracksMenu) AddToDownloadQueue(t track.Track) {
	tm.downloadQueue = append(tm.downloadQueue, t)
}

func contains(s []track.Track, t track.Track) bool {
	for _, v := range s {
		if v.ID() == t.ID() {
			return true
		}
	}
	return false
}

func clearScreen() {
	cmd := exec.Command("clear")
	cmd.Stdout = os.Stdout
	cmd.Run()
}

var controlItems []ui.MenuItem

func (tm *TracksMenu) showMenu(trackItems []ui.MenuItem) {
	if controlItems == nil {
		controlItems = tm.generateControlItems()
	}
	menu := new(ui.Menu)
	menu.AddItems(trackItems)
	menu.AddNewline()
	menu.AddItems(controlItems)
	menu.Run()
}

func (tm *TracksMenu) generateControlItems() []ui.MenuItem {
	return []ui.MenuItem{
		ui.MenuItem{
			Index: "d",
			Desc:  color.GreenString("Download tracks"),
			Run:   func() { tm.TrackProcessor.ProcessAll(tm.downloadQueue) },
		},

		ui.MenuItem{
			Index: "n",
			Desc:  "Next page",
			Run:   func() { tm.Offset += tm.Limit },
		},

		ui.MenuItem{
			Index: "p",
			Desc:  "Prev page",
			Run: func() {
				if tm.Offset >= tm.Limit {
					tm.Offset -= tm.Limit
				} else {
					tm.Offset = 0
				}
			},
		},
	}
}
<commit_msg>Use cls command to clear screen on Windows<commit_after>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package commands

import (
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strconv"

	"github.com/bogem/nehm/track"
	"github.com/bogem/nehm/trackprocessor"
	"github.com/bogem/nehm/ui"
	"github.com/fatih/color"
)

type TracksMenu struct {
	GetTracks      func(offset uint) []track.Track
	Limit          uint
	Offset         uint
	TrackProcessor trackprocessor.TrackProcessor

	downloadQueue []track.Track
}

func (tm TracksMenu) Show() {
	oldOffset := tm.Offset
	ui.Say("Getting information about tracks")
	tracks := tm.GetTracks(tm.Offset)
	for {
		if oldOffset != tm.Offset {
			tracks = tm.GetTracks(tm.Offset)
		}
		oldOffset = tm.Offset
		trackItems := tm.formTrackItems(tracks)
		clearScreen()
		tm.showMenu(trackItems)
	}
}

var trackItems []ui.MenuItem

func (tm *TracksMenu) formTrackItems(tracks []track.Track) []ui.MenuItem {
	if trackItems == nil {
		trackItems = make([]ui.MenuItem, 0, tm.Limit)
	}
	trackItems = trackItems[:0]
	for i, t := range tracks {
		desc := fmt.Sprintf("%v (%v)", t.Fullname(), t.Duration())

		var trackItem ui.MenuItem
		if contains(tm.downloadQueue, t) {
			trackItem = ui.MenuItem{
				Index: color.GreenString("A"),
				Desc:  desc,
			}
		} else {
			tDup := t
			trackItem = ui.MenuItem{
				Index: strconv.Itoa(i + 1),
				Desc:  desc,
				Run:   func() { tm.AddToDownloadQueue(tDup) },
			}
		}
		trackItems = append(trackItems, trackItem)
	}
	return trackItems
}

func (tm *TracksMenu) AddToDownloadQueue(t track.Track) {
	tm.downloadQueue = append(tm.downloadQueue, t)
}

func contains(s []track.Track, t track.Track) bool {
	for _, v := range s {
		if v.ID() == t.ID() {
			return true
		}
	}
	return false
}

func clearScreen() {
	var cmd exec.Cmd
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cls")
	} else {
		cmd = exec.Command("clear")
	}
	cmd.Stdout = os.Stdout
	cmd.Run()
}

var controlItems []ui.MenuItem

func (tm *TracksMenu) showMenu(trackItems []ui.MenuItem) {
	if controlItems == nil {
		controlItems = tm.generateControlItems()
	}
	menu := new(ui.Menu)
	menu.AddItems(trackItems)
	menu.AddNewline()
	menu.AddItems(controlItems)
	menu.Run()
}

func (tm *TracksMenu) generateControlItems() []ui.MenuItem {
	return []ui.MenuItem{
		ui.MenuItem{
			Index: "d",
			Desc:  color.GreenString("Download tracks"),
			Run:   func() { tm.TrackProcessor.ProcessAll(tm.downloadQueue) },
		},

		ui.MenuItem{
			Index: "n",
			Desc:  "Next page",
			Run:   func() { tm.Offset += tm.Limit },
		},

		ui.MenuItem{
			Index: "p",
			Desc:  "Prev page",
			Run: func() {
				if tm.Offset >= tm.Limit {
					tm.Offset -= tm.Limit
				} else {
					tm.Offset = 0
				}
			},
		},
	}
}
<|endoftext|>