<commit_before>package tlv

import (
	"bytes"
	"container/list"
	"encoding/binary"
	"fmt"
	"io"
)

// Type TLV represents a Tag-Length-Value record.
type TLV interface {
	Tag() int
	Length() int
	Value() []byte
}

type record struct {
	tag    int
	length int
	value  []byte
}

// Method Tag returns the record's tag.
func (t *record) Tag() int {
	return t.tag
}

// Method Length returns the record's value's length.
func (t *record) Length() int {
	return t.length
}

// Method Value returns the record's value.
func (t *record) Value() []byte {
	return t.value
}

// Equals returns true if a pair of TLV records are the same.
func Equals(tlv1, tlv2 TLV) bool {
	if tlv1 == nil {
		if tlv2 == nil {
			return true
		}
		return false
	} else if tlv2 == nil {
		return false
	} else if tlv1.Tag() != tlv2.Tag() {
		return false
	} else if tlv1.Length() != tlv2.Length() {
		return false
	} else if !bytes.Equal(tlv1.Value(), tlv2.Value()) {
		return false
	}
	return true
}

// ErrTLVRead is returned when there is an error reading a TLV record;
// similarly, TLVWrite is returned when  there is an error writing a
// TLV record. ErrTagNotFound is returned when a request for a TLV tag
// is made and none can be found.
var (
	ErrTLVRead     = fmt.Errorf("TLV read error")
	ErrTLVWrite    = fmt.Errorf("TLV write error")
	ErrTagNotFound = fmt.Errorf("tag not found")
)

func newTLV(tag int, value []byte) TLV {
	tlv := new(record)
	tlv.tag = tag
	tlv.length = len(value)
	tlv.value = make([]byte, tlv.Length())
	copy(tlv.value, value)
	return tlv
}

func tlvFromBytes(rec []byte) (tlv TLV, err error) {
	recBuf := bytes.NewBuffer(rec)
	return readRecord(recBuf)
}

func readRecord(r io.Reader) (rec TLV, err error) {
	tlv := new(record)

	var n int32
	err = binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return
	}
	tlv.tag = int(n)

	err = binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return
	}
	tlv.length = int(n)

	tlv.value = make([]byte, tlv.Length())
	l, err := r.Read(tlv.value)
	if err != nil {
		return
	} else if l != tlv.Length() {
		return tlv, ErrTLVWrite
	}
	return tlv, nil
}

func writeRecord(tlv TLV, w io.Writer) (err error) {
	tmp := int32(tlv.Tag())
	err = binary.Write(w, binary.LittleEndian, tmp)
	if err != nil {
		return
	}

	tmp = int32(tlv.Length())
	err = binary.Write(w, binary.LittleEndian, tmp)
	if err != nil {
		return
	}

	n, err := w.Write(tlv.Value())
	if err != nil {
		return
	} else if n != tlv.Length() {
		return ErrTLVWrite
	}
	return
}

// Type TLVList is a doubly-linked list containing TLV records.
type TLVList struct {
	records *list.List
}

// New returns a new, empty TLVList.
func New() *TLVList {
	tl := new(TLVList)
	tl.records = list.New()
	return tl
}

// Length returns the number of records in the TLVList.
func (tl *TLVList) Length() int {
	return tl.records.Len()
}

// Get checks the TLVList for any record matching the tag. It returns the
// first one found. If the tag could not be found, Get returns ErrTagNotFound.
func (recs *TLVList) Get(tag int) (t TLV, err error) {
	for e := recs.records.Front(); e != nil; e = e.Next() {
		if e.Value.(*record).Tag() == tag {
			return e.Value.(*record), nil
		}
	}
	return nil, ErrTagNotFound
}

// GetAll checks the TLVList for all records matching the tag, returning a
// slice containing all matching records. If no record has the requested
// tag, an empty slice is returned.
func (recs *TLVList) GetAll(tag int) (ts []TLV) {
	ts = make([]TLV, 0)
	for e := recs.records.Front(); e != nil; e = e.Next() {
		if e.Value.(*record).Tag() == tag {
			ts = append(ts, e.Value.(TLV))
		}
	}
	return ts
}

// Remove removes all records with the requested tag. It returns a count
// of the number of removed records.
func (recs *TLVList) Remove(tag int) int {
	var totalRemoved int
	for {
		var removed int
		for e := recs.records.Front(); e != nil; e = e.Next() {
			if e.Value.(*record).Tag() == tag {
				recs.records.Remove(e)
				removed++
				break
			}
		}
		if removed == 0 {
			break
		}
		totalRemoved += removed
	}
	return totalRemoved
}

// RemoveRecord takes a record as an argument, and removes all matching
// records. It matches on not just tag, but also the value contained in
// the record.
func (recs *TLVList) RemoveRecord(rec TLV) int {
	var totalRemoved int
	for {
		var removed int
		for e := recs.records.Front(); e != nil; e = e.Next() {
			if Equals(e.Value.(*record), rec) {
				recs.records.Remove(e)
				removed++
				break
			}
		}
		if removed == 0 {
			break
		}
		totalRemoved += removed
	}
	return totalRemoved
}

// Add pushes a new TLV record onto the TLVList. It builds the record from
// its arguments.
func (recs *TLVList) Add(tag int, value []byte) {
	rec := newTLV(tag, value)
	recs.records.PushBack(rec)
}

// AddRecord adds a TLV record onto the TLVList.
func (recs *TLVList) AddRecord(rec TLV) {
	recs.records.PushBack(rec)
}

// Write writes out the TLVList to an io.Writer.
func (recs *TLVList) Write(w io.Writer) (err error) {
	for e := recs.records.Front(); e != nil; e = e.Next() {
		err = writeRecord(e.Value.(TLV), w)
		if err != nil {
			return
		}
	}
	return
}

// Read takes an io.Reader and builds a TLVList from that.
func Read(r io.Reader) (recs *TLVList, err error) {
	recs = New()
	for {
		var tlv TLV
		if tlv, err = readRecord(r); err != nil {
			break
		}
		recs.records.PushBack(tlv)
	}

	if err == io.EOF {
		err = nil
	}
	return
}
<commit_msg>Fix typo<commit_after>package tlv

import (
	"bytes"
	"container/list"
	"encoding/binary"
	"fmt"
	"io"
)

// Type TLV represents a Tag-Length-Value record.
type TLV interface {
	Tag() int
	Length() int
	Value() []byte
}

type record struct {
	tag    int
	length int
	value  []byte
}

// Method Tag returns the record's tag.
func (t *record) Tag() int {
	return t.tag
}

// Method Length returns the record's value's length.
func (t *record) Length() int {
	return t.length
}

// Method Value returns the record's value.
func (t *record) Value() []byte {
	return t.value
}

// Equals returns true if a pair of TLV records are the same.
func Equals(tlv1, tlv2 TLV) bool {
	if tlv1 == nil {
		if tlv2 == nil {
			return true
		}
		return false
	} else if tlv2 == nil {
		return false
	} else if tlv1.Tag() != tlv2.Tag() {
		return false
	} else if tlv1.Length() != tlv2.Length() {
		return false
	} else if !bytes.Equal(tlv1.Value(), tlv2.Value()) {
		return false
	}
	return true
}

// ErrTLVRead is returned when there is an error reading a TLV record;
// similarly, TLVWrite is returned when  there is an error writing a
// TLV record. ErrTagNotFound is returned when a request for a TLV tag
// is made and none can be found.
var (
	ErrTLVRead     = fmt.Errorf("TLV read error")
	ErrTLVWrite    = fmt.Errorf("TLV write error")
	ErrTagNotFound = fmt.Errorf("tag not found")
)

func newTLV(tag int, value []byte) TLV {
	tlv := new(record)
	tlv.tag = tag
	tlv.length = len(value)
	tlv.value = make([]byte, tlv.Length())
	copy(tlv.value, value)
	return tlv
}

func tlvFromBytes(rec []byte) (tlv TLV, err error) {
	recBuf := bytes.NewBuffer(rec)
	return readRecord(recBuf)
}

func readRecord(r io.Reader) (rec TLV, err error) {
	tlv := new(record)

	var n int32
	err = binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return
	}
	tlv.tag = int(n)

	err = binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return
	}
	tlv.length = int(n)

	tlv.value = make([]byte, tlv.Length())
	l, err := r.Read(tlv.value)
	if err != nil {
		return
	} else if l != tlv.Length() {
		return tlv, ErrTLVRead
	}
	return tlv, nil
}

func writeRecord(tlv TLV, w io.Writer) (err error) {
	tmp := int32(tlv.Tag())
	err = binary.Write(w, binary.LittleEndian, tmp)
	if err != nil {
		return
	}

	tmp = int32(tlv.Length())
	err = binary.Write(w, binary.LittleEndian, tmp)
	if err != nil {
		return
	}

	n, err := w.Write(tlv.Value())
	if err != nil {
		return
	} else if n != tlv.Length() {
		return ErrTLVWrite
	}
	return
}

// Type TLVList is a doubly-linked list containing TLV records.
type TLVList struct {
	records *list.List
}

// New returns a new, empty TLVList.
func New() *TLVList {
	tl := new(TLVList)
	tl.records = list.New()
	return tl
}

// Length returns the number of records in the TLVList.
func (tl *TLVList) Length() int {
	return tl.records.Len()
}

// Get checks the TLVList for any record matching the tag. It returns the
// first one found. If the tag could not be found, Get returns ErrTagNotFound.
func (recs *TLVList) Get(tag int) (t TLV, err error) {
	for e := recs.records.Front(); e != nil; e = e.Next() {
		if e.Value.(*record).Tag() == tag {
			return e.Value.(*record), nil
		}
	}
	return nil, ErrTagNotFound
}

// GetAll checks the TLVList for all records matching the tag, returning a
// slice containing all matching records. If no record has the requested
// tag, an empty slice is returned.
func (recs *TLVList) GetAll(tag int) (ts []TLV) {
	ts = make([]TLV, 0)
	for e := recs.records.Front(); e != nil; e = e.Next() {
		if e.Value.(*record).Tag() == tag {
			ts = append(ts, e.Value.(TLV))
		}
	}
	return ts
}

// Remove removes all records with the requested tag. It returns a count
// of the number of removed records.
func (recs *TLVList) Remove(tag int) int {
	var totalRemoved int
	for {
		var removed int
		for e := recs.records.Front(); e != nil; e = e.Next() {
			if e.Value.(*record).Tag() == tag {
				recs.records.Remove(e)
				removed++
				break
			}
		}
		if removed == 0 {
			break
		}
		totalRemoved += removed
	}
	return totalRemoved
}

// RemoveRecord takes a record as an argument, and removes all matching
// records. It matches on not just tag, but also the value contained in
// the record.
func (recs *TLVList) RemoveRecord(rec TLV) int {
	var totalRemoved int
	for {
		var removed int
		for e := recs.records.Front(); e != nil; e = e.Next() {
			if Equals(e.Value.(*record), rec) {
				recs.records.Remove(e)
				removed++
				break
			}
		}
		if removed == 0 {
			break
		}
		totalRemoved += removed
	}
	return totalRemoved
}

// Add pushes a new TLV record onto the TLVList. It builds the record from
// its arguments.
func (recs *TLVList) Add(tag int, value []byte) {
	rec := newTLV(tag, value)
	recs.records.PushBack(rec)
}

// AddRecord adds a TLV record onto the TLVList.
func (recs *TLVList) AddRecord(rec TLV) {
	recs.records.PushBack(rec)
}

// Write writes out the TLVList to an io.Writer.
func (recs *TLVList) Write(w io.Writer) (err error) {
	for e := recs.records.Front(); e != nil; e = e.Next() {
		err = writeRecord(e.Value.(TLV), w)
		if err != nil {
			return
		}
	}
	return
}

