<commit_before>package main

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"encoding/base64"
	"encoding/binary"
	"encoding/csv"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"
)

type algorithm struct {
	name   string
	keylen int
}

type publickey struct {
	alg         algorithm
	fingerprint string
	name        string
}

type unixuser struct {
	name string
	home string
}

type accessSummary struct {
	lastUse time.Time
	lastIP  string
	count   int
}

type access struct {
	user        string
	fingerprint string
	ts          time.Time
	ip          string
}

type tableRow struct {
	user        string
	name        string
	alg         algorithm
	lastUse     time.Time
	count       int
	fingerprint string
}

var logPattern = regexp.MustCompile("^([A-Za-z]+ [ 0-9][0-9] [0-9]+:[0-9]+:[0-9]+) [^ ]* sshd\\[[0-9]+\\]: " +
	"Accepted publickey for (.+) from ([0-9a-f.:]+) port [0-9]+ ssh2: [A-Z0-9\\-]+ ([0-9a-f:]+)$")

func main() {
	csv := flag.Bool("csv", false, "Print table as CSV (RFC 4180) using RFC 3339 for dates")
	flag.Parse()

	table, err := buildKeyTable()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if *csv {
		printCSV(table)
	} else {
		printAlignedTable(table)
	}
}

func printAlignedTable(table []tableRow) {
	now := time.Now()

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
	fmt.Fprintf(w, "USER\tNAME\tTYPE\tUSAGE\tCOUNT\tFINGERPRINT\n")

	for _, row := range table {
		var algStr, lastUseStr, countStr string
		if row.count > 0 {
			lastUseStr = durationAsString(now.Sub(row.lastUse))
			countStr = fmt.Sprintf("%5d", row.count)
		} else {
			lastUseStr = "never"
			countStr = "    -"
		}
		if row.alg.name == "RSA" || row.alg.name == "ECDSA" {
			// RSA and ECDSA can be generated with different key lengths
			algStr = fmt.Sprintf("%s-%d", row.alg.name, row.alg.keylen)
		} else {
			// DSA and ED25519 have fixed key lengths
			algStr = row.alg.name
		}
		fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", row.user, row.name,
			algStr, lastUseStr, countStr, row.fingerprint)
	}

	w.Flush()
}

func printCSV(table []tableRow) {
	w := csv.NewWriter(os.Stdout)
	w.Write([]string{
		"user",
		"name",
		"type",
		"keylen",
		"lastuse",
		"count",
		"fingerprint",
	})

	for _, row := range table {
		lastUseStr := ""
		if row.count > 0 {
			// if the key was never used, the timestamp should not be printed
			lastUseStr = row.lastUse.Format(time.RFC3339)
		}
		w.Write([]string{
			row.user,
			row.name,
			row.alg.name,
			strconv.Itoa(row.alg.keylen),
			lastUseStr,
			strconv.Itoa(row.count),
			row.fingerprint,
		})
	}

	w.Flush()
}

func buildKeyTable() ([]tableRow, error) {
	allkeys, err := getAuthorizedKeysForAllUsers()
	if err != nil {
		return nil, err
	}

	logs, err := parseAllLogFiles()
	if err != nil {
		return nil, err
	}

	// sort users by name
	var usernames []string
	for k := range allkeys {
		usernames = append(usernames, k)
	}
	sort.Strings(usernames)

	var table []tableRow

	for _, user := range usernames {
		for _, key := range allkeys[user] {
			summary := logs[user][key.fingerprint]

			table = append(table, tableRow{
				user:        user,
				name:        key.name,
				alg:         key.alg,
				lastUse:     summary.lastUse,
				count:       summary.count,
				fingerprint: key.fingerprint,
			})
		}
	}

	return table, nil
}

// Parse the given stream and return a list of keys, splitted into
// algorithm, pubkey and name.
// Invalid lines are ignored.
func parseAuthorizedKeys(file *io.Reader) ([]publickey, error) {
	// every public key is in its own line
	var keys []publickey
	scanner := bufio.NewScanner(*file)
	for scanner.Scan() {
		splits := strings.SplitN(scanner.Text(), " ", 3)
		if len(splits) != 3 {
			continue
		}
		keys = append(keys, publickey{
			alg:         parseKeyType(splits[1]),
			fingerprint: computeFingerprint(splits[1]),
			name:        splits[2],
		})
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return keys, nil
}

func computeFingerprint(pubkey string) string {
	pubkeyBytes, err := base64.StdEncoding.DecodeString(pubkey)
	if err != nil {
		return ""
	}

	hasher := md5.New()
	hasher.Write(pubkeyBytes)
	hash := hex.EncodeToString(hasher.Sum(nil))

	var colonhash bytes.Buffer
	for pos, ch := range hash {
		colonhash.WriteRune(ch)
		if pos%2 == 1 && pos < len(hash)-1 {
			colonhash.WriteString(":")
		}
	}
	return colonhash.String()
}

func parseKeyType(pubkey string) algorithm {
	name, partLengths, err := splitPubkey(pubkey)
	if err != nil {
		return algorithm{name: "error"}
	}

	if len(partLengths) == 0 {
		return algorithm{name: "error"}
	}

	switch name {
	case "ssh-rsa":
		keylen := 0
		if len(partLengths) == 3 {
			// This should always be computed
			keylen = 8 * (partLengths[2] - 1)
		}
		return algorithm{
			name:   "RSA",
			keylen: keylen,
		}
	case "ssh-ed25519":
		return algorithm{
			name:   "ED25519",
			keylen: 256,
		}
	case "ssh-dss":
		return algorithm{
			name:   "DSA",
			keylen: 1024,
		}
	case "ecdsa-sha2-nistp521":
		return algorithm{
			name:   "ECDSA",
			keylen: 521,
		}
	case "ecdsa-sha2-nistp384":
		return algorithm{
			name:   "ECDSA",
			keylen: 384,
		}
	case "ecdsa-sha2-nistp256":
		return algorithm{
			name:   "ECDSA",
			keylen: 256,
		}
	}

	return algorithm{name: "unknown"}
}

func splitPubkey(pubkey string) (string, []int, error) {
	pubkeyBytes, err := base64.StdEncoding.DecodeString(pubkey)
	if err != nil {
		return "", nil, err
	}

	buf := bytes.NewReader(pubkeyBytes)
	firstPart := ""
	var partLengths []int
	for {
		var length int32
		err := binary.Read(buf, binary.BigEndian, &length)
		if err != nil {
			break
		}

		data := make([]byte, length)
		n, _ := buf.Read(data)
		if int32(n) != length {
			break
		}

		if len(partLengths) == 0 {
			firstPart = string(data)
		}
		partLengths = append(partLengths, int(length))
	}

	return firstPart, partLengths, nil
}

// Parse a log file written by sshd and return all logs with accepted logins
// using an ssh key
func parseLogFile(path string, accesses map[string]map[string]accessSummary) error {
	info, err := os.Stat(path)
	if err != nil {
		return err
	}
	logFileYear := info.ModTime().Year()
	logFileTimezone := info.ModTime().Location()

	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()

	var scanner *bufio.Scanner
	if strings.HasSuffix(path, ".gz") {
		gr, err := gzip.NewReader(file)
		if err != nil {
			return err
		}
		defer gr.Close()

		scanner = bufio.NewScanner(gr)
	} else {
		scanner = bufio.NewScanner(file)
	}

	for scanner.Scan() {
		line := scanner.Text()
		log, ok := parseLogLine(logFileYear, logFileTimezone, line)
		if !ok {
			continue
		}

		// create map of fingerprints if not yet there
		if accesses[log.user] == nil {
			accesses[log.user] = make(map[string]accessSummary)
		}

		access := accesses[log.user][log.fingerprint]
		if access.lastUse.IsZero() || log.ts.After(access.lastUse) {
			access.lastUse = log.ts
			access.lastIP = log.ip
		}
		access.count++
		accesses[log.user][log.fingerprint] = access
	}
	if err := scanner.Err(); err != nil {
		return err
	}

	return nil
}

func parseLogLine(year int, location *time.Location, line string) (access, bool) {
	if !strings.Contains(line, "Accepted publickey for") {
		return access{}, false
	}
	m := logPattern.FindStringSubmatch(line)
	if len(m) != 5 {
		return access{}, false
	}
	ts, err := time.ParseInLocation("2006 Jan 2 15:04:05", strconv.Itoa(year)+" "+m[1], location)
	if err != nil {
		return access{}, false
	}

	return access{
		user:        m[2],
		ip:          m[3],
		fingerprint: m[4],
		ts:          ts,
	}, true
}

// Read /etc/passwd and return all users and their corresponding home directory
func parseAllUsers(file *io.Reader) ([]unixuser, error) {
	var users []unixuser

	scanner := bufio.NewScanner(*file)
	for scanner.Scan() {
		fs := strings.Split(scanner.Text(), ":")
		if len(fs) != 7 {
			continue
		}
		users = append(users, unixuser{name: fs[0], home: fs[5]})
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return users, nil
}

func getAllUsers() ([]unixuser, error) {
	file, err := os.Open("/etc/passwd")
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var reader io.Reader = file
	return parseAllUsers(&reader)
}

// Opens ~/.ssh/authorized_keys for all users and returns
// every key parsed into algorithm, key and name in a map.
func getAuthorizedKeysForAllUsers() (map[string][]publickey, error) {
	keys := make(map[string][]publickey)

	users, err := getAllUsers()
	if err != nil {
		return nil, err
	}

	for _, user := range users {
		// open ~/.ssh/authorized_keys
		file, err := os.Open(path.Join(user.home, ".ssh", "authorized_keys"))
		if err != nil {
			continue
		}
		defer file.Close()

		var reader io.Reader = file
		userkeys, err := parseAuthorizedKeys(&reader)
		if err != nil {
			continue
		}
		keys[user.name] = userkeys
	}

	return keys, nil
}

func durationAsString(dur time.Duration) string {
	var count int
	var unit string

	if dur.Minutes() < 1 {
		return "just now"
	} else if dur.Hours() < 1 {
		count = int(dur.Minutes())
		unit = "minute"
	} else if dur.Hours() < 24 {
		count = int(dur.Hours())
		unit = "hour"
	} else {
		count = int(dur.Hours() / 24)
		unit = "day"
	}

	if count != 1 {
		// add plural 's'
		unit += "s"
	}

	return fmt.Sprintf("%d %s ago", count, unit)
}

// Returns a list of all files matching /var/log/auth.log*
func getLogFiles() ([]string, error) {
	const logDir = "/var/log"
	files, err := ioutil.ReadDir(logDir)
	if err != nil {
		return nil, err
	}

	logFiles := []string{}
	for _, f := range files {
		if matched, err := filepath.Match("auth.log*", f.Name()); err == nil && matched {
			logFiles = append(logFiles, filepath.Join(logDir, f.Name()))
		}
	}

	return logFiles, nil
}

func parseAllLogFiles() (map[string]map[string]accessSummary, error) {
	allfiles, err := getLogFiles()
	if err != nil {
		return nil, err
	}

	logs := make(map[string]map[string]accessSummary)
	for _, file := range allfiles {
		err := parseLogFile(file, logs)
		if err != nil {
			return nil, err
		}
	}

	return logs, nil
}
<commit_msg>Show last ip in aligned table<commit_after>package main

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"encoding/base64"
	"encoding/binary"
	"encoding/csv"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"
)

type algorithm struct {
	name   string
	keylen int
}

type publickey struct {
	alg         algorithm
	fingerprint string
	name        string
}

type unixuser struct {
	name string
	home string
}

type accessSummary struct {
	lastUse time.Time
	lastIP  string
	count   int
}

type access struct {
	user        string
	fingerprint string
	ts          time.Time
	ip          string
}

type tableRow struct {
	user        string
	name        string
	alg         algorithm
	lastUse     time.Time
	count       int
	fingerprint string
	lastIP      string
}

var logPattern = regexp.MustCompile("^([A-Za-z]+ [ 0-9][0-9] [0-9]+:[0-9]+:[0-9]+) [^ ]* sshd\\[[0-9]+\\]: " +
	"Accepted publickey for (.+) from ([0-9a-f.:]+) port [0-9]+ ssh2: [A-Z0-9\\-]+ ([0-9a-f:]+)$")

func main() {
	csv := flag.Bool("csv", false, "Print table as CSV (RFC 4180) using RFC 3339 for dates")
	enableFingerprint := flag.Bool("fingerprint", false, "Show fingerprint column")
	flag.Parse()

	table, err := buildKeyTable()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if *csv {
		printCSV(table)
	} else {
		printAlignedTable(table, *enableFingerprint)
	}
}

func printAlignedTable(table []tableRow, enableFingerprint bool) {
	now := time.Now()

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
	fmt.Fprintf(w, "USER\tNAME\tTYPE\tLAST USE\tCOUNT\tLAST IP")
	if enableFingerprint {
		fmt.Fprintf(w, "\tFINGERPRINT")
	}
	fmt.Fprintln(w)

	for _, row := range table {
		var algStr, lastUseStr, countStr string
		if row.count > 0 {
			lastUseStr = durationAsString(now.Sub(row.lastUse))
			countStr = fmt.Sprintf("%5d", row.count)
		} else {
			lastUseStr = "never"
			countStr = "    -"
		}
		if row.alg.name == "RSA" || row.alg.name == "ECDSA" {
			// RSA and ECDSA can be generated with different key lengths
			algStr = fmt.Sprintf("%s-%d", row.alg.name, row.alg.keylen)
		} else {
			// DSA and ED25519 have fixed key lengths
			algStr = row.alg.name
		}
		fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s", row.user, row.name,
			algStr, lastUseStr, countStr, row.lastIP)
		if enableFingerprint {
			fmt.Fprintf(w, "\t%s", row.fingerprint)
		}
		fmt.Fprintln(w)
	}

	w.Flush()
}

