<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/anchepiece/suggest"
)

const (
	Name     = "suggest-autocorrect"
	FullName = "github.com/anchepiece/suggest-autocorrect"
	Usage    = "suggest-autocorrect is a console based tool to return the closest match in a list"
	Version  = "0.1.0"
	Author   = "anchepiece"
	Email    = "anchepiece@gmail.com"
)

var (
	flagQuery               = flag.String("q", "", "query term to search")
	flagCommands            = flag.String("c", "", "comma-separated list of possible suggestions")
	flagAutocorrectDisabled = flag.Bool("d", false, "disable autocorrect feature")
)

func simpleExample() {

	suggester := suggest.Suggest{}

	query := "fgerp"
	commands := []string{"cat", "mkdir", "fgrep", "history"}

	match, err := suggester.AutocorrectAgainst(query, commands)
	if err == nil {
		fmt.Println("Autocorrected to:", match) // "fgrep"
	}

	suggester.Commands = commands
	if match, err := suggester.Autocorrect(query); err == nil {
		fmt.Println("Also Autocorrected to:", match) // "fgrep"
	}

	query = "println"
	commands = []string{"Fprint", "Fprintf", "Fprintln", "Sprintf", "Print", "Printf", "Println"}
	suggester.Options.SimilarityMinimum = 8

	fmt.Printf("Searching %v for %s\n", query, commands)

	if result, err := suggester.QueryAgainst(query, commands); err == nil {
		if !result.Success() {
			fmt.Println("No close matches")
		} else {
			fmt.Println("Similar matches:", result.Matches) // [Println Fprintln]
			fmt.Println("Autocorrect:", result.Autocorrect) // Println

		}
	}
	os.Exit(0)
}

func simpleExample2() {
	suggester := suggest.Suggest{}

	query := "fgerp"
	commands := []string{"cat", "mkdir", "fgrep", "history"}

	suggester.Commands = commands
	if match, err := suggester.Autocorrect("mkdri"); err == nil {
		fmt.Println("Autocorrected to:", match) // "mkdir"
	}

	// Alternate autocorrect usage pattern
	match, _ := suggester.AutocorrectAgainst(query, commands)
	if match != "" {
		fmt.Println("Autocorrected to:", match) // "fgrep"
	}

	// Alternate usage pattern
	query = "printf"
	commands = []string{"Fprint", "Fprintf", "Fprintln", "Sprintf", "Printf", "Println"}
	suggester.Options.SimilarityMinimum = 8

	fmt.Printf("Searching %v in %s\n", query, commands)

	if result, err := suggester.QueryAgainst(query, commands); err == nil {
		if !result.Success() {
			fmt.Println("No close matches")

		} else {
			fmt.Println("Similar matches:", result.Matches)
			// [Println Fprintln]

			fmt.Println("Autocorrect:", result.Autocorrect)
			// Println
		}
	}
}

func simpleExample3() {
	// make sure our example in doc.go works
	suggester := suggest.Suggest{}
	suggester.Options.SimilarityMinimum = 7
	suggester.Options.AutocorrectDisabled = false

	query := "proflie"
	commands := []string{"perfil", "profiel", "profile", "profil", "account"}

	suggester.Commands = commands
	if result, err := suggester.Query(query); err == nil {
		if !result.Success() {
			fmt.Println("No close matches")
		} else {
			fmt.Println("Similar matches:", result.Matches) // [profile profil profiel]
			fmt.Println("Autocorrect:", result.Autocorrect) // profile
		}
	}
	os.Exit(0)
}

func main() {

	flag.Parse()
	// fmt.Println(Name)

	// simpleExample3()
	// simpleExample2()
	// simpleExample()

	if *flagQuery == "" || *flagCommands == "" {
		fmt.Println("Error: must specify both a query (-q) and the possible commands (-c)")
		os.Exit(1)
	}

	fmt.Println("Entered query:", *flagQuery)

	var commandList []string
	for _, s := range strings.Split(*flagCommands, ",") {
		commandList = append(commandList, strings.TrimSpace(s))
	}
	if len(commandList) > 0 {
		fmt.Println("Entered possible commands:", commandList)
	}

	fmt.Println()
	s := &suggest.Suggest{
		Commands: commandList,
	}
	s.Options.AutocorrectDisabled = *flagAutocorrectDisabled
	s.Options.SimilarityMinimum = 7

	if m := s.ExactMatch(*flagQuery); m != "" {
		fmt.Println("Continuing with exact match!", m)
		return // Run the command!
	} else {
		fmt.Printf("You called a command '%s' which does not exist. \n", *flagQuery)
	}

	if !*flagAutocorrectDisabled {
		if a, err := s.Autocorrect(*flagQuery); err == nil && a != "" {
			fmt.Printf("Continuing under the assumption that you meant '%s'\n", a)
			return // Run the command!
		}
	}

	m, err := s.Query(*flagQuery)
	if err != nil {
		fmt.Println("An error occurred: %v", err)
	}

	if len(m.Matches) == 1 {
		fmt.Println("Did you mean this?")
	}

	if len(m.Matches) > 1 {
		fmt.Println("Did you mean one of these?")
	}

	for _, suggestion := range m.Matches {
		fmt.Printf("\t%s\n", suggestion)
	}
}

// scan through a passed file and append all words to available commands
func scan(r io.Reader) {
	// TODO: handle command line option to parse

	scanner := bufio.NewScanner(r)
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		word := scanner.Text()
		// Match the query to input
		fmt.Println(word)
	}
}
<commit_msg>Fixed 'go vet' issue<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/anchepiece/suggest"
)

const (
	Name     = "suggest-autocorrect"
	FullName = "github.com/anchepiece/suggest-autocorrect"
	Usage    = "suggest-autocorrect is a console based tool to return the closest match in a list"
	Version  = "0.1.0"
	Author   = "anchepiece"
	Email    = "anchepiece@gmail.com"
)

var (
	flagQuery               = flag.String("q", "", "query term to search")
	flagCommands            = flag.String("c", "", "comma-separated list of possible suggestions")
	flagAutocorrectDisabled = flag.Bool("d", false, "disable autocorrect feature")
)

func simpleExample() {

	suggester := suggest.Suggest{}

	query := "fgerp"
	commands := []string{"cat", "mkdir", "fgrep", "history"}

	match, err := suggester.AutocorrectAgainst(query, commands)
	if err == nil {
		fmt.Println("Autocorrected to:", match) // "fgrep"
	}

	suggester.Commands = commands
	if match, err := suggester.Autocorrect(query); err == nil {
		fmt.Println("Also Autocorrected to:", match) // "fgrep"
	}

	query = "println"
	commands = []string{"Fprint", "Fprintf", "Fprintln", "Sprintf", "Print", "Printf", "Println"}
	suggester.Options.SimilarityMinimum = 8

	fmt.Printf("Searching %v for %s\n", query, commands)

	if result, err := suggester.QueryAgainst(query, commands); err == nil {
		if !result.Success() {
			fmt.Println("No close matches")
		} else {
			fmt.Println("Similar matches:", result.Matches) // [Println Fprintln]
			fmt.Println("Autocorrect:", result.Autocorrect) // Println

		}
	}
	os.Exit(0)
}

func simpleExample2() {
	suggester := suggest.Suggest{}

	query := "fgerp"
	commands := []string{"cat", "mkdir", "fgrep", "history"}

	suggester.Commands = commands
	if match, err := suggester.Autocorrect("mkdri"); err == nil {
		fmt.Println("Autocorrected to:", match) // "mkdir"
	}

	// Alternate autocorrect usage pattern
	match, _ := suggester.AutocorrectAgainst(query, commands)
	if match != "" {
		fmt.Println("Autocorrected to:", match) // "fgrep"
	}

	// Alternate usage pattern
	query = "printf"
	commands = []string{"Fprint", "Fprintf", "Fprintln", "Sprintf", "Printf", "Println"}
	suggester.Options.SimilarityMinimum = 8

	fmt.Printf("Searching %v in %s\n", query, commands)

	if result, err := suggester.QueryAgainst(query, commands); err == nil {
		if !result.Success() {
			fmt.Println("No close matches")

		} else {
			fmt.Println("Similar matches:", result.Matches)
			// [Println Fprintln]

			fmt.Println("Autocorrect:", result.Autocorrect)
			// Println
		}
	}
}

func simpleExample3() {
	// make sure our example in doc.go works
	suggester := suggest.Suggest{}
	suggester.Options.SimilarityMinimum = 7
	suggester.Options.AutocorrectDisabled = false

	query := "proflie"
	commands := []string{"perfil", "profiel", "profile", "profil", "account"}

	suggester.Commands = commands
	if result, err := suggester.Query(query); err == nil {
		if !result.Success() {
			fmt.Println("No close matches")
		} else {
			fmt.Println("Similar matches:", result.Matches) // [profile profil profiel]
			fmt.Println("Autocorrect:", result.Autocorrect) // profile
		}
	}
	os.Exit(0)
}

func main() {

	flag.Parse()
	// fmt.Println(Name)

	// simpleExample3()
	// simpleExample2()
	// simpleExample()

	if *flagQuery == "" || *flagCommands == "" {
		fmt.Println("Error: must specify both a query (-q) and the possible commands (-c)")
		os.Exit(1)
	}

	fmt.Println("Entered query:", *flagQuery)

	var commandList []string
	for _, s := range strings.Split(*flagCommands, ",") {
		commandList = append(commandList, strings.TrimSpace(s))
	}
	if len(commandList) > 0 {
		fmt.Println("Entered possible commands:", commandList)
	}

	fmt.Println()
	s := &suggest.Suggest{
		Commands: commandList,
	}
	s.Options.AutocorrectDisabled = *flagAutocorrectDisabled
	s.Options.SimilarityMinimum = 7

	if m := s.ExactMatch(*flagQuery); m != "" {
		fmt.Println("Continuing with exact match!", m)
		return // Run the command!
	} else {
		fmt.Printf("You called a command '%s' which does not exist. \n", *flagQuery)
	}

	if !*flagAutocorrectDisabled {
		if a, err := s.Autocorrect(*flagQuery); err == nil && a != "" {
			fmt.Printf("Continuing under the assumption that you meant '%s'\n", a)
			return // Run the command!
		}
	}

	m, err := s.Query(*flagQuery)
	if err != nil {
		fmt.Printf("An error occurred: %v\n", err)
	}

	if len(m.Matches) == 1 {
		fmt.Println("Did you mean this?")
	}

	if len(m.Matches) > 1 {
		fmt.Println("Did you mean one of these?")
	}

	for _, suggestion := range m.Matches {
		fmt.Printf("\t%s\n", suggestion)
	}
}

// scan through a passed file and append all words to available commands
func scan(r io.Reader) {
	// TODO: handle command line option to parse

	scanner := bufio.NewScanner(r)
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		word := scanner.Text()
		// Match the query to input
		fmt.Println(word)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/gob"
	"fmt"
	"os"

	"github.com/gotk3/gotk3/gtk"
)

const (
	version = "2.1.0.0"
	defIni  = ""
)

var (
	master    map[string][]app
	linmaster map[string][]app
	cats      []string
	lin       []string
	wine      bool
	comEnbld  bool
	wineAvail bool
)

func main() {
	os.MkdirAll("PortableApps/LinuxPACom", 0777)
	master = make(map[string][]app)
	linmaster = make(map[string][]app)
	uiStart()
}

func uiStart() {
	gtk.Init(nil)
	setup()
	win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
	if err != nil {
		fmt.Println("Window not created", err)
	}
	win.SetTitle("LinuxPA")
	win.Connect("destroy", func() {
		savePrefs()
		gtk.MainQuit()
	})
	win.SetDefaultSize(500, 500)
	win.SetPosition(gtk.WIN_POS_CENTER)
	ui(win)
	win.ShowAll()
	win.Show()
	update(win)
	gtk.Main()
}