// Read takes an io.Reader and builds a TLVList from that.
func Read(r io.Reader) (recs *TLVList, err error) {
	recs = New()
	for {
		var tlv TLV
		if tlv, err = readRecord(r); err != nil {
			break
		}
		recs.records.PushBack(tlv)
	}

	if err == io.EOF {
		err = nil
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math"
	"os/exec"
	"strings"

	"github.com/aqatl/mal/dialog"
	"github.com/aqatl/mal/nyaa_scraper"
	"github.com/fatih/color"
	"github.com/jroimartin/gocui"
	"github.com/urfave/cli"
)

func malNyaaCui(ctx *cli.Context) error {
	_, list, err := loadMAL(ctx)
	if err != nil {
		return err
	}
	cfg := LoadConfig()

	entry := list.GetByID(cfg.SelectedID)
	if entry == nil {
		return fmt.Errorf("no entry found")
	}
	return startNyaaCui(
		cfg,
		entry.Title,
		fmt.Sprintf("%s %d/%d", entry.Title, entry.WatchedEpisodes, entry.Episodes),
	)
}

func alNyaaCui(ctx *cli.Context) error {
	al, err := loadAniList(ctx)
	if err != nil {
		return err
	}
	cfg := LoadConfig()

	entry := al.GetMediaListById(cfg.ALSelectedID)
	if entry == nil {
		return fmt.Errorf("no entry found")
	}

	searchTerm := entry.Title.UserPreferred
	if ctx.Bool("alt") {
		fmt.Printf("Select desired title\n\n")
		if searchTerm = chooseStrFromSlice(sliceOfEntryTitles(entry)); searchTerm == "" {
			return fmt.Errorf("no alternative titles")
		}
	} else if ctx.NArg() > 0 {
		searchTerm = strings.Join(ctx.Args(), " ")
	}

	return startNyaaCui(
		cfg,
		searchTerm,
		fmt.Sprintf("%s %d/%d", searchTerm, entry.Progress, entry.Episodes),
	)
}

func startNyaaCui(cfg *Config, searchTerm, displayedInfo string) error {
	gui, err := gocui.NewGui(gocui.Output256)
	defer gui.Close()
	if err != nil {
		return fmt.Errorf("gocui error: %v", err)
	}
	nc := &nyaaCui{
		Gui: gui,
		Cfg: cfg,

		SearchTerm:    searchTerm,
		DisplayedInfo: displayedInfo,
		Category:      nyaa_scraper.AnimeEnglishTranslated,
		Filter:        nyaa_scraper.NoFilter,
	}
	gui.SetManager(nc)
	nc.setGuiKeyBindings(gui)

	gui.Cursor = false
	gui.Mouse = false
	gui.Highlight = true
	gui.SelFgColor = gocui.ColorGreen

	gui.Update(func(gui *gocui.Gui) error {
		nc.Reload(gui)
		return nil
	})

	if err = gui.MainLoop(); err != nil && err != gocui.ErrQuit {
		return err
	}
	return nil
}

const (
	ncInfoView      = "ncInfoView"
	ncResultsView   = "ncResultsView "
	ncShortcutsView = "ncShortcutsView"
)

type nyaaCui struct {
	Gui *gocui.Gui
	Cfg *Config

	SearchTerm    string
	DisplayedInfo string
	Category      nyaa_scraper.NyaaCategory
	Filter        nyaa_scraper.NyaaFilter

	Results     []nyaa_scraper.NyaaEntry
	MaxResults  int
	MaxPages    int
	LoadedPages int

	ResultsView *gocui.View
}

var red = color.New(color.FgRed).SprintFunc()
var cyan = color.New(color.FgCyan).SprintFunc()
var blue = color.New(color.FgBlue).SprintFunc()
var green = color.New(color.FgGreen).SprintFunc()

func (nc *nyaaCui) Layout(gui *gocui.Gui) error {
	w, h := gui.Size()
	if v, err := gui.SetView(ncInfoView, 0, 0, w-1, 2); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}

		v.Title = "Info"
		v.Editable = false

		fmt.Fprintf(v, "[%s]: displaying %d out of %d results",
			nc.DisplayedInfo, len(nc.Results), nc.MaxResults)
	}

	if v, err := gui.SetView(ncResultsView, 0, 3, w-1, h-4); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}

		v.Title = "Search results"
		v.SelBgColor = gocui.ColorGreen
		v.SelBgColor = gocui.ColorGreen
		v.SelFgColor = gocui.ColorBlack
		v.Highlight = true
		v.Editable = true
		v.Editor = gocui.EditorFunc(nc.GetEditor())

		gui.SetCurrentView(ncResultsView)
		nc.ResultsView = v

		//TODO Better/clearer results printing
		for _, result := range nc.Results {
			fmt.Fprintln(v,
				result.Title,
				red(result.Size),
				cyan(result.DateAdded.Format("15:04:05 02-01-2006 MST")),
				green(result.Seeders),
				red(result.Leechers),
				blue(result.CompletedDownloads),
			)
		}
	}

	if v, err := gui.SetView(ncShortcutsView, 0, h-3, w-1, h-1); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}

		v.Title = "Shortcuts"
		v.Editable = false

		c := color.New(color.FgCyan).SprintFunc()
		fmt.Fprintln(v, c("d"), "download", c("l"), "load next page",
			c("c"), "category", c("f"), "filters")
	}

	return nil
}

func (nc *nyaaCui) GetEditor() func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
	return func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
		switch {
		case key == gocui.KeyArrowDown || ch == 'j':
			_, oy := v.Origin()
			_, y := v.Cursor()
			y += oy
			if y < len(nc.Results)-1 {
				v.MoveCursor(0, 1, false)
			}
		case key == gocui.KeyArrowUp || ch == 'k':
			v.MoveCursor(0, -1, false)
		case ch == 'g':
			v.SetCursor(0, 0)
			v.SetOrigin(0, 0)
		case ch == 'G':
			_, viewH := v.Size()
			totalH := len(nc.Results)
			if totalH <= viewH {
				v.SetCursor(0, totalH-1)
			} else {
				v.SetOrigin(0, totalH-viewH)
				v.SetCursor(0, viewH-1)
			}
		case ch == 'd':
			_, y := v.Cursor()
			_, oy := v.Origin()
			y += oy
			nc.Download(y)
		case ch == 'l':
			nc.LoadNextPage()
		case ch == 'c':
			nc.ChangeCategory()
		case ch == 'f':
			nc.ChangeFilter()
		}
	}
}

func (nc *nyaaCui) Reload(gui *gocui.Gui) {
	var resultPage nyaa_scraper.NyaaResultPage
	var searchErr error
	f := func() {
		resultPage, searchErr = nyaa_scraper.Search(nc.SearchTerm, nc.Category, nc.Filter)
	}
	jobDone, err := dialog.StuffLoader(dialog.FitMessage(gui, "Loading "+nc.SearchTerm), f)
	if err != nil {
		gocuiReturnError(gui, err)
	}
	go func() {
		ok := <-jobDone
		if searchErr != nil {
			dialog.JustShowOkDialog(gui, "Error", searchErr.Error())
			return
		}
		if ok {
			nc.Results = resultPage.Results
			nc.MaxResults = resultPage.DisplayedOutOf
			nc.MaxPages = int(math.Ceil(float64(resultPage.DisplayedOutOf) /
				float64(resultPage.DisplayedTo-resultPage.DisplayedFrom+1)))
			nc.LoadedPages = 1
		}

		gui.Update(func(gui *gocui.Gui) error {
			gui.DeleteView(ncResultsView)
			gui.DeleteView(ncInfoView)
			return nil
		})
	}()
}

func (nc *nyaaCui) Download(yIdx int) {
	if yIdx >= len(nc.Results) {
		return
	}

	link := ""
	if entry := nc.Results[yIdx]; entry.MagnetLink != "" {
		link = entry.MagnetLink
	} else if entry.TorrentLink != "" {
		link = entry.TorrentLink
	} else {
		dialog.JustShowOkDialog(nc.Gui, "Error", "No link found")
		return
	}

	link = "\"" + link + "\""
	cmd := exec.Command(nc.Cfg.TorrentClientPath, nc.Cfg.TorrentClientArgs...)
	cmd.Args = append(cmd.Args, link)
	cmd.Args = cmd.Args[1:] //Why they include app name in the arguments???
	if err := cmd.Start(); err != nil {
		gocuiReturnError(nc.Gui, err)
	}
}

func (nc *nyaaCui) LoadNextPage() {
	if nc.LoadedPages >= nc.MaxPages {
		return
	}
	nc.LoadedPages++
	go func() {
		resultPage, _ := nyaa_scraper.SearchSpecificPage(
			nc.SearchTerm,
			nc.Category,
			nc.Filter,
			nc.LoadedPages,
		)
		nc.Results = append(nc.Results, resultPage.Results...)
		nc.Gui.Update(func(gui *gocui.Gui) error {
			_, oy := nc.ResultsView.Origin()
			_, y := nc.ResultsView.Cursor()

			gui.DeleteView(ncInfoView)
			gui.DeleteView(ncResultsView)

			nc.Layout(gui)
			nc.ResultsView.SetOrigin(0, oy)
			nc.ResultsView.SetCursor(0, y)

			return nil
		})
	}()
}

func (nc *nyaaCui) ChangeCategory() {
	selIdxChan, cleanUp, err := dialog.ListSelect(nc.Gui, "Select category", nyaa_scraper.Categories)
	if err != nil {
		gocuiReturnError(nc.Gui, err)
	}
	go func() {
		idx, ok := <-selIdxChan
		nc.Gui.Update(cleanUp)
		if ok {
			nc.Category = nyaa_scraper.Categories[idx]
			nc.Reload(nc.Gui)
		}
	}()
}

func (nc *nyaaCui) ChangeFilter() {
	selIdxChan, cleanUp, err := dialog.ListSelect(nc.Gui, "Select filter", nyaa_scraper.Filters)
	if err != nil {
		gocuiReturnError(nc.Gui, err)
	}
	go func() {
		idx, ok := <-selIdxChan
		nc.Gui.Update(cleanUp)
		if ok {
			nc.Filter = nyaa_scraper.Filters[idx]
			nc.Reload(nc.Gui)
		}
	}()
}

func (nc *nyaaCui) setGuiKeyBindings(gui *gocui.Gui) {
	gui.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quitGocui)
}

func quitGocui(gui *gocui.Gui, view *gocui.View) error {
	return gocui.ErrQuit
}

func gocuiReturnError(gui *gocui.Gui, err error) {
	gui.Update(func(gui *gocui.Gui) error {
		return err
	})
}
<commit_msg>Filter "Trusted only" by default<commit_after>package main

import (
	"fmt"
	"math"
	"os/exec"
	"strings"

	"github.com/aqatl/mal/dialog"
	"github.com/aqatl/mal/nyaa_scraper"
	"github.com/fatih/color"
	"github.com/jroimartin/gocui"
	"github.com/urfave/cli"
)

func malNyaaCui(ctx *cli.Context) error {
	_, list, err := loadMAL(ctx)
	if err != nil {
		return err
	}
	cfg := LoadConfig()

	entry := list.GetByID(cfg.SelectedID)
	if entry == nil {
		return fmt.Errorf("no entry found")
	}
	return startNyaaCui(
		cfg,
		entry.Title,
		fmt.Sprintf("%s %d/%d", entry.Title, entry.WatchedEpisodes, entry.Episodes),
	)
}

func alNyaaCui(ctx *cli.Context) error {
	al, err := loadAniList(ctx)
	if err != nil {
		return err
	}
	cfg := LoadConfig()

	entry := al.GetMediaListById(cfg.ALSelectedID)
	if entry == nil {
		return fmt.Errorf("no entry found")
	}

	searchTerm := entry.Title.UserPreferred
	if ctx.Bool("alt") {
		fmt.Printf("Select desired title\n\n")
		if searchTerm = chooseStrFromSlice(sliceOfEntryTitles(entry)); searchTerm == "" {
			return fmt.Errorf("no alternative titles")
		}
	} else if ctx.NArg() > 0 {
		searchTerm = strings.Join(ctx.Args(), " ")
	}

	return startNyaaCui(
		cfg,
		searchTerm,
		fmt.Sprintf("%s %d/%d", searchTerm, entry.Progress, entry.Episodes),
	)
}

func startNyaaCui(cfg *Config, searchTerm, displayedInfo string) error {
	gui, err := gocui.NewGui(gocui.Output256)
	defer gui.Close()
	if err != nil {
		return fmt.Errorf("gocui error: %v", err)
	}
	nc := &nyaaCui{
		Gui: gui,
		Cfg: cfg,

		SearchTerm:    searchTerm,
		DisplayedInfo: displayedInfo,
		Category:      nyaa_scraper.AnimeEnglishTranslated,
		Filter:        nyaa_scraper.TrustedOnly,
	}
	gui.SetManager(nc)
	nc.setGuiKeyBindings(gui)

	gui.Cursor = false
	gui.Mouse = false
	gui.Highlight = true
	gui.SelFgColor = gocui.ColorGreen

	gui.Update(func(gui *gocui.Gui) error {
		nc.Reload(gui)
		return nil
	})

	if err = gui.MainLoop(); err != nil && err != gocui.ErrQuit {
		return err
	}
	return nil
}

const (
	ncInfoView      = "ncInfoView"
	ncResultsView   = "ncResultsView "
	ncShortcutsView = "ncShortcutsView"
)

type nyaaCui struct {
	Gui *gocui.Gui
	Cfg *Config

	SearchTerm    string
	DisplayedInfo string
	Category      nyaa_scraper.NyaaCategory
	Filter        nyaa_scraper.NyaaFilter

	Results     []nyaa_scraper.NyaaEntry
	MaxResults  int
	MaxPages    int
	LoadedPages int

	ResultsView *gocui.View
}

var red = color.New(color.FgRed).SprintFunc()
var cyan = color.New(color.FgCyan).SprintFunc()
var blue = color.New(color.FgBlue).SprintFunc()
var green = color.New(color.FgGreen).SprintFunc()