func printCSV(table []tableRow) {
	w := csv.NewWriter(os.Stdout)
	w.Write([]string{
		"user",
		"name",
		"type",
		"keylen",
		"lastuse",
		"count",
		"lastip",
		"fingerprint",
	})

	for _, row := range table {
		lastUseStr := ""
		if row.count > 0 {
			// if the key was never used, the timestamp should not be printed
			lastUseStr = row.lastUse.Format(time.RFC3339)
		}
		w.Write([]string{
			row.user,
			row.name,
			row.alg.name,
			strconv.Itoa(row.alg.keylen),
			lastUseStr,
			strconv.Itoa(row.count),
			row.lastIP,
			row.fingerprint,
		})
	}

	w.Flush()
}

func buildKeyTable() ([]tableRow, error) {
	allkeys, err := getAuthorizedKeysForAllUsers()
	if err != nil {
		return nil, err
	}

	logs, err := parseAllLogFiles()
	if err != nil {
		return nil, err
	}

	// sort users by name
	var usernames []string
	for k := range allkeys {
		usernames = append(usernames, k)
	}
	sort.Strings(usernames)

	var table []tableRow

	for _, user := range usernames {
		for _, key := range allkeys[user] {
			summary := logs[user][key.fingerprint]

			table = append(table, tableRow{
				user:        user,
				name:        key.name,
				alg:         key.alg,
				lastUse:     summary.lastUse,
				count:       summary.count,
				fingerprint: key.fingerprint,
				lastIP:      summary.lastIP,
			})
		}
	}

	return table, nil
}

// Parse the given stream and return a list of keys, splitted into
// algorithm, pubkey and name.
// Invalid lines are ignored.
func parseAuthorizedKeys(file *io.Reader) ([]publickey, error) {
	// every public key is in its own line
	var keys []publickey
	scanner := bufio.NewScanner(*file)
	for scanner.Scan() {
		splits := strings.SplitN(scanner.Text(), " ", 3)
		if len(splits) != 3 {
			continue
		}
		keys = append(keys, publickey{
			alg:         parseKeyType(splits[1]),
			fingerprint: computeFingerprint(splits[1]),
			name:        splits[2],
		})
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return keys, nil
}

func computeFingerprint(pubkey string) string {
	pubkeyBytes, err := base64.StdEncoding.DecodeString(pubkey)
	if err != nil {
		return ""
	}

	hasher := md5.New()
	hasher.Write(pubkeyBytes)
	hash := hex.EncodeToString(hasher.Sum(nil))

	var colonhash bytes.Buffer
	for pos, ch := range hash {
		colonhash.WriteRune(ch)
		if pos%2 == 1 && pos < len(hash)-1 {
			colonhash.WriteString(":")
		}
	}
	return colonhash.String()
}

func parseKeyType(pubkey string) algorithm {
	name, partLengths, err := splitPubkey(pubkey)
	if err != nil {
		return algorithm{name: "error"}
	}

	if len(partLengths) == 0 {
		return algorithm{name: "error"}
	}

	switch name {
	case "ssh-rsa":
		keylen := 0
		if len(partLengths) == 3 {
			// This should always be computed
			keylen = 8 * (partLengths[2] - 1)
		}
		return algorithm{
			name:   "RSA",
			keylen: keylen,
		}
	case "ssh-ed25519":
		return algorithm{
			name:   "ED25519",
			keylen: 256,
		}
	case "ssh-dss":
		return algorithm{
			name:   "DSA",
			keylen: 1024,
		}
	case "ecdsa-sha2-nistp521":
		return algorithm{
			name:   "ECDSA",
			keylen: 521,
		}
	case "ecdsa-sha2-nistp384":
		return algorithm{
			name:   "ECDSA",
			keylen: 384,
		}
	case "ecdsa-sha2-nistp256":
		return algorithm{
			name:   "ECDSA",
			keylen: 256,
		}
	}

	return algorithm{name: "unknown"}
}

func splitPubkey(pubkey string) (string, []int, error) {
	pubkeyBytes, err := base64.StdEncoding.DecodeString(pubkey)
	if err != nil {
		return "", nil, err
	}

	buf := bytes.NewReader(pubkeyBytes)
	firstPart := ""
	var partLengths []int
	for {
		var length int32
		err := binary.Read(buf, binary.BigEndian, &length)
		if err != nil {
			break
		}

		data := make([]byte, length)
		n, _ := buf.Read(data)
		if int32(n) != length {
			break
		}

		if len(partLengths) == 0 {
			firstPart = string(data)
		}
		partLengths = append(partLengths, int(length))
	}

	return firstPart, partLengths, nil
}

// Parse a log file written by sshd and return all logs with accepted logins
// using an ssh key
func parseLogFile(path string, accesses map[string]map[string]accessSummary) error {
	info, err := os.Stat(path)
	if err != nil {
		return err
	}
	logFileYear := info.ModTime().Year()
	logFileTimezone := info.ModTime().Location()

	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()

	var scanner *bufio.Scanner
	if strings.HasSuffix(path, ".gz") {
		gr, err := gzip.NewReader(file)
		if err != nil {
			return err
		}
		defer gr.Close()

		scanner = bufio.NewScanner(gr)
	} else {
		scanner = bufio.NewScanner(file)
	}

	for scanner.Scan() {
		line := scanner.Text()
		log, ok := parseLogLine(logFileYear, logFileTimezone, line)
		if !ok {
			continue
		}

		// create map of fingerprints if not yet there
		if accesses[log.user] == nil {
			accesses[log.user] = make(map[string]accessSummary)
		}

		access := accesses[log.user][log.fingerprint]
		if access.lastUse.IsZero() || log.ts.After(access.lastUse) {
			access.lastUse = log.ts
			access.lastIP = log.ip
		}
		access.count++
		accesses[log.user][log.fingerprint] = access
	}
	if err := scanner.Err(); err != nil {
		return err
	}

	return nil
}

func parseLogLine(year int, location *time.Location, line string) (access, bool) {
	if !strings.Contains(line, "Accepted publickey for") {
		return access{}, false
	}
	m := logPattern.FindStringSubmatch(line)
	if len(m) != 5 {
		return access{}, false
	}
	ts, err := time.ParseInLocation("2006 Jan 2 15:04:05", strconv.Itoa(year)+" "+m[1], location)
	if err != nil {
		return access{}, false
	}

	return access{
		user:        m[2],
		ip:          m[3],
		fingerprint: m[4],
		ts:          ts,
	}, true
}

// Read /etc/passwd and return all users and their corresponding home directory
func parseAllUsers(file *io.Reader) ([]unixuser, error) {
	var users []unixuser

	scanner := bufio.NewScanner(*file)
	for scanner.Scan() {
		fs := strings.Split(scanner.Text(), ":")
		if len(fs) != 7 {
			continue
		}
		users = append(users, unixuser{name: fs[0], home: fs[5]})
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return users, nil
}

func getAllUsers() ([]unixuser, error) {
	file, err := os.Open("/etc/passwd")
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var reader io.Reader = file
	return parseAllUsers(&reader)
}

// Opens ~/.ssh/authorized_keys for all users and returns
// every key parsed into algorithm, key and name in a map.
func getAuthorizedKeysForAllUsers() (map[string][]publickey, error) {
	keys := make(map[string][]publickey)

	users, err := getAllUsers()
	if err != nil {
		return nil, err
	}

	for _, user := range users {
		// open ~/.ssh/authorized_keys
		file, err := os.Open(path.Join(user.home, ".ssh", "authorized_keys"))
		if err != nil {
			continue
		}
		defer file.Close()

		var reader io.Reader = file
		userkeys, err := parseAuthorizedKeys(&reader)
		if err != nil {
			continue
		}
		keys[user.name] = userkeys
	}

	return keys, nil
}

func durationAsString(dur time.Duration) string {
	var count int
	var unit string

	if dur.Minutes() < 1 {
		return "just now"
	} else if dur.Hours() < 1 {
		count = int(dur.Minutes())
		unit = "minute"
	} else if dur.Hours() < 24 {
		count = int(dur.Hours())
		unit = "hour"
	} else {
		count = int(dur.Hours() / 24)
		unit = "day"
	}

	if count != 1 {
		// add plural 's'
		unit += "s"
	}

	return fmt.Sprintf("%d %s ago", count, unit)
}

// Returns a list of all files matching /var/log/auth.log*
func getLogFiles() ([]string, error) {
	const logDir = "/var/log"
	files, err := ioutil.ReadDir(logDir)
	if err != nil {
		return nil, err
	}

	logFiles := []string{}
	for _, f := range files {
		if matched, err := filepath.Match("auth.log*", f.Name()); err == nil && matched {
			logFiles = append(logFiles, filepath.Join(logDir, f.Name()))
		}
	}

	return logFiles, nil
}

func parseAllLogFiles() (map[string]map[string]accessSummary, error) {
	allfiles, err := getLogFiles()
	if err != nil {
		return nil, err
	}

	logs := make(map[string]map[string]accessSummary)
	for _, file := range allfiles {
		err := parseLogFile(file, logs)
		if err != nil {
			return nil, err
		}
	}

	return logs, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/lair-framework/api-server/client"
	"github.com/lair-framework/go-lair"
	"github.com/lair-framework/go-nessus"
)

const (
	version  = "2.0.0"
	tool     = "nessus"
	osWeight = 75
	usage    = `
Parses a nessus XML file into a lair project.

Usage:
  drone-nessus <id> <filename>
  export LAIR_ID=<id>; drone-nessus <filename>
Options:
  -v              show version and exit
  -h              show usage and exit
  -k              allow insecure SSL connections
  -force-ports    disable data protection in the API server for excessive ports
  -tags           a comma separated list of tags to add to every host that is imported
`
)

type hostMap struct {
	Hosts         map[string]bool
	Vulnerability *lair.Issue
}

func buildProject(nessus *nessus.NessusData, projectID string, tags []string) (*lair.Project, error) {
	cvePattern := regexp.MustCompile(`(CVE-|CAN-)`)
	falseUDPPattern := regexp.MustCompile(`.*\?$`)
	noteID := 1

	project := &lair.Project{}
	project.Tool = tool
	project.ID = projectID

	vulnHostMap := make(map[string]hostMap)
	for _, reportHost := range nessus.Report.ReportHosts {
		tempIP := reportHost.Name
		host := &lair.Host{
			Tags: tags,
		}
		for _, tag := range reportHost.HostProperties.Tags {
			switch {
			case tag.Name == "operating-system":
				os := &lair.OS{
					Tool:        tool,
					Weight:      osWeight,
					Fingerprint: tag.Data,
				}
				host.OS = *os
			case tag.Name == "host-ip":
				host.IPv4 = tag.Data
			case tag.Name == "mac-address":
				host.MAC = tag.Data
			case tag.Name == "host-fqdn":
				host.Hostnames = append(host.Hostnames, tag.Data)
			case tag.Name == "netbios-name":
				host.Hostnames = append(host.Hostnames, tag.Data)
			}
		}

		portsProcessed := make(map[string]lair.Service)
		for _, item := range reportHost.ReportItems {
			pluginID := item.PluginID
			pluginFamily := item.PluginFamily
			severity := item.Severity
			title := item.PluginName
			port := item.Port
			protocol := item.Protocol
			service := item.SvcName
			evidence := item.PluginOutput

			// Check for false positive UDP...ignore it if found.
			if protocol == "udp" && falseUDPPattern.MatchString(service) {
				continue
			}

			portKey := fmt.Sprintf("%d:%s", port, protocol)
			if _, ok := portsProcessed[portKey]; !ok {
				// Haven't seen this port. Create it.
				p := &lair.Service{
					Port:     port,
					Protocol: protocol,
					Service:  service,
				}
				portsProcessed[portKey] = *p
			}

			if evidence != "" && severity >= 1 && pluginFamily != "Port scanners" && pluginFamily != "Service detection" {
				// Format and add evidence
				note := &lair.Note{
					Title:          fmt.Sprintf("%s (ID%d)", title, noteID),
					Content:        "",
					LastModifiedBy: tool,
				}
				e := strings.Trim(evidence, " \t")
				for _, line := range strings.Split(e, "\n") {
					line = strings.Trim(line, " \t")
					if line != "" {
						note.Content += "    " + line + "\n"
					}
				}
				p := portsProcessed[portKey]
				p.Notes = append(p.Notes, *note)
				portsProcessed[portKey] = p
				noteID++
			}

			if pluginID == "19506" {
				command := &lair.Command{
					Tool:    tool,
					Command: item.PluginOutput,
				}
				if project.Commands == nil || len(project.Commands) == 0 {
					project.Commands = append(project.Commands, *command)
				}
				continue
			}

			if _, ok := vulnHostMap[pluginID]; !ok {
				// Vulnerability has not yet been seen for this host. Add it.
				v := &lair.Issue{}

				v.Title = title
				v.Description = item.Description
				v.Solution = item.Solution
				v.Evidence = evidence
				v.IsFlagged = item.ExploitAvailable
				if item.ExploitAvailable {
					exploitDetail := item.ExploitFrameworkMetasploit
					if exploitDetail {
						note := &lair.Note{
							Title:          "Metasploit Exploit",
							Content:        "Exploit exists. Details unknown.",
							LastModifiedBy: tool,
						}
						if item.MetasploitName != "" {
							note.Content = item.MetasploitName
						}
						v.Notes = append(v.Notes, *note)
					}

					exploitDetail = item.ExploitFrameworkCanvas
					if exploitDetail {
						note := &lair.Note{
							Title:          "Canvas Exploit",
							Content:        "Exploit exists. Details unknown.",
							LastModifiedBy: tool,
						}
						if item.CanvasPackage != "" {
							note.Content = item.CanvasPackage
						}
						v.Notes = append(v.Notes, *note)
					}

					exploitDetail = item.ExploitFrameworkCore
					if exploitDetail {
						note := &lair.Note{
							Title:          "Core Impact Exploit",
							Content:        "Exploit exists. Details unknown.",
							LastModifiedBy: tool,
						}
						if item.CoreName != "" {
							note.Content = item.CoreName
						}
						v.Notes = append(v.Notes, *note)
					}
				}

				v.CVSS = item.CVSSBaseScore
				if v.CVSS == 0 && item.RiskFactor != "" && item.RiskFactor != "Low" {
					switch {
					case item.RiskFactor == "Medium":
						v.CVSS = 5.0
					case item.RiskFactor == "High":
						v.CVSS = 7.5
					case item.RiskFactor == "Critical":
						v.CVSS = 10
					}
				}

				if v.CVSS == 0 {
					// Ignore informational findings
					continue
				}

				// Set the CVEs
				for _, cve := range item.CVE {
					c := cvePattern.ReplaceAllString(cve, "")
					v.CVEs = append(v.CVEs, c)
				}

				// Set the plugin and identified by information
				plugin := &lair.PluginID{Tool: tool, ID: pluginID}
				v.PluginIDs = append(v.PluginIDs, *plugin)
				v.IdentifiedBy = append(v.IdentifiedBy, lair.IdentifiedBy{Tool: tool})

				vulnHostMap[pluginID] = hostMap{Hosts: make(map[string]bool), Vulnerability: v}

			}

			if hm, ok := vulnHostMap[pluginID]; ok {
				hostStr := fmt.Sprintf("%s:%d:%s", host.IPv4, port, protocol)
				hm.Hosts[hostStr] = true
			}
		}

		if host.IPv4 == "" {
			host.IPv4 = tempIP
		}

		// Add ports to host and host to project
		for _, p := range portsProcessed {
			host.Services = append(host.Services, p)
		}
		project.Hosts = append(project.Hosts, *host)
	}

	for _, hm := range vulnHostMap {
		for key := range hm.Hosts {
			tokens := strings.Split(key, ":")
			portNum, err := strconv.Atoi(tokens[1])
			if err != nil {
				return nil, err
			}
			hostKey := &lair.IssueHost{
				IPv4:     tokens[0],
				Port:     portNum,
				Protocol: tokens[2],
			}
			hm.Vulnerability.Hosts = append(hm.Vulnerability.Hosts, *hostKey)
		}
		project.Issues = append(project.Issues, *hm.Vulnerability)
	}

	if len(project.Commands) == 0 {
		c := &lair.Command{Tool: tool, Command: "Nessus scan - command unknown"}
		project.Commands = append(project.Commands, *c)
	}

	return project, nil
}

func main() {
	showVersion := flag.Bool("v", false, "")
	insecureSSL := flag.Bool("k", false, "")
	forcePorts := flag.Bool("force-ports", false, "")
	tags := flag.String("tags", "", "")
	flag.Usage = func() {
		fmt.Println(usage)
	}
	flag.Parse()
	if *showVersion {
		log.Println(version)
		os.Exit(0)
	}
	lairURL := os.Getenv("LAIR_API_SERVER")
	if lairURL == "" {
		log.Fatal("Fatal: Missing LAIR_API_SERVER environment variable")
	}
	lairPID := os.Getenv("LAIR_ID")
	var filename string
	switch len(flag.Args()) {
	case 2:
		lairPID = flag.Arg(0)
		filename = flag.Arg(1)
	case 1:
		filename = flag.Arg(0)
	default:
		log.Fatal("Fatal: Missing required argument")
	}

	u, err := url.Parse(lairURL)
	if err != nil {
		log.Fatalf("Fatal: Error parsing LAIR_API_SERVER URL. Error %s", err.Error())
	}
	if u.User == nil {
		log.Fatal("Fatal: Missing username and/or password")
	}
	user := u.User.Username()
	pass, _ := u.User.Password()
	if user == "" || pass == "" {
		log.Fatal("Fatal: Missing username and/or password")
	}
	c, err := client.New(&client.COptions{
		User:               user,
		Password:           pass,
		Host:               u.Host,
		Scheme:             u.Scheme,
		InsecureSkipVerify: *insecureSSL,
	})

	if err != nil {
		log.Fatalf("Fatal: Error setting up client: Error %s", err.Error())
	}

	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatalf("Fatal: Could not open file. Error %s", err.Error())
	}
	nessusData, err := nessus.Parse(buf)
	if err != nil {
		log.Fatalf("Fatal: Error parsing nessus data. Error %s", err.Error())
	}
	hostTags := []string{}
	if *tags != "" {
		hostTags = strings.Split(*tags, ",")
	}
	project, err := buildProject(nessusData, lairPID, hostTags)
	if err != nil {
		log.Fatalf("Fatal: Error building project. Error %s", err.Error())
	}

	res, err := c.ImportProject(&client.DOptions{ForcePorts: *forcePorts}, project)
	if err != nil {
		log.Fatalf("Fatal: Unable to import project. Error %s", err)
	}
	defer res.Body.Close()
	droneRes := &client.Response{}
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}
	if err := json.Unmarshal(body, droneRes); err != nil {
		log.Fatal(err)
	}
	if droneRes.Status == "Error" {
		log.Fatalf("Fatal: Import failed. Error %s", droneRes.Message)
	}
	log.Println("Success: Operation completed successfully")
}
<commit_msg>Updates fatal error messages<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/lair-framework/api-server/client"
	"github.com/lair-framework/go-lair"
	"github.com/lair-framework/go-nessus"
)