func savePrefs() {
	fil, err := os.Open("PortableApps/LinuxPACom/Prefs.gob")
	if os.IsNotExist(err) {
		fil, err = os.Create("PortableApps/LinuxPACom/Prefs.gob")
	}
	if err != nil {
		return
	}
	enc := gob.NewEncoder(fil)
	enc.Encode(wine)
}

func loadPrefs() {
	fil, err := os.Open("PortableApps/LinuxPACom/Prefs.gob")
	if err != nil {
		return
	}
	dec := gob.NewDecoder(fil)
	dec.Decode(&wine)
}

func contains(arr []string, str string) bool {
	for _, v := range arr {
		if v == str {
			return true
		}
	}
	return false
}
<commit_msg>Bumping version number<commit_after>package main

import (
	"encoding/gob"
	"fmt"
	"os"

	"github.com/gotk3/gotk3/gtk"
)

const (
	version = "2.1.0.5"
	defIni  = ""
)

var (
	master    map[string][]app
	linmaster map[string][]app
	cats      []string
	lin       []string
	wine      bool
	comEnbld  bool
	wineAvail bool
)

func main() {
	os.MkdirAll("PortableApps/LinuxPACom", 0777)
	master = make(map[string][]app)
	linmaster = make(map[string][]app)
	uiStart()
}

func uiStart() {
	gtk.Init(nil)
	setup()
	win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
	if err != nil {
		fmt.Println("Window not created", err)
	}
	win.SetTitle("LinuxPA")
	win.Connect("destroy", func() {
		savePrefs()
		gtk.MainQuit()
	})
	win.SetDefaultSize(500, 500)
	win.SetPosition(gtk.WIN_POS_CENTER)
	ui(win)
	win.ShowAll()
	win.Show()
	update(win)
	gtk.Main()
}

func savePrefs() {
	fil, err := os.Open("PortableApps/LinuxPACom/Prefs.gob")
	if os.IsNotExist(err) {
		fil, err = os.Create("PortableApps/LinuxPACom/Prefs.gob")
	}
	if err != nil {
		return
	}
	enc := gob.NewEncoder(fil)
	enc.Encode(wine)
}

func loadPrefs() {
	fil, err := os.Open("PortableApps/LinuxPACom/Prefs.gob")
	if err != nil {
		return
	}
	dec := gob.NewDecoder(fil)
	dec.Decode(&wine)
}

func contains(arr []string, str string) bool {
	for _, v := range arr {
		if v == str {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
    "net/http"
    "log"
    "html/template"
    "io/ioutil"
    "strings"
    "path"
    "time"
    "sync"
    "fmt"
    "database/sql"
    "encoding/xml"
    _ "github.com/go-sql-driver/mysql"
)

const (
    postByDay = `select DATE(Time) as date, count(RID) as count from messages`+
                ` where channel = '#geekhack' group by date order by count`+
                ` desc limit 10;`
    numWords = `select Nick, COUNT(Nick) as Fucks from messages where message`+
               ` like LOWER('%%%s%%') and channel = '#geekhack' group by nick `+
               `order by Fucks desc limit 10;`
    totalPosts = `select Nick, COUNT(Nick) as Posts from messages where channel`+
                 ` = '#geekhack' group by nick order by Posts desc limit 10;`
    postsByHour = `select HOUR(Time) as date, count(RID) as count from messages`+
                  ` where channel = '#geekhack' group by date order by date;`
)

var (
    templates = template.Must(template.ParseFiles(getFiles("./views/", ".html")...))
    geekhack = NewGeekhack()
    config Config
)

type Config struct {
    DBConn string
    BadWords []BadWord `xml:">BadWord"`
}

type BadWord struct {
    Word string
    Query string
}

type Geekhack struct {
    updateChan chan bool

    mutex sync.RWMutex // Protects:
    PostsByDay []Tuple
    CurseWords map[string][]Tuple
    TotalPosts []Tuple
    age time.Time
}

type Tuple struct {
    Name string
    Count int
}

func NewGeekhack() *Geekhack{
    return &Geekhack{
        CurseWords: make(map[string][]Tuple),
        updateChan: make(chan bool),
    }
}

func (g *Geekhack) shouldUpdate() bool {
    g.mutex.RLock()
    defer g.mutex.RUnlock()
    return time.Since(g.age).Minutes() < 5
}

func runQuery(query string, db *sql.DB) ([]Tuple, error) {
    var nick string
    var posts int
    var tuple []Tuple
    rows, err := db.Query(query)
    if err != nil {
        log.Println(err)
        return nil, err
    }
    for rows.Next() {
        rows.Scan(&nick, &posts)
        tuple = append(tuple, Tuple{nick, posts})
    }
    return tuple, nil
}

func (g *Geekhack) Update() {
    db, err := sql.Open("mysql", config.DBConn)
    if err != nil {
        log.Println(err)
        return
    }
    defer db.Close()

    PostsByDay, err := runQuery(postByDay, db)
    if err != nil {
        log.Println(err)
        return
    }

    TotalPosts, err := runQuery(totalPosts, db)
    if err != nil {
        log.Println(err)
        return
    }
    CurseWords := make(map[string][]Tuple)
    log.Println("Loading Cursewords!")
    for _, word := range config.BadWords {
        log.Println(word.Word, ":", word.Query)
        // This is dumb. Either that or I'm too dumb to figure out how to get
        // the sql.Query() thing to allow wildcards. Maybe it's like that by design?
        tuple, err := runQuery(fmt.Sprintf(numWords, word.Query), db)
        if err != nil {
            log.Println(err)
            return
        }
        CurseWords[word.Word] = tuple
    }
    log.Println("Finished loading Cursewords!")
    g.mutex.Lock()
    defer g.mutex.Unlock()
    g.PostsByDay = PostsByDay
    g.TotalPosts = TotalPosts
    g.CurseWords = CurseWords
}

func (g *Geekhack) Updater() {
    for <-g.updateChan {
        if g.shouldUpdate() {
            g.Update()
        }
    }
}

func getFiles(folder, fileType string) []string {
    files, err := ioutil.ReadDir(folder)
    if err != nil {
        panic(err)
    }
    var templateList []string
    for _, file := range files {
        if strings.HasSuffix(file.Name(), fileType) {
            templateList = append(templateList, folder+file.Name())
        }
    }
    return templateList
}

func serveTemplate(filename string) func(http.ResponseWriter, *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        if err := templates.ExecuteTemplate(w, filename, nil); err != nil {
            log.Println(err)
        }
    }
}

type Keyboards struct {
    Keyboards map[string]string
}

func keyboardHandler(w http.ResponseWriter, r *http.Request) {
    keyboards := getFiles("./static/keyboards/", ".jpg")
    matchedBoards := Keyboards{make(map[string]string)}
    for _, keyboard := range keyboards {
        dir, file := path.Split(keyboard)
        matchedBoards.Keyboards[path.Join("/", dir, file)] = path.Join("/", dir, "thumbs", file)
    }
    if err := templates.ExecuteTemplate(w, "keyboards.html", matchedBoards); err != nil {
        log.Println(err)
    }
}

func geekhackHandler(w http.ResponseWriter, r *http.Request) {
    geekhack.mutex.RLock()
    defer func() {
        geekhack.mutex.RUnlock()
        geekhack.updateChan <- true
    }()
    if err := templates.ExecuteTemplate(w, "geekhack.html", geekhack); err != nil {
        log.Println(err)
    }
}

func serveStatic(filename string) func(http.ResponseWriter, *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, filename)
    }
}

func init() {
    xmlFile, err := ioutil.ReadFile("config.xml")
    if err != nil {
        log.Fatal(err)
    }
    xml.Unmarshal(xmlFile, &config)
    log.Println(config)
    start := time.Now()
    geekhack.Update()
    log.Println("Time to import data: ", time.Since(start))
    go geekhack.Updater()
}

func main() {
    http.HandleFunc("/favicon.ico", serveStatic("./favicon.ico"))
    http.HandleFunc("/sitemap.xml", serveStatic("./sitemap.xml"))
    http.HandleFunc("/", serveTemplate("main.html"))
    http.HandleFunc("/status", serveTemplate("status.html"))
    http.HandleFunc("/keyboards", keyboardHandler)
    http.HandleFunc("/geekhack", geekhackHandler)
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
    http.ListenAndServe(":8080", nil)
}
<commit_msg>Changed update to update words more quickly<commit_after>package main

import (
    "net/http"
    "log"
    "html/template"
    "io/ioutil"
    "strings"
    "path"
    "time"
    "sync"
    "fmt"
    "database/sql"
    "encoding/xml"
    _ "github.com/go-sql-driver/mysql"
)

const (
    postByDay = `select DATE(Time) as date, count(RID) as count from messages`+
                ` where channel = '#geekhack' group by date order by count`+
                ` desc limit 10;`
    totalPosts = `select Nick, COUNT(Nick) as Posts from messages where channel`+
                 ` = '#geekhack' group by nick order by Posts desc limit 10;`
    postsByHour = `select HOUR(Time) as date, count(RID) as count from messages`+
                  ` where channel = '#geekhack' group by date order by date;`
    updateWords = `REPLACE INTO %s
    select newfucks.Nick, newfucks.Posts + %s.Posts, NOW() from
    (select Nick, COUNT(Nick) as Posts from messages where LOWER(message) like '%%%s%%' and channel = '#geekhack' and Time > (select MAX(Updated) from %s) group by Nick) as newfucks
    LEFT OUTER JOIN
    %s
    ON newfucks.Nick = %s.Nick;`
    topTenWords = `select * from %s order by Posts desc limit 10;`
)

var (
    templates = template.Must(template.ParseFiles(getFiles("./views/", ".html")...))
    geekhack = NewGeekhack()
    config Config
)

type Config struct {
    DBConn string
    BadWords []BadWord `xml:">BadWord"`
}

type BadWord struct {
    Word string
    Query string
    Table string
}

type Geekhack struct {
    updateChan chan bool

    mutex sync.RWMutex // Protects:
    PostsByDay []Tuple
    CurseWords map[string][]Tuple
    TotalPosts []Tuple
    age time.Time
}

type Tuple struct {
    Name string
    Count int
}

func NewGeekhack() *Geekhack{
    return &Geekhack{
        CurseWords: make(map[string][]Tuple),
        updateChan: make(chan bool),
    }
}

func (g *Geekhack) shouldUpdate() bool {
    g.mutex.RLock()
    defer g.mutex.RUnlock()
    return time.Since(g.age).Minutes() < 5
}

func runQuery(query string, db *sql.DB) ([]Tuple, error) {
    var nick string
    var posts int
    var tuple []Tuple
    rows, err := db.Query(query)
    if err != nil {
        log.Println(err)
        return nil, err
    }
    for rows.Next() {
        rows.Scan(&nick, &posts)
        tuple = append(tuple, Tuple{nick, posts})
    }
    return tuple, nil
}

func (g *Geekhack) Update() {
    db, err := sql.Open("mysql", config.DBConn)
    if err != nil {
        log.Println(err)
        return
    }
    defer db.Close()

    PostsByDay, err := runQuery(postByDay, db)
    if err != nil {
        log.Println(err)
        return
    }

    TotalPosts, err := runQuery(totalPosts, db)
    if err != nil {
        log.Println(err)
        return
    }
    CurseWords := make(map[string][]Tuple)
    log.Println("Loading Cursewords!")
    for _, word := range config.BadWords {
        log.Println(word.Word, ":", word.Query)
        _, err := db.Exec(fmt.Sprintf(updateWords, word.Table, word.Table, word.Query, word.Table, word.Table, word.Table))
        if err != nil {
            log.Println(err)
            return
        }
        // This is dumb. Either that or I'm too dumb to figure out how to get
        // the sql.Query() thing to allow wildcards. Maybe it's like that by design?
        tuple, err := runQuery(fmt.Sprintf(topTenWords, word.Table), db)
        if err != nil {
            log.Println(err)
            return
        }
        CurseWords[word.Word] = tuple
    }
    log.Println("Finished loading Cursewords!")
    g.mutex.Lock()
    defer g.mutex.Unlock()
    g.PostsByDay = PostsByDay
    g.TotalPosts = TotalPosts
    g.CurseWords = CurseWords
}