func (nc *nyaaCui) Layout(gui *gocui.Gui) error {
	w, h := gui.Size()
	if v, err := gui.SetView(ncInfoView, 0, 0, w-1, 2); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}

		v.Title = "Info"
		v.Editable = false

		fmt.Fprintf(v, "[%s]: displaying %d out of %d results",
			nc.DisplayedInfo, len(nc.Results), nc.MaxResults)
	}

	if v, err := gui.SetView(ncResultsView, 0, 3, w-1, h-4); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}

		v.Title = "Search results"
		v.SelBgColor = gocui.ColorGreen
		v.SelBgColor = gocui.ColorGreen
		v.SelFgColor = gocui.ColorBlack
		v.Highlight = true
		v.Editable = true
		v.Editor = gocui.EditorFunc(nc.GetEditor())

		gui.SetCurrentView(ncResultsView)
		nc.ResultsView = v

		//TODO Better/clearer results printing
		for _, result := range nc.Results {
			fmt.Fprintln(v,
				result.Title,
				red(result.Size),
				cyan(result.DateAdded.Format("15:04:05 02-01-2006 MST")),
				green(result.Seeders),
				red(result.Leechers),
				blue(result.CompletedDownloads),
			)
		}
	}

	if v, err := gui.SetView(ncShortcutsView, 0, h-3, w-1, h-1); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}

		v.Title = "Shortcuts"
		v.Editable = false

		c := color.New(color.FgCyan).SprintFunc()
		fmt.Fprintln(v, c("d"), "download", c("l"), "load next page",
			c("c"), "category", c("f"), "filters")
	}

	return nil
}

func (nc *nyaaCui) GetEditor() func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
	return func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
		switch {
		case key == gocui.KeyArrowDown || ch == 'j':
			_, oy := v.Origin()
			_, y := v.Cursor()
			y += oy
			if y < len(nc.Results)-1 {
				v.MoveCursor(0, 1, false)
			}
		case key == gocui.KeyArrowUp || ch == 'k':
			v.MoveCursor(0, -1, false)
		case ch == 'g':
			v.SetCursor(0, 0)
			v.SetOrigin(0, 0)
		case ch == 'G':
			_, viewH := v.Size()
			totalH := len(nc.Results)
			if totalH <= viewH {
				v.SetCursor(0, totalH-1)
			} else {
				v.SetOrigin(0, totalH-viewH)
				v.SetCursor(0, viewH-1)
			}
		case ch == 'd':
			_, y := v.Cursor()
			_, oy := v.Origin()
			y += oy
			nc.Download(y)
		case ch == 'l':
			nc.LoadNextPage()
		case ch == 'c':
			nc.ChangeCategory()
		case ch == 'f':
			nc.ChangeFilter()
		}
	}
}

func (nc *nyaaCui) Reload(gui *gocui.Gui) {
	var resultPage nyaa_scraper.NyaaResultPage
	var searchErr error
	f := func() {
		resultPage, searchErr = nyaa_scraper.Search(nc.SearchTerm, nc.Category, nc.Filter)
	}
	jobDone, err := dialog.StuffLoader(dialog.FitMessage(gui, "Loading "+nc.SearchTerm), f)
	if err != nil {
		gocuiReturnError(gui, err)
	}
	go func() {
		ok := <-jobDone
		if searchErr != nil {
			dialog.JustShowOkDialog(gui, "Error", searchErr.Error())
			return
		}
		if ok {
			nc.Results = resultPage.Results
			nc.MaxResults = resultPage.DisplayedOutOf
			nc.MaxPages = int(math.Ceil(float64(resultPage.DisplayedOutOf) /
				float64(resultPage.DisplayedTo-resultPage.DisplayedFrom+1)))
			nc.LoadedPages = 1
		}

		gui.Update(func(gui *gocui.Gui) error {
			gui.DeleteView(ncResultsView)
			gui.DeleteView(ncInfoView)
			return nil
		})
	}()
}

func (nc *nyaaCui) Download(yIdx int) {
	if yIdx >= len(nc.Results) {
		return
	}

	link := ""
	if entry := nc.Results[yIdx]; entry.MagnetLink != "" {
		link = entry.MagnetLink
	} else if entry.TorrentLink != "" {
		link = entry.TorrentLink
	} else {
		dialog.JustShowOkDialog(nc.Gui, "Error", "No link found")
		return
	}

	link = "\"" + link + "\""
	cmd := exec.Command(nc.Cfg.TorrentClientPath, nc.Cfg.TorrentClientArgs...)
	cmd.Args = append(cmd.Args, link)
	cmd.Args = cmd.Args[1:] //Why they include app name in the arguments???
	if err := cmd.Start(); err != nil {
		gocuiReturnError(nc.Gui, err)
	}
}

func (nc *nyaaCui) LoadNextPage() {
	if nc.LoadedPages >= nc.MaxPages {
		return
	}
	nc.LoadedPages++
	go func() {
		resultPage, _ := nyaa_scraper.SearchSpecificPage(
			nc.SearchTerm,
			nc.Category,
			nc.Filter,
			nc.LoadedPages,
		)
		nc.Results = append(nc.Results, resultPage.Results...)
		nc.Gui.Update(func(gui *gocui.Gui) error {
			_, oy := nc.ResultsView.Origin()
			_, y := nc.ResultsView.Cursor()

			gui.DeleteView(ncInfoView)
			gui.DeleteView(ncResultsView)

			nc.Layout(gui)
			nc.ResultsView.SetOrigin(0, oy)
			nc.ResultsView.SetCursor(0, y)

			return nil
		})
	}()
}

func (nc *nyaaCui) ChangeCategory() {
	selIdxChan, cleanUp, err := dialog.ListSelect(nc.Gui, "Select category", nyaa_scraper.Categories)
	if err != nil {
		gocuiReturnError(nc.Gui, err)
	}
	go func() {
		idx, ok := <-selIdxChan
		nc.Gui.Update(cleanUp)
		if ok {
			nc.Category = nyaa_scraper.Categories[idx]
			nc.Reload(nc.Gui)
		}
	}()
}

func (nc *nyaaCui) ChangeFilter() {
	selIdxChan, cleanUp, err := dialog.ListSelect(nc.Gui, "Select filter", nyaa_scraper.Filters)
	if err != nil {
		gocuiReturnError(nc.Gui, err)
	}
	go func() {
		idx, ok := <-selIdxChan
		nc.Gui.Update(cleanUp)
		if ok {
			nc.Filter = nyaa_scraper.Filters[idx]
			nc.Reload(nc.Gui)
		}
	}()
}

func (nc *nyaaCui) setGuiKeyBindings(gui *gocui.Gui) {
	gui.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quitGocui)
}

func quitGocui(gui *gocui.Gui, view *gocui.View) error {
	return gocui.ErrQuit
}

func gocuiReturnError(gui *gocui.Gui, err error) {
	gui.Update(func(gui *gocui.Gui) error {
		return err
	})
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"gopkg.in/yaml.v2"
	"io/ioutil"
)

type packageDefinition struct {
	Name string `yaml:"name"`
}

func loadPackageDefinition(pathname string) packageDefinition {
	data, err := ioutil.ReadFile(pathname)

	if err != nil {
		panic(err)
	}

	var pd packageDefinition

	err = yaml.Unmarshal(data, &pd)

	if err != nil {
		panic(err)
	}

	return pd
}
<commit_msg>Iterate over package paths<commit_after>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"strings"
)

type packageDefinition struct {
	Name string `yaml:"name"`
}

func loadPackageDefinition(pathname string) packageDefinition {
	data, err := ioutil.ReadFile(pathname)

	if err != nil {
		panic(err)
	}

	var pd packageDefinition

	err = yaml.Unmarshal(data, &pd)

	if err != nil {
		panic(err)
	}

	return pd
}

type packageIndex struct {
}

func buildPackageIndex(pkgPath string) packageIndex {
	paths := strings.Split(pkgPath, ":")

	for i, path := range paths {
		fmt.Println(i)
		fmt.Println(path)
	}

	return packageIndex{}
}
<|endoftext|>


<commit_before>package memo

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"sync"
	"testing"
	"time"
)

// BPS is emulation download speed for url
const BPS = 10

type slowReader struct {
	delay time.Duration
	r     io.Reader
}

func (sr slowReader) Read(p []byte) (int, error) {
	time.Sleep(sr.delay)
	return sr.r.Read(p[:1])
}

func newReader(r io.Reader, bps int) io.Reader {
	delay := time.Second / time.Duration(bps)
	return slowReader{r: r, delay: delay}
}

// httpGetBodyMock emulates time-bound read functions.
func httpGetBodyMock(str string, done <-chan struct{}) (interface{}, error) {
	s := strings.NewReader(str)
	r := newReader(s, BPS)
	return ioutil.ReadAll(r)
}

func httpGetBody(url string, done <-chan struct{}) (interface{}, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

var HTTPGetBody = httpGetBodyMock

func incomingURLs() <-chan string {
	ch := make(chan string)
	go func() {
		for _, url := range []string{
			"https://golang.org",
			"https://godoc.org",
			"https://play.golang.org",
			"http://gopl.io",
			"https://golang.org",
			"https://godoc.org",
			"https://play.golang.org",
			"http://gopl.io",
		} {
			ch <- url
		}
		close(ch)
	}()
	return ch
}

type M interface {
	Get(key string, done <-chan struct{}) (interface{}, error)
}

func Sequential(t *testing.T, m M) {
	for url := range incomingURLs() {
		start := time.Now()
		value, err := m.Get(url, nil)
		if err != nil {
			log.Print(err)
			continue
		}
		fmt.Printf("%s, %s, %d bytes\n",
			url, time.Since(start), len(value.([]byte)))
	}
}

func Concurrent(t *testing.T, m M) {
	var n sync.WaitGroup
	for url := range incomingURLs() {
		n.Add(1)
		go func(url string) {
			defer n.Done()
			start := time.Now()
			value, err := m.Get(url, nil)
			if err != nil {
				log.Print(err)
				return
			}
			fmt.Printf("%s, %s, %d bytes\n",
				url, time.Since(start), len(value.([]byte)))
		}(url)
	}
	n.Wait()
}

func TestSequential(t *testing.T) {
	m := New(HTTPGetBody)
	defer m.Close()
	Sequential(t, m)
}

func TestConcurrent(t *testing.T) {
	m := New(HTTPGetBody)
	defer m.Close()
	Concurrent(t, m)
}
<commit_msg>[9.3] playing with slow reader<commit_after>package memo

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"sync"
	"testing"
	"time"
)

// BPS is emulation download speed for url
const BPS = 10

type slowReader struct {
	delay time.Duration
	r     io.Reader
}

func (sr slowReader) Read(p []byte) (int, error) {
	time.Sleep(sr.delay)
	return sr.r.Read(p[:1])
}

func newReader(r io.Reader, bps int) io.Reader {
	delay := time.Second / time.Duration(bps)
	return slowReader{r: r, delay: delay}
}

// httpGetBodyMock emulates time-bound read functions.
func httpGetBodyMock(str string, done <-chan struct{}) (interface{}, error) {
	s := strings.NewReader(str)
	r := newReader(s, BPS)
	b := make([]byte, 0)
	x := make([]byte, 1)

Loop:
	for {
		select {
		case <-done:
			return b, fmt.Errorf("canselation error")
		default:
			n, err := r.Read(x)
			if err != nil && err != io.EOF {
				return b, err
			}
			if n == 0 {
				break Loop
			}
			b = append(b, x...)
		}
	}
	return b, nil
}