const (
	version  = "2.0.0"
	tool     = "nessus"
	osWeight = 75
	usage    = `
Parses a nessus XML file into a lair project.

Usage:
  drone-nessus <id> <filename>
  export LAIR_ID=<id>; drone-nessus <filename>
Options:
  -v              show version and exit
  -h              show usage and exit
  -k              allow insecure SSL connections
  -force-ports    disable data protection in the API server for excessive ports
  -tags           a comma separated list of tags to add to every host that is imported
`
)

type hostMap struct {
	Hosts         map[string]bool
	Vulnerability *lair.Issue
}

func buildProject(nessus *nessus.NessusData, projectID string, tags []string) (*lair.Project, error) {
	cvePattern := regexp.MustCompile(`(CVE-|CAN-)`)
	falseUDPPattern := regexp.MustCompile(`.*\?$`)
	noteID := 1

	project := &lair.Project{}
	project.Tool = tool
	project.ID = projectID

	vulnHostMap := make(map[string]hostMap)
	for _, reportHost := range nessus.Report.ReportHosts {
		tempIP := reportHost.Name
		host := &lair.Host{
			Tags: tags,
		}
		for _, tag := range reportHost.HostProperties.Tags {
			switch {
			case tag.Name == "operating-system":
				os := &lair.OS{
					Tool:        tool,
					Weight:      osWeight,
					Fingerprint: tag.Data,
				}
				host.OS = *os
			case tag.Name == "host-ip":
				host.IPv4 = tag.Data
			case tag.Name == "mac-address":
				host.MAC = tag.Data
			case tag.Name == "host-fqdn":
				host.Hostnames = append(host.Hostnames, tag.Data)
			case tag.Name == "netbios-name":
				host.Hostnames = append(host.Hostnames, tag.Data)
			}
		}

		portsProcessed := make(map[string]lair.Service)
		for _, item := range reportHost.ReportItems {
			pluginID := item.PluginID
			pluginFamily := item.PluginFamily
			severity := item.Severity
			title := item.PluginName
			port := item.Port
			protocol := item.Protocol
			service := item.SvcName
			evidence := item.PluginOutput

			// Check for false positive UDP...ignore it if found.
			if protocol == "udp" && falseUDPPattern.MatchString(service) {
				continue
			}

			portKey := fmt.Sprintf("%d:%s", port, protocol)
			if _, ok := portsProcessed[portKey]; !ok {
				// Haven't seen this port. Create it.
				p := &lair.Service{
					Port:     port,
					Protocol: protocol,
					Service:  service,
				}
				portsProcessed[portKey] = *p
			}

			if evidence != "" && severity >= 1 && pluginFamily != "Port scanners" && pluginFamily != "Service detection" {
				// Format and add evidence
				note := &lair.Note{
					Title:          fmt.Sprintf("%s (ID%d)", title, noteID),
					Content:        "",
					LastModifiedBy: tool,
				}
				e := strings.Trim(evidence, " \t")
				for _, line := range strings.Split(e, "\n") {
					line = strings.Trim(line, " \t")
					if line != "" {
						note.Content += "    " + line + "\n"
					}
				}
				p := portsProcessed[portKey]
				p.Notes = append(p.Notes, *note)
				portsProcessed[portKey] = p
				noteID++
			}

			if pluginID == "19506" {
				command := &lair.Command{
					Tool:    tool,
					Command: item.PluginOutput,
				}
				if project.Commands == nil || len(project.Commands) == 0 {
					project.Commands = append(project.Commands, *command)
				}
				continue
			}

			if _, ok := vulnHostMap[pluginID]; !ok {
				// Vulnerability has not yet been seen for this host. Add it.
				v := &lair.Issue{}

				v.Title = title
				v.Description = item.Description
				v.Solution = item.Solution
				v.Evidence = evidence
				v.IsFlagged = item.ExploitAvailable
				if item.ExploitAvailable {
					exploitDetail := item.ExploitFrameworkMetasploit
					if exploitDetail {
						note := &lair.Note{
							Title:          "Metasploit Exploit",
							Content:        "Exploit exists. Details unknown.",
							LastModifiedBy: tool,
						}
						if item.MetasploitName != "" {
							note.Content = item.MetasploitName
						}
						v.Notes = append(v.Notes, *note)
					}

					exploitDetail = item.ExploitFrameworkCanvas
					if exploitDetail {
						note := &lair.Note{
							Title:          "Canvas Exploit",
							Content:        "Exploit exists. Details unknown.",
							LastModifiedBy: tool,
						}
						if item.CanvasPackage != "" {
							note.Content = item.CanvasPackage
						}
						v.Notes = append(v.Notes, *note)
					}

					exploitDetail = item.ExploitFrameworkCore
					if exploitDetail {
						note := &lair.Note{
							Title:          "Core Impact Exploit",
							Content:        "Exploit exists. Details unknown.",
							LastModifiedBy: tool,
						}
						if item.CoreName != "" {
							note.Content = item.CoreName
						}
						v.Notes = append(v.Notes, *note)
					}
				}

				v.CVSS = item.CVSSBaseScore
				if v.CVSS == 0 && item.RiskFactor != "" && item.RiskFactor != "Low" {
					switch {
					case item.RiskFactor == "Medium":
						v.CVSS = 5.0
					case item.RiskFactor == "High":
						v.CVSS = 7.5
					case item.RiskFactor == "Critical":
						v.CVSS = 10
					}
				}

				if v.CVSS == 0 {
					// Ignore informational findings
					continue
				}

				// Set the CVEs
				for _, cve := range item.CVE {
					c := cvePattern.ReplaceAllString(cve, "")
					v.CVEs = append(v.CVEs, c)
				}

				// Set the plugin and identified by information
				plugin := &lair.PluginID{Tool: tool, ID: pluginID}
				v.PluginIDs = append(v.PluginIDs, *plugin)
				v.IdentifiedBy = append(v.IdentifiedBy, lair.IdentifiedBy{Tool: tool})

				vulnHostMap[pluginID] = hostMap{Hosts: make(map[string]bool), Vulnerability: v}

			}

			if hm, ok := vulnHostMap[pluginID]; ok {
				hostStr := fmt.Sprintf("%s:%d:%s", host.IPv4, port, protocol)
				hm.Hosts[hostStr] = true
			}
		}

		if host.IPv4 == "" {
			host.IPv4 = tempIP
		}

		// Add ports to host and host to project
		for _, p := range portsProcessed {
			host.Services = append(host.Services, p)
		}
		project.Hosts = append(project.Hosts, *host)
	}

	for _, hm := range vulnHostMap {
		for key := range hm.Hosts {
			tokens := strings.Split(key, ":")
			portNum, err := strconv.Atoi(tokens[1])
			if err != nil {
				return nil, err
			}
			hostKey := &lair.IssueHost{
				IPv4:     tokens[0],
				Port:     portNum,
				Protocol: tokens[2],
			}
			hm.Vulnerability.Hosts = append(hm.Vulnerability.Hosts, *hostKey)
		}
		project.Issues = append(project.Issues, *hm.Vulnerability)
	}

	if len(project.Commands) == 0 {
		c := &lair.Command{Tool: tool, Command: "Nessus scan - command unknown"}
		project.Commands = append(project.Commands, *c)
	}

	return project, nil
}