func (g *Geekhack) Updater() {
    for <-g.updateChan {
        if g.shouldUpdate() {
            g.Update()
        }
    }
}

func getFiles(folder, fileType string) []string {
    files, err := ioutil.ReadDir(folder)
    if err != nil {
        panic(err)
    }
    var templateList []string
    for _, file := range files {
        if strings.HasSuffix(file.Name(), fileType) {
            templateList = append(templateList, folder+file.Name())
        }
    }
    return templateList
}

func serveTemplate(filename string) func(http.ResponseWriter, *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        if err := templates.ExecuteTemplate(w, filename, nil); err != nil {
            log.Println(err)
        }
    }
}

type Keyboards struct {
    Keyboards map[string]string
}

func keyboardHandler(w http.ResponseWriter, r *http.Request) {
    keyboards := getFiles("./static/keyboards/", ".jpg")
    matchedBoards := Keyboards{make(map[string]string)}
    for _, keyboard := range keyboards {
        dir, file := path.Split(keyboard)
        matchedBoards.Keyboards[path.Join("/", dir, file)] = path.Join("/", dir, "thumbs", file)
    }
    if err := templates.ExecuteTemplate(w, "keyboards.html", matchedBoards); err != nil {
        log.Println(err)
    }
}

func geekhackHandler(w http.ResponseWriter, r *http.Request) {
    geekhack.mutex.RLock()
    defer func() {
        geekhack.mutex.RUnlock()
        geekhack.updateChan <- true
    }()
    if err := templates.ExecuteTemplate(w, "geekhack.html", geekhack); err != nil {
        log.Println(err)
    }
}

func serveStatic(filename string) func(http.ResponseWriter, *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, filename)
    }
}

func init() {
    xmlFile, err := ioutil.ReadFile("config.xml")
    if err != nil {
        log.Fatal(err)
    }
    xml.Unmarshal(xmlFile, &config)
    log.Println(config)
    start := time.Now()
    geekhack.Update()
    log.Println("Time to import data: ", time.Since(start))
    go geekhack.Updater()
}

func main() {
    http.HandleFunc("/favicon.ico", serveStatic("./favicon.ico"))
    http.HandleFunc("/sitemap.xml", serveStatic("./sitemap.xml"))
    http.HandleFunc("/", serveTemplate("main.html"))
    http.HandleFunc("/status", serveTemplate("status.html"))
    http.HandleFunc("/keyboards", keyboardHandler)
    http.HandleFunc("/geekhack", geekhackHandler)
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
    http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/blakesmith/ar"
)

type Conf struct {
	ListenPort   string   `json:"listenPort"`
	RootRepoPath string   `json:"rootRepoPath"`
	SupportArch  []string `json:"supportedArch"`
	EnableSSL    bool     `json:"enableSSL"`
	SSLCert      string   `json:"SSLcert"`
	SSLKey       string   `json:"SSLkey"`
}

func (c Conf) ArchPath(arch string) string {
	return filepath.Join(c.RootRepoPath, "/dists/stable/main/binary-"+arch)
}

type DeleteObj struct {
	Filename string
	Arch     string
}

var mutex sync.Mutex
var configFile = flag.String("c", "conf.json", "config file location")
var parsedConfig = &Conf{}

func main() {
	flag.Parse()
	file, err := ioutil.ReadFile(*configFile)
	if err != nil {
		log.Fatal("unable to read config file, exiting...")
	}
	//config = &Conf{}
	if err := json.Unmarshal(file, &parsedConfig); err != nil {
		log.Fatal("unable to marshal config file, exiting...")
	}

	if !createDirs(*parsedConfig) {
		log.Fatal("error creating directory structure, exiting")
	}
	http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(parsedConfig.RootRepoPath))))
	http.Handle("/upload", uploadHandler(*parsedConfig))
	http.Handle("/delete", deleteHandler(*parsedConfig))
	if parsedConfig.EnableSSL {
		log.Println("running with SSL enabled")
		log.Fatal(http.ListenAndServeTLS(":"+parsedConfig.ListenPort, parsedConfig.SSLCert, parsedConfig.SSLKey, nil))
	} else {
		log.Println("running without SSL enabled")
		log.Fatal(http.ListenAndServe(":"+parsedConfig.ListenPort, nil))
	}
}

func createDirs(config Conf) bool {
	for _, arch := range config.SupportArch {
		if _, err := os.Stat(config.ArchPath(arch)); err != nil {
			if os.IsNotExist(err) {
				log.Printf("Directory for %s does not exist, creating", arch)
				dirErr := os.MkdirAll(config.ArchPath(arch), 0755)
				if dirErr != nil {
					log.Printf("error creating directory for %s: %s\n", arch, dirErr)
					return false
				}
			} else {
				log.Printf("error inspecting %s: %s\n", arch, err)
				return false
			}
		}
	}
	return true
}

func inspectPackage(filename string) string {
	f, err := os.Open(filename)
	if err != nil {
		log.Printf("error opening package file %s: %s\n", filename, err)
		return ""
	}

	arReader := ar.NewReader(f)
	defer f.Close()
	var controlBuf bytes.Buffer

	for {
		header, err := arReader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			log.Println("error in inspectPackage loop: ", err)
			return ""
		}

		if header.Name == "control.tar.gz" {
			io.Copy(&controlBuf, arReader)
			return inspectPackageControl(controlBuf)

		}

	}
	return ""
}

func inspectPackageControl(filename bytes.Buffer) string {
	gzf, err := gzip.NewReader(bytes.NewReader(filename.Bytes()))
	if err != nil {
		log.Println("error creating gzip reader: ", err)
		return ""
	}

	tarReader := tar.NewReader(gzf)
	var controlBuf bytes.Buffer
	for {
		header, err := tarReader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			log.Println("error in inspectPackage loop: ", err)
			return ""
		}

		name := header.Name

		switch header.Typeflag {
		case tar.TypeDir:
			continue
		case tar.TypeReg:
			if name == "./control" {
				io.Copy(&controlBuf, tarReader)
				return controlBuf.String()
			}
		default:
			log.Printf("%s : %c %s %s\n",
				"Unable to figure out type",
				header.Typeflag,
				"in file",
				name,
			)
		}
	}
	return ""
}

func createPackagesGz(config *Conf, arch string) error {
	outfile, err := os.Create(filepath.Join(config.ArchPath(arch), "Packages.gz"))
	if err != nil {
		return fmt.Errorf("failed to create packages.gz: %s", err)
	}
	defer outfile.Close()
	gzOut := gzip.NewWriter(outfile)
	defer gzOut.Close()
	// loop through each directory
	// run inspectPackage
	dirList, err := ioutil.ReadDir(config.ArchPath(arch))
	if err != nil {
		return fmt.Errorf("scanning: %s: %s", config.ArchPath(arch), err)
	}
	for _, debFile := range dirList {
		if strings.HasSuffix(debFile.Name(), "deb") {
			var packBuf bytes.Buffer
			debPath := filepath.Join(config.ArchPath(arch), debFile.Name())
			tempCtlData := inspectPackage(debPath)
			packBuf.WriteString(tempCtlData)
			dir := filepath.Join("dists/stable/main/binary-"+arch, debFile.Name())
			fmt.Fprintf(&packBuf, "Filename: %s\n", dir)
			fmt.Fprintf(&packBuf, "Size: %d\n", debFile.Size())
			f, err := os.Open(debPath)
			if err != nil {
				log.Println("error opening deb file: ", err)
			}
			defer f.Close()

			var (
				md5hash    = md5.New()
				sha1hash   = sha1.New()
				sha256hash = sha256.New()
			)
			_, err = io.Copy(io.MultiWriter(md5hash, sha1hash, sha256hash), f)
			if err != nil {
				log.Println("error with the md5 hash: ", err)
			}
			fmt.Fprintf(&packBuf, "MD5sum: %s\n",
				hex.EncodeToString(md5hash.Sum(nil)))
			if _, err = io.Copy(sha1hash, f); err != nil {
				log.Println("error with the sha1 hash: ", err)
			}
			fmt.Fprintf(&packBuf, "SHA1: %s\n",
				hex.EncodeToString(sha1hash.Sum(nil)))
			if _, err = io.Copy(sha256hash, f); err != nil {
				log.Println("error with the sha256 hash: ", err)
			}
			fmt.Fprintf(&packBuf, "SHA256: %s\n",
				hex.EncodeToString(sha256hash.Sum(nil)))
			packBuf.WriteString("\n\n")
			gzOut.Write(packBuf.Bytes())
			f = nil
		}
	}

	gzOut.Flush()
	return nil
}

func uploadHandler(config Conf) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			log.Println("not a POST")
			return
		}
		queryVals := r.URL.Query()
		archType := "all"
		if queryVals.Get("arch") != "" {
			archType = queryVals.Get("arch")
		}
		reader, err := r.MultipartReader()
		if err != nil {
			httpErrorf(w, "error creating multipart reader: %s", err)
			return
		}
		for {
			part, err := reader.NextPart()
			if err == io.EOF {
				log.Println("breaking")
				break
			}
			log.Println("form part: ", part.FormName())
			if part.FileName() == "" {
				continue
			}
			log.Println("found file: ", part.FileName())
			log.Println("creating files: ", part.FileName())
			dst, err := os.Create(filepath.Join(config.ArchPath(archType), part.FileName()))
			if err != nil {
				httpErrorf(w, "error creating deb file: %s", err)
				return
			}
			defer dst.Close()
			if _, err := io.Copy(dst, part); err != nil {
				httpErrorf(w, "error writing deb file: %s", err)
				return
			}
		}

		log.Println("grabbing lock...")
		mutex.Lock()
		log.Println("got lock, updating package list...")
		createPkgRes := createPackagesGz(&config, archType)
		if err := createPkgRes; err != nil {
			log.Println("error: ", err)
		}
		mutex.Unlock()
		log.Println("lock returned")
		w.WriteHeader(http.StatusOK)
	})
}

func deleteHandler(config Conf) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "DELETE" {
			log.Println("not a DELETE")
			return
		}
		var toDelete DeleteObj
		if err := json.NewDecoder(r.Body).Decode(&toDelete); err != nil {
			httpErrorf(w, "failed to decode json: %s", err)
			return
		}
		debPath := filepath.Join(config.ArchPath(toDelete.Arch), toDelete.Filename)
		if err := os.Remove(debPath); err != nil {
			httpErrorf(w, "failed to delete: %s", err)
			return
		}
		log.Println("grabbing lock...")
		mutex.Lock()
		log.Println("got lock, updating package list...")
		if err := createPackagesGz(&config, toDelete.Arch); err != nil {
			httpErrorf(w, "failed to delete: %s", err)
			return
		}
		mutex.Unlock()
		log.Println("lock returned")
	})
}

func httpErrorf(w http.ResponseWriter, format string, a ...interface{}) {
	err := fmt.Errorf(format, a...)
	log.Println(err)
	http.Error(w, err.Error(), http.StatusInternalServerError)
}
<commit_msg>fix error message<commit_after>package main

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/blakesmith/ar"
)

type Conf struct {
	ListenPort   string   `json:"listenPort"`
	RootRepoPath string   `json:"rootRepoPath"`
	SupportArch  []string `json:"supportedArch"`
	EnableSSL    bool     `json:"enableSSL"`
	SSLCert      string   `json:"SSLcert"`
	SSLKey       string   `json:"SSLkey"`
}

func (c Conf) ArchPath(arch string) string {
	return filepath.Join(c.RootRepoPath, "/dists/stable/main/binary-"+arch)
}

type DeleteObj struct {
	Filename string
	Arch     string
}

var mutex sync.Mutex
var configFile = flag.String("c", "conf.json", "config file location")
var parsedConfig = &Conf{}