func httpGetBody(url string, done <-chan struct{}) (interface{}, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

var HTTPGetBody = httpGetBodyMock

func incomingURLs() <-chan string {
	ch := make(chan string)
	go func() {
		for _, url := range []string{
			"https://golang.org",
			"https://godoc.org",
			"https://play.golang.org",
			"http://gopl.io",
			"https://golang.org",
			"https://godoc.org",
			"https://play.golang.org",
			"http://gopl.io",
		} {
			ch <- url
		}
		close(ch)
	}()
	return ch
}

type M interface {
	Get(key string, done <-chan struct{}) (interface{}, error)
}

func Sequential(t *testing.T, m M) {
	for url := range incomingURLs() {
		start := time.Now()
		value, err := m.Get(url, nil)
		if err != nil {
			log.Print(err)
			continue
		}
		fmt.Printf("%s, %s, %d bytes\n",
			url, time.Since(start), len(value.([]byte)))
	}
}

func Concurrent(t *testing.T, m M) {
	var n sync.WaitGroup
	for url := range incomingURLs() {
		n.Add(1)
		go func(url string) {
			defer n.Done()
			start := time.Now()
			value, err := m.Get(url, nil)
			if err != nil {
				log.Print(err)
				return
			}
			fmt.Printf("%s, %s, %d bytes\n",
				url, time.Since(start), len(value.([]byte)))
		}(url)
	}
	n.Wait()
}

func TestSequential(t *testing.T) {
	m := New(HTTPGetBody)
	defer m.Close()
	Sequential(t, m)
}

func TestConcurrent(t *testing.T) {
	m := New(HTTPGetBody)
	defer m.Close()
	Concurrent(t, m)
}
<|endoftext|>


<commit_before>// Package errors provides common error handling tools
// NOTE: Subject to change, do not rely on this package from outside git-lfs source
package errors

// The LFS error system provides a simple wrapper around Go errors and the
// ability to inspect errors. It is strongly influenced by Dave Cheney's post
// at http://dave.cheney.net/2014/12/24/inspecting-errors.
//
// When passing errors out of lfs package functions, the return type should
// always be `error`. The wrappedError details are not exported. If an error is
// the kind of error a caller should need to investigate, an IsXError()
// function is provided that tells the caller if the error is of that type.
// There should only be a handfull of cases where a simple `error` is
// insufficient.
//
// The error behaviors can be nested when created. For example, the not
// implemented error can also be marked as a fatal error:
//
//	func LfsFunction() error {
//		err := functionCall()
//		if err != nil {
//			return newFatalError(newNotImplementedError(err))
//		}
//		return nil
//	}
//
// Then in the caller:
//
//	err := lfs.LfsFunction()
//	if lfs.IsNotImplementedError(err) {
//		log.Print("feature not implemented")
//	}
//	if lfs.IsFatalError(err) {
//		os.Exit(1)
//	}
//
// Wrapped errors contain a context, which is a map[string]string. These
// contexts can be accessed through the Error*Context functions. Calling these
// functions on a regular Go error will have no effect.
//
// Example:
//
//	err := lfs.SomeFunction()
//	errors.ErrorSetContext(err, "foo", "bar")
//	errors.ErrorGetContext(err, "foo") // => "bar"
//	errors.ErrorDelContext(err, "foo")
//
// Wrapped errors also contain the stack from the point at which they are
// called. Use the '%+v' printf verb to display. See the github.com/pkg/errors
// docs for more info: https://godoc.org/github.com/pkg/errors

import (
	"bytes"
	"fmt"

	"github.com/pkg/errors"
)

// New returns an error with the supplied message. New also records the stack
// trace at thepoint it was called.
func New(message string) error {
	return errors.New(message)
}

// Errorf formats according to a format specifier and returns the string
// as a value that satisfies error.
// Errorf also records the stack trace at the point it was called.
func Errorf(format string, args ...interface{}) error {
	return errors.Errorf(format, args...)
}

// Wrap wraps an error with an additional message.
func Wrap(err error, msg string) error {
	return newWrappedError(err, msg)
}

// Wrapf wraps an error with an additional formatted message.
func Wrapf(err error, format string, args ...interface{}) error {
	if err == nil {
		err = errors.New("")
	}

	message := fmt.Sprintf(format, args...)

	return newWrappedError(err, message)
}

func StackTrace(err error) []string {
	type stacktrace interface {
		StackTrace() errors.StackTrace
	}

	if err, ok := err.(stacktrace); ok {
		frames := err.StackTrace()
		lines := make([]string, len(frames))
		for i, f := range frames {
			lines[i] = fmt.Sprintf("%+v", f)
		}
		return lines
	}

	return nil
}

func Combine(errs []error) error {
	if len(errs) > 0 {
		var buf bytes.Buffer
		for i, err := range errs {
			if i > 0 {
				buf.WriteString("\n")
			}
			buf.WriteString(err.Error())
		}
		return fmt.Errorf(buf.String())
	}
	return nil

}
<commit_msg>Early return instead of nesting (code style)<commit_after>// Package errors provides common error handling tools
// NOTE: Subject to change, do not rely on this package from outside git-lfs source
package errors

// The LFS error system provides a simple wrapper around Go errors and the
// ability to inspect errors. It is strongly influenced by Dave Cheney's post
// at http://dave.cheney.net/2014/12/24/inspecting-errors.
//
// When passing errors out of lfs package functions, the return type should
// always be `error`. The wrappedError details are not exported. If an error is
// the kind of error a caller should need to investigate, an IsXError()
// function is provided that tells the caller if the error is of that type.
// There should only be a handfull of cases where a simple `error` is
// insufficient.
//
// The error behaviors can be nested when created. For example, the not
// implemented error can also be marked as a fatal error:
//
//	func LfsFunction() error {
//		err := functionCall()
//		if err != nil {
//			return newFatalError(newNotImplementedError(err))
//		}
//		return nil
//	}
//
// Then in the caller:
//
//	err := lfs.LfsFunction()
//	if lfs.IsNotImplementedError(err) {
//		log.Print("feature not implemented")
//	}
//	if lfs.IsFatalError(err) {
//		os.Exit(1)
//	}
//
// Wrapped errors contain a context, which is a map[string]string. These
// contexts can be accessed through the Error*Context functions. Calling these
// functions on a regular Go error will have no effect.
//
// Example:
//
//	err := lfs.SomeFunction()
//	errors.ErrorSetContext(err, "foo", "bar")
//	errors.ErrorGetContext(err, "foo") // => "bar"
//	errors.ErrorDelContext(err, "foo")
//
// Wrapped errors also contain the stack from the point at which they are
// called. Use the '%+v' printf verb to display. See the github.com/pkg/errors
// docs for more info: https://godoc.org/github.com/pkg/errors

import (
	"bytes"
	"fmt"

	"github.com/pkg/errors"
)

// New returns an error with the supplied message. New also records the stack
// trace at thepoint it was called.
func New(message string) error {
	return errors.New(message)
}

// Errorf formats according to a format specifier and returns the string
// as a value that satisfies error.
// Errorf also records the stack trace at the point it was called.
func Errorf(format string, args ...interface{}) error {
	return errors.Errorf(format, args...)
}

// Wrap wraps an error with an additional message.
func Wrap(err error, msg string) error {
	return newWrappedError(err, msg)
}

// Wrapf wraps an error with an additional formatted message.
func Wrapf(err error, format string, args ...interface{}) error {
	if err == nil {
		err = errors.New("")
	}

	message := fmt.Sprintf(format, args...)

	return newWrappedError(err, message)
}

func StackTrace(err error) []string {
	type stacktrace interface {
		StackTrace() errors.StackTrace
	}

	if err, ok := err.(stacktrace); ok {
		frames := err.StackTrace()
		lines := make([]string, len(frames))
		for i, f := range frames {
			lines[i] = fmt.Sprintf("%+v", f)
		}
		return lines
	}

	return nil
}

func Combine(errs []error) error {
	if len(errs) == 0 {
		return nil
	}

	var buf bytes.Buffer
	for i, err := range errs {
		if i > 0 {
			buf.WriteString("\n")
		}
		buf.WriteString(err.Error())
	}
	return fmt.Errorf(buf.String())
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc.
//
// 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 cli contains methods useful for implementing administrative command
// line utilities.
package cli

import (
	"context"
	"fmt"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	log "github.com/golang/glog"
	"github.com/golang/protobuf/ptypes"
	"google.golang.org/grpc"

	"github.com/google/fleetspeak/fleetspeak/src/admin/history"
	"github.com/google/fleetspeak/fleetspeak/src/common"

	sgrpc "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server"
	spb "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server"
)

// dateFmt is a fairly dense, 23 character date format string, suitable for
// tabular date information.
const dateFmt = "15:04:05.000 2006.01.02"

// Usage prints usage information describing the command line flags and behavior
// of programs based on Execute.
func Usage() {
	n := path.Base(os.Args[0])
	fmt.Fprintf(os.Stderr,
		"Usage:\n"+
			"    %s listclients\n"+
			"    %s listcontacts <client_id> [limit]\n"+
			"    %s analysehistory <client_id>\n"+
			"    %s blacklistclient <client_id>\n"+
			"\n", n, n, n, n)
}

// Execute examines command line flags and executes one of the standard command line
// actions, as summarized by Usage. It requires a grpc connection to an admin server
// and the command line parameters to interpret.
func Execute(conn *grpc.ClientConn, args ...string) {
	admin := sgrpc.NewAdminClient(conn)

	if len(args) == 0 {
		fmt.Fprint(os.Stderr, "A command is required.\n")
		Usage()
		os.Exit(1)
	}

	switch args[0] {
	case "listclients":
		ListClients(admin, args[1:]...)
	case "listcontacts":
		ListContacts(admin, args[1:]...)
	case "analysehistory":
		AnalyseHistory(admin, args[1:]...)
	case "blacklistclient":
		BlacklistClient(admin, args[1:]...)
	default:
		fmt.Fprintf(os.Stderr, "Unknown command: %v\n", args[0])
		Usage()
		os.Exit(1)
	}
}

// ListClients prints a list of all clients in the fleetspeak system.
func ListClients(c sgrpc.AdminClient, args ...string) {
	if len(args) > 0 {
		Usage()
		os.Exit(1)
	}
	ctx := context.Background()
	res, err := c.ListClients(ctx, &spb.ListClientsRequest{}, grpc.MaxCallRecvMsgSize(1024*1024*1024))
	if err != nil {
		log.Exitf("ListClients RPC failed: %v", err)
	}
	if len(res.Clients) == 0 {
		fmt.Println("No clients found.")
		return
	}
	sort.Sort(byContactTime(res.Clients))
	fmt.Printf("%-16s %-23s %s\n", "Client ID:", "Last Seen:", "Labels:")
	for _, cl := range res.Clients {
		id, err := common.BytesToClientID(cl.ClientId)
		if err != nil {
			log.Errorf("Ignoring invalid client id [%v], %v", cl.ClientId, err)
			continue
		}
		var ls []string
		for _, l := range cl.Labels {
			ls = append(ls, l.ServiceName+":"+l.Label)
		}
		ts, err := ptypes.Timestamp(cl.LastContactTime)
		if err != nil {
			log.Errorf("Unable to parse last contact time for %v: %v", id, err)
		}
		fmt.Printf("%v %v [%v]\n", id, ts.Format(dateFmt), strings.Join(ls, ","))
	}
}

// byContactTime adapts []*spb.Client for use by sort.Sort.
type byContactTime []*spb.Client

func (b byContactTime) Len() int           { return len(b) }
func (b byContactTime) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b byContactTime) Less(i, j int) bool { return contactTime(b[i]).Before(contactTime(b[j])) }

func contactTime(c *spb.Client) time.Time {
	return time.Unix(c.LastContactTime.Seconds, int64(c.LastContactTime.Nanos))
}

// ListContacts prints a list contacts that the system has recorded for a
// client. args[0] must be a client id. If present, args[1] limits to the most
// recent args[1] contacts.
func ListContacts(c sgrpc.AdminClient, args ...string) {
	if len(args) == 0 || len(args) > 2 {
		Usage()
		os.Exit(1)
	}
	id, err := common.StringToClientID(args[0])
	if err != nil {
		log.Exitf("Unable to parse %s as client id: %v", args[0], err)
	}
	var lim int
	if len(args) == 2 {
		lim, err = strconv.Atoi(args[1])
		if err != nil {
			log.Exitf("Unable to parse %s as a limit: %v", args[1], err)
		}
	}

	ctx := context.Background()
	res, err := c.ListClientContacts(ctx, &spb.ListClientContactsRequest{ClientId: id.Bytes()}, grpc.MaxCallRecvMsgSize(1024*1024*1024))
	if err != nil {
		log.Exitf("ListClientContacts RPC failed: %v", err)
	}
	if len(res.Contacts) == 0 {
		fmt.Println("No contacts found.")
		return
	}

	fmt.Printf("Found %d contacts.\n", len(res.Contacts))

	sort.Sort(byTimestamp(res.Contacts))
	fmt.Printf("%-23s %s", "Timestamp:", "Observed IP:\n")
	for i, con := range res.Contacts {
		if lim > 0 && i > lim {
			break
		}
		ts, err := ptypes.Timestamp(con.Timestamp)
		if err != nil {
			log.Errorf("Unable to parse timestamp for contact: %v", err)
			continue
		}
		fmt.Printf("%s %s\n", ts.Format(dateFmt), con.ObservedAddress)
	}
}

// byTimestamp adapts []*spb.ClientContact for use by sort.Sort.
type byTimestamp []*spb.ClientContact

func (b byTimestamp) Len() int           { return len(b) }
func (b byTimestamp) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b byTimestamp) Less(i, j int) bool { return timestamp(b[i]).Before(timestamp(b[j])) }

func timestamp(c *spb.ClientContact) time.Time {
	return time.Unix(c.Timestamp.Seconds, int64(c.Timestamp.Nanos))
}

// AnalyseHistory prints a summary analysis of a client's history. args[0] must
// be a client id.
func AnalyseHistory(c sgrpc.AdminClient, args ...string) {
	if len(args) != 1 {
		Usage()
		os.Exit(1)
	}
	id, err := common.StringToClientID(args[0])
	if err != nil {
		log.Exitf("Unable to parse %s as client id: %v", args[0], err)
	}
	ctx := context.Background()
	res, err := c.ListClientContacts(ctx, &spb.ListClientContactsRequest{ClientId: id.Bytes()}, grpc.MaxCallRecvMsgSize(1024*1024*1024))
	if err != nil {
		log.Exitf("ListClientContacts RPC failed: %v", err)
	}
	if len(res.Contacts) == 0 {
		fmt.Println("No contacts found.")
		return
	}
	s, err := history.Summarize(res.Contacts)
	if err != nil {
		log.Exitf("Error creating summary: %v", err)
	}
	fmt.Printf(`Raw Summary:
  First Recorded Contact: %v
  Last Recorded Contact: %v
  Contact Count: %d
  Observed IP Count: %d
  Split Points: %d
  Splits: %d
  Skips: %d
`, s.Start, s.End, s.Count, s.IPCount, s.SplitPoints, s.Splits, s.Skips)
	if s.Splits > 0 {
		fmt.Printf("This client appears to have be restored %d times from %d different backup images.\n", s.Splits, s.SplitPoints)
	}
	if s.Skips > s.Splits {
		fmt.Printf("Observed %d Skips, but only %d splits. The machine may have been cloned.\n", s.Skips, s.Splits)
	}
}