func main() {
	showVersion := flag.Bool("v", false, "")
	insecureSSL := flag.Bool("k", false, "")
	forcePorts := flag.Bool("force-ports", false, "")
	tags := flag.String("tags", "", "")
	flag.Usage = func() {
		fmt.Println(usage)
	}
	flag.Parse()
	if *showVersion {
		log.Println(version)
		os.Exit(0)
	}
	lairURL := os.Getenv("LAIR_API_SERVER")
	if lairURL == "" {
		log.Fatal("Fatal: Missing LAIR_API_SERVER environment variable")
	}
	lairPID := os.Getenv("LAIR_ID")
	var filename string
	switch len(flag.Args()) {
	case 2:
		lairPID = flag.Arg(0)
		filename = flag.Arg(1)
	case 1:
		filename = flag.Arg(0)
	default:
		log.Fatal("Fatal: Missing required argument")
	}

	u, err := url.Parse(lairURL)
	if err != nil {
		log.Fatalf("Fatal: Error parsing LAIR_API_SERVER URL. Error %s", err.Error())
	}
	if u.User == nil {
		log.Fatal("Fatal: Missing username and/or password")
	}
	user := u.User.Username()
	pass, _ := u.User.Password()
	if user == "" || pass == "" {
		log.Fatal("Fatal: Missing username and/or password")
	}
	c, err := client.New(&client.COptions{
		User:               user,
		Password:           pass,
		Host:               u.Host,
		Scheme:             u.Scheme,
		InsecureSkipVerify: *insecureSSL,
	})

	if err != nil {
		log.Fatalf("Fatal: Error setting up client: Error %s", err.Error())
	}

	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatalf("Fatal: Could not open file. Error %s", err.Error())
	}
	nessusData, err := nessus.Parse(buf)
	if err != nil {
		log.Fatalf("Fatal: Error parsing nessus data. Error %s", err.Error())
	}
	hostTags := []string{}
	if *tags != "" {
		hostTags = strings.Split(*tags, ",")
	}
	project, err := buildProject(nessusData, lairPID, hostTags)
	if err != nil {
		log.Fatalf("Fatal: Error building project. Error %s", err.Error())
	}

	res, err := c.ImportProject(&client.DOptions{ForcePorts: *forcePorts}, project)
	if err != nil {
		log.Fatalf("Fatal: Unable to import project. Error %s", err)
	}
	defer res.Body.Close()
	droneRes := &client.Response{}
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatalf("Fatal: Error %s", err.Error())
	}
	if err := json.Unmarshal(body, droneRes); err != nil {
		log.Fatalf("Fatal: Could not unmarshal JSON. Error %s", err.Error())
	}
	if droneRes.Status == "Error" {
		log.Fatalf("Fatal: Import failed. Error %s", droneRes.Message)
	}
	log.Println("Success: Operation completed successfully")
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/spf13/cobra"
	"github.com/yungsang/talk2docker/commands"
)

func main() {
	var appName = "Talk2Docker"

	var app = &cobra.Command{
		Use:   "talk2docker",
		Short: "A simple Docker client to talk to Docker daemon",
		Long:  appName + " - A simple Docker client to talk to Docker daemon",
	}
	app.PersistentFlags().String(
		"config", "$HOME/.talk2docker/config", "Path to the configuration file")
	app.PersistentFlags().String(
		"host", "", "Hostname to use its config (runtime only)")

	// ps command
	var cmdPs = &cobra.Command{
		Use:   "ps",
		Short: "List containers",
		Long:  appName + " ps - List containers",
		Run:   commands.Ps,
	}
	cmdPs.Flags().BoolP(
		"all", "a", false, "Show all containers. Only running containers are shown by default.")
	cmdPs.Flags().BoolP(
		"latest", "l", false, "Show only the latest created container, include non-running ones.")
	cmdPs.Flags().BoolP(
		"quiet", "q", false, "Only display numeric IDs")
	cmdPs.Flags().BoolP(
		"size", "s", false, "Display sizes")
	cmdPs.Flags().BoolP(
		"no-header", "n", false, "Omit the header")
	app.AddCommand(cmdPs)

	// images command
	var cmdImages = &cobra.Command{
		Use:     "ls [NAME[:TAG]]",
		Aliases: []string{"images"},
		Short:   "List images",
		Long:    appName + " ls - List images",
		Run:     commands.ListImages,
	}
	cmdImages.Flags().BoolP(
		"all", "a", false, "Show all images. Only named/taged and leaf images are shown by default.")
	cmdImages.Flags().BoolP(
		"quiet", "q", false, "Only display numeric IDs")
	cmdImages.Flags().BoolP(
		"no-header", "n", false, "Omit the header")
	app.AddCommand(cmdImages)

	// image command
	var cmdImage = &cobra.Command{
		Use:   "image [command]",
		Short: "Manage images",
		Long:  appName + " image - Manage images",
		Run: func(ctx *cobra.Command, args []string) {
			ctx.Usage()
		},
	}

	var cmdListImages = &cobra.Command{
		Use:     "list [NAME[:TAG]]",
		Aliases: []string{"ls"},
		Short:   "List images",
		Long:    appName + " image list - List images",
		Run:     commands.ListImages,
	}
	cmdListImages.Flags().BoolP(
		"all", "a", false, "Show all images. Only named/taged and leaf images are shown by default.")
	cmdListImages.Flags().BoolP(
		"quiet", "q", false, "Only display numeric IDs")
	cmdListImages.Flags().BoolP(
		"no-header", "n", false, "Omit the header")
	cmdImage.AddCommand(cmdListImages)

	app.AddCommand(cmdImage)

	// host command
	var cmdHost = &cobra.Command{
		Use:   "host [command]",
		Short: "Manage hosts",
		Long:  appName + " host - Manage hosts",
		Run: func(ctx *cobra.Command, args []string) {
			ctx.Usage()
		},
	}

	var cmdListHosts = &cobra.Command{
		Use:     "list",
		Aliases: []string{"ls"},
		Short:   "List hosts",
		Long:    appName + " host list - List hosts",
		Run:     commands.ListHosts,
	}
	cmdListHosts.Flags().BoolP(
		"quiet", "q", false, "Only display numeric IDs")
	cmdListHosts.Flags().BoolP(
		"no-header", "n", false, "Omit the header")
	cmdHost.AddCommand(cmdListHosts)

	var cmdSwitchHost = &cobra.Command{
		Use:     "switch <NAME>",
		Aliases: []string{"sw"},
		Short:   "Switch the default host",
		Long:    appName + " host switch - Switch the default host",
		Run:     commands.SwitchHost,
	}
	cmdHost.AddCommand(cmdSwitchHost)

	var cmdAddHost = &cobra.Command{
		Use:   "add <NAME> <URL> [DESCRIPTION]",
		Short: "Add a new host into the config file",
		Long:  appName + " host add - Add a new host into the config",
		Run:   commands.AddHost,
	}
	cmdHost.AddCommand(cmdAddHost)

	var cmdRmHost = &cobra.Command{
		Use:   "rm <NAME>",
		Short: "Rmove a host from the config file",
		Long:  appName + " host rm - Rmove a host from the config file",
		Run:   commands.RmHost,
	}
	cmdHost.AddCommand(cmdRmHost)

	var cmdEditHost = &cobra.Command{
		Use:     "edit <NAME>",
		Aliases: []string{"ed"},
		Short:   "Edit the config file",
		Long:    appName + " host edit - Edit the config file",
		Run:     commands.EditHosts,
	}
	cmdHost.AddCommand(cmdEditHost)

	app.AddCommand(cmdHost)

	// hosts command
	var cmdHosts = &cobra.Command{
		Use:   "hosts",
		Short: "Shortcut to list hosts",
		Long:  appName + " hosts - List hosts",
		Run:   commands.ListHosts,
	}
	cmdHosts.Flags().BoolP(
		"quiet", "q", false, "Only display numeric IDs")
	cmdHosts.Flags().BoolP(
		"no-header", "n", false, "Omit the header")
	app.AddCommand(cmdHosts)

	// version command
	var cmdVersion = &cobra.Command{
		Use:   "version",
		Short: "Show the version information",
		Long:  appName + " version - Show the version information",
		Run:   commands.Version,
	}
	app.AddCommand(cmdVersion)

	app.Execute()
}
<commit_msg>Add more aliases<commit_after>package main

import (
	"github.com/spf13/cobra"
	"github.com/yungsang/talk2docker/commands"
)

func main() {
	var appName = "Talk2Docker"

	var app = &cobra.Command{
		Use:   "talk2docker",
		Short: "A simple Docker client to talk to Docker daemon",
		Long:  appName + " - A simple Docker client to talk to Docker daemon",
	}
	app.PersistentFlags().String(
		"config", "$HOME/.talk2docker/config", "Path to the configuration file")
	app.PersistentFlags().String(
		"host", "", "Hostname to use its config (runtime only)")

	// ps command
	var cmdPs = &cobra.Command{
		Use:   "ps",
		Short: "List containers",
		Long:  appName + " ps - List containers",
		Run:   commands.Ps,
	}
	cmdPs.Flags().BoolP(
		"all", "a", false, "Show all containers. Only running containers are shown by default.")
	cmdPs.Flags().BoolP(
		"latest", "l", false, "Show only the latest created container, include non-running ones.")
	cmdPs.Flags().BoolP(
		"quiet", "q", false, "Only display numeric IDs")
	cmdPs.Flags().BoolP(
		"size", "s", false, "Display sizes")
	cmdPs.Flags().BoolP(
		"no-header", "n", false, "Omit the header")
	app.AddCommand(cmdPs)

	// images command
	var cmdImages = &cobra.Command{
		Use:     "ls [NAME[:TAG]]",
		Aliases: []string{"images"},
		Short:   "List images",
		Long:    appName + " ls - List images",
		Run:     commands.ListImages,
	}
	cmdImages.Flags().BoolP(
		"all", "a", false, "Show all images. Only named/taged and leaf images are shown by default.")
	cmdImages.Flags().BoolP(
		"quiet", "q", false, "Only display numeric IDs")
	cmdImages.Flags().BoolP(
		"no-header", "n", false, "Omit the header")
	app.AddCommand(cmdImages)

	// image command
	var cmdImage = &cobra.Command{
		Use:     "image [command]",
		Aliases: []string{"img"},
		Short:   "Manage images",
		Long:    appName + " image - Manage images",
		Run: func(ctx *cobra.Command, args []string) {
			ctx.Usage()
		},
	}

	var cmdListImages = &cobra.Command{
		Use:     "list [NAME[:TAG]]",
		Aliases: []string{"ls"},
		Short:   "List images",
		Long:    appName + " image list - List images",
		Run:     commands.ListImages,
	}
	cmdListImages.Flags().BoolP(
		"all", "a", false, "Show all images. Only named/taged and leaf images are shown by default.")
	cmdListImages.Flags().BoolP(
		"quiet", "q", false, "Only display numeric IDs")
	cmdListImages.Flags().BoolP(
		"no-header", "n", false, "Omit the header")
	cmdImage.AddCommand(cmdListImages)

	app.AddCommand(cmdImage)

	// host command
	var cmdHost = &cobra.Command{
		Use:   "host [command]",
		Short: "Manage hosts",
		Long:  appName + " host - Manage hosts",
		Run: func(ctx *cobra.Command, args []string) {
			ctx.Usage()
		},
	}

	var cmdListHosts = &cobra.Command{
		Use:     "list",
		Aliases: []string{"ls"},
		Short:   "List hosts",
		Long:    appName + " host list - List hosts",
		Run:     commands.ListHosts,
	}
	cmdListHosts.Flags().BoolP(
		"quiet", "q", false, "Only display numeric IDs")
	cmdListHosts.Flags().BoolP(
		"no-header", "n", false, "Omit the header")
	cmdHost.AddCommand(cmdListHosts)

	var cmdSwitchHost = &cobra.Command{
		Use:     "switch <NAME>",
		Aliases: []string{"sw"},
		Short:   "Switch the default host",
		Long:    appName + " host switch - Switch the default host",
		Run:     commands.SwitchHost,
	}
	cmdHost.AddCommand(cmdSwitchHost)

	var cmdAddHost = &cobra.Command{
		Use:   "add <NAME> <URL> [DESCRIPTION]",
		Short: "Add a new host into the config file",
		Long:  appName + " host add - Add a new host into the config",
		Run:   commands.AddHost,
	}
	cmdHost.AddCommand(cmdAddHost)

	var cmdRmHost = &cobra.Command{
		Use:   "rm <NAME>",
		Short: "Rmove a host from the config file",
		Long:  appName + " host rm - Rmove a host from the config file",
		Run:   commands.RmHost,
	}
	cmdHost.AddCommand(cmdRmHost)

	var cmdEditHost = &cobra.Command{
		Use:     "edit <NAME>",
		Aliases: []string{"ed"},
		Short:   "Edit the config file",
		Long:    appName + " host edit - Edit the config file",
		Run:     commands.EditHosts,
	}
	cmdHost.AddCommand(cmdEditHost)

	app.AddCommand(cmdHost)

	// hosts command
	var cmdHosts = &cobra.Command{
		Use:   "hosts",
		Short: "Shortcut to list hosts",
		Long:  appName + " hosts - List hosts",
		Run:   commands.ListHosts,
	}
	cmdHosts.Flags().BoolP(
		"quiet", "q", false, "Only display numeric IDs")
	cmdHosts.Flags().BoolP(
		"no-header", "n", false, "Omit the header")
	app.AddCommand(cmdHosts)

	// version command
	var cmdVersion = &cobra.Command{
		Use:     "version",
		Aliases: []string{"v"},
		Short:   "Show the version information",
		Long:    appName + " version - Show the version information",
		Run:     commands.Version,
	}
	app.AddCommand(cmdVersion)

	app.Execute()
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"time"

	irukaLogger "github.com/bottlenose-inc/go-iruka/logger" // go-iruka bunyan-style logger package
	"github.com/bottlenose-inc/go-iruka/metrics"            // go-iruka Prometheus metrics package
	"github.com/codegangsta/cli"                            // CLI helper
	"github.com/prometheus/client_golang/prometheus"        // Prometheus client library
)

var (
	scrapeInterval       int
	nsqdUrl              string
	allTopics            []string
	logger               *irukaLogger.Logger
	depthGaugeVec        *prometheus.GaugeVec
	inFlightGaugeVec     *prometheus.GaugeVec
	backendDepthGaugeVec *prometheus.GaugeVec
	timeoutGaugeVec      *prometheus.GaugeVec
	requeueGaugeVec      *prometheus.GaugeVec
	deferredGaugeVec     *prometheus.GaugeVec
	messageCountGaugeVec *prometheus.GaugeVec
	clientCountGaugeVec  *prometheus.GaugeVec
	channelCountGaugeVec *prometheus.GaugeVec
	buildInfoMetric      *prometheus.GaugeVec
)