func main() {
	flag.Parse()
	file, err := ioutil.ReadFile(*configFile)
	if err != nil {
		log.Fatal("unable to read config file, exiting...")
	}
	//config = &Conf{}
	if err := json.Unmarshal(file, &parsedConfig); err != nil {
		log.Fatal("unable to marshal config file, exiting...")
	}

	if !createDirs(*parsedConfig) {
		log.Fatal("error creating directory structure, exiting")
	}
	http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(parsedConfig.RootRepoPath))))
	http.Handle("/upload", uploadHandler(*parsedConfig))
	http.Handle("/delete", deleteHandler(*parsedConfig))
	if parsedConfig.EnableSSL {
		log.Println("running with SSL enabled")
		log.Fatal(http.ListenAndServeTLS(":"+parsedConfig.ListenPort, parsedConfig.SSLCert, parsedConfig.SSLKey, nil))
	} else {
		log.Println("running without SSL enabled")
		log.Fatal(http.ListenAndServe(":"+parsedConfig.ListenPort, nil))
	}
}

func createDirs(config Conf) bool {
	for _, arch := range config.SupportArch {
		if _, err := os.Stat(config.ArchPath(arch)); err != nil {
			if os.IsNotExist(err) {
				log.Printf("Directory for %s does not exist, creating", arch)
				dirErr := os.MkdirAll(config.ArchPath(arch), 0755)
				if dirErr != nil {
					log.Printf("error creating directory for %s: %s\n", arch, dirErr)
					return false
				}
			} else {
				log.Printf("error inspecting %s: %s\n", arch, err)
				return false
			}
		}
	}
	return true
}

func inspectPackage(filename string) string {
	f, err := os.Open(filename)
	if err != nil {
		log.Printf("error opening package file %s: %s\n", filename, err)
		return ""
	}

	arReader := ar.NewReader(f)
	defer f.Close()
	var controlBuf bytes.Buffer

	for {
		header, err := arReader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			log.Println("error in inspectPackage loop: ", err)
			return ""
		}

		if header.Name == "control.tar.gz" {
			io.Copy(&controlBuf, arReader)
			return inspectPackageControl(controlBuf)

		}

	}
	return ""
}

func inspectPackageControl(filename bytes.Buffer) string {
	gzf, err := gzip.NewReader(bytes.NewReader(filename.Bytes()))
	if err != nil {
		log.Println("error creating gzip reader: ", err)
		return ""
	}

	tarReader := tar.NewReader(gzf)
	var controlBuf bytes.Buffer
	for {
		header, err := tarReader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			log.Println("error in inspectPackage loop: ", err)
			return ""
		}

		name := header.Name

		switch header.Typeflag {
		case tar.TypeDir:
			continue
		case tar.TypeReg:
			if name == "./control" {
				io.Copy(&controlBuf, tarReader)
				return controlBuf.String()
			}
		default:
			log.Printf("%s : %c %s %s\n",
				"Unable to figure out type",
				header.Typeflag,
				"in file",
				name,
			)
		}
	}
	return ""
}

func createPackagesGz(config *Conf, arch string) error {
	outfile, err := os.Create(filepath.Join(config.ArchPath(arch), "Packages.gz"))
	if err != nil {
		return fmt.Errorf("failed to create packages.gz: %s", err)
	}
	defer outfile.Close()
	gzOut := gzip.NewWriter(outfile)
	defer gzOut.Close()
	// loop through each directory
	// run inspectPackage
	dirList, err := ioutil.ReadDir(config.ArchPath(arch))
	if err != nil {
		return fmt.Errorf("scanning: %s: %s", config.ArchPath(arch), err)
	}
	for _, debFile := range dirList {
		if strings.HasSuffix(debFile.Name(), "deb") {
			var packBuf bytes.Buffer
			debPath := filepath.Join(config.ArchPath(arch), debFile.Name())
			tempCtlData := inspectPackage(debPath)
			packBuf.WriteString(tempCtlData)
			dir := filepath.Join("dists/stable/main/binary-"+arch, debFile.Name())
			fmt.Fprintf(&packBuf, "Filename: %s\n", dir)
			fmt.Fprintf(&packBuf, "Size: %d\n", debFile.Size())
			f, err := os.Open(debPath)
			if err != nil {
				log.Println("error opening deb file: ", err)
			}
			defer f.Close()

			var (
				md5hash    = md5.New()
				sha1hash   = sha1.New()
				sha256hash = sha256.New()
			)
			_, err = io.Copy(io.MultiWriter(md5hash, sha1hash, sha256hash), f)
			if err != nil {
				log.Println("error with the md5 hash: ", err)
			}
			fmt.Fprintf(&packBuf, "MD5sum: %s\n",
				hex.EncodeToString(md5hash.Sum(nil)))
			if _, err = io.Copy(sha1hash, f); err != nil {
				log.Println("error with the sha1 hash: ", err)
			}
			fmt.Fprintf(&packBuf, "SHA1: %s\n",
				hex.EncodeToString(sha1hash.Sum(nil)))
			if _, err = io.Copy(sha256hash, f); err != nil {
				log.Println("error with the sha256 hash: ", err)
			}
			fmt.Fprintf(&packBuf, "SHA256: %s\n",
				hex.EncodeToString(sha256hash.Sum(nil)))
			packBuf.WriteString("\n\n")
			gzOut.Write(packBuf.Bytes())
			f = nil
		}
	}

	gzOut.Flush()
	return nil
}

func uploadHandler(config Conf) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			log.Println("not a POST")
			return
		}
		queryVals := r.URL.Query()
		archType := "all"
		if queryVals.Get("arch") != "" {
			archType = queryVals.Get("arch")
		}
		reader, err := r.MultipartReader()
		if err != nil {
			httpErrorf(w, "error creating multipart reader: %s", err)
			return
		}
		for {
			part, err := reader.NextPart()
			if err == io.EOF {
				log.Println("breaking")
				break
			}
			log.Println("form part: ", part.FormName())
			if part.FileName() == "" {
				continue
			}
			log.Println("found file: ", part.FileName())
			log.Println("creating files: ", part.FileName())
			dst, err := os.Create(filepath.Join(config.ArchPath(archType), part.FileName()))
			if err != nil {
				httpErrorf(w, "error creating deb file: %s", err)
				return
			}
			defer dst.Close()
			if _, err := io.Copy(dst, part); err != nil {
				httpErrorf(w, "error writing deb file: %s", err)
				return
			}
		}

		log.Println("grabbing lock...")
		mutex.Lock()
		log.Println("got lock, updating package list...")
		createPkgRes := createPackagesGz(&config, archType)
		if err := createPkgRes; err != nil {
			log.Println("error: ", err)
		}
		mutex.Unlock()
		log.Println("lock returned")
		w.WriteHeader(http.StatusOK)
	})
}

func deleteHandler(config Conf) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "DELETE" {
			log.Println("not a DELETE")
			return
		}
		var toDelete DeleteObj
		if err := json.NewDecoder(r.Body).Decode(&toDelete); err != nil {
			httpErrorf(w, "failed to decode json: %s", err)
			return
		}
		debPath := filepath.Join(config.ArchPath(toDelete.Arch), toDelete.Filename)
		if err := os.Remove(debPath); err != nil {
			httpErrorf(w, "failed to delete: %s", err)
			return
		}
		log.Println("grabbing lock...")
		mutex.Lock()
		log.Println("got lock, updating package list...")
		if err := createPackagesGz(&config, toDelete.Arch); err != nil {
			httpErrorf(w, "failed to create package: %s", err)
			return
		}
		mutex.Unlock()
		log.Println("lock returned")
	})
}

func httpErrorf(w http.ResponseWriter, format string, a ...interface{}) {
	err := fmt.Errorf(format, a...)
	log.Println(err)
	http.Error(w, err.Error(), http.StatusInternalServerError)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"sort"

	"github.com/jagregory/cfval/parse"
	"github.com/jagregory/cfval/reporting"
	"github.com/jagregory/cfval/resources"
	"github.com/jagregory/cfval/schema"
	"github.com/mitchellh/cli"
)

type ByPath reporting.Reports

func (a ByPath) Len() int           { return len(a) }
func (a ByPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByPath) Less(i, j int) bool { return a[i].PathReadable < a[j].PathReadable }

var ui = &cli.ColoredUi{
	InfoColor:  cli.UiColorNone,
	ErrorColor: cli.UiColorRed,
	WarnColor:  cli.UiColorYellow,
	Ui: &cli.BasicUi{
		Reader:      os.Stdin,
		Writer:      os.Stdout,
		ErrorWriter: os.Stderr,
	},
}

func groupReports(reports reporting.Reports) map[string]reporting.Reports {
	group := make(map[string]reporting.Reports)

	for _, r := range reports {
		if items, ok := group[r.PathReadable]; ok {
			group[r.PathReadable] = append(items, r)
		} else {
			group[r.PathReadable] = reporting.Reports{r}
		}
	}

	return group
}

func printGroupedReports(reports reporting.Reports) {
	sort.Sort(ByPath(reports))
	grouped := groupReports(reports)

	for path, reports := range grouped {
		ui.Info(path)

		for _, report := range reports {
			if report.Level == reporting.Failure {
				ui.Error("  ✗ " + report.Message)
			} else if report.Level == reporting.Warning {
				ui.Warn("  ⁈ " + report.Message)
			} else {
				ui.Info("  ➜ " + report.Message)
			}
		}

		fmt.Println()
	}
}

func printReports(reports reporting.Reports) {
	sort.Sort(ByPath(reports))

	maxLength := 0
	for _, report := range reports {
		path := report.PathReadable
		if len(path) > maxLength {
			maxLength = len(path)
		}
	}

	for _, report := range reports {
		path := report.PathReadable

		str := path
		str += " "
		for i := 0; i < maxLength-len(path); i++ {
			str += "."
		}
		str += "... "
		str += report.Message

		if report.Level == reporting.Failure {
			ui.Error(str)
		} else if report.Level == reporting.Warning {
			ui.Warn(str)
		} else {
			ui.Info(str)
		}
	}

	if len(reports) > 0 {
		fmt.Println()
	}
}

func getReadStream(args []string) (io.Reader, error) {
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		return os.Stdin, nil
	} else if len(args) > 0 {
		return os.Open(args[0])
	} else {
		return nil, fmt.Errorf("Provide either a filename or pipe something")
	}
}

type ValidateCommand struct{}

func (ValidateCommand) Help() string {
	return `
Usage: cfval validate [filename]
       cat filename | cfval validate

  Given a CloudFormation JSON template, validate will parse and execute various
  tests against the template. Any problems will be printed and a non-zero exit
  code reported.

Options:

  -experiment:map-array-coercion  (Experimental) Objects can be used in properties which expect an array
  -format                         Output formatting (online|grouped)
  -warnings-as-errors             Treat warnings as errors
`
}

func (ValidateCommand) Synopsis() string {
	return "Validate a CloudFormation template"
}

func (c ValidateCommand) Run(args []string) int {
	var warningsAsErrors bool
	var format string
	var experimentMapArrayCoercion bool

	cmdFlags := flag.NewFlagSet("validate", flag.ContinueOnError)
	cmdFlags.BoolVar(&warningsAsErrors, "warnings-as-errors", false, "Treats any warnings as errors and fails the validation")
	cmdFlags.BoolVar(&experimentMapArrayCoercion, "experiment:map-array-coercion", false, "(Experimental) Objects can be used in properties which expect an array")
	cmdFlags.StringVar(&format, "format", "oneline", "Output formatting (online|grouped)")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	args = cmdFlags.Args()

	stream, err := getReadStream(args)
	if err != nil {
		fmt.Println(err)
		return 1
	}

	bytes, err := ioutil.ReadAll(stream)
	if err != nil {
		fmt.Println("Error reading JSON from Stdin")
		return 1
	}

	template, err := parse.ParseTemplateJSON(bytes)
	if err != nil {
		fmt.Println("Error parsing JSON:", err)
		return 1
	}

	_, reports := schema.TemplateValidate(
		template,
		schema.NewResourceDefinitions(resources.AwsTypes),
		schema.ValidationOptions{
			schema.OptionExperimentMapArrayCoercion: experimentMapArrayCoercion,
		},
	)
	stats := reports.Stats()

	if format == "grouped" {
		printGroupedReports(reports)
	} else {
		printReports(reports)
	}

	if warningsAsErrors || stats.Failures > 0 {
		fmt.Printf("Fail: %d failures, %d warnings\n", stats.Failures, stats.Warnings)
		return 1
	}

	fmt.Printf("Pass: %d failures, %d warnings\n", stats.Failures, stats.Warnings)
	return 0
}