// BlacklistClient blacklists a client id, forcing any client(s) using it to
// rekey. args[0] must be a client id.
func BlacklistClient(c sgrpc.AdminClient, args ...string) {
	if len(args) != 1 {
		Usage()
		os.Exit(1)
	}
	id, err := common.StringToClientID(args[0])
	if err != nil {
		log.Exitf("Unable to parse %s as client id: %v", args[0], err)
	}
	ctx := context.Background()
	if _, err := c.BlacklistClient(ctx, &spb.BlacklistClientRequest{ClientId: id.Bytes()}); err != nil {
		log.Exitf("BlacklistClient RPC failed: %v", err)
	}
}
<commit_msg>admin utility mentions blacklist status<commit_after>// Copyright 2017 Google Inc.
//
// 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 cli contains methods useful for implementing administrative command
// line utilities.
package cli

import (
	"context"
	"fmt"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	log "github.com/golang/glog"
	"github.com/golang/protobuf/ptypes"
	"google.golang.org/grpc"

	"github.com/google/fleetspeak/fleetspeak/src/admin/history"
	"github.com/google/fleetspeak/fleetspeak/src/common"

	sgrpc "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server"
	spb "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server"
)

// dateFmt is a fairly dense, 23 character date format string, suitable for
// tabular date information.
const dateFmt = "15:04:05.000 2006.01.02"

// Usage prints usage information describing the command line flags and behavior
// of programs based on Execute.
func Usage() {
	n := path.Base(os.Args[0])
	fmt.Fprintf(os.Stderr,
		"Usage:\n"+
			"    %s listclients\n"+
			"    %s listcontacts <client_id> [limit]\n"+
			"    %s analysehistory <client_id>\n"+
			"    %s blacklistclient <client_id>\n"+
			"\n", n, n, n, n)
}

// Execute examines command line flags and executes one of the standard command line
// actions, as summarized by Usage. It requires a grpc connection to an admin server
// and the command line parameters to interpret.
func Execute(conn *grpc.ClientConn, args ...string) {
	admin := sgrpc.NewAdminClient(conn)

	if len(args) == 0 {
		fmt.Fprint(os.Stderr, "A command is required.\n")
		Usage()
		os.Exit(1)
	}

	switch args[0] {
	case "listclients":
		ListClients(admin, args[1:]...)
	case "listcontacts":
		ListContacts(admin, args[1:]...)
	case "analysehistory":
		AnalyseHistory(admin, args[1:]...)
	case "blacklistclient":
		BlacklistClient(admin, args[1:]...)
	default:
		fmt.Fprintf(os.Stderr, "Unknown command: %v\n", args[0])
		Usage()
		os.Exit(1)
	}
}

// ListClients prints a list of all clients in the fleetspeak system.
func ListClients(c sgrpc.AdminClient, args ...string) {
	if len(args) > 0 {
		Usage()
		os.Exit(1)
	}
	ctx := context.Background()
	res, err := c.ListClients(ctx, &spb.ListClientsRequest{}, grpc.MaxCallRecvMsgSize(1024*1024*1024))
	if err != nil {
		log.Exitf("ListClients RPC failed: %v", err)
	}
	if len(res.Clients) == 0 {
		fmt.Println("No clients found.")
		return
	}
	sort.Sort(byContactTime(res.Clients))
	fmt.Printf("%-16s %-23s %s\n", "Client ID:", "Last Seen:", "Labels:")
	for _, cl := range res.Clients {
		id, err := common.BytesToClientID(cl.ClientId)
		if err != nil {
			log.Errorf("Ignoring invalid client id [%v], %v", cl.ClientId, err)
			continue
		}
		var ls []string
		for _, l := range cl.Labels {
			ls = append(ls, l.ServiceName+":"+l.Label)
		}
		ts, err := ptypes.Timestamp(cl.LastContactTime)
		if err != nil {
			log.Errorf("Unable to parse last contact time for %v: %v", id, err)
		}
		tag := ""
		if cl.Blacklisted {
			tag = " *blacklisted*"
		}
		fmt.Printf("%v %v [%v]%s\n", id, ts.Format(dateFmt), strings.Join(ls, ","), tag)
	}
}

// byContactTime adapts []*spb.Client for use by sort.Sort.
type byContactTime []*spb.Client

func (b byContactTime) Len() int           { return len(b) }
func (b byContactTime) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b byContactTime) Less(i, j int) bool { return contactTime(b[i]).Before(contactTime(b[j])) }

func contactTime(c *spb.Client) time.Time {
	return time.Unix(c.LastContactTime.Seconds, int64(c.LastContactTime.Nanos))
}

// ListContacts prints a list contacts that the system has recorded for a
// client. args[0] must be a client id. If present, args[1] limits to the most
// recent args[1] contacts.
func ListContacts(c sgrpc.AdminClient, args ...string) {
	if len(args) == 0 || len(args) > 2 {
		Usage()
		os.Exit(1)
	}
	id, err := common.StringToClientID(args[0])
	if err != nil {
		log.Exitf("Unable to parse %s as client id: %v", args[0], err)
	}
	var lim int
	if len(args) == 2 {
		lim, err = strconv.Atoi(args[1])
		if err != nil {
			log.Exitf("Unable to parse %s as a limit: %v", args[1], err)
		}
	}

	ctx := context.Background()
	res, err := c.ListClientContacts(ctx, &spb.ListClientContactsRequest{ClientId: id.Bytes()}, grpc.MaxCallRecvMsgSize(1024*1024*1024))
	if err != nil {
		log.Exitf("ListClientContacts RPC failed: %v", err)
	}
	if len(res.Contacts) == 0 {
		fmt.Println("No contacts found.")
		return
	}

	fmt.Printf("Found %d contacts.\n", len(res.Contacts))

	sort.Sort(byTimestamp(res.Contacts))
	fmt.Printf("%-23s %s", "Timestamp:", "Observed IP:\n")
	for i, con := range res.Contacts {
		if lim > 0 && i > lim {
			break
		}
		ts, err := ptypes.Timestamp(con.Timestamp)
		if err != nil {
			log.Errorf("Unable to parse timestamp for contact: %v", err)
			continue
		}
		fmt.Printf("%s %s\n", ts.Format(dateFmt), con.ObservedAddress)
	}
}

// byTimestamp adapts []*spb.ClientContact for use by sort.Sort.
type byTimestamp []*spb.ClientContact

func (b byTimestamp) Len() int           { return len(b) }
func (b byTimestamp) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b byTimestamp) Less(i, j int) bool { return timestamp(b[i]).Before(timestamp(b[j])) }

func timestamp(c *spb.ClientContact) time.Time {
	return time.Unix(c.Timestamp.Seconds, int64(c.Timestamp.Nanos))
}

// AnalyseHistory prints a summary analysis of a client's history. args[0] must
// be a client id.
func AnalyseHistory(c sgrpc.AdminClient, args ...string) {
	if len(args) != 1 {
		Usage()
		os.Exit(1)
	}
	id, err := common.StringToClientID(args[0])
	if err != nil {
		log.Exitf("Unable to parse %s as client id: %v", args[0], err)
	}
	ctx := context.Background()
	res, err := c.ListClientContacts(ctx, &spb.ListClientContactsRequest{ClientId: id.Bytes()}, grpc.MaxCallRecvMsgSize(1024*1024*1024))
	if err != nil {
		log.Exitf("ListClientContacts RPC failed: %v", err)
	}
	if len(res.Contacts) == 0 {
		fmt.Println("No contacts found.")
		return
	}
	s, err := history.Summarize(res.Contacts)
	if err != nil {
		log.Exitf("Error creating summary: %v", err)
	}
	fmt.Printf(`Raw Summary:
  First Recorded Contact: %v
  Last Recorded Contact: %v
  Contact Count: %d
  Observed IP Count: %d
  Split Points: %d
  Splits: %d
  Skips: %d
`, s.Start, s.End, s.Count, s.IPCount, s.SplitPoints, s.Splits, s.Skips)
	if s.Splits > 0 {
		fmt.Printf("This client appears to have be restored %d times from %d different backup images.\n", s.Splits, s.SplitPoints)
	}
	if s.Skips > s.Splits {
		fmt.Printf("Observed %d Skips, but only %d splits. The machine may have been cloned.\n", s.Skips, s.Splits)
	}
}

// BlacklistClient blacklists a client id, forcing any client(s) using it to
// rekey. args[0] must be a client id.
func BlacklistClient(c sgrpc.AdminClient, args ...string) {
	if len(args) != 1 {
		Usage()
		os.Exit(1)
	}
	id, err := common.StringToClientID(args[0])
	if err != nil {
		log.Exitf("Unable to parse %s as client id: %v", args[0], err)
	}
	ctx := context.Background()
	if _, err := c.BlacklistClient(ctx, &spb.BlacklistClientRequest{ClientId: id.Bytes()}); err != nil {
		log.Exitf("BlacklistClient RPC failed: %v", err)
	}
}
<|endoftext|>


<commit_before>package libvirt

import (
	"os"

	"github.com/hyperhq/runv/hypervisor/network"
	"github.com/hyperhq/runv/hypervisor/pod"
)

func (ld *LibvirtDriver) BuildinNetwork() bool {
	return false
}

func (ld *LibvirtDriver) InitNetwork(bIface, bIP string) error {
	return nil
}

func (lc *LibvirtContext) ConfigureNetwork(vmId, requestedIP string,
	maps []pod.UserContainerPort, config pod.UserInterface) (*network.Settings, error) {
	return nil, nil
}

func (lc *LibvirtContext) AllocateNetwork(vmId, requestedIP string,
	maps []pod.UserContainerPort) (*network.Settings, error) {
	return nil, nil
}

func (lc *LibvirtContext) ReleaseNetwork(vmId, releasedIP string, maps []pod.UserContainerPort,
	file *os.File) error {
	return nil
}
<commit_msg>Don't create tap device for libvirt domain<commit_after>package libvirt

import (
	"os"

	"github.com/hyperhq/runv/hypervisor/network"
	"github.com/hyperhq/runv/hypervisor/pod"
)

func (ld *LibvirtDriver) BuildinNetwork() bool {
	return false
}

func (ld *LibvirtDriver) InitNetwork(bIface, bIP string) error {
	return nil
}

func (lc *LibvirtContext) ConfigureNetwork(vmId, requestedIP string,
	maps []pod.UserContainerPort, config pod.UserInterface) (*network.Settings, error) {
	return network.Configure(vmId, requestedIP, true, maps, config)
}

func (lc *LibvirtContext) AllocateNetwork(vmId, requestedIP string,
	maps []pod.UserContainerPort) (*network.Settings, error) {
	return network.Allocate(vmId, requestedIP, true, maps)
}

func (lc *LibvirtContext) ReleaseNetwork(vmId, releasedIP string, maps []pod.UserContainerPort,
	file *os.File) error {
	return network.Release(vmId, releasedIP, maps, nil)
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/hashicorp/hil"
	"github.com/hashicorp/hil/ast"
	"github.com/mitchellh/reflectwalk"
)

// interpolationWalker implements interfaces for the reflectwalk package
// (github.com/mitchellh/reflectwalk) that can be used to automatically
// execute a callback for an interpolation.
type interpolationWalker struct {
	// F is the function to call for every interpolation. It can be nil.
	//
	// If Replace is true, then the return value of F will be used to
	// replace the interpolation.
	F       interpolationWalkerFunc
	Replace bool

	// ContextF is an advanced version of F that also receives the
	// location of where it is in the structure. This lets you do
	// context-aware validation.
	ContextF interpolationWalkerContextFunc

	key         []string
	lastValue   reflect.Value
	loc         reflectwalk.Location
	cs          []reflect.Value
	csKey       []reflect.Value
	csData      interface{}
	sliceIndex  int
	unknownKeys []string
}

// interpolationWalkerFunc is the callback called by interpolationWalk.
// It is called with any interpolation found. It should return a value
// to replace the interpolation with, along with any errors.
//
// If Replace is set to false in interpolationWalker, then the replace
// value can be anything as it will have no effect.
type interpolationWalkerFunc func(ast.Node) (interface{}, error)

// interpolationWalkerContextFunc is called by interpolationWalk if
// ContextF is set. This receives both the interpolation and the location
// where the interpolation is.
//
// This callback can be used to validate the location of the interpolation
// within the configuration.
type interpolationWalkerContextFunc func(reflectwalk.Location, ast.Node)

func (w *interpolationWalker) Enter(loc reflectwalk.Location) error {
	w.loc = loc
	if loc == reflectwalk.WalkLoc {
		w.sliceIndex = -1
	}
	return nil
}

func (w *interpolationWalker) Exit(loc reflectwalk.Location) error {
	w.loc = reflectwalk.None

	switch loc {
	case reflectwalk.Map:
		w.cs = w.cs[:len(w.cs)-1]
	case reflectwalk.MapValue:
		w.key = w.key[:len(w.key)-1]
		w.csKey = w.csKey[:len(w.csKey)-1]
	case reflectwalk.Slice:
		// Split any values that need to be split
		w.splitSlice()
		w.cs = w.cs[:len(w.cs)-1]
	case reflectwalk.SliceElem:
		w.csKey = w.csKey[:len(w.csKey)-1]
		w.sliceIndex = -1
	}

	return nil
}