func main() {
	app := cli.NewApp()
	app.Version = "0.1.0"
	app.Name = "nsqd-prometheus-exporter"
	app.Usage = "Scrapes nsqd stats and serves them up as Prometheus metrics"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "nsqdUrl, n",
			Value:  "http://localhost:4151",
			Usage:  "URL of nsqd to export stats from",
			EnvVar: "NSQD_URL",
		},
		cli.StringFlag{
			Name:   "listenPort, lp",
			Value:  "30000",
			Usage:  "Port on which prometheus will expose metrics",
			EnvVar: "LISTEN_PORT",
		},
		cli.StringFlag{
			Name:   "scrapeInterval, s",
			Value:  "30",
			Usage:  "How often (in seconds) nsqd stats should be scraped",
			EnvVar: "SCRAPE_INTERVAL",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:  "run",
			Usage: "./nsqd-prometheus-exporter [options] run",
			Action: func(c *cli.Context) {
				// Initialize logger
				var err error
				logger, err = irukaLogger.NewLogger(app.Name + " v" + app.Version)
				if err != nil {
					log.Fatal("Unable to initialize go-iruka logger, exiting: " + err.Error())
				}

				// Set and validate configuration
				nsqdUrl = c.GlobalString("nsqdUrl")
				if nsqdUrl == "" {
					logger.Warning("Invalid nsqd URL set, continuing with default (http://localhost:4151)")
					nsqdUrl = "http://localhost:4151"
				}
				scrapeInterval = c.GlobalInt("scrapeInterval")
				if scrapeInterval < 1 {
					logger.Warning("Invalid scrape interval set, continuing with default (30s)")
					scrapeInterval = 30
				}

				// Initialize Prometheus metrics
				var emptyMap map[string]string
				labels := []string{"type", "topic", "paused", "channel"}
				buildInfoMetric, _ = metrics.CreateGaugeVector("nsqd_prometheus_exporter_build_info", "", "",
					"nsqd-prometheus-exporter build info", emptyMap, []string{"version"})
				buildInfoMetric.WithLabelValues(app.Version).Set(1)
				// # HELP nsq_depth Queue depth
				// # TYPE nsq_depth gauge
				depthGaugeVec, _ = metrics.CreateGaugeVector("nsq_depth", "", "", "Queue depth", emptyMap, labels)
				// # HELP nsq_backend_depth Queue backend depth
				// # TYPE nsq_backend_depth gauge
				backendDepthGaugeVec, _ = metrics.CreateGaugeVector("nsq_backend_depth", "", "", "Queue backend depth", emptyMap, labels)
				// # HELP nsq_in_flight_count In flight count
				// # TYPE nsq_in_flight_count gauge
				inFlightGaugeVec, _ = metrics.CreateGaugeVector("nsq_in_flight_count", "", "", "In flight count", emptyMap, labels)
				// # HELP nsq_timeout_count Timeout count
				// # TYPE nsq_timeout_count gauge
				timeoutGaugeVec, _ = metrics.CreateGaugeVector("nsq_timeout_count", "", "", "Timeout count", emptyMap, labels)
				// # HELP nsq_requeue_count Requeue Count
				// # TYPE nsq_requeue_count gauge
				requeueGaugeVec, _ = metrics.CreateGaugeVector("nsq_requeue_count", "", "", "Requeue Count", emptyMap, labels)
				// # HELP nsq_deferred_count Deferred count
				// # TYPE nsq_deferred_count gauge
				deferredGaugeVec, _ = metrics.CreateGaugeVector("nsq_deferred_count", "", "", "Deferred count", emptyMap, labels)
				// # HELP nsq_message_count Queue message count
				// # TYPE nsq_message_count gauge
				messageCountGaugeVec, _ = metrics.CreateGaugeVector("nsq_message_count", "", "", "Queue message count", emptyMap, labels)
				// # HELP nsq_client_count Number of clients
				// # TYPE nsq_client_count gauge
				clientCountGaugeVec, _ = metrics.CreateGaugeVector("nsq_client_count", "", "", "Number of clients", emptyMap, labels)
				// # HELP nsq_channel_count Number of channels
				// # TYPE nsq_channel_count gauge
				channelCountGaugeVec, _ = metrics.CreateGaugeVector("nsq_channel_count", "", "", "Number of channels", emptyMap, []string{"type", "topic", "paused"})

				go fetchAndSetStats()

				err = metrics.StartPrometheusMetricsServer(app.Name, logger, c.GlobalInt("listenPort"))
				if err != nil {
					os.Exit(1)
				}
			},
		},
	}

	app.Run(os.Args)
}

// fetchAndSetStats scrapes stats from nsqd and updates all the Prometheus metrics on the provided interval.
func fetchAndSetStats() {
	for {
		// Fetch stats
		stats, err := getNsqdStats(nsqdUrl)
		if err != nil {
			logger.Fatal("Error scraping stats from nsqd: " + err.Error())
			os.Exit(1)
		}

		// Exit if any "dead" topics are detected
		for _, topicName := range allTopics {
			found := false
			for _, topic := range stats.Topics {
				if topicName == topic.Name {
					found = true
					break
				}
			}
			if !found {
				logger.Fatal("At least one old topic no longer included in nsqd stats - exiting")
				os.Exit(0)
			}
		}

		// Loop through topics and set metrics
		allTopics = nil // Rebuild list of all topics
		for _, topic := range stats.Topics {
			allTopics = append(allTopics, topic.Name)
			paused := "false"
			if topic.Paused {
				paused = "true"
			}
			depthGaugeVec.WithLabelValues("topic", topic.Name, paused, "").Set(float64(topic.Depth))
			backendDepthGaugeVec.WithLabelValues("topic", topic.Name, paused, "").Set(float64(topic.BackendDepth))
			channelCountGaugeVec.WithLabelValues("topic", topic.Name, paused).Set(float64(len(topic.Channels)))

			// Loop through a topic's channels and set metrics
			for _, channel := range topic.Channels {
				paused = "false"
				if channel.Paused {
					paused = "true"
				}
				depthGaugeVec.WithLabelValues("channel", channel.Name, paused, channel.Name).Set(float64(channel.Depth))
				backendDepthGaugeVec.WithLabelValues("channel", channel.Name, paused, channel.Name).Set(float64(channel.BackendDepth))
				inFlightGaugeVec.WithLabelValues("channel", channel.Name, paused, channel.Name).Set(float64(channel.InFlightCount))
				timeoutGaugeVec.WithLabelValues("channel", channel.Name, paused, channel.Name).Set(float64(channel.TimeoutCount))
				requeueGaugeVec.WithLabelValues("channel", channel.Name, paused, channel.Name).Set(float64(channel.RequeueCount))
				deferredGaugeVec.WithLabelValues("channel", channel.Name, paused, channel.Name).Set(float64(channel.DeferredCount))
				messageCountGaugeVec.WithLabelValues("channel", channel.Name, paused, channel.Name).Set(float64(channel.MessageCount))
				clientCountGaugeVec.WithLabelValues("channel", channel.Name, paused, channel.Name).Set(float64(len(channel.Clients)))
			}
		}

		// Scrape every scrapeInterval
		time.Sleep(time.Duration(scrapeInterval) * time.Second)
	}
}
<commit_msg>report topic name correctly for channel metrics<commit_after>package main

import (
	"log"
	"os"
	"time"

	irukaLogger "github.com/bottlenose-inc/go-iruka/logger" // go-iruka bunyan-style logger package
	"github.com/bottlenose-inc/go-iruka/metrics"            // go-iruka Prometheus metrics package
	"github.com/codegangsta/cli"                            // CLI helper
	"github.com/prometheus/client_golang/prometheus"        // Prometheus client library
)

var (
	scrapeInterval       int
	nsqdUrl              string
	allTopics            []string
	logger               *irukaLogger.Logger
	depthGaugeVec        *prometheus.GaugeVec
	inFlightGaugeVec     *prometheus.GaugeVec
	backendDepthGaugeVec *prometheus.GaugeVec
	timeoutGaugeVec      *prometheus.GaugeVec
	requeueGaugeVec      *prometheus.GaugeVec
	deferredGaugeVec     *prometheus.GaugeVec
	messageCountGaugeVec *prometheus.GaugeVec
	clientCountGaugeVec  *prometheus.GaugeVec
	channelCountGaugeVec *prometheus.GaugeVec
	buildInfoMetric      *prometheus.GaugeVec
)

func main() {
	app := cli.NewApp()
	app.Version = "0.1.0"
	app.Name = "nsqd-prometheus-exporter"
	app.Usage = "Scrapes nsqd stats and serves them up as Prometheus metrics"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "nsqdUrl, n",
			Value:  "http://localhost:4151",
			Usage:  "URL of nsqd to export stats from",
			EnvVar: "NSQD_URL",
		},
		cli.StringFlag{
			Name:   "listenPort, lp",
			Value:  "30000",
			Usage:  "Port on which prometheus will expose metrics",
			EnvVar: "LISTEN_PORT",
		},
		cli.StringFlag{
			Name:   "scrapeInterval, s",
			Value:  "30",
			Usage:  "How often (in seconds) nsqd stats should be scraped",
			EnvVar: "SCRAPE_INTERVAL",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:  "run",
			Usage: "./nsqd-prometheus-exporter [options] run",
			Action: func(c *cli.Context) {
				// Initialize logger
				var err error
				logger, err = irukaLogger.NewLogger(app.Name + " v" + app.Version)
				if err != nil {
					log.Fatal("Unable to initialize go-iruka logger, exiting: " + err.Error())
				}

				// Set and validate configuration
				nsqdUrl = c.GlobalString("nsqdUrl")
				if nsqdUrl == "" {
					logger.Warning("Invalid nsqd URL set, continuing with default (http://localhost:4151)")
					nsqdUrl = "http://localhost:4151"
				}
				scrapeInterval = c.GlobalInt("scrapeInterval")
				if scrapeInterval < 1 {
					logger.Warning("Invalid scrape interval set, continuing with default (30s)")
					scrapeInterval = 30
				}

				// Initialize Prometheus metrics
				var emptyMap map[string]string
				labels := []string{"type", "topic", "paused", "channel"}
				buildInfoMetric, _ = metrics.CreateGaugeVector("nsqd_prometheus_exporter_build_info", "", "",
					"nsqd-prometheus-exporter build info", emptyMap, []string{"version"})
				buildInfoMetric.WithLabelValues(app.Version).Set(1)
				// # HELP nsq_depth Queue depth
				// # TYPE nsq_depth gauge
				depthGaugeVec, _ = metrics.CreateGaugeVector("nsq_depth", "", "", "Queue depth", emptyMap, labels)
				// # HELP nsq_backend_depth Queue backend depth
				// # TYPE nsq_backend_depth gauge
				backendDepthGaugeVec, _ = metrics.CreateGaugeVector("nsq_backend_depth", "", "", "Queue backend depth", emptyMap, labels)
				// # HELP nsq_in_flight_count In flight count
				// # TYPE nsq_in_flight_count gauge
				inFlightGaugeVec, _ = metrics.CreateGaugeVector("nsq_in_flight_count", "", "", "In flight count", emptyMap, labels)
				// # HELP nsq_timeout_count Timeout count
				// # TYPE nsq_timeout_count gauge
				timeoutGaugeVec, _ = metrics.CreateGaugeVector("nsq_timeout_count", "", "", "Timeout count", emptyMap, labels)
				// # HELP nsq_requeue_count Requeue Count
				// # TYPE nsq_requeue_count gauge
				requeueGaugeVec, _ = metrics.CreateGaugeVector("nsq_requeue_count", "", "", "Requeue Count", emptyMap, labels)
				// # HELP nsq_deferred_count Deferred count
				// # TYPE nsq_deferred_count gauge
				deferredGaugeVec, _ = metrics.CreateGaugeVector("nsq_deferred_count", "", "", "Deferred count", emptyMap, labels)
				// # HELP nsq_message_count Queue message count
				// # TYPE nsq_message_count gauge
				messageCountGaugeVec, _ = metrics.CreateGaugeVector("nsq_message_count", "", "", "Queue message count", emptyMap, labels)
				// # HELP nsq_client_count Number of clients
				// # TYPE nsq_client_count gauge
				clientCountGaugeVec, _ = metrics.CreateGaugeVector("nsq_client_count", "", "", "Number of clients", emptyMap, labels)
				// # HELP nsq_channel_count Number of channels
				// # TYPE nsq_channel_count gauge
				channelCountGaugeVec, _ = metrics.CreateGaugeVector("nsq_channel_count", "", "", "Number of channels", emptyMap, []string{"type", "topic", "paused"})

				go fetchAndSetStats()

				err = metrics.StartPrometheusMetricsServer(app.Name, logger, c.GlobalInt("listenPort"))
				if err != nil {
					os.Exit(1)
				}
			},
		},
	}

	app.Run(os.Args)
}