func main() {
	app := cli.NewCLI("cfval", Version)
	app.Args = os.Args[1:]
	app.Commands = map[string]cli.CommandFactory{
		"validate": func() (cli.Command, error) {
			return ValidateCommand{}, nil
		},
	}

	exitStatus, err := app.Run()
	if err != nil {
		log.Println(err)
	}

	os.Exit(exitStatus)
}
<commit_msg>Switch Error output to write to STDOUT, as its not true process errors and makes piping/grepping easier<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"sort"

	"github.com/jagregory/cfval/parse"
	"github.com/jagregory/cfval/reporting"
	"github.com/jagregory/cfval/resources"
	"github.com/jagregory/cfval/schema"
	"github.com/mitchellh/cli"
)

type ByPath reporting.Reports

func (a ByPath) Len() int           { return len(a) }
func (a ByPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByPath) Less(i, j int) bool { return a[i].PathReadable < a[j].PathReadable }

var ui = &cli.ColoredUi{
	InfoColor:  cli.UiColorNone,
	ErrorColor: cli.UiColorRed,
	WarnColor:  cli.UiColorYellow,
	Ui: &cli.BasicUi{
		Reader:      os.Stdin,
		Writer:      os.Stdout,
		ErrorWriter: os.Stdout,
	},
}

func groupReports(reports reporting.Reports) map[string]reporting.Reports {
	group := make(map[string]reporting.Reports)

	for _, r := range reports {
		if items, ok := group[r.PathReadable]; ok {
			group[r.PathReadable] = append(items, r)
		} else {
			group[r.PathReadable] = reporting.Reports{r}
		}
	}

	return group
}

func printGroupedReports(reports reporting.Reports) {
	sort.Sort(ByPath(reports))
	grouped := groupReports(reports)

	for path, reports := range grouped {
		ui.Info(path)

		for _, report := range reports {
			if report.Level == reporting.Failure {
				ui.Error("  ✗ " + report.Message)
			} else if report.Level == reporting.Warning {
				ui.Warn("  ⁈ " + report.Message)
			} else {
				ui.Info("  ➜ " + report.Message)
			}
		}

		fmt.Println()
	}
}

func printReports(reports reporting.Reports) {
	sort.Sort(ByPath(reports))

	maxLength := 0
	for _, report := range reports {
		path := report.PathReadable
		if len(path) > maxLength {
			maxLength = len(path)
		}
	}

	for _, report := range reports {
		path := report.PathReadable

		str := path
		str += " "
		for i := 0; i < maxLength-len(path); i++ {
			str += "."
		}
		str += "... "
		str += report.Message

		if report.Level == reporting.Failure {
			ui.Error(str)
		} else if report.Level == reporting.Warning {
			ui.Warn(str)
		} else {
			ui.Info(str)
		}
	}

	if len(reports) > 0 {
		fmt.Println()
	}
}

func getReadStream(args []string) (io.Reader, error) {
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		return os.Stdin, nil
	} else if len(args) > 0 {
		return os.Open(args[0])
	} else {
		return nil, fmt.Errorf("Provide either a filename or pipe something")
	}
}

type ValidateCommand struct{}

func (ValidateCommand) Help() string {
	return `
Usage: cfval validate [filename]
       cat filename | cfval validate

  Given a CloudFormation JSON template, validate will parse and execute various
  tests against the template. Any problems will be printed and a non-zero exit
  code reported.

Options:

  -experiment:map-array-coercion  (Experimental) Objects can be used in properties which expect an array
  -format                         Output formatting (online|grouped)
  -warnings-as-errors             Treat warnings as errors
`
}

func (ValidateCommand) Synopsis() string {
	return "Validate a CloudFormation template"
}

func (c ValidateCommand) Run(args []string) int {
	var warningsAsErrors bool
	var format string
	var experimentMapArrayCoercion bool

	cmdFlags := flag.NewFlagSet("validate", flag.ContinueOnError)
	cmdFlags.BoolVar(&warningsAsErrors, "warnings-as-errors", false, "Treats any warnings as errors and fails the validation")
	cmdFlags.BoolVar(&experimentMapArrayCoercion, "experiment:map-array-coercion", false, "(Experimental) Objects can be used in properties which expect an array")
	cmdFlags.StringVar(&format, "format", "oneline", "Output formatting (online|grouped)")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	args = cmdFlags.Args()

	stream, err := getReadStream(args)
	if err != nil {
		fmt.Println(err)
		return 1
	}

	bytes, err := ioutil.ReadAll(stream)
	if err != nil {
		fmt.Println("Error reading JSON from Stdin")
		return 1
	}

	template, err := parse.ParseTemplateJSON(bytes)
	if err != nil {
		fmt.Println("Error parsing JSON:", err)
		return 1
	}

	_, reports := schema.TemplateValidate(
		template,
		schema.NewResourceDefinitions(resources.AwsTypes),
		schema.ValidationOptions{
			schema.OptionExperimentMapArrayCoercion: experimentMapArrayCoercion,
		},
	)
	stats := reports.Stats()

	if format == "grouped" {
		printGroupedReports(reports)
	} else {
		printReports(reports)
	}

	if warningsAsErrors || stats.Failures > 0 {
		fmt.Printf("Fail: %d failures, %d warnings\n", stats.Failures, stats.Warnings)
		return 1
	}

	fmt.Printf("Pass: %d failures, %d warnings\n", stats.Failures, stats.Warnings)
	return 0
}