func (w *interpolationWalker) Map(m reflect.Value) error {
	w.cs = append(w.cs, m)
	return nil
}

func (w *interpolationWalker) MapElem(m, k, v reflect.Value) error {
	w.csData = k
	w.csKey = append(w.csKey, k)

	if w.sliceIndex != -1 {
		w.key = append(w.key, fmt.Sprintf("%d.%s", w.sliceIndex, k.String()))
	} else {
		w.key = append(w.key, k.String())
	}

	w.lastValue = v
	return nil
}

func (w *interpolationWalker) Slice(s reflect.Value) error {
	w.cs = append(w.cs, s)
	return nil
}

func (w *interpolationWalker) SliceElem(i int, elem reflect.Value) error {
	w.csKey = append(w.csKey, reflect.ValueOf(i))
	w.sliceIndex = i
	return nil
}

func (w *interpolationWalker) Primitive(v reflect.Value) error {
	setV := v

	// We only care about strings
	if v.Kind() == reflect.Interface {
		setV = v
		v = v.Elem()
	}
	if v.Kind() != reflect.String {
		return nil
	}

	astRoot, err := hil.Parse(v.String())
	if err != nil {
		return err
	}

	// If the AST we got is just a literal string value with the same
	// value then we ignore it. We have to check if its the same value
	// because it is possible to input a string, get out a string, and
	// have it be different. For example: "foo-$${bar}" turns into
	// "foo-${bar}"
	if n, ok := astRoot.(*ast.LiteralNode); ok {
		if s, ok := n.Value.(string); ok && s == v.String() {
			return nil
		}
	}

	if w.ContextF != nil {
		w.ContextF(w.loc, astRoot)
	}

	if w.F == nil {
		return nil
	}

	replaceVal, err := w.F(astRoot)
	if err != nil {
		return fmt.Errorf(
			"%s in:\n\n%s",
			err, v.String())
	}

	if w.Replace {
		// We need to determine if we need to remove this element
		// if the result contains any "UnknownVariableValue" which is
		// set if it is computed. This behavior is different if we're
		// splitting (in a SliceElem) or not.
		remove := false
		if w.loc == reflectwalk.SliceElem {
			switch typedReplaceVal := replaceVal.(type) {
			case string:
				if typedReplaceVal == UnknownVariableValue {
					remove = true
				}
			case []interface{}:
				if hasUnknownValue(typedReplaceVal) {
					remove = true
				}
			}
		} else if replaceVal == UnknownVariableValue {
			remove = true
		}

		if remove {
			// Append the key to the unknown keys
			w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, "."))

			//w.removeCurrent()
			//return nil
		}

		resultVal := reflect.ValueOf(replaceVal)
		switch w.loc {
		case reflectwalk.MapKey:
			m := w.cs[len(w.cs)-1]

			// Delete the old value
			var zero reflect.Value
			m.SetMapIndex(w.csData.(reflect.Value), zero)

			// Set the new key with the existing value
			m.SetMapIndex(resultVal, w.lastValue)

			// Set the key to be the new key
			w.csData = resultVal
		case reflectwalk.MapValue:
			// If we're in a map, then the only way to set a map value is
			// to set it directly.
			m := w.cs[len(w.cs)-1]
			mk := w.csData.(reflect.Value)
			m.SetMapIndex(mk, resultVal)
		default:
			// Otherwise, we should be addressable
			setV.Set(resultVal)
		}
	}

	return nil
}

func (w *interpolationWalker) removeCurrent() {
	// Append the key to the unknown keys
	w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, "."))

	for i := 1; i <= len(w.cs); i++ {
		c := w.cs[len(w.cs)-i]
		switch c.Kind() {
		case reflect.Map:
			// Zero value so that we delete the map key
			var val reflect.Value

			// Get the key and delete it
			k := w.csData.(reflect.Value)
			c.SetMapIndex(k, val)
			return
		}
	}

	panic("No container found for removeCurrent")
}

func (w *interpolationWalker) replaceCurrent(v reflect.Value) {
	c := w.cs[len(w.cs)-2]
	switch c.Kind() {
	case reflect.Map:
		// Get the key and delete it
		k := w.csKey[len(w.csKey)-1]
		c.SetMapIndex(k, v)
	}
}

func hasUnknownValue(variable []interface{}) bool {
	for _, value := range variable {
		if strVal, ok := value.(string); ok {
			if strVal == UnknownVariableValue {
				return true
			}
		}
	}
	return false
}

func (w *interpolationWalker) splitSlice() {
	raw := w.cs[len(w.cs)-1]

	var s []interface{}
	switch v := raw.Interface().(type) {
	case []interface{}:
		s = v
	case []map[string]interface{}:
		return
	}

	split := false
	for _, val := range s {
		if varVal, ok := val.(ast.Variable); ok && varVal.Type == ast.TypeList {
			split = true
		}
		if _, ok := val.([]interface{}); ok {
			split = true
		}
	}

	if !split {
		return
	}

	result := make([]interface{}, 0)
	for _, v := range s {
		switch val := v.(type) {
		case ast.Variable:
			switch val.Type {
			case ast.TypeList:
				elements := val.Value.([]ast.Variable)
				for _, element := range elements {
					result = append(result, element.Value)
				}
			default:
				result = append(result, val.Value)
			}
		case []interface{}:
			for _, element := range val {
				result = append(result, element)
			}
		default:
			result = append(result, v)
		}
	}

	w.replaceCurrent(reflect.ValueOf(result))
}
<commit_msg>config: clean up unused fucntions<commit_after>package config

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/hashicorp/hil"
	"github.com/hashicorp/hil/ast"
	"github.com/mitchellh/reflectwalk"
)

// interpolationWalker implements interfaces for the reflectwalk package
// (github.com/mitchellh/reflectwalk) that can be used to automatically
// execute a callback for an interpolation.
type interpolationWalker struct {
	// F is the function to call for every interpolation. It can be nil.
	//
	// If Replace is true, then the return value of F will be used to
	// replace the interpolation.
	F       interpolationWalkerFunc
	Replace bool

	// ContextF is an advanced version of F that also receives the
	// location of where it is in the structure. This lets you do
	// context-aware validation.
	ContextF interpolationWalkerContextFunc

	key         []string
	lastValue   reflect.Value
	loc         reflectwalk.Location
	cs          []reflect.Value
	csKey       []reflect.Value
	csData      interface{}
	sliceIndex  int
	unknownKeys []string
}

// interpolationWalkerFunc is the callback called by interpolationWalk.
// It is called with any interpolation found. It should return a value
// to replace the interpolation with, along with any errors.
//
// If Replace is set to false in interpolationWalker, then the replace
// value can be anything as it will have no effect.
type interpolationWalkerFunc func(ast.Node) (interface{}, error)

// interpolationWalkerContextFunc is called by interpolationWalk if
// ContextF is set. This receives both the interpolation and the location
// where the interpolation is.
//
// This callback can be used to validate the location of the interpolation
// within the configuration.
type interpolationWalkerContextFunc func(reflectwalk.Location, ast.Node)

func (w *interpolationWalker) Enter(loc reflectwalk.Location) error {
	w.loc = loc
	if loc == reflectwalk.WalkLoc {
		w.sliceIndex = -1
	}
	return nil
}

func (w *interpolationWalker) Exit(loc reflectwalk.Location) error {
	w.loc = reflectwalk.None

	switch loc {
	case reflectwalk.Map:
		w.cs = w.cs[:len(w.cs)-1]
	case reflectwalk.MapValue:
		w.key = w.key[:len(w.key)-1]
		w.csKey = w.csKey[:len(w.csKey)-1]
	case reflectwalk.Slice:
		// Split any values that need to be split
		w.splitSlice()
		w.cs = w.cs[:len(w.cs)-1]
	case reflectwalk.SliceElem:
		w.csKey = w.csKey[:len(w.csKey)-1]
		w.sliceIndex = -1
	}

	return nil
}

func (w *interpolationWalker) Map(m reflect.Value) error {
	w.cs = append(w.cs, m)
	return nil
}

func (w *interpolationWalker) MapElem(m, k, v reflect.Value) error {
	w.csData = k
	w.csKey = append(w.csKey, k)

	if w.sliceIndex != -1 {
		w.key = append(w.key, fmt.Sprintf("%d.%s", w.sliceIndex, k.String()))
	} else {
		w.key = append(w.key, k.String())
	}

	w.lastValue = v
	return nil
}

func (w *interpolationWalker) Slice(s reflect.Value) error {
	w.cs = append(w.cs, s)
	return nil
}

func (w *interpolationWalker) SliceElem(i int, elem reflect.Value) error {
	w.csKey = append(w.csKey, reflect.ValueOf(i))
	w.sliceIndex = i
	return nil
}

func (w *interpolationWalker) Primitive(v reflect.Value) error {
	setV := v

	// We only care about strings
	if v.Kind() == reflect.Interface {
		setV = v
		v = v.Elem()
	}
	if v.Kind() != reflect.String {
		return nil
	}

	astRoot, err := hil.Parse(v.String())
	if err != nil {
		return err
	}

	// If the AST we got is just a literal string value with the same
	// value then we ignore it. We have to check if its the same value
	// because it is possible to input a string, get out a string, and
	// have it be different. For example: "foo-$${bar}" turns into
	// "foo-${bar}"
	if n, ok := astRoot.(*ast.LiteralNode); ok {
		if s, ok := n.Value.(string); ok && s == v.String() {
			return nil
		}
	}

	if w.ContextF != nil {
		w.ContextF(w.loc, astRoot)
	}

	if w.F == nil {
		return nil
	}

	replaceVal, err := w.F(astRoot)
	if err != nil {
		return fmt.Errorf(
			"%s in:\n\n%s",
			err, v.String())
	}

	if w.Replace {
		// We need to determine if we need to remove this element
		// if the result contains any "UnknownVariableValue" which is
		// set if it is computed. This behavior is different if we're
		// splitting (in a SliceElem) or not.
		remove := false
		if w.loc == reflectwalk.SliceElem {
			switch typedReplaceVal := replaceVal.(type) {
			case string:
				if typedReplaceVal == UnknownVariableValue {
					remove = true
				}
			case []interface{}:
				if hasUnknownValue(typedReplaceVal) {
					remove = true
				}
			}
		} else if replaceVal == UnknownVariableValue {
			remove = true
		}

		if remove {
			w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, "."))
		}

		resultVal := reflect.ValueOf(replaceVal)
		switch w.loc {
		case reflectwalk.MapKey:
			m := w.cs[len(w.cs)-1]

			// Delete the old value
			var zero reflect.Value
			m.SetMapIndex(w.csData.(reflect.Value), zero)

			// Set the new key with the existing value
			m.SetMapIndex(resultVal, w.lastValue)

			// Set the key to be the new key
			w.csData = resultVal
		case reflectwalk.MapValue:
			// If we're in a map, then the only way to set a map value is
			// to set it directly.
			m := w.cs[len(w.cs)-1]
			mk := w.csData.(reflect.Value)
			m.SetMapIndex(mk, resultVal)
		default:
			// Otherwise, we should be addressable
			setV.Set(resultVal)
		}
	}

	return nil
}

func (w *interpolationWalker) replaceCurrent(v reflect.Value) {
	c := w.cs[len(w.cs)-2]
	switch c.Kind() {
	case reflect.Map:
		// Get the key and delete it
		k := w.csKey[len(w.csKey)-1]
		c.SetMapIndex(k, v)
	}
}

func hasUnknownValue(variable []interface{}) bool {
	for _, value := range variable {
		if strVal, ok := value.(string); ok {
			if strVal == UnknownVariableValue {
				return true
			}
		}
	}
	return false
}

func (w *interpolationWalker) splitSlice() {
	raw := w.cs[len(w.cs)-1]

	var s []interface{}
	switch v := raw.Interface().(type) {
	case []interface{}:
		s = v
	case []map[string]interface{}:
		return
	}

	split := false
	for _, val := range s {
		if varVal, ok := val.(ast.Variable); ok && varVal.Type == ast.TypeList {
			split = true
		}
		if _, ok := val.([]interface{}); ok {
			split = true
		}
	}

	if !split {
		return
	}

	result := make([]interface{}, 0)
	for _, v := range s {
		switch val := v.(type) {
		case ast.Variable:
			switch val.Type {
			case ast.TypeList:
				elements := val.Value.([]ast.Variable)
				for _, element := range elements {
					result = append(result, element.Value)
				}
			default:
				result = append(result, val.Value)
			}
		case []interface{}:
			for _, element := range val {
				result = append(result, element)
			}
		default:
			result = append(result, v)
		}
	}

	w.replaceCurrent(reflect.ValueOf(result))
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"flag"
	"github.com/goerlang/epmd"
	"log"
	"net"
	"os"
	"os/signal"
	"runtime/pprof"
	"strconv"
	"time"
)