// fetchAndSetStats scrapes stats from nsqd and updates all the Prometheus metrics on the provided interval.
func fetchAndSetStats() {
	for {
		// Fetch stats
		stats, err := getNsqdStats(nsqdUrl)
		if err != nil {
			logger.Fatal("Error scraping stats from nsqd: " + err.Error())
			os.Exit(1)
		}

		// Exit if any "dead" topics are detected
		for _, topicName := range allTopics {
			found := false
			for _, topic := range stats.Topics {
				if topicName == topic.Name {
					found = true
					break
				}
			}
			if !found {
				logger.Fatal("At least one old topic no longer included in nsqd stats - exiting")
				os.Exit(0)
			}
		}

		// Loop through topics and set metrics
		allTopics = nil // Rebuild list of all topics
		for _, topic := range stats.Topics {
			allTopics = append(allTopics, topic.Name)
			paused := "false"
			if topic.Paused {
				paused = "true"
			}
			depthGaugeVec.WithLabelValues("topic", topic.Name, paused, "").Set(float64(topic.Depth))
			backendDepthGaugeVec.WithLabelValues("topic", topic.Name, paused, "").Set(float64(topic.BackendDepth))
			channelCountGaugeVec.WithLabelValues("topic", topic.Name, paused).Set(float64(len(topic.Channels)))

			// Loop through a topic's channels and set metrics
			for _, channel := range topic.Channels {
				paused = "false"
				if channel.Paused {
					paused = "true"
				}
				depthGaugeVec.WithLabelValues("channel", topic.Name, paused, channel.Name).Set(float64(channel.Depth))
				backendDepthGaugeVec.WithLabelValues("channel", topic.Name, paused, channel.Name).Set(float64(channel.BackendDepth))
				inFlightGaugeVec.WithLabelValues("channel", topic.Name, paused, channel.Name).Set(float64(channel.InFlightCount))
				timeoutGaugeVec.WithLabelValues("channel", topic.Name, paused, channel.Name).Set(float64(channel.TimeoutCount))
				requeueGaugeVec.WithLabelValues("channel", topic.Name, paused, channel.Name).Set(float64(channel.RequeueCount))
				deferredGaugeVec.WithLabelValues("channel", topic.Name, paused, channel.Name).Set(float64(channel.DeferredCount))
				messageCountGaugeVec.WithLabelValues("channel", topic.Name, paused, channel.Name).Set(float64(channel.MessageCount))
				clientCountGaugeVec.WithLabelValues("channel", topic.Name, paused, channel.Name).Set(float64(len(channel.Clients)))
			}
		}

		// Scrape every scrapeInterval
		time.Sleep(time.Duration(scrapeInterval) * time.Second)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"os/signal"

	"github.com/tinzenite/core"
)

const path = "/home/tamino/Music"

func main() {

	channel, err := core.CreateChannel("TestMe", nil)
	if err != nil {
		panic(err)
	}
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	address, _ := channel.Address()
	log.Println("ID:\n" + address)
	select {
	case <-c:
		channel.Close()
	}
}
<commit_msg>test stuff<commit_after>package main

import (
	"log"
	"os"
	"os/signal"

	"github.com/tinzenite/core"
)

const path = "/home/tamino/Music"

func main() {

	if !test() {
		return
	}

	channel, err := core.CreateChannel("TestMe", nil)
	if err != nil {
		panic(err)
	}
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	address, _ := channel.Address()
	log.Println("ID:\n" + address)
	select {
	case <-c:
		channel.Close()
	}
}

func test() bool {
	_, err := core.CreateTinzenite(path, false)
	log.Println(err.Error())
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"
)

func main() {
	stat, err := os.Stdin.Stat()
	if err != nil {
		log.Fatal(err)
	}
	if stat.Mode()&os.ModeCharDevice != 0 {
		log.Fatal("please pipe in some data")
	}

	s := bufio.NewScanner(os.Stdin)
	for s.Scan() {
		t := s.Text()
		line := strings.TrimSpace(s.Text())
		if strings.HasPrefix(line, "nnf:") {
			printNotNilFatal(t)
		} else if strings.HasPrefix(line, "lpf(") {
			printLogPrintf(t)
		} else if strings.HasPrefix(line, "fpf(") {
			fPrintf(t)
		} else if strings.HasPrefix(line, "fpl(") {
			fPrintln(t)
		} else if strings.HasPrefix(line, "lpl(") {
			lPrintln(t)
		} else if line == "gomain" {
			goMain()
		} else if strings.Contains(t, "(t.t)") {
			fmt.Println(strings.Replace(t, "(t.t)", "(t *testing.T)", 1))
		} else {
			fmt.Println(t)
		}
	}
}

func printNotNilFatal(line string) {
	l := len(line) - len(strings.TrimLeft(line, " "))
	pad := strings.Repeat(" ", l)
	msg := strings.SplitN(line, ":", 2)[1]

	// print error block
	fmt.Printf("%sif err != nil{\n", pad)
	fmt.Printf("%s%slog.Fatal(%q, err)\n", pad, pad, msg+": ")
	fmt.Printf("%s}\n", pad)
}

func printLogPrintf(line string) {
	fmt.Println(strings.Replace(line, "lpf(", "log.Printf(", 1))
}

func fPrintf(line string) {
	fmt.Println(strings.Replace(line, "fpf(", "fmt.Printf(", 1))
}

func lPrintln(line string) {
	fmt.Println(strings.Replace(line, "lpl(", "log.Println(", 1))
}

func fPrintln(line string) {
	fmt.Println(strings.Replace(line, "fpl(", "fmt.Println(", 1))
}

func goMain() {
	fmt.Println(`package main

import (
    "fmt"
)

func main() {
    fmt.Println("gopher")
}
`)
}
<commit_msg>added shebangs<commit_after>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"
)

func main() {
	stat, err := os.Stdin.Stat()
	if err != nil {
		log.Fatal(err)
	}
	if stat.Mode()&os.ModeCharDevice != 0 {
		log.Fatal("please pipe in some data")
	}

	s := bufio.NewScanner(os.Stdin)
	for s.Scan() {
		t := s.Text()
		line := strings.TrimSpace(s.Text())
		if strings.HasPrefix(line, "nnf:") {
			printNotNilFatal(t)
		} else if strings.HasPrefix(line, "lpf(") {
			printLogPrintf(t)
		} else if strings.HasPrefix(line, "fpf(") {
			fPrintf(t)
		} else if strings.HasPrefix(line, "fpl(") {
			fPrintln(t)
		} else if strings.HasPrefix(line, "lpl(") {
			lPrintln(t)
		} else if line == "gomain" {
			goMain()
		} else if line == "ubb" {
			fmt.Println("#!/usr/bin/env bash")
		} else if line == "ubp" {
			fmt.Println("#!/usr/bin/env python")
		} else if strings.Contains(t, "(t.t)") {
			fmt.Println(strings.Replace(t, "(t.t)", "(t *testing.T)", 1))
		} else {
			fmt.Println(t)
		}
	}
}

func printNotNilFatal(line string) {
	l := len(line) - len(strings.TrimLeft(line, " "))
	pad := strings.Repeat(" ", l)
	msg := strings.SplitN(line, ":", 2)[1]

	// print error block
	fmt.Printf("%sif err != nil{\n", pad)
	fmt.Printf("%s%slog.Fatal(%q, err)\n", pad, pad, msg+": ")
	fmt.Printf("%s}\n", pad)
}

func printLogPrintf(line string) {
	fmt.Println(strings.Replace(line, "lpf(", "log.Printf(", 1))
}

func fPrintf(line string) {
	fmt.Println(strings.Replace(line, "fpf(", "fmt.Printf(", 1))
}

func lPrintln(line string) {
	fmt.Println(strings.Replace(line, "lpl(", "log.Println(", 1))
}

func fPrintln(line string) {
	fmt.Println(strings.Replace(line, "fpl(", "fmt.Println(", 1))
}

func goMain() {
	fmt.Println(`package main

import (
    "fmt"
)

func main() {
    fmt.Println("gopher")
}
`)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"os/exec"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

type config struct {
	owner  string
	repo   string
	sshUrl string
	token  string
}

func checkArg(arg string, msg string) {
	if arg == "" {
		fmt.Println(msg)
		os.Exit(-1)
	}
}

func parseFlags() *config {
	cfg := new(config)

	flag.StringVar(&cfg.owner, "owner", "", "GitHub owner")
	flag.StringVar(&cfg.repo, "repo", "", "GitHub repo")
	flag.StringVar(&cfg.token, "token", "", "GitHub token")

	flag.Parse()

	checkArg(cfg.owner, "Missing GitHub owner")
	checkArg(cfg.repo, "Missing Github repo")
	checkArg(cfg.token, "Missing GitHub token")

	cfg.sshUrl = fmt.Sprintf("git@github.com:%v/%v.git", cfg.owner, cfg.repo)

	return cfg
}

func newClient(ctx context.Context, cfg *config) *github.Client {
	sts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: cfg.token})
	return github.NewClient(oauth2.NewClient(ctx, sts))
}

func execCommand(name string, arg ...string) {
	cmd := exec.Command(name, arg...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

func main() {
	cfg := parseFlags()
	ctx := context.Background()
	client := newClient(ctx, cfg)

	forks, _, err := client.Repositories.ListForks(ctx, cfg.owner, cfg.repo, nil)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	execCommand("git", []string{"clone", cfg.sshUrl}...)
	execCommand("git", []string{"-C", cfg.repo, "remote", "rename", "origin", "upstream"}...)

	for _, fork := range forks {
		execCommand("git", []string{"-C", cfg.repo, "remote", "add", *fork.Owner.Login, *fork.SSHURL}...)
	}
}
<commit_msg>Extract listForks<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"os/exec"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

type config struct {
	owner  string
	repo   string
	sshUrl string
	token  string
}

func checkArg(arg string, msg string) {
	if arg == "" {
		fmt.Println(msg)
		os.Exit(-1)
	}
}

func parseFlags() *config {
	cfg := new(config)

	flag.StringVar(&cfg.owner, "owner", "", "GitHub owner")
	flag.StringVar(&cfg.repo, "repo", "", "GitHub repo")
	flag.StringVar(&cfg.token, "token", "", "GitHub token")

	flag.Parse()

	checkArg(cfg.owner, "Missing GitHub owner")
	checkArg(cfg.repo, "Missing Github repo")
	checkArg(cfg.token, "Missing GitHub token")

	cfg.sshUrl = fmt.Sprintf("git@github.com:%v/%v.git", cfg.owner, cfg.repo)

	return cfg
}

func newClient(ctx context.Context, cfg *config) *github.Client {
	sts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: cfg.token})
	return github.NewClient(oauth2.NewClient(ctx, sts))
}

func listForks(ctx context.Context, cfg *config, client *github.Client) []*github.Repository {
	forks, _, err := client.Repositories.ListForks(ctx, cfg.owner, cfg.repo, nil)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
	return forks
}