func main() {
	app := cli.NewCLI("cfval", Version)
	app.Args = os.Args[1:]
	app.Commands = map[string]cli.CommandFactory{
		"validate": func() (cli.Command, error) {
			return ValidateCommand{}, nil
		},
	}

	exitStatus, err := app.Run()
	if err != nil {
		log.Println(err)
	}

	os.Exit(exitStatus)
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Mark Samman <https://github.com/marksamman/gotorrent>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package main

import (
	"bytes"
	"fmt"
	"log"
	"math/rand"
	"os"
	"sync"
	"time"
)

type Client struct {
	peerID   []byte
	torrents []*Torrent
}

var client Client

func main() {
	torrentCount := len(os.Args) - 1
	if torrentCount == 0 {
		fmt.Println("usage: gotorrent file.torrent[ file2.torrent[ ...]]")
		return
	}

	// Seed rand
	rand.Seed(time.Now().UnixNano())

	// Generate a 20 byte peerId
	var peerID bytes.Buffer
	peerID.WriteString("-GO10000")
	for i := 0; i < 12; i++ {
		peerID.WriteByte(byte(rand.Intn(255)))
	}
	client.peerID = peerID.Bytes()

	// Open torrent file
	for i := 1; i <= torrentCount; i++ {
		torrent := Torrent{}
		if err := torrent.open(os.Args[i]); err != nil {
			log.Print(err)
			continue
		}

		fmt.Println("Name:", torrent.getName())
		fmt.Println("Announce URL:", torrent.getAnnounceURL())
		fmt.Println("Comment:", torrent.getComment())
		fmt.Printf("Total size: %.2f MB\n", float64(torrent.getTotalSize())/1024/1024)
		client.torrents = append(client.torrents, &torrent)
	}

	var group sync.WaitGroup
	group.Add(len(client.torrents))
	for _, torrent := range client.torrents {
		go func(torrent *Torrent) {
			defer group.Done()
			if err := torrent.download(); err != nil {
				log.Print(err)
			}
		}(torrent)
	}
	group.Wait()
}
<commit_msg>No need to defer group.Done() here<commit_after>/*
 * Copyright (c) 2014 Mark Samman <https://github.com/marksamman/gotorrent>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package main

import (
	"bytes"
	"fmt"
	"log"
	"math/rand"
	"os"
	"sync"
	"time"
)

type Client struct {
	peerID   []byte
	torrents []*Torrent
}

var client Client

func main() {
	torrentCount := len(os.Args) - 1
	if torrentCount == 0 {
		fmt.Println("usage: gotorrent file.torrent[ file2.torrent[ ...]]")
		return
	}

	// Seed rand
	rand.Seed(time.Now().UnixNano())

	// Generate a 20 byte peerId
	var peerID bytes.Buffer
	peerID.WriteString("-GO10000")
	for i := 0; i < 12; i++ {
		peerID.WriteByte(byte(rand.Intn(255)))
	}
	client.peerID = peerID.Bytes()

	// Open torrent file
	for i := 1; i <= torrentCount; i++ {
		torrent := Torrent{}
		if err := torrent.open(os.Args[i]); err != nil {
			log.Print(err)
			continue
		}

		fmt.Println("Name:", torrent.getName())
		fmt.Println("Announce URL:", torrent.getAnnounceURL())
		fmt.Println("Comment:", torrent.getComment())
		fmt.Printf("Total size: %.2f MB\n", float64(torrent.getTotalSize())/1024/1024)
		client.torrents = append(client.torrents, &torrent)
	}

	var group sync.WaitGroup
	group.Add(len(client.torrents))
	for _, torrent := range client.torrents {
		go func(torrent *Torrent) {
			if err := torrent.download(); err != nil {
				log.Print(err)
			}
			group.Done()
		}(torrent)
	}
	group.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/go-martini/martini"
	"github.com/wmbest2/rats_server/rats"
	"github.com/wmbest2/rats_server/test"
	"io"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"
)

func uuid() (string, error) {
	uuid := make([]byte, 16)
	n, err := rand.Read(uuid)
	if n != len(uuid) || err != nil {
		return "", err
	}
	// TODO: verify the two lines implement RFC 4122 correctly
	uuid[8] = 0x80 // variant bits see page 5
	uuid[4] = 0x40 // version 4 Pseudo Random, see page 7

	return hex.EncodeToString(uuid), nil
}

func Mongo(db string) martini.Handler {
	session, err := mgo.Dial(db)
	if err != nil {
		panic(err)
	}

	return func(c martini.Context) {
		reqSession := session.Clone()
		c.Map(reqSession.DB("rats"))
		defer reqSession.Close()

		c.Next()
	}
}

func makeTestFolder() (string, string) {
	uuid, err := uuid()
	if err != nil {
		panic(err)
	}

	dir := fmt.Sprintf("test_runs/%s", uuid)
	os.MkdirAll(dir, os.ModeDir|os.ModePerm|os.ModeTemporary)
	return uuid, dir
}

func save(key string, filename string, r *http.Request) (bool, error) {
	apk, _, _ := r.FormFile(key)
	if apk != nil {
		apk_file, err := os.Create(filename)

		if err != nil {
			return false, err
		}

		_, err = io.Copy(apk_file, apk)
		apk.Close()
		apk_file.Close()

		if err != nil {
			return false, err
		}

		return true, nil
	}
	return false, nil
}

func RunTests(w http.ResponseWriter, r *http.Request, db *mgo.Database) (int, []byte) {
	uuid, dir := makeTestFolder()

	main := fmt.Sprintf("%s/main.apk", dir)
	install, err := save("apk", main, r)

	if err != nil {
		panic(err)
	}

    f := fmt.Sprintf("%s/test.apk", dir)
	_, err = save("test-apk", f, r)

	if err != nil {
		panic("A Test Apk must be supplied")
	}

	count, _ := strconv.Atoi(r.FormValue("count"))
	serialList := r.FormValue("serials")
    strict := r.FormValue("strict")

    var serials []string
    if serialList != "" {
        serials = strings.Split(serialList, ",")
    }

    filter := &rats.DeviceFilter{
        Count: count,
        Strict: strict == "true",
    }
    filter.Serials = serials

	manifest := rats.GetManifest(f)
    filter.MinSdk = manifest.Sdk.Min
    filter.MaxSdk = manifest.Sdk.Max

	devices := <-rats.GetDevices(filter)
	rats.Reserve(devices)

	if install {
		rats.Install(main, devices)
	}

	rats.Install(f, devices)

	rats.Unlock(devices)

	s := test.RunTests(manifest, devices)
	s.Name = uuid
	s.Timestamp = time.Now()
	s.Project = manifest.Instrument.Target

	if dbErr := db.C("runs").Insert(&s); dbErr != nil {
		return http.StatusConflict, []byte(dbErr.Error())
	}

	rats.Uninstall(manifest.Package, devices)
	rats.Uninstall(manifest.Instrument.Target, devices)
	rats.Release(devices)

	os.RemoveAll(dir)

	str, err := json.Marshal(s)
	if err != nil {
		panic(err)
	}

    code := http.StatusOK
    if !s.Success {
        code = http.StatusInternalServerError
    }

	return code, str
}

func GetRunDevice(r *http.Request, parms martini.Params, db *mgo.Database) (int, []byte) {
	var runs test.TestSuites
	q := bson.M{"name": parms["id"], "testsuites.hostname": parms["device"]}
	query := db.C("runs").Find(q).Limit(1)
	query.One(&runs)
	b, _ := json.Marshal(runs.TestSuites[0])
	return http.StatusOK, b
}

func GetRun(r *http.Request, parms martini.Params, db *mgo.Database) (int, []byte) {
	var runs test.TestSuites
	query := db.C("runs").Find(bson.M{"name": parms["id"]}).Limit(1)
	query.One(&runs)
	b, _ := json.Marshal(runs)
	return http.StatusOK, b
}

func GetRuns(r *http.Request, parms martini.Params, db *mgo.Database) (int, []byte) {
	page := 0
	p := r.URL.Query().Get("page")
	if p != "" {
		page, _ = strconv.Atoi(p)
	}

	size := 25
	s := r.URL.Query().Get("count")
	if s != "" {
		size, _ = strconv.Atoi(s)
	}

	var runs []*test.TestSuites
	query := db.C("runs").Find(bson.M{}).Limit(size).Skip(page * size)
	query.Select(bson.M{"testsuites.testcases": 0, "testsuites.device.inuse": 0})
	query.Sort("-timestamp")
	query.All(&runs)
	b, _ := json.Marshal(runs)
	return http.StatusOK, b
}

func GetDevices(parms martini.Params) (int, []byte) {
	b, _ := json.Marshal(<-rats.GetAllDevices())
	return http.StatusOK, b
}

func serveStatic(m *martini.Martini) {
	_, file, _, _ := runtime.Caller(0)
	here := filepath.Dir(file)
	static := filepath.Join(here, "/public")
	m.Use(martini.Static(string(static)))
	m.Use(martini.Static("public"))
}

func main() {
	go rats.UpdateAdb(5)

    var mongodb = flag.String("db", "mongodb://localhost/rats", "Mongo db url")
    var port = flag.Int("port", 3000, "Port to serve")
    var debug = flag.Bool("debug", false, "Log debug information")

    flag.Parse()

	m := martini.New()
	m.Use(martini.Recovery())
	m.Use(Mongo(*mongodb))
	serveStatic(m)

    if *debug {
        m.Use(martini.Logger())
    }

	r := martini.NewRouter()
	r.Get(`/api/devices`, GetDevices)
	r.Post("/api/run", RunTests)
	r.Get("/api/runs", GetRuns)
	r.Get("/api/runs/:id", GetRun)
	r.Get("/api/runs/:id/:device", GetRunDevice)

	m.Action(r.Handle)
    fmt.Printf("Listening on port %d\n", *port)
    log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), m))
}
<commit_msg>Fixed issue where drilling in to device shows wrong device<commit_after>package main

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/go-martini/martini"
	"github.com/wmbest2/rats_server/rats"
	"github.com/wmbest2/rats_server/test"
	"io"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"
)

func uuid() (string, error) {
	uuid := make([]byte, 16)
	n, err := rand.Read(uuid)
	if n != len(uuid) || err != nil {
		return "", err
	}
	// TODO: verify the two lines implement RFC 4122 correctly
	uuid[8] = 0x80 // variant bits see page 5
	uuid[4] = 0x40 // version 4 Pseudo Random, see page 7

	return hex.EncodeToString(uuid), nil
}

func Mongo(db string) martini.Handler {
	session, err := mgo.Dial(db)
	if err != nil {
		panic(err)
	}

	return func(c martini.Context) {
		reqSession := session.Clone()
		c.Map(reqSession.DB("rats"))
		defer reqSession.Close()

		c.Next()
	}
}

func makeTestFolder() (string, string) {
	uuid, err := uuid()
	if err != nil {
		panic(err)
	}

	dir := fmt.Sprintf("test_runs/%s", uuid)
	os.MkdirAll(dir, os.ModeDir|os.ModePerm|os.ModeTemporary)
	return uuid, dir
}

func save(key string, filename string, r *http.Request) (bool, error) {
	apk, _, _ := r.FormFile(key)
	if apk != nil {
		apk_file, err := os.Create(filename)

		if err != nil {
			return false, err
		}

		_, err = io.Copy(apk_file, apk)
		apk.Close()
		apk_file.Close()

		if err != nil {
			return false, err
		}

		return true, nil
	}
	return false, nil
}

func RunTests(w http.ResponseWriter, r *http.Request, db *mgo.Database) (int, []byte) {
	uuid, dir := makeTestFolder()

	main := fmt.Sprintf("%s/main.apk", dir)
	install, err := save("apk", main, r)

	if err != nil {
		panic(err)
	}

    f := fmt.Sprintf("%s/test.apk", dir)
	_, err = save("test-apk", f, r)

	if err != nil {
		panic("A Test Apk must be supplied")
	}

	count, _ := strconv.Atoi(r.FormValue("count"))
	serialList := r.FormValue("serials")
    strict := r.FormValue("strict")

    var serials []string
    if serialList != "" {
        serials = strings.Split(serialList, ",")
    }

    filter := &rats.DeviceFilter{
        Count: count,
        Strict: strict == "true",
    }
    filter.Serials = serials

	manifest := rats.GetManifest(f)
    filter.MinSdk = manifest.Sdk.Min
    filter.MaxSdk = manifest.Sdk.Max

	devices := <-rats.GetDevices(filter)
	rats.Reserve(devices)

	if install {
		rats.Install(main, devices)
	}

	rats.Install(f, devices)

	rats.Unlock(devices)

	s := test.RunTests(manifest, devices)
	s.Name = uuid
	s.Timestamp = time.Now()
	s.Project = manifest.Instrument.Target

	if dbErr := db.C("runs").Insert(&s); dbErr != nil {
		return http.StatusConflict, []byte(dbErr.Error())
	}

    go func() {
        rats.Uninstall(manifest.Package, devices)
        rats.Uninstall(manifest.Instrument.Target, devices)

        rats.Release(devices)
    }()

	os.RemoveAll(dir)

	str, err := json.Marshal(s)
	if err != nil {
		panic(err)
	}

    code := http.StatusOK
    if !s.Success {
        code = http.StatusInternalServerError
    }

	return code, str
}

func GetRunDevice(r *http.Request, parms martini.Params, db *mgo.Database) (int, []byte) {
	var runs test.TestSuites
    q := bson.M{"name": parms["id"]}
    s := bson.M{ "testsuites": bson.M{"$elemMatch": bson.M{"hostname": parms["device"]}}}
    query := db.C("runs").Find(q).Select(s).Limit(1)
	query.One(&runs)
	b, _ := json.Marshal(runs.TestSuites[0])
	return http.StatusOK, b
}

func GetRun(r *http.Request, parms martini.Params, db *mgo.Database) (int, []byte) {
	var runs test.TestSuites
	query := db.C("runs").Find(bson.M{"name": parms["id"]}).Limit(1)
	query.One(&runs)
	b, _ := json.Marshal(runs)
	return http.StatusOK, b
}

func GetRuns(r *http.Request, parms martini.Params, db *mgo.Database) (int, []byte) {
	page := 0
	p := r.URL.Query().Get("page")
	if p != "" {
		page, _ = strconv.Atoi(p)
	}

	size := 25
	s := r.URL.Query().Get("count")
	if s != "" {
		size, _ = strconv.Atoi(s)
	}

	var runs []*test.TestSuites
	query := db.C("runs").Find(bson.M{}).Limit(size).Skip(page * size)
	query.Select(bson.M{"testsuites.testcases": 0, "testsuites.device.inuse": 0})
	query.Sort("-timestamp")
	query.All(&runs)
	b, _ := json.Marshal(runs)
	return http.StatusOK, b
}

func GetDevices(parms martini.Params) (int, []byte) {
	b, _ := json.Marshal(<-rats.GetAllDevices())
	return http.StatusOK, b
}

func serveStatic(m *martini.Martini) {
	_, file, _, _ := runtime.Caller(0)
	here := filepath.Dir(file)
	static := filepath.Join(here, "/public")
	m.Use(martini.Static(string(static)))
	m.Use(martini.Static("public"))
}

func main() {
	go rats.UpdateAdb(5)

    var mongodb = flag.String("db", "mongodb://localhost/rats", "Mongo db url")
    var port = flag.Int("port", 3000, "Port to serve")
    var debug = flag.Bool("debug", false, "Log debug information")

    flag.Parse()

	m := martini.New()
	m.Use(martini.Recovery())
	m.Use(Mongo(*mongodb))
	serveStatic(m)

    if *debug {
        m.Use(martini.Logger())
    }

	r := martini.NewRouter()
	r.Get(`/api/devices`, GetDevices)
	r.Post("/api/run", RunTests)
	r.Get("/api/runs", GetRuns)
	r.Get("/api/runs/:id", GetRun)
	r.Get("/api/runs/:id/:device", GetRunDevice)

	m.Action(r.Handle)
    fmt.Printf("Listening on port %d\n", *port)
    log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), m))
}
<|endoftext|>


<commit_before>package main

// language: go
// sudo: false
// matrix:
//   include:
//   - go: 1.x
//     env: LATEST=true
// before_install:
// - go get github.com/mitchellh/gox
// install:
// -
// script:
// - go get -t -v ./...
// -
// - go test -v -race ./... -bench=.
// - if [ "${LATEST}" = "true" ]; then gox -os="windows linux darwin" -arch="amd64" -verbose
//   ./...; fi
// deploy:
//   provider: releases
//   api_key:
//     secure: fQxUOsW7veYOzyAcxJmVrUjMDsdn/eXzw9KoEUXnkZ4auJLvfhbBhqnJV0NKI9/7sM/KylLVHgoLPuHv5Ne6KQ5XQBuLWIKAm7NkaLfNNFi1JOjYoM3jMCEDoqawaNAhtU5EkCUQEKLhcATC9GMMhR2vPn1Ak4aLOXSUXQuVHajt4oCvUK84OduM9FmHvS/mAwL9g0U/HkvTzHYxQMkiUkREc8kUrkbABh0WtrMWwPguYIfQjG79XdeFnAFsWYakodfZK76ao59O/GHh0BYrLpcazAaK5JU2FpvTS5RiwOOUbR7zn8HYUk+hn1ol5ZFZyGz5n82VmbT9gK43fH1s5dZg+KiJCa5ITtW8YNcAfc2Gb1lciooV7xzMMFBj3NIhG6XISrVQ507Qh2Qdkg5hVGxU/7vSewtadGxOC7Z2ILKLQq5ckFicDsh+ZEIJObdOCFWy0VCox4Ei+fT+SFS8EpDn1u4bv8jfpq/9MAu/HDeHUeA1v0xHT5K5ZCqtsMWskROrJMfY34l50nzytb2ZxRZ4B8Oe1/x3/cB8zsq5jSkV7ND2ca4a3iPnwFica4/EShuhswSbodZk5TbI5MTuDGcRvonFwzPZ+Bcxrq+rYs2Q1idaPE6AmYOgkiFkz2JuecGUKaAc/7dhxBjx7O4M2ODzS7X13RdJYkTtUEgdBv0=
//   file:
//   - frontend_windows_amd64.exe
//   - frontend_linux_amd64
//   - frontend_darwin_amd64
//   on:
//     branch: master

import (
	"fmt"
	"log"

	"github.com/jroimartin/gocui"

	"math/rand"
	"time"

	//"net/url"

	"strings"
)

const CHATWINDOW = "CHATWINDOW"
const ONLINEWINDOW = "ONLINEWINDOW"
const ROOMWINDOW = "ROOMWINDOW"

// type server struct{}

func init() {
	rand.Seed(time.Now().UnixNano())
	// BClient = b.BChatClient{}

}

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func RandStringRunes(n int) string {
	a := make([]rune, n)
	for i := range a {
		a[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(a)
}
func main() {
	// Setup Ws connection
	// u := url.URL{Scheme: "wss", Host: "damp-springs-83733.herokuapp.com", Path: "/bchatws"}
	// d := websocket.Dialer{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
	// c, _, err := d.Dial(u.String(), nil)
	// if err != nil {
	// 	log.Fatalln(err)
	// }

	//c.WriteJSON()

	//bhelpersb..BMessage{MsgType:B_CONNECT, Uid:RandStringRunes(32)}
	// BClient = b.BChatClient{WsConn: c, Uid: RandStringRunes(32), Name: "Anon", Room: b.MAIN_ROOM}
	// BClient.SendMessage(b.BMessage{MsgType: b.B_CONNECT, Uid: RandStringRunes(32), Payload: BClient.Name})

	// Setup CUI
	g, err := gocui.NewGui(gocui.OutputNormal)
	if err != nil {
		log.Panicln(err)
	}
	defer g.Close()

	g.Highlight = true
	g.Cursor = true

	g.SetManagerFunc(setLayout)

	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
		log.Panicln(err)
	}
	if err := g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, onEnterEvt()); err != nil {
		log.Panicln(err)
	}

	// go handleConnection(c, g)

	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
		log.Panicln(err)
	}
}

// func handleConnection(c *websocket.Conn, g *gocui.Gui) {
// 	var bMessage b.BMessage
// 	c.ReadJSON(&bMessage)
// 	processMsg(bMessage, g)
// 	for {
// 		err := c.ReadJSON(&bMessage)
// 		if err != nil {
// 			log.Fatal(err)
// 		}
// 		processMsg(bMessage, g)
// 	}
// }

// func processMsg(msg b.BMessage, g *gocui.Gui) {
// 	g.Update(func(g *gocui.Gui) error {

// 		v, _ := g.View(CHATWINDOW)
// 		if msg.MsgType == b.B_CONNECT || msg.MsgType == b.B_DISCONNECT || msg.MsgType == b.B_ROOMCHANGE {
// 			o, _ := g.View(ONLINEWINDOW)
// 			o.Clear()
// 			fmt.Fprint(o, msg.OnlineData)

// 			o, _ = g.View(ROOMWINDOW)
// 			o.Clear()
// 			fmt.Fprintf(o, msg.RoomData)
// 		}
// 		if msg.MsgType == b.B_NAMECHANGE {
// 			o, _ := g.View(ONLINEWINDOW)
// 			o.Clear()
// 			fmt.Fprint(o, msg.OnlineData)
// 		}

// 		fmt.Fprintln(v, fmt.Sprintf("\u001b[33m%s\u001b[0m (%s) %s", msg.TimeStamp.Format("2006-01-02 15:04"), msg.Name, msg.Payload))
// 		return nil
// 	})
// }

func setActiveView(g *gocui.Gui, name string) (*gocui.View, error) {
	if _, err := g.SetCurrentView(name); err != nil {
		return nil, err
	}
	return g.SetViewOnTop(name)
}

func onEnterEvt() func(g *gocui.Gui, v *gocui.View) error {
	return func(g *gocui.Gui, v *gocui.View) error {
		buf := strings.Replace(v.Buffer(), "\n", "", -1)
		if len(buf) < 1 {
			v.SetCursor(0, 0)
			return nil
		}
		// msgType := b.B_MESSAGE
		// if strings.Contains(buf, "/name") && len(strings.SplitAfter(buf, "/name")) > 1 {
		// 	newName := strings.SplitAfter(buf, "/name")[1]
		// 	newName = strings.TrimSpace(newName)
		// 	buf = fmt.Sprintf("%s changed name to %s", BClient.Name, newName)
		// 	BClient.Name = newName
		// 	msgType = b.B_NAMECHANGE

		// } else if strings.Contains(buf, "/room") && len(strings.SplitAfter(buf, "/room")) > 1 {
		// 	msgType = b.B_ROOMCHANGE
		// 	newRoom := strings.SplitAfter(buf, "/room")[1]
		// 	newRoom = strings.TrimSpace(newRoom)
		// 	BClient.Room = newRoom
		// 	//buf = fmt.Sprintf("%s left room", BClient.Name)
		// }
		// err := c.WriteJSON(b.BMessage{
		// 	MsgType:   msgType,
		// 	TimeStamp: time.Now(),
		// 	Name:      BClient.Name,
		// 	Room:      BClient.Room,
		// 	Uid:       BClient.Uid,
		// 	Payload:   buf,
		// })
		// if err != nil {
		// 	log.Println(err)
		// }

		v.Clear()
		v.SetCursor(0, 0)
		return nil
	}

}
func setLayout(g *gocui.Gui) error {
	maxX, maxY := g.Size()
	if v, err := g.SetView(ONLINEWINDOW, 0, 0, 20, 14); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Title = "Online"
		v.Autoscroll = true
		fmt.Fprintln(v, "")
	}

	if v, err := g.SetView(ROOMWINDOW, 0, 15, 20, maxY-1); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Title = "Rooms"
		fmt.Fprintln(v, "")
	}

	if v, err := g.SetView("input", 21, maxY-3, maxX-1, maxY-1); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Autoscroll = true
		v.Title = "Type To Chat"
		v.Editable = true
		v.Wrap = true
		//fmt.Fprintf(v, "H")
	}

	if v, err := g.SetView(CHATWINDOW, 21, 0, maxX-1, maxY-4); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Autoscroll = true
		v.Title = "BChats"
		v.Editable = false
		v.Wrap = true
	}

	if _, err := setActiveView(g, "input"); err != nil {
		return err
	}
	return nil
}
func quit(g *gocui.Gui, v *gocui.View) error {
	return gocui.ErrQuit
}
<commit_msg>test verify commit<commit_after>package main

// language: go
// sudo: false
// matrix:
//   include:
//   - go: 1.x
//     env: LATEST=true
// before_install:
// - go get github.com/mitchellh/gox
// install:
// -
// script:
// - go get -t -v ./...
// -
// - go test -v -race ./... -bench=.
// - if [ "${LATEST}" = "true" ]; then gox -os="windows linux darwin" -arch="amd64" -verbose
//   ./...; fi
// deploy:
//   provider: releases
//   api_key:
//     secure: fQxUOsW7veYOzyAcxJmVrUjMDsdn/eXzw9KoEUXnkZ4auJLvfhbBhqnJV0NKI9/7sM/KylLVHgoLPuHv5Ne6KQ5XQBuLWIKAm7NkaLfNNFi1JOjYoM3jMCEDoqawaNAhtU5EkCUQEKLhcATC9GMMhR2vPn1Ak4aLOXSUXQuVHajt4oCvUK84OduM9FmHvS/mAwL9g0U/HkvTzHYxQMkiUkREc8kUrkbABh0WtrMWwPguYIfQjG79XdeFnAFsWYakodfZK76ao59O/GHh0BYrLpcazAaK5JU2FpvTS5RiwOOUbR7zn8HYUk+hn1ol5ZFZyGz5n82VmbT9gK43fH1s5dZg+KiJCa5ITtW8YNcAfc2Gb1lciooV7xzMMFBj3NIhG6XISrVQ507Qh2Qdkg5hVGxU/7vSewtadGxOC7Z2ILKLQq5ckFicDsh+ZEIJObdOCFWy0VCox4Ei+fT+SFS8EpDn1u4bv8jfpq/9MAu/HDeHUeA1v0xHT5K5ZCqtsMWskROrJMfY34l50nzytb2ZxRZ4B8Oe1/x3/cB8zsq5jSkV7ND2ca4a3iPnwFica4/EShuhswSbodZk5TbI5MTuDGcRvonFwzPZ+Bcxrq+rYs2Q1idaPE6AmYOgkiFkz2JuecGUKaAc/7dhxBjx7O4M2ODzS7X13RdJYkTtUEgdBv0=
//   file:
//   - frontend_windows_amd64.exe
//   - frontend_linux_amd64
//   - frontend_darwin_amd64
//   on:
//     branch: master

import (
	"fmt"
	"log"

	"github.com/jroimartin/gocui"

	"math/rand"
	"time"

	//"net/url"

	"strings"
)

const CHATWINDOW = "CHATWINDOW"
const ONLINEWINDOW = "ONLINEWINDOW"
const ROOMWINDOW = "ROOMWINDOWs"

// type server struct{}

func init() {
	rand.Seed(time.Now().UnixNano())
	// BClient = b.BChatClient{}

}

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func RandStringRunes(n int) string {
	a := make([]rune, n)
	for i := range a {
		a[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(a)
}
func main() {
	// Setup Ws connection
	// u := url.URL{Scheme: "wss", Host: "damp-springs-83733.herokuapp.com", Path: "/bchatws"}
	// d := websocket.Dialer{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
	// c, _, err := d.Dial(u.String(), nil)
	// if err != nil {
	// 	log.Fatalln(err)
	// }

	//c.WriteJSON()

	//bhelpersb..BMessage{MsgType:B_CONNECT, Uid:RandStringRunes(32)}
	// BClient = b.BChatClient{WsConn: c, Uid: RandStringRunes(32), Name: "Anon", Room: b.MAIN_ROOM}
	// BClient.SendMessage(b.BMessage{MsgType: b.B_CONNECT, Uid: RandStringRunes(32), Payload: BClient.Name})

	// Setup CUI
	g, err := gocui.NewGui(gocui.OutputNormal)
	if err != nil {
		log.Panicln(err)
	}
	defer g.Close()

	g.Highlight = true
	g.Cursor = true

	g.SetManagerFunc(setLayout)

	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
		log.Panicln(err)
	}
	if err := g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, onEnterEvt()); err != nil {
		log.Panicln(err)
	}

	// go handleConnection(c, g)

	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
		log.Panicln(err)
	}
}

// func handleConnection(c *websocket.Conn, g *gocui.Gui) {
// 	var bMessage b.BMessage
// 	c.ReadJSON(&bMessage)
// 	processMsg(bMessage, g)
// 	for {
// 		err := c.ReadJSON(&bMessage)
// 		if err != nil {
// 			log.Fatal(err)
// 		}
// 		processMsg(bMessage, g)
// 	}
// }

// func processMsg(msg b.BMessage, g *gocui.Gui) {
// 	g.Update(func(g *gocui.Gui) error {

// 		v, _ := g.View(CHATWINDOW)
// 		if msg.MsgType == b.B_CONNECT || msg.MsgType == b.B_DISCONNECT || msg.MsgType == b.B_ROOMCHANGE {
// 			o, _ := g.View(ONLINEWINDOW)
// 			o.Clear()
// 			fmt.Fprint(o, msg.OnlineData)

// 			o, _ = g.View(ROOMWINDOW)
// 			o.Clear()
// 			fmt.Fprintf(o, msg.RoomData)
// 		}
// 		if msg.MsgType == b.B_NAMECHANGE {
// 			o, _ := g.View(ONLINEWINDOW)
// 			o.Clear()
// 			fmt.Fprint(o, msg.OnlineData)
// 		}

// 		fmt.Fprintln(v, fmt.Sprintf("\u001b[33m%s\u001b[0m (%s) %s", msg.TimeStamp.Format("2006-01-02 15:04"), msg.Name, msg.Payload))
// 		return nil
// 	})
// }

func setActiveView(g *gocui.Gui, name string) (*gocui.View, error) {
	if _, err := g.SetCurrentView(name); err != nil {
		return nil, err
	}
	return g.SetViewOnTop(name)
}

func onEnterEvt() func(g *gocui.Gui, v *gocui.View) error {
	return func(g *gocui.Gui, v *gocui.View) error {
		buf := strings.Replace(v.Buffer(), "\n", "", -1)
		if len(buf) < 1 {
			v.SetCursor(0, 0)
			return nil
		}
		// msgType := b.B_MESSAGE
		// if strings.Contains(buf, "/name") && len(strings.SplitAfter(buf, "/name")) > 1 {
		// 	newName := strings.SplitAfter(buf, "/name")[1]
		// 	newName = strings.TrimSpace(newName)
		// 	buf = fmt.Sprintf("%s changed name to %s", BClient.Name, newName)
		// 	BClient.Name = newName
		// 	msgType = b.B_NAMECHANGE

		// } else if strings.Contains(buf, "/room") && len(strings.SplitAfter(buf, "/room")) > 1 {
		// 	msgType = b.B_ROOMCHANGE
		// 	newRoom := strings.SplitAfter(buf, "/room")[1]
		// 	newRoom = strings.TrimSpace(newRoom)
		// 	BClient.Room = newRoom
		// 	//buf = fmt.Sprintf("%s left room", BClient.Name)
		// }
		// err := c.WriteJSON(b.BMessage{
		// 	MsgType:   msgType,
		// 	TimeStamp: time.Now(),
		// 	Name:      BClient.Name,
		// 	Room:      BClient.Room,
		// 	Uid:       BClient.Uid,
		// 	Payload:   buf,
		// })
		// if err != nil {
		// 	log.Println(err)
		// }

		v.Clear()
		v.SetCursor(0, 0)
		return nil
	}

}
func setLayout(g *gocui.Gui) error {
	maxX, maxY := g.Size()
	if v, err := g.SetView(ONLINEWINDOW, 0, 0, 20, 14); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Title = "Online"
		v.Autoscroll = true
		fmt.Fprintln(v, "")
	}

	if v, err := g.SetView(ROOMWINDOW, 0, 15, 20, maxY-1); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Title = "Rooms"
		fmt.Fprintln(v, "")
	}

	if v, err := g.SetView("input", 21, maxY-3, maxX-1, maxY-1); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Autoscroll = true
		v.Title = "Type To Chat"
		v.Editable = true
		v.Wrap = true
		//fmt.Fprintf(v, "H")
	}

	if v, err := g.SetView(CHATWINDOW, 21, 0, maxX-1, maxY-4); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Autoscroll = true
		v.Title = "BChats"
		v.Editable = false
		v.Wrap = true
	}

	if _, err := setActiveView(g, "input"); err != nil {
		return err
	}
	return nil
}
func quit(g *gocui.Gui, v *gocui.View) error {
	return gocui.ErrQuit
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"hash/fnv"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"time"
)

var (
	NWorkers      = flag.Int("n", 4, "The number of workers to start")
	Interval      = flag.Int("i", 10, "The interval in seconds at which to flush metrics")
	UDPAddr       = flag.String("http", ":8125", "Address to listen for UDP requests on")
	Key           = flag.String("key", "fart", "Your Datadog API Key")
	ExpirySeconds = flag.Int("expiry", 300, "The number of seconds metrics will be retained if they stop showing up")
	BufferSize    = flag.Int("buffersize", 4096, "The size of the buffer of work for the worker pool")
	APIURL        = flag.String("apiurl", "https://app.datadoghq.com", "The URL to which Metrics will be posted")
)

/* The WorkQueue is a buffered channel that we can send work to */
// TODO This should likely be configurable or unbuffered?
// var WorkQueue = make(chan PacketRequest, 100)

func main() {
	// Parse the command-line flags.
	flag.Parse()

	// Start the dispatcher.
	log.Println("Starting workers")
	workers := make([]*Worker, *NWorkers)
	for i := 0; i < *NWorkers; i++ {
		worker := NewWorker(i+1, *Interval, *ExpirySeconds)
		worker.Start()
		workers[i] = worker
	}

	// Start the UDP server!
	log.Println("UDP server listening on", *UDPAddr)
	ServerAddr, err := net.ResolveUDPAddr("udp", ":8125")
	if err != nil {
		log.Println(err.Error())
	}

	ServerConn, err := net.ListenUDP("udp", ServerAddr)
	if err != nil {
		log.Println(err.Error())
	}

	defer ServerConn.Close()

	ticker := time.NewTicker(time.Duration(*Interval) * time.Second)
	go func() {
		for t := range ticker.C {
			metrics := make([][]DDMetric, *NWorkers)
			for i, w := range workers {
				log.Printf("Flushing worker %d at %v", i, t)
				metrics = append(metrics, w.Flush(time.Now()))
			}
			flush(metrics)
		}
	}()

	buf := make([]byte, *BufferSize)

	for {
		n, _, err := ServerConn.ReadFromUDP(buf)
		if err != nil {
			log.Println("Error reading from UDP: ", err)
		}

		// We could maybe free up the Read above by moving
		// this part to a buffered channel?
		m, err := ParseMetric(buf[:n])
		if err != nil {
			log.Println("Error parsing packet: ", err)
			// TODO A metric!
			continue
		}

		// Hash the incoming key so we can consistently choose a worker
		// by modding the last byte
		h := fnv.New32()
		h.Write([]byte(m.Name))
		index := h.Sum32() % uint32(*NWorkers)

		// We're ready to have a worker process this packet, so add it
		// to the work queue. Note that if the queue is full, we'll block
		// here.
		workers[index].WorkChan <- *m
	}
}

// Flush takes the slices of metrics, combines then and marshals them to json
// for posting to Datadog.
func flush(postMetrics [][]DDMetric) {
	totalCount := 0
	var finalMetrics []DDMetric
	// TODO This seems very inefficient
	for _, metrics := range postMetrics {
		totalCount += len(metrics)
		finalMetrics = append(finalMetrics, metrics...)
	}
	// Check to see if we have anything to do
	if totalCount > 0 {
		// Make a metric for how many metrics we metriced (be sure to add one to the total for it!)
		// finalMetrics = append(
		// 	finalMetrics,
		// 	// TODO Is this the right type?
		// 	NewPostMetric("veneur.stats.metrics_posted", float32(totalCount+1), "", "counter", *Interval),
		// )
		postJSON, _ := json.Marshal(map[string][]DDMetric{
			"series": finalMetrics,
		})

		log.Println(string(postJSON))

		resp, err := http.Post(fmt.Sprintf("%s/api/v1/series?api_key=%s", *APIURL, *Key), "application/json", bytes.NewBuffer(postJSON))
		if err != nil {
			log.Printf("Error posting: %q", err)
		}
		defer resp.Body.Close()
		log.Println("Response Status:", resp.Status)
		log.Println("Response Headers:", resp.Header)
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Printf("Error reading response body: %q", err)
		}
		log.Printf("Response: %q", body)
	} else {
		log.Println("Nothing to flush.")
	}
}
<commit_msg>Clean up some warnings.<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"hash/fnv"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"time"
)