var noEpmd bool
var listenPort string
var regLimit int
var unregTTL int
var cpuProfile string

func init() {
	flag.StringVar(&listenPort, "port", "4369", "listen port")
	flag.BoolVar(&noEpmd, "no-epmd", false, "disable epmd")
	flag.IntVar(&regLimit, "nodes-limit", 1000, "limit size of registration table to prune unregistered nodes")
	flag.IntVar(&unregTTL, "unreg-ttl", 10, "prune unregistered nodes if unregistration older than this value in minutes")
	flag.StringVar(&cpuProfile, "profile-cpu", "", "profile CPU to file")
}

type regAns struct {
	reply   []byte
	isClose bool
}

type regReq struct {
	buf     []byte
	replyTo chan regAns
	conn    net.Conn
}

func main() {
	flag.Parse()
	if cpuProfile != "" {
		f, err := os.Create(cpuProfile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	stopCh := make(chan bool)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		for sig := range c {
			log.Printf("Signal %#v", sig)
			stopCh <- true
		}
	}()

	if !cliEnabled() {
		var err error
		var l net.Listener
		if !noEpmd {
			l, err = net.Listen("tcp", net.JoinHostPort("", listenPort))
			if err != nil || noEpmd {
				// Cannot bind, eclus instance already running, connect to it
				eclusCli()
			}
		} else {
			if !noEpmd {
				epm := make(chan regReq, 10)
				go epmReg(epm)
				go func() {
					for {
						conn, err := l.Accept()
						log.Printf("Accept new")
						if err != nil {
							log.Printf(err.Error())
						} else {
							go mLoop(conn, epm)
						}
					}
				}()
			}
			if nodeEnabled() {
				go runNode()
			}
			<-stopCh
		}
	} else {
		eclusCli()
	}
}

type nodeRec struct {
	*epmd.NodeInfo
	Time   time.Time
	Active bool
	conn   net.Conn
}

func epmReg(in <-chan regReq) {
	var nReg = make(map[string]*nodeRec)
	for {
		select {
		case req := <-in:
			buf := req.buf
			if len(buf) == 0 {
				rs := len(nReg)
				log.Printf("REG %d records", rs)
				now := time.Now()

				for node, rec := range nReg {
					if rec.conn == req.conn {
						log.Printf("Connection for %s dropped", node)
						nReg[node].Active = false
						nReg[node].Time = now
						nReg[node].conn = nil
					} else if rs > regLimit && !rec.Active && now.Sub(rec.Time).Minutes() > float64(unregTTL) {
						log.Printf("REG prune %s:%+v", node, rec)
						delete(nReg, node)
					}
				}
				continue
			}
			replyTo := req.replyTo
			log.Printf("IN: %v", buf)
			switch epmd.MessageId(buf[0]) {
			case epmd.ALIVE2_REQ:
				nConn := req.conn
				nInfo := epmd.Read_ALIVE2_REQ(buf)
				log.Printf("NodeInfo: %+v", nInfo)
				var reply []byte

				if rec, ok := nReg[nInfo.Name]; ok {
					log.Printf("Node %s found", nInfo.Name)
					if rec.Active {
						log.Printf("Node %s is running", nInfo.Name)
						reply = epmd.Compose_ALIVE2_RESP(false)
					} else {
						log.Printf("Node %s is not running", nInfo.Name)
						rec.conn = nConn

						nInfo.Creation = (rec.Creation % 3) + 1
						rec.NodeInfo = nInfo
						rec.Active = true
						reply = epmd.Compose_ALIVE2_RESP(true, rec.NodeInfo)
					}
				} else {
					log.Printf("New node %s", nInfo.Name)
					nInfo.Creation = 1
					rec := &nodeRec{
						NodeInfo: nInfo,
						conn:     nConn,
						Time:     time.Now(),
						Active:   true,
					}
					nReg[nInfo.Name] = rec
					reply = epmd.Compose_ALIVE2_RESP(true, rec.NodeInfo)
				}
				replyTo <- regAns{reply: reply, isClose: false}
			case epmd.PORT_PLEASE2_REQ:
				nName := epmd.Read_PORT_PLEASE2_REQ(buf)
				var reply []byte
				if rec, ok := nReg[nName]; ok && rec.Active {
					reply = epmd.Compose_PORT2_RESP(rec.NodeInfo)
				} else {
					reply = epmd.Compose_PORT2_RESP(nil)
				}
				replyTo <- regAns{reply: reply, isClose: true}
			case epmd.STOP_REQ:
				nName := epmd.Read_STOP_REQ(buf)
				var reply []byte
				if rec, ok := nReg[nName]; ok && rec.Active {
					// TODO: stop node
					reply = epmd.Compose_STOP_RESP(true)
				} else {
					reply = epmd.Compose_STOP_RESP(false)
				}
				replyTo <- regAns{reply: reply, isClose: true}
			case epmd.NAMES_REQ, epmd.DUMP_REQ:
				lp, err := strconv.Atoi(listenPort)
				if err != nil {
					log.Printf("Cannot convert %s to integer", listenPort)
					replyTo <- regAns{reply: nil, isClose: true}
				} else {
					replyB := new(bytes.Buffer)
					epmd.Compose_START_NAMES_RESP(replyB, lp)
					for _, rec := range nReg {
						if epmd.MessageId(buf[0]) == epmd.NAMES_REQ {
							if rec.Active {
								epmd.Append_NAMES_RESP(replyB, rec.NodeInfo)
							} else {
								if rec.Active {
									epmd.Append_DUMP_RESP_ACTIVE(replyB, rec.NodeInfo)
								} else {
									epmd.Append_DUMP_RESP_UNUSED(replyB, rec.NodeInfo)
								}
							}
						}
					}
					replyTo <- regAns{reply: replyB.Bytes(), isClose: true}
				}
			case epmd.KILL_REQ:
				reply := epmd.Compose_KILL_RESP()
				replyTo <- regAns{reply: reply, isClose: true}
			default:
				switch cliMessageId(buf[0]) {
				case REQ_NAMES:
					reply := ansNames(nReg)
					replyTo <- regAns{reply: reply, isClose: true}
				default:
					replyTo <- regAns{reply: nil, isClose: true}
				}
			}
		}
	}
}

func mLoop(c net.Conn, epm chan regReq) {
	buf := make([]byte, 1024)
	for {
		n, err := c.Read(buf)
		if err != nil {
			c.Close()
			log.Printf("Stop loop: %v", err)
			epm <- regReq{buf: []byte{}, conn: c}
			return
		}
		length := binary.BigEndian.Uint16(buf[0:2])
		if length != uint16(n-2) {
			log.Printf("Incomplete packet from erlang node to epmd: %d from %d", n, length)
			break
		}
		log.Printf("Read %d, %d: %v", n, length, buf[2:n])
		if isClose := handleMsg(c, buf[2:n], epm); isClose {
			break
		}
	}
	c.Close()
}

func handleMsg(c net.Conn, buf []byte, epm chan regReq) bool {
	myChan := make(chan regAns)
	epm <- regReq{buf: buf, replyTo: myChan, conn: c}
	select {
	case ans := <-myChan:
		log.Printf("Got reply: %+v", ans)
		if ans.reply != nil {
			c.Write(ans.reply)
		}
		return ans.isClose
	}
	return true
}
<commit_msg>Fix the issue with wrongly placed parentheses<commit_after>package main

import (
	"bytes"
	"encoding/binary"
	"flag"
	"github.com/goerlang/epmd"
	"log"
	"net"
	"os"
	"os/signal"
	"runtime/pprof"
	"strconv"
	"time"
)

var noEpmd bool
var listenPort string
var regLimit int
var unregTTL int
var cpuProfile string

func init() {
	flag.StringVar(&listenPort, "port", "4369", "listen port")
	flag.BoolVar(&noEpmd, "no-epmd", false, "disable epmd")
	flag.IntVar(&regLimit, "nodes-limit", 1000, "limit size of registration table to prune unregistered nodes")
	flag.IntVar(&unregTTL, "unreg-ttl", 10, "prune unregistered nodes if unregistration older than this value in minutes")
	flag.StringVar(&cpuProfile, "profile-cpu", "", "profile CPU to file")
}

type regAns struct {
	reply   []byte
	isClose bool
}

type regReq struct {
	buf     []byte
	replyTo chan regAns
	conn    net.Conn
}

func main() {
	flag.Parse()
	if cpuProfile != "" {
		f, err := os.Create(cpuProfile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	stopCh := make(chan bool)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		for sig := range c {
			log.Printf("Signal %#v", sig)
			stopCh <- true
		}
	}()

	if !cliEnabled() {
		var err error
		var l net.Listener
		if !noEpmd {
			l, err = net.Listen("tcp", net.JoinHostPort("", listenPort))
			if err != nil || noEpmd {
				// Cannot bind, eclus instance already running, connect to it
				eclusCli()
			} else {
				epm := make(chan regReq, 10)
				go epmReg(epm)
				go func() {
					for {
						conn, err := l.Accept()
						log.Printf("Accept new")
						if err != nil {
							log.Printf(err.Error())
						} else {
							go mLoop(conn, epm)
						}
					}
				}()
				if nodeEnabled() {
					go runNode()
				}
				<-stopCh
			}
		}
	} else {
		eclusCli()
	}
}

type nodeRec struct {
	*epmd.NodeInfo
	Time   time.Time
	Active bool
	conn   net.Conn
}

func epmReg(in <-chan regReq) {
	var nReg = make(map[string]*nodeRec)
	for {
		select {
		case req := <-in:
			buf := req.buf
			if len(buf) == 0 {
				rs := len(nReg)
				log.Printf("REG %d records", rs)
				now := time.Now()

				for node, rec := range nReg {
					if rec.conn == req.conn {
						log.Printf("Connection for %s dropped", node)
						nReg[node].Active = false
						nReg[node].Time = now
						nReg[node].conn = nil
					} else if rs > regLimit && !rec.Active && now.Sub(rec.Time).Minutes() > float64(unregTTL) {
						log.Printf("REG prune %s:%+v", node, rec)
						delete(nReg, node)
					}
				}
				continue
			}
			replyTo := req.replyTo
			log.Printf("IN: %v", buf)
			switch epmd.MessageId(buf[0]) {
			case epmd.ALIVE2_REQ:
				nConn := req.conn
				nInfo := epmd.Read_ALIVE2_REQ(buf)
				log.Printf("NodeInfo: %+v", nInfo)
				var reply []byte

				if rec, ok := nReg[nInfo.Name]; ok {
					log.Printf("Node %s found", nInfo.Name)
					if rec.Active {
						log.Printf("Node %s is running", nInfo.Name)
						reply = epmd.Compose_ALIVE2_RESP(false)
					} else {
						log.Printf("Node %s is not running", nInfo.Name)
						rec.conn = nConn

						nInfo.Creation = (rec.Creation % 3) + 1
						rec.NodeInfo = nInfo
						rec.Active = true
						reply = epmd.Compose_ALIVE2_RESP(true, rec.NodeInfo)
					}
				} else {
					log.Printf("New node %s", nInfo.Name)
					nInfo.Creation = 1
					rec := &nodeRec{
						NodeInfo: nInfo,
						conn:     nConn,
						Time:     time.Now(),
						Active:   true,
					}
					nReg[nInfo.Name] = rec
					reply = epmd.Compose_ALIVE2_RESP(true, rec.NodeInfo)
				}
				replyTo <- regAns{reply: reply, isClose: false}
			case epmd.PORT_PLEASE2_REQ:
				nName := epmd.Read_PORT_PLEASE2_REQ(buf)
				var reply []byte
				if rec, ok := nReg[nName]; ok && rec.Active {
					reply = epmd.Compose_PORT2_RESP(rec.NodeInfo)
				} else {
					reply = epmd.Compose_PORT2_RESP(nil)
				}
				replyTo <- regAns{reply: reply, isClose: true}
			case epmd.STOP_REQ:
				nName := epmd.Read_STOP_REQ(buf)
				var reply []byte
				if rec, ok := nReg[nName]; ok && rec.Active {
					// TODO: stop node
					reply = epmd.Compose_STOP_RESP(true)
				} else {
					reply = epmd.Compose_STOP_RESP(false)
				}
				replyTo <- regAns{reply: reply, isClose: true}
			case epmd.NAMES_REQ, epmd.DUMP_REQ:
				lp, err := strconv.Atoi(listenPort)
				if err != nil {
					log.Printf("Cannot convert %s to integer", listenPort)
					replyTo <- regAns{reply: nil, isClose: true}
				} else {
					replyB := new(bytes.Buffer)
					epmd.Compose_START_NAMES_RESP(replyB, lp)
					for _, rec := range nReg {
						if epmd.MessageId(buf[0]) == epmd.NAMES_REQ {
							if rec.Active {
								epmd.Append_NAMES_RESP(replyB, rec.NodeInfo)
							} else {
								if rec.Active {
									epmd.Append_DUMP_RESP_ACTIVE(replyB, rec.NodeInfo)
								} else {
									epmd.Append_DUMP_RESP_UNUSED(replyB, rec.NodeInfo)
								}
							}
						}
					}
					replyTo <- regAns{reply: replyB.Bytes(), isClose: true}
				}
			case epmd.KILL_REQ:
				reply := epmd.Compose_KILL_RESP()
				replyTo <- regAns{reply: reply, isClose: true}
			default:
				switch cliMessageId(buf[0]) {
				case REQ_NAMES:
					reply := ansNames(nReg)
					replyTo <- regAns{reply: reply, isClose: true}
				default:
					replyTo <- regAns{reply: nil, isClose: true}
				}
			}
		}
	}
}

func mLoop(c net.Conn, epm chan regReq) {
	buf := make([]byte, 1024)
	for {
		n, err := c.Read(buf)
		if err != nil {
			c.Close()
			log.Printf("Stop loop: %v", err)
			epm <- regReq{buf: []byte{}, conn: c}
			return
		}
		length := binary.BigEndian.Uint16(buf[0:2])
		if length != uint16(n-2) {
			log.Printf("Incomplete packet from erlang node to epmd: %d from %d", n, length)
			break
		}
		log.Printf("Read %d, %d: %v", n, length, buf[2:n])
		if isClose := handleMsg(c, buf[2:n], epm); isClose {
			break
		}
	}
	c.Close()
}

func handleMsg(c net.Conn, buf []byte, epm chan regReq) bool {
	myChan := make(chan regAns)
	epm <- regReq{buf: buf, replyTo: myChan, conn: c}
	select {
	case ans := <-myChan:
		log.Printf("Got reply: %+v", ans)
		if ans.reply != nil {
			c.Write(ans.reply)
		}
		return ans.isClose
	}
	return true
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.8.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "dev"

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<commit_msg>v0.8.0-rc1<commit_after>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.8.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "rc1"

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.8.5"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = ""

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<commit_msg>release: clean up after v0.8.5<commit_after>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.8.6"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "dev"

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"net/url"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"

	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/fly/commands/internal/executehelpers"
	"github.com/concourse/concourse/fly/commands/internal/flaghelpers"
	"github.com/concourse/concourse/fly/config"
	"github.com/concourse/concourse/fly/eventstream"
	"github.com/concourse/concourse/fly/rc"
	"github.com/concourse/concourse/fly/ui"
	"github.com/concourse/concourse/go-concourse/concourse"
)

type ExecuteCommand struct {
	TaskConfig     atc.PathFlag                   `short:"c" long:"config" required:"true"                description:"The task config to execute"`
	Privileged     bool                           `short:"p" long:"privileged"                            description:"Run the task with full privileges"`
	IncludeIgnored bool                           `          long:"include-ignored"                       description:"Including .gitignored paths. Disregards .gitignore entries and uploads everything"`
	Inputs         []flaghelpers.InputPairFlag    `short:"i" long:"input"       value-name:"NAME=PATH"    description:"An input to provide to the task (can be specified multiple times)"`
	InputMappings  []flaghelpers.VariablePairFlag `short:"m" long:"input-mapping"       value-name:"[NAME=STRING]"    description:"Map a resource to a different name as task input"`
	InputsFrom     flaghelpers.JobFlag            `short:"j" long:"inputs-from" value-name:"PIPELINE/JOB" description:"A job to base the inputs on"`
	Outputs        []flaghelpers.OutputPairFlag   `short:"o" long:"output"      value-name:"NAME=PATH"    description:"An output to fetch from the task (can be specified multiple times)"`
	Image          string                         `long:"image" description:"Image resource for the one-off build"`
	Tags           []string                       `          long:"tag"         value-name:"TAG"          description:"A tag for a specific environment (can be specified multiple times)"`
}

func (command *ExecuteCommand) Execute(args []string) error {
	target, err := rc.LoadTarget(Fly.Target, Fly.Verbose)
	if err != nil {
		return err
	}

	err = target.Validate()
	if err != nil {
		return err
	}

	taskConfigFile := command.TaskConfig
	includeIgnored := command.IncludeIgnored

	taskConfig, err := config.LoadTaskConfig(string(taskConfigFile), args)
	if err != nil {
		return err
	}

	client := target.Client()

	fact := atc.NewPlanFactory(time.Now().Unix())

	inputMappings := executehelpers.DetermineInputMappings(command.InputMappings)
	inputs, imageResource, err := executehelpers.DetermineInputs(
		fact,
		target.Team(),
		taskConfig.Inputs,
		command.Inputs,
		inputMappings,
		command.Image,
		command.InputsFrom,
	)
	if err != nil {
		return err
	}

	if imageResource != nil {
		taskConfig.ImageResource = imageResource
	}

	outputs, err := executehelpers.DetermineOutputs(
		fact,
		taskConfig.Outputs,
		command.Outputs,
	)
	if err != nil {
		return err
	}

	plan, err := executehelpers.CreateBuildPlan(
		fact,
		target,
		command.Privileged,
		inputs,
		inputMappings,
		outputs,
		taskConfig,
		command.Tags,
	)

	if err != nil {
		return err
	}

	clientURL, err := url.Parse(client.URL())
	if err != nil {
		return err
	}

	var build atc.Build
	var buildURL *url.URL

	if command.InputsFrom.PipelineName != "" {
		build, err = target.Team().CreatePipelineBuild(command.InputsFrom.PipelineName, plan)
		if err != nil {
			return err
		}

		buildURL, err = url.Parse(fmt.Sprintf("/teams/%s/pipelines/%s/builds/%s", build.TeamName, build.PipelineName, build.Name))
		if err != nil {
			return err
		}

	} else {
		build, err = target.Team().CreateBuild(plan)
		if err != nil {
			return err
		}

		buildURL, err = url.Parse(fmt.Sprintf("/builds/%d", build.ID))
		if err != nil {
			return err
		}
	}

	fmt.Printf("executing build %d at %s \n", build.ID, clientURL.ResolveReference(buildURL))

	terminate := make(chan os.Signal, 1)

	go abortOnSignal(client, terminate, build)

	signal.Notify(terminate, syscall.SIGINT, syscall.SIGTERM)

	inputChan := make(chan interface{})
	go func() {
		for _, i := range inputs {
			if i.Path != "" {
				executehelpers.Upload(client, build.ID, i, includeIgnored)
			}
		}
		close(inputChan)
	}()

	var outputChans []chan (interface{})
	if len(outputs) > 0 {
		for i, output := range outputs {
			outputChans = append(outputChans, make(chan interface{}, 1))
			go func(o executehelpers.Output, outputChan chan<- interface{}) {
				if o.Path != "" {
					executehelpers.Download(client, build.ID, o)
				}

				close(outputChan)
			}(output, outputChans[i])
		}
	}

	eventSource, err := client.BuildEvents(fmt.Sprintf("%d", build.ID))
	if err != nil {
		return err
	}

	exitCode := eventstream.Render(os.Stdout, eventSource)
	eventSource.Close()

	<-inputChan

	if len(outputs) > 0 {
		for _, outputChan := range outputChans {
			<-outputChan
		}
	}

	os.Exit(exitCode)

	return nil
}

func abortOnSignal(
	client concourse.Client,
	terminate <-chan os.Signal,
	build atc.Build,
) {
	<-terminate

	fmt.Fprintf(ui.Stderr, "\naborting...\n")

	err := client.AbortBuild(strconv.Itoa(build.ID))
	if err != nil {
		fmt.Fprintln(ui.Stderr, "failed to abort:", err)
		return
	}

	// if told to terminate again, exit immediately
	<-terminate
	fmt.Fprintln(ui.Stderr, "exiting immediately")
	os.Exit(2)
}
<commit_msg>fly: exit when aborting a build fails<commit_after>package commands

import (
	"fmt"
	"net/url"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"

	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/fly/commands/internal/executehelpers"
	"github.com/concourse/concourse/fly/commands/internal/flaghelpers"
	"github.com/concourse/concourse/fly/config"
	"github.com/concourse/concourse/fly/eventstream"
	"github.com/concourse/concourse/fly/rc"
	"github.com/concourse/concourse/fly/ui"
	"github.com/concourse/concourse/go-concourse/concourse"
)

type ExecuteCommand struct {
	TaskConfig     atc.PathFlag                   `short:"c" long:"config" required:"true"                description:"The task config to execute"`
	Privileged     bool                           `short:"p" long:"privileged"                            description:"Run the task with full privileges"`
	IncludeIgnored bool                           `          long:"include-ignored"                       description:"Including .gitignored paths. Disregards .gitignore entries and uploads everything"`
	Inputs         []flaghelpers.InputPairFlag    `short:"i" long:"input"       value-name:"NAME=PATH"    description:"An input to provide to the task (can be specified multiple times)"`
	InputMappings  []flaghelpers.VariablePairFlag `short:"m" long:"input-mapping"       value-name:"[NAME=STRING]"    description:"Map a resource to a different name as task input"`
	InputsFrom     flaghelpers.JobFlag            `short:"j" long:"inputs-from" value-name:"PIPELINE/JOB" description:"A job to base the inputs on"`
	Outputs        []flaghelpers.OutputPairFlag   `short:"o" long:"output"      value-name:"NAME=PATH"    description:"An output to fetch from the task (can be specified multiple times)"`
	Image          string                         `long:"image" description:"Image resource for the one-off build"`
	Tags           []string                       `          long:"tag"         value-name:"TAG"          description:"A tag for a specific environment (can be specified multiple times)"`
}

func (command *ExecuteCommand) Execute(args []string) error {
	target, err := rc.LoadTarget(Fly.Target, Fly.Verbose)
	if err != nil {
		return err
	}

	err = target.Validate()
	if err != nil {
		return err
	}

	taskConfigFile := command.TaskConfig
	includeIgnored := command.IncludeIgnored

	taskConfig, err := config.LoadTaskConfig(string(taskConfigFile), args)
	if err != nil {
		return err
	}

	client := target.Client()

	fact := atc.NewPlanFactory(time.Now().Unix())

	inputMappings := executehelpers.DetermineInputMappings(command.InputMappings)
	inputs, imageResource, err := executehelpers.DetermineInputs(
		fact,
		target.Team(),
		taskConfig.Inputs,
		command.Inputs,
		inputMappings,
		command.Image,
		command.InputsFrom,
	)
	if err != nil {
		return err
	}

	if imageResource != nil {
		taskConfig.ImageResource = imageResource
	}

	outputs, err := executehelpers.DetermineOutputs(
		fact,
		taskConfig.Outputs,
		command.Outputs,
	)
	if err != nil {
		return err
	}

	plan, err := executehelpers.CreateBuildPlan(
		fact,
		target,
		command.Privileged,
		inputs,
		inputMappings,
		outputs,
		taskConfig,
		command.Tags,
	)

	if err != nil {
		return err
	}

	clientURL, err := url.Parse(client.URL())
	if err != nil {
		return err
	}

	var build atc.Build
	var buildURL *url.URL

	if command.InputsFrom.PipelineName != "" {
		build, err = target.Team().CreatePipelineBuild(command.InputsFrom.PipelineName, plan)
		if err != nil {
			return err
		}

		buildURL, err = url.Parse(fmt.Sprintf("/teams/%s/pipelines/%s/builds/%s", build.TeamName, build.PipelineName, build.Name))
		if err != nil {
			return err
		}

	} else {
		build, err = target.Team().CreateBuild(plan)
		if err != nil {
			return err
		}

		buildURL, err = url.Parse(fmt.Sprintf("/builds/%d", build.ID))
		if err != nil {
			return err
		}
	}

	fmt.Printf("executing build %d at %s \n", build.ID, clientURL.ResolveReference(buildURL))

	terminate := make(chan os.Signal, 1)

	go abortOnSignal(client, terminate, build)

	signal.Notify(terminate, syscall.SIGINT, syscall.SIGTERM)

	inputChan := make(chan interface{})
	go func() {
		for _, i := range inputs {
			if i.Path != "" {
				executehelpers.Upload(client, build.ID, i, includeIgnored)
			}
		}
		close(inputChan)
	}()

	var outputChans []chan (interface{})
	if len(outputs) > 0 {
		for i, output := range outputs {
			outputChans = append(outputChans, make(chan interface{}, 1))
			go func(o executehelpers.Output, outputChan chan<- interface{}) {
				if o.Path != "" {
					executehelpers.Download(client, build.ID, o)
				}

				close(outputChan)
			}(output, outputChans[i])
		}
	}

	eventSource, err := client.BuildEvents(fmt.Sprintf("%d", build.ID))
	if err != nil {
		return err
	}

	exitCode := eventstream.Render(os.Stdout, eventSource)
	eventSource.Close()

	<-inputChan

	if len(outputs) > 0 {
		for _, outputChan := range outputChans {
			<-outputChan
		}
	}

	os.Exit(exitCode)

	return nil
}

func abortOnSignal(
	client concourse.Client,
	terminate <-chan os.Signal,
	build atc.Build,
) {
	<-terminate

	fmt.Fprintf(ui.Stderr, "\naborting...\n")

	err := client.AbortBuild(strconv.Itoa(build.ID))
	if err != nil {
		fmt.Fprintln(ui.Stderr, "failed to abort:", err)
		os.Exit(2)
	}

	// if told to terminate again, exit immediately
	<-terminate
	fmt.Fprintln(ui.Stderr, "exiting immediately")
	os.Exit(2)
}
<|endoftext|>