func execCommand(name string, arg ...string) {
	cmd := exec.Command(name, arg...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

func main() {
	cfg := parseFlags()
	ctx := context.Background()
	client := newClient(ctx, cfg)
	forks := listForks(ctx, cfg, client)

	execCommand("git", []string{"clone", cfg.sshUrl}...)
	execCommand("git", []string{"-C", cfg.repo, "remote", "rename", "origin", "upstream"}...)

	for _, fork := range forks {
		execCommand("git", []string{"-C", cfg.repo, "remote", "add", *fork.Owner.Login, *fork.SSHURL}...)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"pault.ag/go/config"
)

func Missing(paths ...string) bool {
	for _, path := range paths {
		if _, err := os.Stat(path); os.IsNotExist(err) {
			return true
		}
	}
	return false
}

type MiniCA struct {
	KeySize      int    `flag:"key-size"       description:"Key Size"`
	CommonName   string `flag:"common-name"    description:"Common Name of the Cert"`
	Org          string `flag:"conf.Org"       description:"Organization of the Cert"`
	Type         string `flag:"type"           description:"Cert Type (client or server)"`
	CaCommonName string `flag:"ca-common-name" description:"Common Name of the CA Cert"`
	CaCert       string `flag:"ca-cert"        description:"Path to the CA Cert"`
	CaKey        string `flag:"ca-key"         description:"Path to the CA Key"`
	CaKeySize    int    `flag:"ca-key-size"    description:"CA Key Size"`
}

func main() {
	conf := MiniCA{
		Org:       "Example Organization",
		KeySize:   2048,
		CaCert:    "cacert.crt",
		CaKey:     "cakey.key",
		CaKeySize: 2048,
	}
	flags, err := config.LoadFlags("minica", &conf)
	if err != nil {
		panic(err)
	}

	flags.Parse(os.Args[1:])

	if conf.CommonName == "" {
		flags.Usage()
		return
	}

	if Missing(conf.CaCert, conf.CaKey) {
		if err := GenerateCACertificate(
			conf.CaCert, conf.CaKey,
			conf.Org, conf.CaCommonName,
			conf.CaKeySize,
		); err != nil {
			panic(err)
		}
	}

	isClientCert := false
	switch conf.Type {
	case "client":
		isClientCert = true
	case "server":
		isClientCert = false
	default:
		panic(fmt.Errorf("Unknown type"))
	}

	cn := conf.CommonName
	newCrt := fmt.Sprintf("%s.crt", cn)
	newKey := fmt.Sprintf("%s.key", cn)

	fmt.Printf(`Creating a %s cert:

Common Name: %s
Org:         %s
Cert Flavor: %s
Output crt:  %s
Output key:  %s
`, conf.Type, conf.CommonName, conf.Org, conf.Type, newCrt, newKey)

	if err := GenerateCert(
		[]string{cn},
		newCrt, newKey, conf.CaCert, conf.CaKey,
		conf.Org, cn,
		conf.KeySize,
		isClientCert,
	); err != nil {
		panic(err)
	}
}
<commit_msg>update some crap<commit_after>package main

import (
	"fmt"
	"os"

	"pault.ag/go/config"
)

func Missing(paths ...string) bool {
	for _, path := range paths {
		if _, err := os.Stat(path); os.IsNotExist(err) {
			return true
		}
	}
	return false
}

type MiniCA struct {
	KeySize      int    `flag:"key-size"       description:"Key Size"`
	CommonName   string `flag:"common-name"    description:"Common Name of the Cert"`
	Org          string `flag:"conf.Org"       description:"Organization of the Cert"`
	Type         string `flag:"type"           description:"Cert Type (client or server)"`
	CaCommonName string `flag:"ca-common-name" description:"Common Name of the CA Cert"`
	CaCert       string `flag:"ca-cert"        description:"Path to the CA Cert"`
	CaKey        string `flag:"ca-key"         description:"Path to the CA Key"`
	CaKeySize    int    `flag:"ca-key-size"    description:"CA Key Size"`
}

func main() {
	conf := MiniCA{
		Org:       "Example Organization",
		KeySize:   2048,
		CaCert:    "cacert.crt",
		CaKey:     "cakey.key",
		CaKeySize: 2048,
	}
	flags, err := config.LoadFlags("minica", &conf)
	if err != nil {
		panic(err)
	}

	flags.Parse(os.Args[1:])

	if conf.CommonName == "" {
		flags.Usage()
		return
	}

	if Missing(conf.CaCert, conf.CaKey) {
		if err := GenerateCACertificate(
			conf.CaCert, conf.CaKey,
			conf.Org, conf.CaCommonName,
			conf.CaKeySize,
		); err != nil {
			panic(err)
		}
	}

	isClientCert := false
	switch conf.Type {
	case "client":
		isClientCert = true
	case "server":
		isClientCert = false
	default:
		fmt.Printf(`Unknown cert type!

Currently supported types:

	client
	server

`)
		flags.Usage()
		os.Exit(2)
	}

	cn := conf.CommonName
	newCrt := fmt.Sprintf("%s.crt", cn)
	newKey := fmt.Sprintf("%s.key", cn)

	fmt.Printf(`Creating a %s cert:

Common Name: %s
Org:         %s
Cert Flavor: %s
Output crt:  %s
Output key:  %s
`, conf.Type, conf.CommonName, conf.Org, conf.Type, newCrt, newKey)

	if err := GenerateCert(
		[]string{cn},
		newCrt, newKey, conf.CaCert, conf.CaKey,
		conf.Org, cn,
		conf.KeySize,
		isClientCert,
	); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/VojtechVitek/go-trello"
	"github.com/fatih/color"
	"github.com/mitchellh/go-homedir"
)

type Config struct {
	AppKey   string `toml:"app_key"`
	Token    string `toml:"token"`
	Username string `toml:"username"`
}

func main() {
	appKey := os.Getenv("TRELLO_APP_KEY")
	token := os.Getenv("TRELLO_TOKEN")
	username := os.Getenv("TRELLO_USER")

	if appKey == "" || token == "" || username == "" {
		home, err := homedir.Dir()
		if err != nil {
			log.Fatal(err)
		}

		var config Config
		_, err = toml.DecodeFile(home+"/.trelloclirc", &config)
		if err != nil {
			log.Fatal(err)
		}

		appKey = config.AppKey
		token = config.Token
		username = config.Username
	}

	trello, err := trello.NewAuthClient(appKey, &token)
	if err != nil {
		log.Fatal(err)
	}

	user, err := trello.Member(username)
	if err != nil {
		log.Fatal(err)
	}

	boards, err := user.Boards()
	if err != nil {
		log.Fatal(err)
	}

	if len(boards) > 0 {
		board := boards[0]
		fmt.Printf("* %v (%v)\n", board.Name, board.ShortUrl)

		lists, err := board.Lists()
		if err != nil {
			log.Fatal(err)
		}

		for _, list := range lists {
			cards, _ := list.Cards()
			if len(cards) > 0 {
				fmt.Println("  *", list.Name)

				for _, card := range cards {
					for _, label := range card.Labels {
						if label.Color == "red" {
							color.Set(color.FgRed)
						} else if label.Color == "yellow" {
							color.Set(color.FgYellow)
						} else if label.Color == "green" {
							color.Set(color.FgGreen)
						} else if label.Color == "purple" {
							color.Set(color.FgMagenta)
						}
					}

					if card.Due != "" {
						t, _ := time.Parse(time.RFC3339Nano, card.Due)
						fmt.Printf("    * %s %s\n", t.Format("[2006-01-02]"), card.Name)
					} else {
						fmt.Printf("    * %s\n", card.Name)
					}

					color.Unset()
				}
			}
		}
	}
}
<commit_msg>Add setColor method<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/VojtechVitek/go-trello"
	"github.com/fatih/color"
	"github.com/mitchellh/go-homedir"
)

type Config struct {
	AppKey   string `toml:"app_key"`
	Token    string `toml:"token"`
	Username string `toml:"username"`
}

func setColor(c string) {
	if c == "red" {
		color.Set(color.FgRed)
	} else if c == "yellow" {
		color.Set(color.FgYellow)
	} else if c == "green" {
		color.Set(color.FgGreen)
	} else if c == "purple" {
		color.Set(color.FgMagenta)
	}
}

func main() {
	appKey := os.Getenv("TRELLO_APP_KEY")
	token := os.Getenv("TRELLO_TOKEN")
	username := os.Getenv("TRELLO_USER")

	if appKey == "" || token == "" || username == "" {
		home, err := homedir.Dir()
		if err != nil {
			log.Fatal(err)
		}

		var config Config
		_, err = toml.DecodeFile(home+"/.trelloclirc", &config)
		if err != nil {
			log.Fatal(err)
		}

		appKey = config.AppKey
		token = config.Token
		username = config.Username
	}

	trello, err := trello.NewAuthClient(appKey, &token)
	if err != nil {
		log.Fatal(err)
	}

	user, err := trello.Member(username)
	if err != nil {
		log.Fatal(err)
	}

	boards, err := user.Boards()
	if err != nil {
		log.Fatal(err)
	}

	if len(boards) > 0 {
		board := boards[0]
		fmt.Printf("* %v (%v)\n", board.Name, board.ShortUrl)

		lists, err := board.Lists()
		if err != nil {
			log.Fatal(err)
		}

		for _, list := range lists {
			cards, _ := list.Cards()
			if len(cards) > 0 {
				fmt.Println("  *", list.Name)

				for _, card := range cards {
					for _, label := range card.Labels {
						setColor(label.Color)
						break
					}

					if card.Due != "" {
						t, _ := time.Parse(time.RFC3339Nano, card.Due)
						fmt.Printf("    * %s %s\n", t.Format("[2006-01-02]"), card.Name)
					} else {
						fmt.Printf("    * %s\n", card.Name)
					}

					color.Unset()
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>// Main entry point for Rancher.
//
// Notes on code in Main: some of the code in runMain is copied from the copy-
// right holder, Mitchell Hashimoto (github.com/mitchellh), as I am using his
// cli package.
package main

import (
	"fmt"
	"os"
	"runtime"

	"github.com/mohae/cli"
	"github.com/mohae/rancher/app"
	jww "github.com/spf13/jwalterweatherman"
)

func main() {
	cpus := runtime.NumCPU()
	if cpus > 1 {
		runtime.GOMAXPROCS(runtime.NumCPU() - 1)
	}
	os.Exit(realMain())
}

func realMain() int {
	// runMain parses the Flag for glog, sets up CLI stuff for the supported
	// subcommands and runs Rancher.
	err := app.SetEnv()
	if err != nil {
		fmt.Printf("An error while processing Rancher Environment variables: %s\n", err)
		return -1
	}
	// Logging setup
	ranchr.SetLogging()
	args := os.Args[1:]
	cli := &cli.CLI{
		Name:     "rancher",
		Version:  Version,
		Args:     args,
		Commands: Commands,
		HelpFunc: cli.BasicHelpFunc("rancher"),
	}
	exitCode, err := cli.Run()
	if err != nil {
		jww.ERROR.Printf("Rancher encountered an error: %s\n", err)

	}
	return exitCode
}
<commit_msg>alias app as app to remove the 'imported and not used: "github.com/mohae/rancher/app" as ranch error', which shouldn't be happening<commit_after>// Main entry point for Rancher.
//
// Notes on code in Main: some of the code in runMain is copied from the copy-
// right holder, Mitchell Hashimoto (github.com/mitchellh), as I am using his
// cli package.
package main

import (
	"fmt"
	"os"
	"runtime"

	"github.com/mohae/cli"
	app "github.com/mohae/rancher/app"
	jww "github.com/spf13/jwalterweatherman"
)

func main() {
	cpus := runtime.NumCPU()
	if cpus > 1 {
		runtime.GOMAXPROCS(runtime.NumCPU() - 1)
	}
	os.Exit(realMain())
}

func realMain() int {
	// runMain parses the Flag for glog, sets up CLI stuff for the supported
	// subcommands and runs Rancher.
	err := app.SetEnv()
	if err != nil {
		fmt.Printf("An error while processing Rancher Environment variables: %s\n", err)
		return -1
	}
	// Logging setup
	app.SetLogging()
	args := os.Args[1:]
	cli := &cli.CLI{
		Name:     "rancher",
		Version:  Version,
		Args:     args,
		Commands: Commands,
		HelpFunc: cli.BasicHelpFunc("rancher"),
	}
	exitCode, err := cli.Run()
	if err != nil {
		jww.ERROR.Printf("Rancher encountered an error: %s\n", err)

	}
	return exitCode
}
<|endoftext|>


<commit_before>package main

import "os"
import "fmt"
import "mpris2"

func main() {
	if len(os.Args) != 2 {
		fmt.Printf(os.Args[0] + " [play | pause | playpause]")
	} else {
		switch os.Args[1] {
		case "playpause":

		default:
			fmt.Printf("%s is currently unsupported\n", os.Args[1])
		}
	}
}
<commit_msg>update<commit_after><|endoftext|>


<commit_before>// Copyright 2016 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

// sci is a shameful CI.
//
// It is a simple Github webhook that runs a Go build and an hardcoded
// command upon PR or push from whitelisted users.
//
// It posts the stdout to a Github gist and updates the PR status.
//
// It doesn't stream data so it cannot be used for slow task.
package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

type config struct {
	Port              int
	WebHookSecret     string   // https://developer.github.com/webhooks/
	Oauth2AccessToken string   // https://github.com/settings/tokens, check "repo:status" and "gist"
	UseSSH            bool     // Use ssh (instead of https) for checkout. Required for private repositories.
	Owners            []string // Allowed users to run tests on.
	Branches          []string // Branches that tests will be run on.
	Name              string   // Display name to use in the status report on Github.
	Check             []string // Command to run to test the repository. It is run from the repository's root.
}

func loadConfig() (*config, error) {
	c := &config{
		Port:              8080,
		WebHookSecret:     "Create a secret and set it at github.com/'name'/'repo'/settings/hooks",
		Oauth2AccessToken: "Get one at https://github.com/settings/tokens",
		UseSSH:            false,
		Owners:            []string{"joe"},
		Branches:          []string{"refs/heads/master"},
		Name:              "sci",
		Check:             []string{"go", "test"},
	}
	b, err := ioutil.ReadFile("sci.json")
	if err != nil {
		// Write a default file and exit.
		b, err = json.MarshalIndent(c, "", "  ")
		if err != nil {
			return nil, err
		}
		if err := ioutil.WriteFile("sci.json", b, 0600); err != nil {
			return nil, err
		}
		return nil, errors.New("wrote new sci.json")
	}
	if err := json.Unmarshal(b, c); err != nil {
		return nil, err
	}
	d, err := json.MarshalIndent(c, "", "  ")
	if err != nil {
		return nil, err
	}
	// Update the file in canonical format.
	if !bytes.Equal(b, d) {
		log.Printf("Updating sci.json in canonical format")
		if err := ioutil.WriteFile("sci.json", b, 0600); err != nil {
			return nil, err
		}
	}
	return c, nil
}

func isInList(i string, list []string) bool {
	// Brute force is fine for short list.
	for _, s := range list {
		if i == s {
			return true
		}
	}
	return false
}

func run(cwd string, cmd ...string) (string, bool) {
	cmds := strings.Join(cmd, " ")
	log.Printf("- cwd=%s : %s", cwd, cmds)
	c := exec.Command(cmd[0], cmd[1:]...)
	c.Dir = cwd
	start := time.Now()
	out, err := c.CombinedOutput()
	duration := time.Since(start)
	// Assumes UTF-8.
	return fmt.Sprintf("$ %s\nin %s\n%s", cmds, duration, string(out)), err == nil
}

// runCheck syncs then runs the check.
//
// It runs the check in a temporary GOPATH at the specified commit.
func runCheck(cmd []string, repoName string, useSSH bool, commit, gopath string) (string, string, bool) {
	metadata := fmt.Sprintf("Commit: %s\nVersion: %s\nGOROOT: %s\nGOPATH: %s\nCPUs: %d\n---\n",
		commit, runtime.Version(), runtime.GOROOT(), gopath, runtime.NumCPU())
	repoPath := "github.com/" + repoName
	base := filepath.Join(gopath, "src", repoPath)
	if _, err := os.Stat(base); err != nil {
		up := path.Dir(base)
		if err := os.MkdirAll(up, 0700); err != nil && !os.IsExist(err) {
			log.Printf("- %v", err)
		}
		url := "https://" + repoPath
		if useSSH {
			url = "git@github.com:" + repoName
		}
		out1, ok := run(up, "git", "clone", "--quiet", url)
		metadata += out1
		if !ok {
			return metadata, "", ok
		}
	} else {
		out1, ok := run(base, "git", "fetch", "--prune", "--quiet")
		metadata += out1
		if !ok {
			return metadata, "", ok
		}
	}
	out1, ok := run(base, "git", "checkout", "--quiet", commit)
	metadata += out1
	if !ok {
		return metadata, "", ok
	}
	// TODO(maruel): update dependencies manually!
	out1, ok = run(gopath, "go", "get", "-v", "-d", "-t", repoPath)
	metadata += out1
	if !ok {
		return metadata, "", ok
	}
	out, ok := run(base, cmd...)
	return metadata, out, ok
}

type server struct {
	c      *config
	client *github.Client
	gopath string
	mu     sync.Mutex
}

func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Printf("HTTP: %s %s", r.RemoteAddr, r.URL.Path)
	defer r.Body.Close()
	if r.Method != "POST" {
		http.Error(w, "Invalid method", http.StatusMethodNotAllowed)
		log.Printf("- invalid method")
		return
	}
	payload, err := github.ValidatePayload(r, []byte(s.c.WebHookSecret))
	if err != nil {
		http.Error(w, "Invalid secret", http.StatusUnauthorized)
		log.Printf("- invalid secret")
		return
	}
	event, err := github.ParseWebHook(github.WebHookType(r), payload)
	if err != nil {
		http.Error(w, "Invalid payload", http.StatusBadRequest)
		log.Printf("- invalid payload")
		return
	}
	switch event := event.(type) {
	case *github.PullRequestEvent:
		log.Printf("- PR #%d %s %s", *event.PullRequest.ID, *event.Sender.Login, *event.Action)
		if *event.Action != "opened" && *event.Action != "synchronized" {
			log.Printf("- ignoring action %q for PR from %q", *event.Action, *event.Sender.Login)
			io.WriteString(w, "{}")
			return
		}
		if !isInList(*event.Sender.Login, s.c.Owners) {
			log.Printf("- ignoring owner %q for PR", *event.Sender.Login)
			io.WriteString(w, "{}")
			return
		}
		if err := s.runCheck(*event.Repo.FullName, *event.PullRequest.Head.SHA); err != nil {
			log.Printf("- %v")
		}
	case *github.PushEvent:
		log.Printf("- Push %s %s", *event.Ref, *event.HeadCommit.ID)
		if !isInList(*event.Ref, s.c.Branches) {
			log.Printf("- ignoring branch %q for push", *event.Ref)
			io.WriteString(w, "{}")
			return
		}
		if err := s.runCheck(*event.Repo.FullName, *event.HeadCommit.ID); err != nil {
			log.Printf("- %v")
		}
	default:
		log.Printf("- ignoring hook type %s", reflect.TypeOf(event).Elem().Name())
	}
	io.WriteString(w, "{}")
}

func (s *server) runCheck(repo, commit string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	log.Printf("- Running test for %s at %s", repo, commit)
	metadata, out, success := runCheck(s.c.Check, repo, s.c.UseSSH, commit, s.gopath)

	// https://developer.github.com/v3/gists/#create-a-gist
	gist := &github.Gist{
		Description: github.String("output for https://github.com/" + repo + "/commit/" + commit),
		// It is still accessible via the URL;
		Public: github.Bool(false),
		Files: map[github.GistFilename]github.GistFile{
			"metadata": github.GistFile{Content: &metadata},
			"stdout":   github.GistFile{Content: &out},
		},
	}
	var err error
	if gist, _, err = s.client.Gists.Create(gist); err != nil {
		return err
	}
	log.Printf("- Gist at %s", *gist.HTMLURL)

	// https://developer.github.com/v3/repos/statuses/#create-a-status
	status := &github.RepoStatus{
		State:       github.String("success"),
		TargetURL:   gist.HTMLURL,
		Description: github.String("ran test"),
		Context:     github.String("sci"),
	}
	if !success {
		status.State = github.String("failure")
	}
	parts := strings.SplitN(repo, "/", 2)
	_, _, err = s.client.Repositories.CreateStatus(parts[0], parts[1], commit, status)
	return err
}

func mainImpl() error {
	test := flag.String("test", "", "runs a simulation locally, specify the git repository name (not URL) to test, e.g. 'maruel/sci'")
	flag.Parse()
	c, err := loadConfig()
	if err != nil {
		return err
	}
	wd, err := os.Getwd()
	if err != nil {
		return err
	}
	gopath := filepath.Join(wd, "sci-gopath")
	os.Setenv("GOPATH", gopath)
	if len(*test) != 0 {
		metadata, out, success := runCheck(c.Check, *test, c.UseSSH, "HEAD", gopath)
		_, err := fmt.Printf("%s---\n%sSuccess: %t\n", metadata, out, success)
		return err
	}

	tc := oauth2.NewClient(oauth2.NoContext, oauth2.StaticTokenSource(&oauth2.Token{AccessToken: c.Oauth2AccessToken}))
	s := server{c: c, client: github.NewClient(tc), gopath: gopath}
	http.Handle("/", &s)
	log.Printf("Running in %s", wd)
	log.Printf("Listening on %d", c.Port)
	return http.ListenAndServe(fmt.Sprintf(":%d", c.Port), nil)
}

func main() {
	if err := mainImpl(); err != nil {
		fmt.Fprintf(os.Stderr, "sci: %s.\n", err)
		os.Exit(1)
	}
}
<commit_msg>Fix updating in canonical format for real<commit_after>// Copyright 2016 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

// sci is a shameful CI.
//
// It is a simple Github webhook that runs a Go build and an hardcoded
// command upon PR or push from whitelisted users.
//
// It posts the stdout to a Github gist and updates the PR status.
//
// It doesn't stream data so it cannot be used for slow task.
package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

type config struct {
	Port              int
	WebHookSecret     string   // https://developer.github.com/webhooks/
	Oauth2AccessToken string   // https://github.com/settings/tokens, check "repo:status" and "gist"
	UseSSH            bool     // Use ssh (instead of https) for checkout. Required for private repositories.
	Owners            []string // Allowed users to run tests on.
	Branches          []string // Branches that tests will be run on.
	Name              string   // Display name to use in the status report on Github.
	Check             []string // Command to run to test the repository. It is run from the repository's root.
}

func loadConfig() (*config, error) {
	c := &config{
		Port:              8080,
		WebHookSecret:     "Create a secret and set it at github.com/'name'/'repo'/settings/hooks",
		Oauth2AccessToken: "Get one at https://github.com/settings/tokens",
		UseSSH:            false,
		Owners:            []string{"joe"},
		Branches:          []string{"refs/heads/master"},
		Name:              "sci",
		Check:             []string{"go", "test"},
	}
	b, err := ioutil.ReadFile("sci.json")
	if err != nil {
		// Write a default file and exit.
		b, err = json.MarshalIndent(c, "", "  ")
		if err != nil {
			return nil, err
		}
		if err := ioutil.WriteFile("sci.json", b, 0600); err != nil {
			return nil, err
		}
		return nil, errors.New("wrote new sci.json")
	}
	if err := json.Unmarshal(b, c); err != nil {
		return nil, err
	}
	d, err := json.MarshalIndent(c, "", "  ")
	if err != nil {
		return nil, err
	}
	// Update the file in canonical format.
	if !bytes.Equal(b, d) {
		log.Printf("Updating sci.json in canonical format")
		if err := ioutil.WriteFile("sci.json", d, 0600); err != nil {
			return nil, err
		}
	}
	return c, nil
}

func isInList(i string, list []string) bool {
	// Brute force is fine for short list.
	for _, s := range list {
		if i == s {
			return true
		}
	}
	return false
}

func run(cwd string, cmd ...string) (string, bool) {
	cmds := strings.Join(cmd, " ")
	log.Printf("- cwd=%s : %s", cwd, cmds)
	c := exec.Command(cmd[0], cmd[1:]...)
	c.Dir = cwd
	start := time.Now()
	out, err := c.CombinedOutput()
	duration := time.Since(start)
	// Assumes UTF-8.
	return fmt.Sprintf("$ %s\nin %s\n%s", cmds, duration, string(out)), err == nil
}

// runCheck syncs then runs the check.
//
// It runs the check in a temporary GOPATH at the specified commit.
func runCheck(cmd []string, repoName string, useSSH bool, commit, gopath string) (string, string, bool) {
	metadata := fmt.Sprintf("Commit: %s\nVersion: %s\nGOROOT: %s\nGOPATH: %s\nCPUs: %d\n---\n",
		commit, runtime.Version(), runtime.GOROOT(), gopath, runtime.NumCPU())
	repoPath := "github.com/" + repoName
	base := filepath.Join(gopath, "src", repoPath)
	if _, err := os.Stat(base); err != nil {
		up := path.Dir(base)
		if err := os.MkdirAll(up, 0700); err != nil && !os.IsExist(err) {
			log.Printf("- %v", err)
		}
		url := "https://" + repoPath
		if useSSH {
			url = "git@github.com:" + repoName
		}
		out1, ok := run(up, "git", "clone", "--quiet", url)
		metadata += out1
		if !ok {
			return metadata, "", ok
		}
	} else {
		out1, ok := run(base, "git", "fetch", "--prune", "--quiet")
		metadata += out1
		if !ok {
			return metadata, "", ok
		}
	}
	out1, ok := run(base, "git", "checkout", "--quiet", commit)
	metadata += out1
	if !ok {
		return metadata, "", ok
	}
	// TODO(maruel): update dependencies manually!
	out1, ok = run(gopath, "go", "get", "-v", "-d", "-t", repoPath)
	metadata += out1
	if !ok {
		return metadata, "", ok
	}
	out, ok := run(base, cmd...)
	return metadata, out, ok
}

type server struct {
	c      *config
	client *github.Client
	gopath string
	mu     sync.Mutex
}

func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Printf("HTTP: %s %s", r.RemoteAddr, r.URL.Path)
	defer r.Body.Close()
	if r.Method != "POST" {
		http.Error(w, "Invalid method", http.StatusMethodNotAllowed)
		log.Printf("- invalid method")
		return
	}
	payload, err := github.ValidatePayload(r, []byte(s.c.WebHookSecret))
	if err != nil {
		http.Error(w, "Invalid secret", http.StatusUnauthorized)
		log.Printf("- invalid secret")
		return
	}
	event, err := github.ParseWebHook(github.WebHookType(r), payload)
	if err != nil {
		http.Error(w, "Invalid payload", http.StatusBadRequest)
		log.Printf("- invalid payload")
		return
	}
	switch event := event.(type) {
	case *github.PullRequestEvent:
		log.Printf("- PR #%d %s %s", *event.PullRequest.ID, *event.Sender.Login, *event.Action)
		if *event.Action != "opened" && *event.Action != "synchronized" {
			log.Printf("- ignoring action %q for PR from %q", *event.Action, *event.Sender.Login)
			io.WriteString(w, "{}")
			return
		}
		if !isInList(*event.Sender.Login, s.c.Owners) {
			log.Printf("- ignoring owner %q for PR", *event.Sender.Login)
			io.WriteString(w, "{}")
			return
		}
		if err := s.runCheck(*event.Repo.FullName, *event.PullRequest.Head.SHA); err != nil {
			log.Printf("- %v")
		}
	case *github.PushEvent:
		log.Printf("- Push %s %s", *event.Ref, *event.HeadCommit.ID)
		if !isInList(*event.Ref, s.c.Branches) {
			log.Printf("- ignoring branch %q for push", *event.Ref)
			io.WriteString(w, "{}")
			return
		}
		if err := s.runCheck(*event.Repo.FullName, *event.HeadCommit.ID); err != nil {
			log.Printf("- %v")
		}
	default:
		log.Printf("- ignoring hook type %s", reflect.TypeOf(event).Elem().Name())
	}
	io.WriteString(w, "{}")
}

func (s *server) runCheck(repo, commit string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	log.Printf("- Running test for %s at %s", repo, commit)
	metadata, out, success := runCheck(s.c.Check, repo, s.c.UseSSH, commit, s.gopath)

	// https://developer.github.com/v3/gists/#create-a-gist
	gist := &github.Gist{
		Description: github.String("output for https://github.com/" + repo + "/commit/" + commit),
		// It is still accessible via the URL;
		Public: github.Bool(false),
		Files: map[github.GistFilename]github.GistFile{
			"metadata": github.GistFile{Content: &metadata},
			"stdout":   github.GistFile{Content: &out},
		},
	}
	var err error
	if gist, _, err = s.client.Gists.Create(gist); err != nil {
		return err
	}
	log.Printf("- Gist at %s", *gist.HTMLURL)

	// https://developer.github.com/v3/repos/statuses/#create-a-status
	status := &github.RepoStatus{
		State:       github.String("success"),
		TargetURL:   gist.HTMLURL,
		Description: github.String("ran test"),
		Context:     github.String("sci"),
	}
	if !success {
		status.State = github.String("failure")
	}
	parts := strings.SplitN(repo, "/", 2)
	_, _, err = s.client.Repositories.CreateStatus(parts[0], parts[1], commit, status)
	return err
}

func mainImpl() error {
	test := flag.String("test", "", "runs a simulation locally, specify the git repository name (not URL) to test, e.g. 'maruel/sci'")
	flag.Parse()
	c, err := loadConfig()
	if err != nil {
		return err
	}
	wd, err := os.Getwd()
	if err != nil {
		return err
	}
	gopath := filepath.Join(wd, "sci-gopath")
	os.Setenv("GOPATH", gopath)
	if len(*test) != 0 {
		metadata, out, success := runCheck(c.Check, *test, c.UseSSH, "HEAD", gopath)
		_, err := fmt.Printf("%s---\n%sSuccess: %t\n", metadata, out, success)
		return err
	}

	tc := oauth2.NewClient(oauth2.NoContext, oauth2.StaticTokenSource(&oauth2.Token{AccessToken: c.Oauth2AccessToken}))
	s := server{c: c, client: github.NewClient(tc), gopath: gopath}
	http.Handle("/", &s)
	log.Printf("Running in %s", wd)
	log.Printf("Listening on %d", c.Port)
	return http.ListenAndServe(fmt.Sprintf(":%d", c.Port), nil)
}

func main() {
	if err := mainImpl(); err != nil {
		fmt.Fprintf(os.Stderr, "sci: %s.\n", err)
		os.Exit(1)
	}
}
<|endoftext|>