var (
	nWorkers      = flag.Int("n", 4, "The number of workers to start")
	interval      = flag.Int("i", 10, "The interval in seconds at which to flush metrics")
	udpAddr       = flag.String("http", ":8125", "Address to listen for UDP requests on")
	key           = flag.String("key", "fart", "Your Datadog API Key")
	expirySeconds = flag.Int("expiry", 300, "The number of seconds metrics will be retained if they stop showing up")
	bufferSize    = flag.Int("buffersize", 4096, "The size of the buffer of work for the worker pool")
	apiURL        = flag.String("apiurl", "https://app.datadoghq.com", "The URL to which Metrics will be posted")
)

/* The WorkQueue is a buffered channel that we can send work to */
// TODO This should likely be configurable or unbuffered?
// var WorkQueue = make(chan PacketRequest, 100)

func main() {
	// Parse the command-line flags.
	flag.Parse()

	// Start the dispatcher.
	log.Println("Starting workers")
	workers := make([]*Worker, *nWorkers)
	for i := 0; i < *nWorkers; i++ {
		worker := NewWorker(i+1, *interval, *expirySeconds)
		worker.Start()
		workers[i] = worker
	}

	// Start the UDP server!
	log.Println("UDP server listening on", *udpAddr)
	serverAddr, err := net.ResolveUDPAddr("udp", ":8125")
	if err != nil {
		log.Println(err.Error())
	}

	ServerConn, err := net.ListenUDP("udp", serverAddr)
	if err != nil {
		log.Println(err.Error())
	}

	defer ServerConn.Close()

	ticker := time.NewTicker(time.Duration(*interval) * time.Second)
	go func() {
		for t := range ticker.C {
			metrics := make([][]DDMetric, *nWorkers)
			for i, w := range workers {
				log.Printf("Flushing worker %d at %v", i, t)
				metrics = append(metrics, w.Flush(time.Now()))
			}
			flush(metrics)
		}
	}()

	buf := make([]byte, *bufferSize)

	for {
		n, _, err := ServerConn.ReadFromUDP(buf)
		if err != nil {
			log.Println("Error reading from UDP: ", err)
		}

		// We could maybe free up the Read above by moving
		// this part to a buffered channel?
		m, err := ParseMetric(buf[:n])
		if err != nil {
			log.Println("Error parsing packet: ", err)
			// TODO A metric!
			continue
		}

		// Hash the incoming key so we can consistently choose a worker
		// by modding the last byte
		h := fnv.New32()
		h.Write([]byte(m.Name))
		index := h.Sum32() % uint32(*nWorkers)

		// We're ready to have a worker process this packet, so add it
		// to the work queue. Note that if the queue is full, we'll block
		// here.
		workers[index].WorkChan <- *m
	}
}

// Flush takes the slices of metrics, combines then and marshals them to json
// for posting to Datadog.
func flush(postMetrics [][]DDMetric) {
	totalCount := 0
	var finalMetrics []DDMetric
	// TODO This seems very inefficient
	for _, metrics := range postMetrics {
		totalCount += len(metrics)
		finalMetrics = append(finalMetrics, metrics...)
	}
	// Check to see if we have anything to do
	if totalCount > 0 {
		// Make a metric for how many metrics we metriced (be sure to add one to the total for it!)
		// finalMetrics = append(
		// 	finalMetrics,
		// 	// TODO Is this the right type?
		// 	NewPostMetric("veneur.stats.metrics_posted", float32(totalCount+1), "", "counter", *Interval),
		// )
		postJSON, _ := json.Marshal(map[string][]DDMetric{
			"series": finalMetrics,
		})

		log.Println(string(postJSON))

		resp, err := http.Post(fmt.Sprintf("%s/api/v1/series?api_key=%s", *apiURL, *Key), "application/json", bytes.NewBuffer(postJSON))
		if err != nil {
			log.Printf("Error posting: %q", err)
		}
		defer resp.Body.Close()
		log.Println("Response Status:", resp.Status)
		log.Println("Response Headers:", resp.Header)
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Printf("Error reading response body: %q", err)
		}
		log.Printf("Response: %q", body)
	} else {
		log.Println("Nothing to flush.")
	}
}
<|endoftext|>