<commit_before>package main

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/dustin/go-humanize"
	"github.com/mbndr/logo"
	"github.com/mmmorris1975/aws-runas/lib"
	"gopkg.in/alecthomas/kingpin.v2"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"
)

const (
	// VERSION - The program version
	VERSION = "1.0.0-rc1"
)

var (
	listRoles    *bool
	listMfa      *bool
	showExpire   *bool
	sesCreds     *bool
	refresh      *bool
	verbose      *bool
	makeConf     *bool
	updateFlag   *bool
	profile      *string
	mfaArn       *string
	duration     *time.Duration
	roleDuration *time.Duration
	cmd          *[]string
	log          *logo.Logger
	logLevel     = logo.WARN
)

func init() {
	const (
		cmdDesc             = "Create an environment for interacting with the AWS API using an assumed role"
		durationArgDesc     = "duration of the retrieved session token"
		roleDurationArgDesc = "duration of the assume role credentials"
		listRoleArgDesc     = "list role ARNs you are able to assume"
		listMfaArgDesc      = "list the ARN of the MFA device associated with your account"
		showExpArgDesc      = "Show token expiration time"
		sesCredArgDesc      = "print eval()-able session token info, or run command using session token credentials"
		refreshArgDesc      = "force a refresh of the cached credentials"
		verboseArgDesc      = "print verbose/debug messages"
		profileArgDesc      = "name of profile, or role ARN"
		cmdArgDesc          = "command to execute using configured profile"
		mfaArnDesc          = "ARN of MFA device needed to perform Assume Role operation"
		makeConfArgDesc     = "Build an AWS extended switch-role plugin configuration for all available roles"
		updateArgDesc       = "Check for updates to aws-runas"
	)

	duration = kingpin.Flag("duration", durationArgDesc).Short('d').Duration()
	roleDuration = kingpin.Flag("role-duration", roleDurationArgDesc).Short('a').Duration()
	listRoles = kingpin.Flag("list-roles", listRoleArgDesc).Short('l').Bool()
	listMfa = kingpin.Flag("list-mfa", listMfaArgDesc).Short('m').Bool()
	showExpire = kingpin.Flag("expiration", showExpArgDesc).Short('e').Bool()
	makeConf = kingpin.Flag("make-conf", makeConfArgDesc).Short('c').Bool()
	sesCreds = kingpin.Flag("session", sesCredArgDesc).Short('s').Bool()
	refresh = kingpin.Flag("refresh", refreshArgDesc).Short('r').Bool()
	verbose = kingpin.Flag("verbose", verboseArgDesc).Short('v').Bool()
	mfaArn = kingpin.Flag("mfa-arn", mfaArnDesc).Short('M').String()
	updateFlag = kingpin.Flag("update", updateArgDesc).Short('u').Bool()

	// if AWS_PROFILE env var is NOT set, it MUST be 1st non-flag arg
	// if AWS_PROFILE env var is set, all non-flag args will be treated as cmd
	if v, ok := os.LookupEnv("AWS_PROFILE"); !ok {
		profile = kingpin.Arg("profile", profileArgDesc).String()
	} else {
		profile = aws.String(v)
	}

	cmd = CmdArg(kingpin.Arg("cmd", cmdArgDesc))

	kingpin.Version(VERSION)
	kingpin.CommandLine.VersionFlag.Short('V')
	kingpin.CommandLine.HelpFlag.Short('h')
	kingpin.CommandLine.Help = cmdDesc
}

func main() {
	// Tell kingpin to stop parsing flags once we start processing 'cmd', allows something like:
	// `aws-runas --verbose profile command -a --long_arg`
	// without needing an explicit `--` between 'profile' and 'cmd'
	kingpin.CommandLine.Interspersed(false)
	kingpin.Parse()

	if *verbose {
		logLevel = logo.DEBUG
	}
	log = logo.NewSimpleLogger(os.Stderr, logLevel, "aws-runas.main", true)

	log.Debugf("PROFILE: %s", *profile)
	sess := lib.AwsSession(*profile)

	cm, err := lib.NewAwsConfigManager(&lib.ConfigManagerOptions{LogLevel: logLevel})
	if err != nil {
		log.Fatalf("Error loading configuration: %v", err)
	}

	switch {
	case *listMfa:
		log.Debug("List MFA")
		printMfa(sess)
	case *listRoles, *makeConf:
		u := iamUser(sess)
		userName := *u.UserName

		rg := lib.NewAwsRoleGetter(sess, userName, &lib.RoleGetterOptions{LogLevel: logLevel})
		roles := rg.Roles()

		if *listRoles {
			log.Debug("List Roles")
			fmt.Printf("Available role ARNs for %s (%s)\n", userName, *u.Arn)
			for _, v := range roles {
				fmt.Printf("  %s\n", v)
			}
		}

		if *makeConf {
			log.Debug("Make Configuration Files.")
			mfa := lookupMfa(sess)

			if err := cm.BuildConfig(roles, mfa); err != nil {
				log.Fatalf("Error building config file: %v", err)
			}
		}
	case *updateFlag:
		log.Debug("Update check")
		if err := lib.VersionCheck(VERSION); err != nil {
			log.Debugf("Error from VersionCheck(): %v", err)
		}
	default:
		p, err := awsProfile(cm, *profile)
		if err != nil {
			log.Fatalf("Error building profile: %v", err)
		}

		// Add command-line option overrides
		if duration != nil && (*duration).Nanoseconds() > 0 {
			p.SessionDuration = *duration
		}

		if roleDuration != nil && (*roleDuration).Nanoseconds() > 0 {
			p.CredDuration = *roleDuration
		}

		if mfaArn != nil && len(*mfaArn) > 0 {
			p.MfaSerial = *mfaArn
		}
		log.Debugf("RESOLVED PROFILE: %+v", p)

		credProvider := credProvider(p)

		if *refresh {
			os.Remove(credProvider.CacheFile())
		}

		if *showExpire {
			printExpire(credProvider)
		}

		c := credentials.NewCredentials(credProvider)
		creds, err := c.Get()
		if err != nil {
			log.Fatalf("Error getting credentials: %v", err)
		}

		updateEnv(creds, p.Region)

		if len(*cmd) > 0 {
			c := exec.Command((*cmd)[0], (*cmd)[1:]...)
			c.Stdin = os.Stdin
			c.Stdout = os.Stdout
			c.Stderr = os.Stderr

			err := c.Run()
			if err != nil {
				log.Debug("Error running command")
				log.Fatalf("%v", err)
			}
		} else {
			printCredentials()
		}
	}
}

func assumeRoleInput(p *lib.AWSProfile) *sts.AssumeRoleInput {
	i := new(sts.AssumeRoleInput)
	if p.CredDuration.Seconds() == 0 {
		i.DurationSeconds = aws.Int64(int64(lib.ASSUME_ROLE_DEFAULT_DURATION.Seconds()))
	} else {
		i.DurationSeconds = aws.Int64(int64(p.CredDuration.Seconds()))
	}

	if len(p.RoleArn.String()) > 0 {
		i.RoleArn = aws.String(p.RoleArn.String())
	}

	if len(p.RoleSessionName) > 0 {
		i.RoleSessionName = aws.String(p.RoleSessionName)
	}

	if len(p.ExternalId) > 0 {
		i.ExternalId = aws.String(p.ExternalId)
	}

	return i
}

func updateEnv(creds credentials.Value, region string) {
	// Explicitly unset AWS_PROFILE to avoid unintended consequences
	os.Unsetenv("AWS_PROFILE")

	// Pass AWS_REGION through if it was set in our env, or found in config.
	// Ensure that called program gets the expected region.  Also set
	// AWS_DEFAULT_REGION so awscli works as expected, otherwise it will use
	// any region from the profile (or default, since we're not providing profile)
	if len(region) > 0 {
		os.Setenv("AWS_REGION", region)
		os.Setenv("AWS_DEFAULT_REGION", region)
	}

	os.Setenv("AWS_ACCESS_KEY_ID", creds.AccessKeyID)
	os.Setenv("AWS_SECRET_ACCESS_KEY", creds.SecretAccessKey)

	// If session token creds were returned, set them. Otherwise explicitly
	// unset them to keep the sdk from getting confused.  AFAIK, we should
	// always have SessionTokens, since our entire process revolves around them.
	// But always code defensively
	if len(creds.SessionToken) > 0 {
		os.Setenv("AWS_SESSION_TOKEN", creds.SessionToken)
		os.Setenv("AWS_SECURITY_TOKEN", creds.SessionToken)
	} else {
		os.Unsetenv("AWS_SESSION_TOKEN")
		os.Unsetenv("AWS_SECURITY_TOKEN")
	}
}

func iamUser(s *session.Session) *iam.User {
	i := iam.New(s)

	u, err := i.GetUser(new(iam.GetUserInput))
	if err != nil {
		log.Fatalf("Error getting IAM user info: %v", err)
	}

	log.Debugf("USER: %+v", u)
	return u.User
}

func awsProfile(cm lib.ConfigManager, name string) (*lib.AWSProfile, error) {
	var p *lib.AWSProfile

	// Lookup default profile name, in case we were not passed a profile,
	// or role_arn as part of the command
	defProfile := session.DefaultSharedConfigProfile
	v, ok := os.LookupEnv("AWS_DEFAULT_PROFILE")
	if ok {
		defProfile = v
	}
	if len(name) < 1 {
		name = defProfile
	}

	a, err := arn.Parse(name)
	if err != nil {
		p, err = cm.GetProfile(aws.String(name))
		if err != nil {
			return nil, fmt.Errorf("unable to get configuration for profile '%s': %v", name, err)
		}

		if len(p.Name) < 1 {
			// helps keep cache file naming sane
			p.Name = defProfile
		}
	} else {
		if strings.HasPrefix(a.String(), lib.IAM_ARN) {
			// Unset AWS_PROFILE here, in case the role ARN came in via environment
			// variable, otherwise is messes up GetProfile
			os.Unsetenv("AWS_PROFILE")
			p, err = cm.GetProfile(aws.String(defProfile))
			if err != nil {
				return nil, err
			}
			p.RoleArn = a
		} else {
			return nil, fmt.Errorf("profile argument is not an IAM role ARN")
		}
	}

	return p, nil
}

func printCredentials() {
	format := "%s %s='%s'\n"
	exportToken := "export"
	switch runtime.GOOS {
	case "windows":
		exportToken = "set"
	}

	envVars := []string{
		"AWS_REGION", "AWS_DEFAULT_REGION",
		"AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY",
		"AWS_SESSION_TOKEN", "AWS_SECURITY_TOKEN",
	}

	for _, v := range envVars {
		val, ok := os.LookupEnv(v)
		if ok {
			fmt.Printf(format, exportToken, v, val)
		}
	}
}

func printMfa(s *session.Session) {
	mfa, err := lib.LookupMfa(s)
	if err != nil {
		log.Fatalf("Error retrieving MFA info: %v", err)
	}

	for _, d := range mfa {
		fmt.Printf("%s\n", *d.SerialNumber)
	}
}

func lookupMfa(s *session.Session) *string {
	var mfa *string
	if mfaArn != nil && len(*mfaArn) > 0 {
		// MFA arn provided by cmdline option
		mfa = mfaArn
	} else {
		m, err := lib.LookupMfa(s)
		if err != nil {
			log.Errorf("MFA lookup failed, will not configure MFA: %v", err)
		}

		if len(m) > 0 {
			// use 1st MFA device found
			mfa = m[0].SerialNumber
		}
	}
	return mfa
}

func credProvider(p *lib.AWSProfile) lib.SessionTokenProvider {
	opts := lib.CachedCredentialsProviderOptions{
		LogLevel:  logLevel,
		MfaSerial: p.MfaSerial,
	}

	var credProvider lib.SessionTokenProvider
	if *sesCreds || len(p.RoleArn.Resource) < 1 {
		if log != nil {
			log.Debugf("Getting SESSION TOKEN credentials")
		}
		opts.CredentialDuration = p.SessionDuration
		credProvider = lib.NewSessionTokenProvider(p, &opts)
	} else {
		if log != nil {
			log.Debugf("Getting ASSUME ROLE credentials")
		}
		opts.CredentialDuration = p.CredDuration
		credProvider = lib.NewAssumeRoleProvider(p, &opts)
	}
	return credProvider
}

func printExpire(p lib.CachedCredentialProvider) {
	exp := p.ExpirationTime()
	format := exp.Format("2006-01-02 15:04:05")
	hmn := humanize.Time(exp)

	tense := "will expire"
	if exp.Before(time.Now()) {
		tense = "expired"
	}
	fmt.Fprintf(os.Stderr, "Credentials %s on %s (%s)\n", tense, format, hmn)
}
<commit_msg>update to rc2 and release the bash completion feature<commit_after>package main

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/dustin/go-humanize"
	"github.com/mbndr/logo"
	"github.com/mmmorris1975/aws-runas/lib"
	"gopkg.in/alecthomas/kingpin.v2"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"
)

const (
	// VERSION - The program version
	VERSION = "1.0.0-rc2"
)

var (
	listRoles    *bool
	listMfa      *bool
	showExpire   *bool
	sesCreds     *bool
	refresh      *bool
	verbose      *bool
	makeConf     *bool
	updateFlag   *bool
	profile      *string
	mfaArn       *string
	duration     *time.Duration
	roleDuration *time.Duration
	cmd          *[]string
	log          *logo.Logger
	logLevel     = logo.WARN
)

func init() {
	const (
		cmdDesc             = "Create an environment for interacting with the AWS API using an assumed role"
		durationArgDesc     = "duration of the retrieved session token"
		roleDurationArgDesc = "duration of the assume role credentials"
		listRoleArgDesc     = "list role ARNs you are able to assume"
		listMfaArgDesc      = "list the ARN of the MFA device associated with your account"
		showExpArgDesc      = "Show token expiration time"
		sesCredArgDesc      = "print eval()-able session token info, or run command using session token credentials"
		refreshArgDesc      = "force a refresh of the cached credentials"
		verboseArgDesc      = "print verbose/debug messages"
		profileArgDesc      = "name of profile, or role ARN"
		cmdArgDesc          = "command to execute using configured profile"
		mfaArnDesc          = "ARN of MFA device needed to perform Assume Role operation"
		makeConfArgDesc     = "Build an AWS extended switch-role plugin configuration for all available roles"
		updateArgDesc       = "Check for updates to aws-runas"
	)

	duration = kingpin.Flag("duration", durationArgDesc).Short('d').Duration()
	roleDuration = kingpin.Flag("role-duration", roleDurationArgDesc).Short('a').Duration()
	listRoles = kingpin.Flag("list-roles", listRoleArgDesc).Short('l').Bool()
	listMfa = kingpin.Flag("list-mfa", listMfaArgDesc).Short('m').Bool()
	showExpire = kingpin.Flag("expiration", showExpArgDesc).Short('e').Bool()
	makeConf = kingpin.Flag("make-conf", makeConfArgDesc).Short('c').Bool()
	sesCreds = kingpin.Flag("session", sesCredArgDesc).Short('s').Bool()
	refresh = kingpin.Flag("refresh", refreshArgDesc).Short('r').Bool()
	verbose = kingpin.Flag("verbose", verboseArgDesc).Short('v').Bool()
	mfaArn = kingpin.Flag("mfa-arn", mfaArnDesc).Short('M').String()
	updateFlag = kingpin.Flag("update", updateArgDesc).Short('u').Bool()

	// if AWS_PROFILE env var is NOT set, it MUST be 1st non-flag arg
	// if AWS_PROFILE env var is set, all non-flag args will be treated as cmd
	if v, ok := os.LookupEnv("AWS_PROFILE"); !ok {
		profile = kingpin.Arg("profile", profileArgDesc).String()
	} else {
		profile = aws.String(v)
	}

	cmd = CmdArg(kingpin.Arg("cmd", cmdArgDesc))

	kingpin.Version(VERSION)
	kingpin.CommandLine.VersionFlag.Short('V')
	kingpin.CommandLine.HelpFlag.Short('h')
	kingpin.CommandLine.Help = cmdDesc
}

func main() {
	// Tell kingpin to stop parsing flags once we start processing 'cmd', allows something like:
	// `aws-runas --verbose profile command -a --long_arg`
	// without needing an explicit `--` between 'profile' and 'cmd'
	kingpin.CommandLine.Interspersed(false)
	kingpin.Parse()

	if *verbose {
		logLevel = logo.DEBUG
	}
	log = logo.NewSimpleLogger(os.Stderr, logLevel, "aws-runas.main", true)

	log.Debugf("PROFILE: %s", *profile)
	sess := lib.AwsSession(*profile)

	cm, err := lib.NewAwsConfigManager(&lib.ConfigManagerOptions{LogLevel: logLevel})
	if err != nil {
		log.Fatalf("Error loading configuration: %v", err)
	}

	switch {
	case *listMfa:
		log.Debug("List MFA")
		printMfa(sess)
	case *listRoles, *makeConf:
		u := iamUser(sess)
		userName := *u.UserName

		rg := lib.NewAwsRoleGetter(sess, userName, &lib.RoleGetterOptions{LogLevel: logLevel})
		roles := rg.Roles()

		if *listRoles {
			log.Debug("List Roles")
			fmt.Printf("Available role ARNs for %s (%s)\n", userName, *u.Arn)
			for _, v := range roles {
				fmt.Printf("  %s\n", v)
			}
		}

		if *makeConf {
			log.Debug("Make Configuration Files.")
			mfa := lookupMfa(sess)

			if err := cm.BuildConfig(roles, mfa); err != nil {
				log.Fatalf("Error building config file: %v", err)
			}
		}
	case *updateFlag:
		log.Debug("Update check")
		if err := lib.VersionCheck(VERSION); err != nil {
			log.Debugf("Error from VersionCheck(): %v", err)
		}
	default:
		p, err := awsProfile(cm, *profile)
		if err != nil {
			log.Fatalf("Error building profile: %v", err)
		}

		// Add command-line option overrides
		if duration != nil && (*duration).Nanoseconds() > 0 {
			p.SessionDuration = *duration
		}

		if roleDuration != nil && (*roleDuration).Nanoseconds() > 0 {
			p.CredDuration = *roleDuration
		}

		if mfaArn != nil && len(*mfaArn) > 0 {
			p.MfaSerial = *mfaArn
		}
		log.Debugf("RESOLVED PROFILE: %+v", p)

		credProvider := credProvider(p)

		if *refresh {
			os.Remove(credProvider.CacheFile())
		}

		if *showExpire {
			printExpire(credProvider)
		}

		c := credentials.NewCredentials(credProvider)
		creds, err := c.Get()
		if err != nil {
			log.Fatalf("Error getting credentials: %v", err)
		}

		updateEnv(creds, p.Region)

		if len(*cmd) > 0 {
			c := exec.Command((*cmd)[0], (*cmd)[1:]...)
			c.Stdin = os.Stdin
			c.Stdout = os.Stdout
			c.Stderr = os.Stderr

			err := c.Run()
			if err != nil {
				log.Debug("Error running command")
				log.Fatalf("%v", err)
			}
		} else {
			printCredentials()
		}
	}
}

func assumeRoleInput(p *lib.AWSProfile) *sts.AssumeRoleInput {
	i := new(sts.AssumeRoleInput)
	if p.CredDuration.Seconds() == 0 {
		i.DurationSeconds = aws.Int64(int64(lib.ASSUME_ROLE_DEFAULT_DURATION.Seconds()))
	} else {
		i.DurationSeconds = aws.Int64(int64(p.CredDuration.Seconds()))
	}

	if len(p.RoleArn.String()) > 0 {
		i.RoleArn = aws.String(p.RoleArn.String())
	}

	if len(p.RoleSessionName) > 0 {
		i.RoleSessionName = aws.String(p.RoleSessionName)
	}

	if len(p.ExternalId) > 0 {
		i.ExternalId = aws.String(p.ExternalId)
	}

	return i
}

func updateEnv(creds credentials.Value, region string) {
	// Explicitly unset AWS_PROFILE to avoid unintended consequences
	os.Unsetenv("AWS_PROFILE")

	// Pass AWS_REGION through if it was set in our env, or found in config.
	// Ensure that called program gets the expected region.  Also set
	// AWS_DEFAULT_REGION so awscli works as expected, otherwise it will use
	// any region from the profile (or default, since we're not providing profile)
	if len(region) > 0 {
		os.Setenv("AWS_REGION", region)
		os.Setenv("AWS_DEFAULT_REGION", region)
	}

	os.Setenv("AWS_ACCESS_KEY_ID", creds.AccessKeyID)
	os.Setenv("AWS_SECRET_ACCESS_KEY", creds.SecretAccessKey)

	// If session token creds were returned, set them. Otherwise explicitly
	// unset them to keep the sdk from getting confused.  AFAIK, we should
	// always have SessionTokens, since our entire process revolves around them.
	// But always code defensively
	if len(creds.SessionToken) > 0 {
		os.Setenv("AWS_SESSION_TOKEN", creds.SessionToken)
		os.Setenv("AWS_SECURITY_TOKEN", creds.SessionToken)
	} else {
		os.Unsetenv("AWS_SESSION_TOKEN")
		os.Unsetenv("AWS_SECURITY_TOKEN")
	}
}

func iamUser(s *session.Session) *iam.User {
	i := iam.New(s)

	u, err := i.GetUser(new(iam.GetUserInput))
	if err != nil {
		log.Fatalf("Error getting IAM user info: %v", err)
	}

	log.Debugf("USER: %+v", u)
	return u.User
}

func awsProfile(cm lib.ConfigManager, name string) (*lib.AWSProfile, error) {
	var p *lib.AWSProfile

	// Lookup default profile name, in case we were not passed a profile,
	// or role_arn as part of the command
	defProfile := session.DefaultSharedConfigProfile
	v, ok := os.LookupEnv("AWS_DEFAULT_PROFILE")
	if ok {
		defProfile = v
	}
	if len(name) < 1 {
		name = defProfile
	}

	a, err := arn.Parse(name)
	if err != nil {
		p, err = cm.GetProfile(aws.String(name))
		if err != nil {
			return nil, fmt.Errorf("unable to get configuration for profile '%s': %v", name, err)
		}

		if len(p.Name) < 1 {
			// helps keep cache file naming sane
			p.Name = defProfile
		}
	} else {
		if strings.HasPrefix(a.String(), lib.IAM_ARN) {
			// Unset AWS_PROFILE here, in case the role ARN came in via environment
			// variable, otherwise is messes up GetProfile
			os.Unsetenv("AWS_PROFILE")
			p, err = cm.GetProfile(aws.String(defProfile))
			if err != nil {
				return nil, err
			}
			p.RoleArn = a
		} else {
			return nil, fmt.Errorf("profile argument is not an IAM role ARN")
		}
	}

	return p, nil
}

func printCredentials() {
	format := "%s %s='%s'\n"
	exportToken := "export"
	switch runtime.GOOS {
	case "windows":
		exportToken = "set"
	}

	envVars := []string{
		"AWS_REGION", "AWS_DEFAULT_REGION",
		"AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY",
		"AWS_SESSION_TOKEN", "AWS_SECURITY_TOKEN",
	}

	for _, v := range envVars {
		val, ok := os.LookupEnv(v)
		if ok {
			fmt.Printf(format, exportToken, v, val)
		}
	}
}

func printMfa(s *session.Session) {
	mfa, err := lib.LookupMfa(s)
	if err != nil {
		log.Fatalf("Error retrieving MFA info: %v", err)
	}

	for _, d := range mfa {
		fmt.Printf("%s\n", *d.SerialNumber)
	}
}

func lookupMfa(s *session.Session) *string {
	var mfa *string
	if mfaArn != nil && len(*mfaArn) > 0 {
		// MFA arn provided by cmdline option
		mfa = mfaArn
	} else {
		m, err := lib.LookupMfa(s)
		if err != nil {
			log.Errorf("MFA lookup failed, will not configure MFA: %v", err)
		}

		if len(m) > 0 {
			// use 1st MFA device found
			mfa = m[0].SerialNumber
		}
	}
	return mfa
}

func credProvider(p *lib.AWSProfile) lib.SessionTokenProvider {
	opts := lib.CachedCredentialsProviderOptions{
		LogLevel:  logLevel,
		MfaSerial: p.MfaSerial,
	}

	var credProvider lib.SessionTokenProvider
	if *sesCreds || len(p.RoleArn.Resource) < 1 {
		if log != nil {
			log.Debugf("Getting SESSION TOKEN credentials")
		}
		opts.CredentialDuration = p.SessionDuration
		credProvider = lib.NewSessionTokenProvider(p, &opts)
	} else {
		if log != nil {
			log.Debugf("Getting ASSUME ROLE credentials")
		}
		opts.CredentialDuration = p.CredDuration
		credProvider = lib.NewAssumeRoleProvider(p, &opts)
	}
	return credProvider
}

func printExpire(p lib.CachedCredentialProvider) {
	exp := p.ExpirationTime()
	format := exp.Format("2006-01-02 15:04:05")
	hmn := humanize.Time(exp)

	tense := "will expire"
	if exp.Before(time.Now()) {
		tense = "expired"
	}
	fmt.Fprintf(os.Stderr, "Credentials %s on %s (%s)\n", tense, format, hmn)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html"
	"io"
	"log"
	"net/http"

	"github.com/dchest/uniuri"
	"github.com/ewhal/pygments"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
)

const (
	ADDRESS  = "http://localhost:9900"
	LENGTH   = 6
	PORT     = ":9900"
	USERNAME = ""
	PASS     = ""
	NAME     = ""
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

type Response struct {
	ID     string `json:"id"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

func check(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

func generateName() string {
	s := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	query, err := db.Query("select id from pastebin")
	for query.Next() {
		var id string
		err := query.Scan(&id)
		if err != nil {

		}
		if id == s {
			generateName()
		}
	}
	db.Close()

	return s

}
func hash(paste string) string {
	hasher := sha1.New()

	hasher.Write([]byte(paste))
	sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
	return sha
}

func save(raw string, lang string) []string {
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	sha := hash(raw)
	query, err := db.Query("select id, hash, data, delkey from pastebin")
	for query.Next() {
		var id, hash, paste, delkey string
		err := query.Scan(&id, &hash, &paste, &delkey)
		check(err)
		if hash == sha {
			url := ADDRESS + "/p/" + id
			return []string{id, hash, url, paste, delkey}
		}
	}
	id := generateName()
	var url string
	if lang == "" {
		url = ADDRESS + "/p/" + id
	} else {
		url = ADDRESS + "/p/" + id + "/" + lang
	}
	delKey := uniuri.NewLen(40)
	paste := html.EscapeString(raw)

	stmt, err := db.Prepare("INSERT INTO pastebin(id, hash, data, delkey) values(?,?,?,?)")
	check(err)
	_, err = stmt.Exec(id, sha, paste, delKey)
	check(err)
	db.Close()
	return []string{id, sha, url, paste, delKey}
}

func delHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	delkey := vars["delKey"]

	db, err := sql.Open("mysql", DATABASE)
	check(err)

	stmt, err := db.Prepare("delete from pastebin where delkey=?")
	check(err)

	res, err := stmt.Exec(html.EscapeString(delkey))
	check(err)

	_, err = res.RowsAffected()
	if err == sql.ErrNoRows {
		io.WriteString(w, "Error invalid paste")
	} else {
		io.WriteString(w, paste+" deleted")
	}
	db.Close()

}
func saveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	output := vars["output"]
	switch r.Method {
	case "POST":
		paste := r.FormValue("p")
		lang := r.FormValue("lang")
		if paste == "" {
			http.Error(w, "Empty paste", 500)
			return
		}
		values := save(paste, lang)
		b := &Response{
			ID:     values[0],
			HASH:   values[1],
			URL:    values[2],
			SIZE:   len(values[3]),
			DELKEY: values[4],
		}

		switch output {
		case "json":

			w.Header().Set("Content-Type", "application/json")
			err := json.NewEncoder(w).Encode(b)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		case "xml":
			x, err := xml.MarshalIndent(b, "", "  ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/xml")
			w.Write(x)
		case "html":
			w.Header().Set("Content-Type", "text/html")
			io.WriteString(w, "
URL: "+b.URL+"
")
			io.WriteString(w, "
Delete Key: "+b.DELKEY+"
")

		default:
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

func langHandler(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]
	s := getPaste(paste)
	highlight, err := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "full, style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,", "utf-8")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, highlight)

}

func getPaste(paste string) string {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	var s string
	err = db.QueryRow("select data from pastebin where id=?", param1).Scan(&s)
	db.Close()
	check(err)

	if err == sql.ErrNoRows {
		return "Error invalid paste"
	} else {
		return html.UnescapeString(s)
	}

}
func pasteHandler(w http.ResponseWriter, r *http.Request) {
}

func rawHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s := getPaste(paste)
	w.Header().Set("Content-Type", "plain/text")
	io.WriteString(w, s)

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/p/{pasteId}", pasteHandler)
	router.HandleFunc("/raw/{pasteId}", rawHandler)
	router.HandleFunc("/p/{pasteId}/{lang}", langHandler)
	router.HandleFunc("/save", saveHandler)
	router.HandleFunc("/save/{output}", saveHandler)
	router.HandleFunc("/del/{pasteId}/{delKey}", delHandler)
	router.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>Correct asset file name<commit_after>package main

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html"
	"html/template"
	"io"
	"log"
	"net/http"

	"github.com/dchest/uniuri"
	"github.com/ewhal/pygments"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
)

const (
	ADDRESS  = "http://localhost:9900"
	LENGTH   = 6
	PORT     = ":9900"
	USERNAME = ""
	PASS     = ""
	NAME     = ""
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

type Response struct {
	ID     string `json:"id"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

type Page struct {
	Title string
	Body  []byte
}

func check(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

func generateName() string {
	s := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	query, err := db.Query("select id from pastebin")
	for query.Next() {
		var id string
		err := query.Scan(&id)
		if err != nil {

		}
		if id == s {
			generateName()
		}
	}
	db.Close()

	return s

}
func hash(paste string) string {
	hasher := sha1.New()

	hasher.Write([]byte(paste))
	sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
	return sha
}

func save(raw string, lang string) []string {
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	sha := hash(raw)
	query, err := db.Query("select id, hash, data, delkey from pastebin")
	for query.Next() {
		var id, hash, paste, delkey string
		err := query.Scan(&id, &hash, &paste, &delkey)
		check(err)
		if hash == sha {
			url := ADDRESS + "/p/" + id
			return []string{id, hash, url, paste, delkey}
		}
	}
	id := generateName()
	var url string
	if lang == "" {
		url = ADDRESS + "/p/" + id
	} else {
		url = ADDRESS + "/p/" + id + "/" + lang
	}
	delKey := uniuri.NewLen(40)
	paste := html.EscapeString(raw)

	stmt, err := db.Prepare("INSERT INTO pastebin(id, hash, data, delkey) values(?,?,?,?)")
	check(err)
	_, err = stmt.Exec(id, sha, paste, delKey)
	check(err)
	db.Close()
	return []string{id, sha, url, paste, delKey}
}

func delHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	delkey := vars["delKey"]

	db, err := sql.Open("mysql", DATABASE)
	check(err)

	stmt, err := db.Prepare("delete from pastebin where delkey=?")
	check(err)

	res, err := stmt.Exec(html.EscapeString(delkey))
	check(err)

	_, err = res.RowsAffected()
	if err == sql.ErrNoRows {
		io.WriteString(w, "Error invalid paste")
	} else {
		io.WriteString(w, paste+" deleted")
	}
	db.Close()

}
func saveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	output := vars["output"]
	switch r.Method {
	case "POST":
		paste := r.FormValue("p")
		lang := r.FormValue("lang")
		if paste == "" {
			http.Error(w, "Empty paste", 500)
			return
		}
		values := save(paste, lang)
		b := &Response{
			ID:     values[0],
			HASH:   values[1],
			URL:    values[2],
			SIZE:   len(values[3]),
			DELKEY: values[4],
		}

		switch output {
		case "json":

			w.Header().Set("Content-Type", "application/json")
			err := json.NewEncoder(w).Encode(b)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		case "xml":
			x, err := xml.MarshalIndent(b, "", "  ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/xml")
			w.Write(x)

		case "html":
			w.Header().Set("Content-Type", "text/html")
			io.WriteString(w, "
URL: "+b.URL+"
")
			io.WriteString(w, "
Delete Key: "+b.DELKEY+"
")

		default:
			w.Header().Set("Content-Type", "plain/text")
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

func langHandler(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]
	s := getPaste(paste)
	highlight, err := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "full, style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,", "utf-8")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, highlight)

}

func getPaste(paste string) string {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	var s string
	err = db.QueryRow("select data from pastebin where id=?", param1).Scan(&s)
	db.Close()
	check(err)

	if err == sql.ErrNoRows {
		return "Error invalid paste"
	} else {
		return html.UnescapeString(s)
	}

}

var templates = template.Must(template.ParseFiles("assets/paste.html"))

func pasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s := getPaste(paste)
	p := &Page{
		Title: paste,
		Body:  []byte(s),
	}
	err := templates.ExecuteTemplate(w, "assets/paste.html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func rawHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s := getPaste(paste)
	w.Header().Set("Content-Type", "plain/text")
	io.WriteString(w, s)

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/p/{pasteId}", pasteHandler)
	router.HandleFunc("/raw/{pasteId}", rawHandler)
	router.HandleFunc("/p/{pasteId}/{lang}", langHandler)
	router.HandleFunc("/save", saveHandler)
	router.HandleFunc("/save/{output}", saveHandler)
	router.HandleFunc("/del/{pasteId}/{delKey}", delHandler)
	router.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/alanctgardner/gogen-avro/generator"
	"io/ioutil"
	"os"
)

func main() {
	file := os.Args[1]
	schema, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Printf("Error reading file %q - %v", file, err)
		return
	}
	goDefs, err := generator.GenerateForSchema(schema)
	if err != nil {
		fmt.Printf("Error generating schema for file %q - %v", file, err)
		return
	}
	fmt.Printf(goDefs)
}
<commit_msg>Print errors to stderr and return a non-zero error code<commit_after>package main

import (
	"fmt"
	"github.com/alanctgardner/gogen-avro/generator"
	"io/ioutil"
	"os"
)

func main() {
	file := os.Args[1]
	schema, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading file %q - %v\n", file, err)
		os.Exit(1)
	}
	goDefs, err := generator.GenerateForSchema(schema)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error generating schema for file %q - %v\n", file, err)
		os.Exit(1)
	}
	fmt.Printf(goDefs)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/alecthomas/kingpin"
)

type Severity string

// Linter message severity levels.
const (
	Warning Severity = "warning"
	Error   Severity = "error"
)

type Linter string

func (l Linter) Command() string {
	return string(l[0:strings.Index(string(l), ":")])
}

func (l Linter) Pattern() string {
	return string(l[strings.Index(string(l), ":"):])
}

var (
	predefinedPatterns = map[string]string{
		"PATH:LINE:COL:MESSAGE": `(?P<path>[^:]+):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)`,
		"PATH:LINE:MESSAGE":     `(?P<path>[^:]+):(?P<line>\d+):\s*(?P<message>.*)`,
	}
	lintersFlag = map[string]string{
		// main.go:8:10: should omit type map[string]string from declaration of var linters; it will be inferred from the right-hand side
		"golint": "golint {path}:PATH:LINE:COL:MESSAGE",
		// test/stutter.go:19: missing argument for Printf("%d"): format reads arg 1, have only 0 args
		"vet":         "go tool vet {path}:PATH:LINE:MESSAGE",
		"gotype":      "gotype {path}:PATH:LINE:COL:MESSAGE",
		"errcheck":    `errcheck {path}:(?P<path>[^:]+):(?P<line>\d+):(?P<col>\d+)\t(?P<message>.*)`,
		"varcheck":    "varcheck {path}:PATH:LINE:MESSAGE",
		"structcheck": "structcheck {path}:PATH:LINE:MESSAGE",
		"defercheck":  "defercheck {path}:PATH:LINE:MESSAGE",
	}
	linterMessageOverrideFlag = map[string]string{
		"errcheck":    "error return value not checked ({message})",
		"varcheck":    "unused global variable {message}",
		"structcheck": "unused struct field {message}",
	}
	linterSeverityFlag = map[string]string{
		"errcheck":    "warning",
		"golint":      "warning",
		"varcheck":    "warning",
		"structcheck": "warning",
	}
	installMap = map[string]string{
		"golint":      "go get github.com/golang/lint/golint",
		"gotype":      "go get code.google.com/p/go.tools/cmd/gotype",
		"errcheck":    "go get github.com/kisielk/errcheck",
		"defercheck":  "go get github.com/opennota/check/cmd/defercheck",
		"varcheck":    "go get github.com/opennota/check/cmd/varcheck",
		"structcheck": "go get github.com/opennota/check/cmd/structcheck",
	}
	pathArg            = kingpin.Arg("path", "Directory to lint.").Default(".").String()
	installFlag        = kingpin.Flag("install", "Attempt to install all known linters.").Bool()
	disableLintersFlag = kingpin.Flag("disable", "List of linters to disable.").PlaceHolder("LINTER").Short('D').Strings()
	debugFlag          = kingpin.Flag("debug", "Display messages for failed linters, etc.").Short('d').Bool()
	concurrencyFlag    = kingpin.Flag("concurrency", "Number of concurrent linters to run.").Default("16").Short('j').Int()
)

func init() {
	kingpin.Flag("linter", "Specify a linter.").PlaceHolder("NAME:COMMAND:PATTERN").StringMapVar(&lintersFlag)
	kingpin.Flag("message-overrides", "Override message from linter. {message} will be expanded to the original message.").PlaceHolder("LINTER:MESSAGE").StringMapVar(&linterMessageOverrideFlag)
	kingpin.Flag("severity", "Map of linter severities.").PlaceHolder("LINTER:SEVERITY").StringMapVar(&linterSeverityFlag)
}

type Issue struct {
	severity Severity
	path     string
	line     int
	col      int
	message  string
}

func (m *Issue) String() string {
	col := ""
	if m.col != 0 {
		col = fmt.Sprintf("%d", m.col)
	}
	return fmt.Sprintf("%s:%d:%s:%s: %s", m.path, m.line, col, m.severity, m.message)
}

type Issues []*Issue

func (m Issues) Len() int      { return len(m) }
func (m Issues) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m Issues) Less(i, j int) bool {
	return m[i].path < m[j].path || m[i].line < m[j].line || m[i].col < m[j].col
}

func debug(format string, args ...interface{}) {
	if *debugFlag {
		fmt.Fprintf(os.Stderr, "DEBUG: "+format+"\n", args...)
	}
}

func formatLinters() string {
	w := bytes.NewBuffer(nil)
	for command, description := range lintersFlag {
		linter := Linter(description)
		fmt.Fprintf(w, "    %s -> %s -> %s\n", command, linter.Command(), linter.Pattern())
	}
	return w.String()
}

func formatSeverity() string {
	w := bytes.NewBuffer(nil)
	for name, severity := range linterSeverityFlag {
		fmt.Fprintf(w, "    %s -> %s\n", name, severity)
	}
	return w.String()
}

func main() {
	kingpin.CommandLine.Help = fmt.Sprintf(`Aggregate and normalise the output of a whole bunch of Go linters.

Default linters:

%s

Severity override map (default is "error"):

%s
`, formatLinters(), formatSeverity())
	kingpin.Parse()

	if *installFlag {
		for name, cmd := range installMap {
			fmt.Printf("Installing %s -> %s\n", name, cmd)
			c := exec.Command("/bin/sh", "-c", cmd)
			c.Stdout = os.Stdout
			c.Stderr = os.Stderr
			err := c.Run()
			if err != nil {
				kingpin.CommandLine.Errorf(os.Stderr, "failed to install %s: %s", name, err)
			}
		}
		return
	}

	runtime.GOMAXPROCS(*concurrencyFlag)

	disable := map[string]bool{}
	for _, linter := range *disableLintersFlag {
		disable[linter] = true
	}

	start := time.Now()
	paths := *pathArg
	concurrency := make(chan bool, *concurrencyFlag)
	issues := make(chan *Issue, 1000)
	wg := &sync.WaitGroup{}
	for name, description := range lintersFlag {
		if _, ok := disable[name]; ok {
			debug("linter %s disabled", name)
			continue
		}
		parts := strings.SplitN(description, ":", 2)
		command := parts[0]
		pattern := parts[1]

		wg.Add(1)
		go func(name, command, pattern string) {
			concurrency <- true
			executeLinter(issues, name, command, pattern, paths)
			<-concurrency
			wg.Done()
		}(name, command, pattern)
	}

	wg.Wait()
	close(issues)
	for issue := range issues {
		fmt.Printf("%s\n", issue)
	}
	elapsed := time.Now().Sub(start)
	debug("total elapsed time %s", elapsed)
}

func executeLinter(issues chan *Issue, name, command, pattern, paths string) {
	debug("linting with %s: %s", name, command)

	start := time.Now()
	if p, ok := predefinedPatterns[pattern]; ok {
		pattern = p
	}
	re, err := regexp.Compile(pattern)
	kingpin.FatalIfError(err, "invalid pattern for '"+command+"'")

	command = strings.Replace(command, "{path}", paths, -1)
	debug("executing %s", command)
	cmd := exec.Command("/bin/sh", "-c", command)
	out, err := cmd.CombinedOutput()
	if err != nil {
		if _, ok := err.(*exec.ExitError); !ok {
			debug("warning: %s failed: %s", command, err)
			return
		}
		debug("warning: %s returned %s", command, err)
	}
	for _, line := range bytes.Split(out, []byte("\n")) {
		groups := re.FindAllSubmatch(line, -1)
		if groups == nil {
			continue
		}
		issue := &Issue{}
		for i, name := range re.SubexpNames() {
			part := string(groups[0][i])
			switch name {
			case "path":
				issue.path = part

			case "line":
				n, err := strconv.ParseInt(part, 10, 32)
				kingpin.FatalIfError(err, "line matched invalid integer")
				issue.line = int(n)

			case "col":
				n, err := strconv.ParseInt(part, 10, 32)
				kingpin.FatalIfError(err, "col matched invalid integer")
				issue.col = int(n)

			case "message":
				issue.message = part

			case "":

			default:
				kingpin.Fatalf("invalid subgroup %s", name)
			}
		}
		if m, ok := linterMessageOverrideFlag[name]; ok {
			issue.message = strings.Replace(m, "{message}", issue.message, -1)
		}
		if sev, ok := linterSeverityFlag[name]; ok {
			issue.severity = Severity(sev)
		} else {
			issue.severity = "error"
		}
		issues <- issue
	}

	elapsed := time.Now().Sub(start)
	debug("%s linter took %s", name, elapsed)
}
<commit_msg>Add --fast flag.<commit_after>package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/alecthomas/kingpin"
)

type Severity string

// Linter message severity levels.
const (
	Warning Severity = "warning"
	Error   Severity = "error"
)

type Linter string

func (l Linter) Command() string {
	return string(l[0:strings.Index(string(l), ":")])
}

func (l Linter) Pattern() string {
	return string(l[strings.Index(string(l), ":"):])
}

var (
	predefinedPatterns = map[string]string{
		"PATH:LINE:COL:MESSAGE": `(?P<path>[^:]+):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)`,
		"PATH:LINE:MESSAGE":     `(?P<path>[^:]+):(?P<line>\d+):\s*(?P<message>.*)`,
	}
	lintersFlag = map[string]string{
		// main.go:8:10: should omit type map[string]string from declaration of var linters; it will be inferred from the right-hand side
		"golint": "golint {path}:PATH:LINE:COL:MESSAGE",
		// test/stutter.go:19: missing argument for Printf("%d"): format reads arg 1, have only 0 args
		"vet":         "go tool vet {path}:PATH:LINE:MESSAGE",
		"gotype":      "gotype {path}:PATH:LINE:COL:MESSAGE",
		"errcheck":    `errcheck {path}:(?P<path>[^:]+):(?P<line>\d+):(?P<col>\d+)\t(?P<message>.*)`,
		"varcheck":    "varcheck {path}:PATH:LINE:MESSAGE",
		"structcheck": "structcheck {path}:PATH:LINE:MESSAGE",
		"defercheck":  "defercheck {path}:PATH:LINE:MESSAGE",
	}
	linterMessageOverrideFlag = map[string]string{
		"errcheck":    "error return value not checked ({message})",
		"varcheck":    "unused global variable {message}",
		"structcheck": "unused struct field {message}",
	}
	linterSeverityFlag = map[string]string{
		"errcheck":    "warning",
		"golint":      "warning",
		"varcheck":    "warning",
		"structcheck": "warning",
	}
	installMap = map[string]string{
		"golint":      "go get github.com/golang/lint/golint",
		"gotype":      "go get code.google.com/p/go.tools/cmd/gotype",
		"errcheck":    "go get github.com/kisielk/errcheck",
		"defercheck":  "go get github.com/opennota/check/cmd/defercheck",
		"varcheck":    "go get github.com/opennota/check/cmd/varcheck",
		"structcheck": "go get github.com/opennota/check/cmd/structcheck",
	}
	pathArg            = kingpin.Arg("path", "Directory to lint.").Default(".").String()
	fastFlag           = kingpin.Flag("fast", "Only run fast linters.").Bool()
	installFlag        = kingpin.Flag("install", "Attempt to install all known linters.").Bool()
	disableLintersFlag = kingpin.Flag("disable", "List of linters to disable.").PlaceHolder("LINTER").Short('D').Strings()
	debugFlag          = kingpin.Flag("debug", "Display messages for failed linters, etc.").Short('d').Bool()
	concurrencyFlag    = kingpin.Flag("concurrency", "Number of concurrent linters to run.").Default("16").Short('j').Int()
)

func init() {
	kingpin.Flag("linter", "Specify a linter.").PlaceHolder("NAME:COMMAND:PATTERN").StringMapVar(&lintersFlag)
	kingpin.Flag("message-overrides", "Override message from linter. {message} will be expanded to the original message.").PlaceHolder("LINTER:MESSAGE").StringMapVar(&linterMessageOverrideFlag)
	kingpin.Flag("severity", "Map of linter severities.").PlaceHolder("LINTER:SEVERITY").StringMapVar(&linterSeverityFlag)
}

type Issue struct {
	severity Severity
	path     string
	line     int
	col      int
	message  string
}

func (m *Issue) String() string {
	col := ""
	if m.col != 0 {
		col = fmt.Sprintf("%d", m.col)
	}
	return fmt.Sprintf("%s:%d:%s:%s: %s", m.path, m.line, col, m.severity, m.message)
}

type Issues []*Issue

func (m Issues) Len() int      { return len(m) }
func (m Issues) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m Issues) Less(i, j int) bool {
	return m[i].path < m[j].path || m[i].line < m[j].line || m[i].col < m[j].col
}

func debug(format string, args ...interface{}) {
	if *debugFlag {
		fmt.Fprintf(os.Stderr, "DEBUG: "+format+"\n", args...)
	}
}

func formatLinters() string {
	w := bytes.NewBuffer(nil)
	for command, description := range lintersFlag {
		linter := Linter(description)
		fmt.Fprintf(w, "    %s -> %s -> %s\n", command, linter.Command(), linter.Pattern())
	}
	return w.String()
}

func formatSeverity() string {
	w := bytes.NewBuffer(nil)
	for name, severity := range linterSeverityFlag {
		fmt.Fprintf(w, "    %s -> %s\n", name, severity)
	}
	return w.String()
}

func main() {
	kingpin.CommandLine.Help = fmt.Sprintf(`Aggregate and normalise the output of a whole bunch of Go linters.

Default linters:

%s

Severity override map (default is "error"):

%s
`, formatLinters(), formatSeverity())
	kingpin.Parse()

	if *fastFlag {
		*disableLintersFlag = append(*disableLintersFlag, "structcheck", "varcheck", "errcheck")
	}

	if *installFlag {
		for name, cmd := range installMap {
			fmt.Printf("Installing %s -> %s\n", name, cmd)
			c := exec.Command("/bin/sh", "-c", cmd)
			c.Stdout = os.Stdout
			c.Stderr = os.Stderr
			err := c.Run()
			if err != nil {
				kingpin.CommandLine.Errorf(os.Stderr, "failed to install %s: %s", name, err)
			}
		}
		return
	}

	runtime.GOMAXPROCS(*concurrencyFlag)

	disable := map[string]bool{}
	for _, linter := range *disableLintersFlag {
		disable[linter] = true
	}

	start := time.Now()
	paths := *pathArg
	concurrency := make(chan bool, *concurrencyFlag)
	issues := make(chan *Issue, 1000)
	wg := &sync.WaitGroup{}
	for name, description := range lintersFlag {
		if _, ok := disable[name]; ok {
			debug("linter %s disabled", name)
			continue
		}
		parts := strings.SplitN(description, ":", 2)
		command := parts[0]
		pattern := parts[1]

		wg.Add(1)
		go func(name, command, pattern string) {
			concurrency <- true
			executeLinter(issues, name, command, pattern, paths)
			<-concurrency
			wg.Done()
		}(name, command, pattern)
	}

	wg.Wait()
	close(issues)
	for issue := range issues {
		fmt.Printf("%s\n", issue)
	}
	elapsed := time.Now().Sub(start)
	debug("total elapsed time %s", elapsed)
}

func executeLinter(issues chan *Issue, name, command, pattern, paths string) {
	debug("linting with %s: %s", name, command)

	start := time.Now()
	if p, ok := predefinedPatterns[pattern]; ok {
		pattern = p
	}
	re, err := regexp.Compile(pattern)
	kingpin.FatalIfError(err, "invalid pattern for '"+command+"'")

	command = strings.Replace(command, "{path}", paths, -1)
	debug("executing %s", command)
	cmd := exec.Command("/bin/sh", "-c", command)
	out, err := cmd.CombinedOutput()
	if err != nil {
		if _, ok := err.(*exec.ExitError); !ok {
			debug("warning: %s failed: %s", command, err)
			return
		}
		debug("warning: %s returned %s", command, err)
	}
	for _, line := range bytes.Split(out, []byte("\n")) {
		groups := re.FindAllSubmatch(line, -1)
		if groups == nil {
			continue
		}
		issue := &Issue{}
		for i, name := range re.SubexpNames() {
			part := string(groups[0][i])
			switch name {
			case "path":
				issue.path = part

			case "line":
				n, err := strconv.ParseInt(part, 10, 32)
				kingpin.FatalIfError(err, "line matched invalid integer")
				issue.line = int(n)

			case "col":
				n, err := strconv.ParseInt(part, 10, 32)
				kingpin.FatalIfError(err, "col matched invalid integer")
				issue.col = int(n)

			case "message":
				issue.message = part

			case "":

			default:
				kingpin.Fatalf("invalid subgroup %s", name)
			}
		}
		if m, ok := linterMessageOverrideFlag[name]; ok {
			issue.message = strings.Replace(m, "{message}", issue.message, -1)
		}
		if sev, ok := linterSeverityFlag[name]; ok {
			issue.severity = Severity(sev)
		} else {
			issue.severity = "error"
		}
		issues <- issue
	}

	elapsed := time.Now().Sub(start)
	debug("%s linter took %s", name, elapsed)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"strings"
	"time"

	"gopkg.in/mgo.v2/bson"

	log "github.com/Sirupsen/logrus"
	"github.com/bmuller/arrow/lib"
	"github.com/brandfolder/gin-gorelic"
	"github.com/carlescere/scheduler"
	"github.com/dwarvesf/working-on/db"
	"github.com/gin-gonic/contrib/ginrus"
	"github.com/gin-gonic/gin"
	"github.com/nlopes/slack"
)

type Item struct {
	ID        bson.ObjectId `json:"id" bson:"_id"`
	UserID    string        `json:"user_id" bson:"user_id"`
	Name      string        `json:"user_name" bson:"user_name"`
	Text      string        `json:"text" bson:"text"`
	CreatedAt time.Time     `json:"created_at" bson:"created_at"`
}

func main() {

	// Read configuration from file and env
	port := os.Getenv("PORT")
	digestTime := os.Getenv("DIGEST_TIME")
	gorelic.InitNewrelicAgent(os.Getenv("NEW_RELIC_LICENSE_KEY"), "working", false)

	// Setup schedule jobs
	digestJob := postDigest
	scheduler.Every().Day().At(digestTime).Run(digestJob)

	// Prepare router
	router := gin.New()
	router.Use(gorelic.Handler)
	router.Use(ginrus.Ginrus(log.StandardLogger(), time.RFC3339, true))

	// router.LoadHTMLGlob("templates/*.tmpl.html")
	router.Static("/static", "static")
	router.POST("/on", on)
	router.POST("/til", til)

	// Start server
	router.Run(":" + port)
}

func til(c *gin.Context) {

	text := c.PostForm("text")
	text = strings.TrimSpace(text)

	if text == "" {
		log.Fatalln("Message is nil")
		return
	}

	userID := c.PostForm("user_id")
	userName := c.PostForm("user_name")

	text = text + " #til"
	addItem(text, userID, userName)
}

func on(c *gin.Context) {

	text := c.PostForm("text")
	text = strings.TrimSpace(text)

	if text == "" {
		log.Fatalln("Message is nil")
		return
	}

	userID := c.PostForm("user_id")
	userName := c.PostForm("user_name")

	addItem(text, userID, userName)
}

// Message will be passed to server with '-' prefix via various way
//	+ Direct message with the bots
//	+ Use slash command `/working <message>`
//	+ Use cli `working-on <message>`
//	+ Might use Chrome plugin
//	+ ...
// Token is secondary param to indicate the user
func addItem(text string, userID string, userName string) {

	// Parse token and message
	var item Item

	item.ID = bson.NewObjectId()
	item.CreatedAt = time.Now()
	item.Name = userName
	item.UserID = userID
	item.Text = text

	ctx, err := db.NewContext()
	if err != nil {
		panic(err)
	}

	defer ctx.Close()

	// Add Item to database
	err = ctx.C("items").Insert(item)
	if err != nil {
		log.Fatalln(err)
	}

	// Repost to the target channel
	channel := os.Getenv("WORKING_CHANNEL")
	botToken := os.Getenv("BOT_TOKEN")

	if botToken == "" {
		log.Fatal("No token provided")
	}

	// <@U024BE7LH|bob>
	title := "*" + userName + "* is working on: " + text

	postWorkingItem(botToken, channel, title)

	// Parse configuration
	var configuration Configuration
	bytes, err := ioutil.ReadFile("setting.json")
	if err != nil {
		log.Fatalln("Cannot read setting file")
	}

	err = json.Unmarshal(bytes, &configuration)
	if err != nil {
		log.Fatalln("Cannot parse setting")
	}

	// Post item to project group
	for _, config := range configuration.Items {
		for _, tag := range config.Tags {
			if strings.Contains(text, tag) {
				log.Info("Hit" + tag)
				log.Info("Token: " + config.Token)

				// Post to target channel
				postWorkingItem(config.Token, config.Channel, title)
			}
		}
	}
}

func postWorkingItem(token string, channel string, text string) {
	s := slack.New(token)
	params := slack.PostMessageParameters{}
	params.IconURL = "http://i.imgur.com/fLcxkel.png"
	params.Username = "oshin"
	s.PostMessage(channel, text, params)
}

type Configuration struct {
	Items []ConfigurationItem `json:"items"`
}

type ConfigurationItem struct {
	Channel string   `json:"channel"`
	Tags    []string `json:"tags"`
	Token   string   `json:"token"`
}

// Post summary to Slack channel
func postDigest() {

	channel := os.Getenv("DIGEST_CHANNEL")
	botToken := os.Getenv("BOT_TOKEN")

	if botToken == "" {
		log.Fatal("No token provided")
		os.Exit(1)
	}

	s := slack.New(botToken)
	users, err := s.GetUsers()

	if err != nil {
		log.Fatal("Cannot get users")
		os.Exit(1)
	}

	ctx, err := db.NewContext()
	if err != nil {
		panic(err)
	}

	defer ctx.Close()

	log.Info("Preparing data")
	// If count > 0, it means there is data to show
	count := 0
	title := " >> Yesterday I did: "
	params := slack.PostMessageParameters{}
	fields := []slack.AttachmentField{}

	yesterday := arrow.Yesterday().UTC()
	toDate := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, time.UTC)

	// Prepare attachment of done items
	for _, user := range users {

		if user.IsBot || user.Deleted {
			continue
		}

		// log.Info("Process user: " + user.Name + " - " + user.Id)

		// Query done items from Database
		var values string
		var items []Item

		err = ctx.C("items").Find(bson.M{"$and": []bson.M{
			bson.M{"user_id": user.Id},
			bson.M{"created_at": bson.M{"$gt": toDate}},
		},
		}).All(&items)

		if err != nil {
			log.Fatal("Cannot query done items.")
			os.Exit(1)
		}

		for _, item := range items {
			values = "\t" + values + " + " + item.Text + "\n"
		}

		// <@U024BE7LH|bob>
		if len(items) > 0 {

			count = count + 1
			field := slack.AttachmentField{
				Title: "@" + user.Name,
				Value: values,
			}

			fields = append(fields, field)
		}
	}

	params.Attachments = []slack.Attachment{
		slack.Attachment{
			Color:  "#7CD197",
			Fields: fields,
		},
	}

	params.IconURL = "http://i.imgur.com/fLcxkel.png"
	params.Username = "oshin"

	if count > 0 {
		s.PostMessage(channel, title, params)
	}
}
<commit_msg>update postDigest to post via multiple bots<commit_after>package main

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"os"
	"strings"
	"time"

	"gopkg.in/mgo.v2/bson"

	log "github.com/Sirupsen/logrus"
	"github.com/bmuller/arrow/lib"
	"github.com/brandfolder/gin-gorelic"
	"github.com/carlescere/scheduler"
	"github.com/dwarvesf/working-on/db"
	"github.com/gin-gonic/contrib/ginrus"
	"github.com/gin-gonic/gin"
	"github.com/nlopes/slack"
)

type Item struct {
	ID        bson.ObjectId `json:"id" bson:"_id"`
	UserID    string        `json:"user_id" bson:"user_id"`
	Name      string        `json:"user_name" bson:"user_name"`
	Text      string        `json:"text" bson:"text"`
	CreatedAt time.Time     `json:"created_at" bson:"created_at"`
}

func main() {

	// Read configuration from file and env
	port := os.Getenv("PORT")
	digestTime := os.Getenv("DIGEST_TIME")
	gorelic.InitNewrelicAgent(os.Getenv("NEW_RELIC_LICENSE_KEY"), "working", false)

	digestConfig, err := parseConfig("digest.json")
	if err != nil {
		log.Fatalln(err)
	}

	// Setup schedule digest jobs
	for _, i := range digestConfig.Items {
		// channel := os.Getenv("DIGEST_CHANNEL")
		// botToken := os.Getenv("BOT_TOKEN")

		var strictTag bool
		if i.Tags != nil {
			strictTag = true
		}
		digestJob := postDigest(i.Channel, i.Token, i.Tags, strictTag)
		_, err := scheduler.Every().Day().At(digestTime).Run(digestJob)
		if err != nil {
			log.Infoln(err)
		}
	}

	settingConfig, err := parseConfig("setting.json")
	if err != nil {
		log.Fatalln(err)
	}

	// Prepare router
	router := gin.New()
	router.Use(gorelic.Handler)
	router.Use(ginrus.Ginrus(log.StandardLogger(), time.RFC3339, true))

	// router.LoadHTMLGlob("templates/*.tmpl.html")
	router.Static("/static", "static")
	router.POST("/on", on(*settingConfig))
	router.POST("/til", til(*settingConfig))

	// Start server
	router.Run(":" + port)
}

func til(config Configuration) func(c *gin.Context) {
	return func(c *gin.Context) {
		text := c.PostForm("text")
		text = strings.TrimSpace(text)

		if text == "" {
			log.Fatalln("Message is nil")
			return
		}

		userID := c.PostForm("user_id")
		userName := c.PostForm("user_name")

		text = text + " #til"
		addItem(text, userID, userName, config)
	}
}

func on(config Configuration) func(c *gin.Context) {
	return func(c *gin.Context) {
		text := c.PostForm("text")
		text = strings.TrimSpace(text)

		if text == "" {
			log.Fatalln("Message is nil")
			return
		}

		userID := c.PostForm("user_id")
		userName := c.PostForm("user_name")

		addItem(text, userID, userName, config)
	}
}

// Message will be passed to server with '-' prefix via various way
//	+ Direct message with the bots
//	+ Use slash command `/working <message>`
//	+ Use cli `working-on <message>`
//	+ Might use Chrome plugin
//	+ ...
// Token is secondary param to indicate the user
func addItem(text string, userID string, userName string, configuration Configuration) {

	// Parse token and message
	var item Item

	item.ID = bson.NewObjectId()
	item.CreatedAt = time.Now()
	item.Name = userName
	item.UserID = userID
	item.Text = text

	ctx, err := db.NewContext()
	if err != nil {
		panic(err)
	}

	defer ctx.Close()

	// Add Item to database
	err = ctx.C("items").Insert(item)
	if err != nil {
		log.Fatalln(err)
	}

	// Repost to the target channel
	channel := os.Getenv("WORKING_CHANNEL")
	botToken := os.Getenv("BOT_TOKEN")

	if botToken == "" {
		log.Fatal("No token provided")
	}

	// <@U024BE7LH|bob>
	title := "*" + userName + "* is working on: " + text

	postWorkingItem(botToken, channel, title)

	// Post item to project group
	for _, config := range configuration.Items {
		for _, tag := range config.Tags {
			if strings.Contains(text, tag) {
				log.Info("Hit" + tag)
				log.Info("Token: " + config.Token)

				// Post to target channel
				postWorkingItem(config.Token, config.Channel, title)
			}
		}
	}
}

func postWorkingItem(token string, channel string, text string) {
	s := slack.New(token)
	params := slack.PostMessageParameters{}
	params.IconURL = "http://i.imgur.com/fLcxkel.png"
	params.Username = "oshin"
	s.PostMessage(channel, text, params)
}

type Configuration struct {
	Items []ConfigurationItem `json:"items"`
}

type ConfigurationItem struct {
	Channel string   `json:"channel"`
	Tags    []string `json:"tags"`
	Token   string   `json:"token"`
}

func parseConfig(path string) (*Configuration, error) {
	var configuration Configuration

	// Parse configuration
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, errors.New("Cannot read setting file")
	}

	err = json.Unmarshal(bytes, &configuration)
	if err != nil {
		return nil, errors.New("Cannot parse setting")
	}

	return &configuration, nil
}

// Post summary to Slack channel.
// Only post to specific channel when tags are met.
func postDigest(channel, botToken string, tags []string, strictTag bool) func() {
	return func() {
		if botToken == "" {
			log.Fatal("No token provided")
			os.Exit(1)
		}

		s := slack.New(botToken)
		users, err := s.GetUsers()

		if err != nil {
			log.Fatal("Cannot get users")
			os.Exit(1)
		}

		ctx, err := db.NewContext()
		if err != nil {
			panic(err)
		}

		defer ctx.Close()

		log.Info("Preparing data")
		// If count > 0, it means there is data to show
		count := 0
		title := " >> Yesterday I did: "
		params := slack.PostMessageParameters{}
		fields := []slack.AttachmentField{}

		yesterday := arrow.Yesterday().UTC()
		toDate := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, time.UTC)

		// Prepare attachment of done items
		for _, user := range users {

			if user.IsBot || user.Deleted {
				continue
			}

			// log.Info("Process user: " + user.Name + " - " + user.Id)

			// Query done items from Database
			var values string
			var items []Item

			err = ctx.C("items").Find(
				bson.M{
					"$and": []bson.M{
						bson.M{"user_id": user.Id},
						bson.M{"created_at": bson.M{"$gt": toDate}},
					},
				}).All(&items)

			if err != nil {
				log.Fatal("Cannot query done items.")
				os.Exit(1)
			}

			for i, item := range items {
				// delete items which text does not contain tags
				if strictTag && tags != nil {
					var containTag bool
					for _, tag := range tags {
						if strings.Contains(item.Text, tag) {
							containTag = true
							break
						}
					}

					// if item's Text doesn't contains any tags then delete it
					// from the items slice
					if !containTag {
						items = append(items[:i], items[i+1:]...)
					}
				}

				// construct text format
				values = "\t" + values + " + " + item.Text + "\n"
			}

			// <@U024BE7LH|bob>
			if len(items) > 0 {

				count = count + 1
				field := slack.AttachmentField{
					Title: "@" + user.Name,
					Value: values,
				}

				fields = append(fields, field)
			}
		}

		params.Attachments = []slack.Attachment{
			slack.Attachment{
				Color:  "#7CD197",
				Fields: fields,
			},
		}

		params.IconURL = "http://i.imgur.com/fLcxkel.png"
		params.Username = "oshin"

		if count > 0 {
			s.PostMessage(channel, title, params)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"
)

type Line struct {
	Epoch   float64
	Db      int
	Command string
	Key     string
	Args    string
}

type Command struct {
	Name string
	Cnt  int
	Sum  float64
	Avg  float64
}

type Commands []Command

type Profile struct {
	Call string
	Args string
	Cnt  int
	Max  float64
	Min  float64
	Avg  float64
	Sum  float64
}

type Profiles []Profile

type ByCnt struct{ Profiles }
type ByMax struct{ Profiles }
type ByAvg struct{ Profiles }

type ByCommand struct{ Commands }
type ByHeavyCommand struct{ Commands }

func (p Profiles) Len() int      { return len(p) }
func (p Profiles) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func (c Commands) Len() int      { return len(c) }
func (c Commands) Swap(i, j int) { c[i], c[j] = c[j], c[i] }

func (p ByCnt) Less(i, j int) bool { return p.Profiles[i].Cnt < p.Profiles[j].Cnt }
func (p ByMax) Less(i, j int) bool { return p.Profiles[i].Max < p.Profiles[j].Max }
func (p ByAvg) Less(i, j int) bool { return p.Profiles[i].Avg < p.Profiles[j].Avg }

func (c ByCommand) Less(i, j int) bool      { return c.Commands[i].Cnt < c.Commands[j].Cnt }
func (c ByHeavyCommand) Less(i, j int) bool { return c.Commands[i].Sum < c.Commands[j].Sum }

const (
	SORT_TYPE_BY_MAX = "max"
	SORT_TYPE_BY_AVG = "avg"
	SORT_TYPE_BY_CNT = "cnt"
)

var (
	filePath    = flag.String("f", "", "redis-cli monitor output file")
	listNum     = flag.Int("n", 10, "Show Slowest Calls Count")
	sortType    = flag.String("s", "max", "Set SlowestCalls Type: max, avg, cnt")
	minCountNum = flag.Int("min", 0, "Show Slowest Calls Count over the minCountNum")

	// regexp
	// refs: https://play.golang.org/p/yl6B1oWtvE
	// 0: line
	// 1: epoch
	// 2; db
	// 3: command
	// 4: command args
	// 5: key
	// 6: args
	lineRegexpRule = `([\d\.]+)\s\[(\d+)\s\d+\.\d+\.\d+\.\d+:\d+]\s"(\w+)"(\s"([^(?<!\\)"]+)"|\s"([^(?<!\\)"]+)"\s(.+)|)`
	lineRegexp     = regexp.MustCompile(lineRegexpRule)

	readFile    *os.File
	monitorLogs Profiles
	commands    Commands

	logIndex  = make(map[string]int)
	logLength = 0
	logCursor = 0

	commandIndex  = make(map[string]int)
	commandLength = 0
	commandCursor = 0

	lineCount = 0
)

func SetProfileIndex(call string) {
	if _, ok := logIndex[call]; ok {
		logCursor = logIndex[call]
	} else {
		logIndex[call] = logLength
		logCursor = logLength
		logLength++
		monitorLogs = append(monitorLogs, Profile{
			Call: call,
			Cnt:  0,
			Min:  0,
			Avg:  0,
			Sum:  0,
		})
	}
}

func SetCommandIndex(command string) {
	if _, ok := commandIndex[command]; ok {
		commandCursor = commandIndex[command]
	} else {
		commandIndex[command] = commandLength
		commandCursor = commandLength
		commandLength++
		commands = append(commands, Command{
			Name: command,
			Cnt:  0,
			Avg:  0,
			Sum:  0,
		})
	}
}

func main() {
	flag.Parse()

	if !strings.Contains(*sortType, SORT_TYPE_BY_MAX) && !strings.Contains(*sortType, SORT_TYPE_BY_AVG) && !strings.Contains(*sortType, SORT_TYPE_BY_CNT) {
		log.Fatal("Please set sort type max, avg or cnt")
	}

	if *filePath != "" {
		rf, err := os.Open(*filePath)
		if err != nil {
			log.Fatal(err)
		}
		readFile = rf
		defer rf.Close()
	} else {
		readFile = os.Stdin
	}

	scanner := bufio.NewScanner(readFile)
	var beforeLine Line

	for scanner.Scan() {
		group := lineRegexp.FindStringSubmatch(scanner.Text())
		if 1 > len(group) {
			continue
		}

		epoch, err := strconv.ParseFloat(group[1], 64)
		if err != nil {
			log.Println(err)
			continue
		}
		db, err := strconv.Atoi(group[2])
		if err != nil {
			log.Println(err)
			continue
		}

		line := Line{
			Epoch:   epoch,
			Db:      db,
			Command: group[3],
			Key:     group[5],
			Args:    group[6],
		}

		var commandTime float64
		if beforeLine.Command != "" {
			commandTime = line.Epoch - beforeLine.Epoch
		} else {
			commandTime = 0
		}

		SetProfileIndex(fmt.Sprintf("%s %s", line.Command, line.Key))
		SetCommandIndex(line.Command)

		if monitorLogs[logCursor].Max < commandTime {
			monitorLogs[logCursor].Max = commandTime
		}

		if commandTime < monitorLogs[logCursor].Min {
			monitorLogs[logCursor].Min = commandTime
		}
		monitorLogs[logCursor].Cnt++
		monitorLogs[logCursor].Sum = monitorLogs[logCursor].Sum + commandTime
		monitorLogs[logCursor].Avg = monitorLogs[logCursor].Sum / float64(monitorLogs[logCursor].Cnt)

		commands[commandCursor].Cnt++
		commands[commandCursor].Sum = commands[commandCursor].Sum + commandTime

		beforeLine = line
		lineCount++
	}

	PrintResult()
}

func PrintTitle(key string) {
	fmt.Println(key)
	fmt.Println("==================================")
}

func PrintResult() {
	PrintTitle("Overall Stats")
	fmt.Printf("LineCount \t %d \n\n", lineCount)
	fmt.Printf("\n")

	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 8, 0, '\t', tabwriter.AlignRight)
	PrintTitle("Commands Rate")
	sort.Sort(sort.Reverse((ByCommand{commands})))
	for _, v := range commands {
		fmt.Fprintf(w, "%s\t %d \n", v.Name, v.Cnt)
	}
	w.Flush()
	fmt.Printf("\n")

	PrintTitle("Heavy Commands")
	fmt.Fprintln(w, "Command \tSum(msec)")
	sort.Sort(sort.Reverse((ByHeavyCommand{commands})))
	for _, v := range commands {
		fmt.Fprintf(w, "%s \t %f \n", v.Name, v.Sum)
	}
	w.Flush()
	fmt.Printf("\n")

	PrintTitle("Slowest Calls")
	if strings.Contains(*sortType, SORT_TYPE_BY_AVG) {
		sort.Sort(sort.Reverse((ByAvg{monitorLogs})))
	} else if strings.Contains(*sortType, SORT_TYPE_BY_CNT) {
		sort.Sort(sort.Reverse((ByCnt{monitorLogs})))
	} else {
		sort.Sort(sort.Reverse((ByMax{monitorLogs})))
	}

	fmt.Fprintln(w, "KEY \tCount \tMax(msec) \t Avg(msec)")
	for i, v := range monitorLogs {
		if v.Cnt < *minCountNum {
			continue
		}
		if *listNum < i {
			break
		}
		fmt.Fprintf(w, "%s\t %d \t %f\t %f\n", v.Call, v.Cnt, v.Max, v.Avg)
	}
	w.Flush()
}
<commit_msg>Fix count calc method<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"
)

type Line struct {
	Epoch   float64
	Db      int
	Command string
	Key     string
	Args    string
}

type Command struct {
	Name string
	Cnt  int
	Sum  float64
	Avg  float64
}

type Commands []Command

type Profile struct {
	Call string
	Args string
	Cnt  int
	Max  float64
	Min  float64
	Avg  float64
	Sum  float64
}

type Profiles []Profile

type ByCnt struct{ Profiles }
type ByMax struct{ Profiles }
type ByAvg struct{ Profiles }

type ByCommand struct{ Commands }
type ByHeavyCommand struct{ Commands }

func (p Profiles) Len() int      { return len(p) }
func (p Profiles) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func (c Commands) Len() int      { return len(c) }
func (c Commands) Swap(i, j int) { c[i], c[j] = c[j], c[i] }

func (p ByCnt) Less(i, j int) bool { return p.Profiles[i].Cnt < p.Profiles[j].Cnt }
func (p ByMax) Less(i, j int) bool { return p.Profiles[i].Max < p.Profiles[j].Max }
func (p ByAvg) Less(i, j int) bool { return p.Profiles[i].Avg < p.Profiles[j].Avg }

func (c ByCommand) Less(i, j int) bool      { return c.Commands[i].Cnt < c.Commands[j].Cnt }
func (c ByHeavyCommand) Less(i, j int) bool { return c.Commands[i].Sum < c.Commands[j].Sum }

const (
	SORT_TYPE_BY_MAX = "max"
	SORT_TYPE_BY_AVG = "avg"
	SORT_TYPE_BY_CNT = "cnt"
)

var (
	filePath    = flag.String("f", "", "redis-cli monitor output file")
	listNum     = flag.Int("n", 10, "Show Slowest Calls Count")
	sortType    = flag.String("s", "max", "Set SlowestCalls Type: max, avg, cnt")
	minCountNum = flag.Int("min", 0, "Show Slowest Calls Count over the minCountNum")

	// regexp
	// refs: https://play.golang.org/p/yl6B1oWtvE
	// 0: line
	// 1: epoch
	// 2; db
	// 3: command
	// 4: command args
	// 5: key
	// 6: args
	lineRegexpRule = `([\d\.]+)\s\[(\d+)\s\d+\.\d+\.\d+\.\d+:\d+]\s"(\w+)"(\s"([^(?<!\\)"]+)"|\s"([^(?<!\\)"]+)"\s(.+)|)`
	lineRegexp     = regexp.MustCompile(lineRegexpRule)

	readFile    *os.File
	monitorLogs Profiles
	commands    Commands

	logIndex  = make(map[string]int)
	logLength = 0
	logCursor = 0

	commandIndex  = make(map[string]int)
	commandLength = 0
	commandCursor = 0

	lineCount = 0
)

func SetProfileIndex(call string) {
	if _, ok := logIndex[call]; ok {
		logCursor = logIndex[call]
	} else {
		logIndex[call] = logLength
		logCursor = logLength
		logLength++
		monitorLogs = append(monitorLogs, Profile{
			Call: call,
			Cnt:  0,
			Min:  0,
			Avg:  0,
			Sum:  0,
		})
	}
}

func SetCommandIndex(command string) {
	if _, ok := commandIndex[command]; ok {
		commandCursor = commandIndex[command]
	} else {
		commandIndex[command] = commandLength
		commandCursor = commandLength
		commandLength++
		commands = append(commands, Command{
			Name: command,
			Cnt:  0,
			Avg:  0,
			Sum:  0,
		})
	}
}

func main() {
	flag.Parse()

	if !strings.Contains(*sortType, SORT_TYPE_BY_MAX) && !strings.Contains(*sortType, SORT_TYPE_BY_AVG) && !strings.Contains(*sortType, SORT_TYPE_BY_CNT) {
		log.Fatal("Please set sort type max, avg or cnt")
	}

	if *filePath != "" {
		rf, err := os.Open(*filePath)
		if err != nil {
			log.Fatal(err)
		}
		readFile = rf
		defer rf.Close()
	} else {
		readFile = os.Stdin
	}

	scanner := bufio.NewScanner(readFile)
	var beforeLine Line

	for scanner.Scan() {
		group := lineRegexp.FindStringSubmatch(scanner.Text())
		if 1 > len(group) {
			continue
		}

		epoch, err := strconv.ParseFloat(group[1], 64)
		if err != nil {
			log.Println(err)
			continue
		}
		db, err := strconv.Atoi(group[2])
		if err != nil {
			log.Println(err)
			continue
		}

		line := Line{
			Epoch:   epoch,
			Db:      db,
			Command: group[3],
			Key:     group[5],
			Args:    group[6],
		}

		var commandTime float64
		if beforeLine.Command != "" {
			commandTime = line.Epoch - beforeLine.Epoch
		} else {
			commandTime = 0
		}

		SetProfileIndex(fmt.Sprintf("%s %s", line.Command, line.Key))
		SetCommandIndex(line.Command)

		if monitorLogs[logCursor].Max < commandTime {
			monitorLogs[logCursor].Max = commandTime
		}

		if commandTime < monitorLogs[logCursor].Min {
			monitorLogs[logCursor].Min = commandTime
		}
		monitorLogs[logCursor].Cnt++
		monitorLogs[logCursor].Sum = monitorLogs[logCursor].Sum + commandTime
		monitorLogs[logCursor].Avg = monitorLogs[logCursor].Sum / float64(monitorLogs[logCursor].Cnt)

		commands[commandCursor].Cnt++
		commands[commandCursor].Sum = commands[commandCursor].Sum + commandTime

		beforeLine = line
		lineCount++
	}

	PrintResult()
}

func PrintTitle(key string) {
	fmt.Println(key)
	fmt.Println("==================================")
}

func PrintResult() {
	PrintTitle("Overall Stats")
	fmt.Printf("LineCount \t %d \n\n", lineCount)
	fmt.Printf("\n")

	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 8, 0, '\t', tabwriter.AlignRight)
	PrintTitle("Commands Rate")
	sort.Sort(sort.Reverse((ByCommand{commands})))
	for _, v := range commands {
		fmt.Fprintf(w, "%s\t %d \n", v.Name, v.Cnt)
	}
	w.Flush()
	fmt.Printf("\n")

	PrintTitle("Heavy Commands")
	fmt.Fprintln(w, "Command \tSum(msec)")
	sort.Sort(sort.Reverse((ByHeavyCommand{commands})))
	for _, v := range commands {
		fmt.Fprintf(w, "%s \t %f \n", v.Name, v.Sum)
	}
	w.Flush()
	fmt.Printf("\n")

	PrintTitle("Slowest Calls")
	if strings.Contains(*sortType, SORT_TYPE_BY_AVG) {
		sort.Sort(sort.Reverse((ByAvg{monitorLogs})))
	} else if strings.Contains(*sortType, SORT_TYPE_BY_CNT) {
		sort.Sort(sort.Reverse((ByCnt{monitorLogs})))
	} else {
		sort.Sort(sort.Reverse((ByMax{monitorLogs})))
	}

	fmt.Fprintln(w, "KEY \tCount \tMax(msec) \t Avg(msec)")
	count := 0
	for _, v := range monitorLogs {
		if v.Cnt < *minCountNum {
			continue
		}
		if *listNum < count {
			break
		}
		fmt.Fprintf(w, "%s\t %d \t %f\t %f\n", v.Call, v.Cnt, v.Max, v.Avg)
		count++
	}
	w.Flush()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"gopkg.in/BlueDragonX/go-log.v0"
	"gopkg.in/BlueDragonX/go-settings.v0"
	"os"
	"os/signal"
	"syscall"
)

var (
	logger *log.Logger
	config *settings.Settings
)

func configure() {
	options := ParseOptionsOrExit(os.Args)
	config = settings.LoadOrExit(options.Config)
	config.Set("exec", options.Exec)

	if len(options.Etcd) > 0 {
		config.Set("etcd.uris", options.Etcd)
	}

	if options.LogTarget != "" {
		config.Set("logging.target", options.LogTarget)
	}

	if options.LogLevel != "" {
		config.Set("logging.level", options.LogLevel)
	}

	logOpts := []log.Option{}
	if logTarget, err := config.String("logging.target"); err == nil {
		logOpts = append(logOpts, log.Target(logTarget))
	}
	if logLevel, err := config.String("logging.level"); err == nil {
		logOpts = append(logOpts, log.Level(logLevel))
	}
	logger = log.NewOrExit(logOpts...)

	etcdURI := config.StringDflt("etcd.uri", "")
	etcdURIs := config.StringArrayDflt("etcd.uris", []string{})
	if len(etcdURIs) == 0 && etcdURI != "" {
		rawURIs := make([]interface{}, 1)
		rawURIs[0] = etcdURI
		config.Set("etcd.uris", rawURIs)
	}
}

// Run the app.
func main() {
	configure()

	// begin startup sequence
	client, err := NewClient(config.ObjectDflt("etcd", &settings.Settings{}))
	if err != nil {
		logger.Fatalf("failed to create client: %s", err)
	}

	fmt.Printf("Etcd Prefix: %s\n", client.prefix)
	fmt.Printf("Etcd Client: %v\n", client.client)

	watchersConfig := config.ObjectMapDflt("watchers", map[string]*settings.Settings{})
	watchers := make([]*Watcher, 0, len(watchersConfig))
	for _, watcherConfig := range watchersConfig {
		if watcher, err := NewWatcher(client, watcherConfig); err == nil {
			watchers = append(watchers, watcher)
			fmt.Printf("Added watcher %s\n", watcher.Name())
		} else {
			logger.Fatal(err.Error())
		}
	}

	manager := NewWatchManager(client, watchers)
	fmt.Printf("WatchManager: %v\n", manager)

	// exec
	logger.Info("executing watchers")
	exec := config.StringArrayDflt("exec", []string{})
	if err = manager.Execute(exec); err != nil {
		logger.Fatalf("failed to execute: %s", err)
	}

	if len(exec) == 0 {
		// run
		signals := make(chan os.Signal, 1)
		signal.Notify(signals, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
		logger.Info("starting")
		manager.Start()
		logger.Info("started")
		<-signals
		logger.Info("stopping")
		manager.Stop()
		logger.Info("stopped")
	}
}
<commit_msg>Create a default global logger on start.<commit_after>package main

import (
	"fmt"
	"gopkg.in/BlueDragonX/go-log.v0"
	"gopkg.in/BlueDragonX/go-settings.v0"
	"os"
	"os/signal"
	"syscall"
)

var (
	logger *log.Logger = log.NewOrExit()
	config *settings.Settings
)

func configure() {
	options := ParseOptionsOrExit(os.Args)
	config = settings.LoadOrExit(options.Config)
	config.Set("exec", options.Exec)

	if len(options.Etcd) > 0 {
		config.Set("etcd.uris", options.Etcd)
	}

	if options.LogTarget != "" {
		config.Set("logging.target", options.LogTarget)
	}

	if options.LogLevel != "" {
		config.Set("logging.level", options.LogLevel)
	}

	logOpts := []log.Option{}
	if logTarget, err := config.String("logging.target"); err == nil {
		logOpts = append(logOpts, log.Target(logTarget))
	}
	if logLevel, err := config.String("logging.level"); err == nil {
		logOpts = append(logOpts, log.Level(logLevel))
	}
	logger = log.NewOrExit(logOpts...)

	etcdURI := config.StringDflt("etcd.uri", "")
	etcdURIs := config.StringArrayDflt("etcd.uris", []string{})
	if len(etcdURIs) == 0 && etcdURI != "" {
		rawURIs := make([]interface{}, 1)
		rawURIs[0] = etcdURI
		config.Set("etcd.uris", rawURIs)
	}
}

// Run the app.
func main() {
	configure()

	// begin startup sequence
	client, err := NewClient(config.ObjectDflt("etcd", &settings.Settings{}))
	if err != nil {
		logger.Fatalf("failed to create client: %s", err)
	}

	fmt.Printf("Etcd Prefix: %s\n", client.prefix)
	fmt.Printf("Etcd Client: %v\n", client.client)

	watchersConfig := config.ObjectMapDflt("watchers", map[string]*settings.Settings{})
	watchers := make([]*Watcher, 0, len(watchersConfig))
	for _, watcherConfig := range watchersConfig {
		if watcher, err := NewWatcher(client, watcherConfig); err == nil {
			watchers = append(watchers, watcher)
			fmt.Printf("Added watcher %s\n", watcher.Name())
		} else {
			logger.Fatal(err.Error())
		}
	}

	manager := NewWatchManager(client, watchers)
	fmt.Printf("WatchManager: %v\n", manager)

	// exec
	logger.Info("executing watchers")
	exec := config.StringArrayDflt("exec", []string{})
	if err = manager.Execute(exec); err != nil {
		logger.Fatalf("failed to execute: %s", err)
	}

	if len(exec) == 0 {
		// run
		signals := make(chan os.Signal, 1)
		signal.Notify(signals, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
		logger.Info("starting")
		manager.Start()
		logger.Info("started")
		<-signals
		logger.Info("stopping")
		manager.Stop()
		logger.Info("stopped")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math"
	"os"
	"regexp"
	"strconv"

	"golang.org/x/crypto/ssh/terminal"
)

type key int

type cursorPos struct {
	row, col int
}

const (
	unknown key = iota
	up
	down
	enter
)

var (
	cursorPosRegex = regexp.MustCompile("^\033\\[([0-9]+);([0-9]+)R$")
)

func clearLine() {
	fmt.Print("\033[2K")
}

func hideCursor() {
	fmt.Print("\033[?25l")
}

func showCursor() {
	fmt.Print("\033[?25h")
}

func fullScreenEnter() {
	fmt.Printf("\033[?1049h\033[H")
}

func fullScreenExit() {
	fmt.Printf("\033[?1049l")
}

func main() {
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		panic(err)
	}
	defer terminal.Restore(0, oldState)
	hideCursor()
	defer showCursor()

	keyPresses := make(chan key)
	cursorPosReply := make(chan cursorPos)
	go func() {
		buf := make([]byte, 128)
		for {
			n, err := os.Stdin.Read(buf)
			if err != nil {
				panic(err)
			}
			input := string(buf[:n])
			switch {
			case input == "\033[A":
				keyPresses <- up
			case input == "\033[B":
				keyPresses <- down
			case input == "\x0D":
				keyPresses <- enter
			case cursorPosRegex.MatchString(input):
				matches := cursorPosRegex.FindStringSubmatch(input)
				row, col := matches[1], matches[2]
				r, _ := strconv.Atoi(row)
				c, _ := strconv.Atoi(col)
				cursorPosReply <- cursorPos{row: r, col: c}
			default:
				keyPresses <- unknown
			}
		}
	}()

	// ask for the cursor position
	fmt.Printf("\033[6n")

	pos := <-cursorPosReply

	_, height, _ := terminal.GetSize(0)

	var lines int
	var selectionIndex int

	options := []string{"waffles", "ice cream", "candy", "biscuits"}

	draw := func() {
		fmt.Println(`Make a selection using the arrow keys:`)
		fmt.Print("\r")
		for i, v := range options {
			fmt.Print("  ")
			if i == selectionIndex {
				fmt.Print("\033[7m")
			}
			fmt.Printf("%s\n", v)
			if i == selectionIndex {
				fmt.Print("\033[0m")
			}
			fmt.Print("\r")
		}
		lines = len(options) + 2
	}

	clear := func() {
		// the line where we started is also filled with text so we don't need to
		// count it when moving up
		moveOffset := lines - 1
		// correct the position when we're at the bottom of the screen
		correct := height - pos.row
		correct = moveOffset - int(math.Min(float64(correct), float64(moveOffset)))

		// set the cursor to where we started
		fmt.Printf("\033[%d;%dH", pos.row-correct, pos.col)

		// erase from the cursor onwards
		fmt.Printf("\033[J")
	}

	draw()

	for {
		select {
		case key := <-keyPresses:
			switch key {
			case enter:
				clearLine()
				fmt.Printf("\renjoy your %s\n\r", options[selectionIndex])
				return
			case up:
				selectionIndex = ((selectionIndex - 1) + len(options)) % len(options)
				clear()
				draw()
			case down:
				selectionIndex = ((selectionIndex + 1) + len(options)) % len(options)
				clear()
				draw()
			case unknown:
				clearLine()
				fmt.Printf("\ruse arrow up and down, then enter to select")
			}
		}
	}
}
<commit_msg>handle ctrl-c<commit_after>package main

import (
	"fmt"
	"math"
	"os"
	"regexp"
	"strconv"

	"golang.org/x/crypto/ssh/terminal"
)

type key int

type cursorPos struct {
	row, col int
}

const (
	unknown key = iota
	up
	down
	enter
	ctrlc
)

var (
	cursorPosRegex = regexp.MustCompile("^\033\\[([0-9]+);([0-9]+)R$")
)

func clearLine() {
	fmt.Print("\033[2K")
}

func hideCursor() {
	fmt.Print("\033[?25l")
}

func showCursor() {
	fmt.Print("\033[?25h")
}

func fullScreenEnter() {
	fmt.Printf("\033[?1049h\033[H")
}

func fullScreenExit() {
	fmt.Printf("\033[?1049l")
}

func main() {
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		panic(err)
	}
	defer terminal.Restore(0, oldState)
	hideCursor()
	defer showCursor()

	keyPresses := make(chan key)
	cursorPosReply := make(chan cursorPos)
	go func() {
		buf := make([]byte, 128)
		for {
			n, err := os.Stdin.Read(buf)
			if err != nil {
				panic(err)
			}
			input := string(buf[:n])
			switch {
			case input == "\033[A":
				keyPresses <- up
			case input == "\033[B":
				keyPresses <- down
			case input == "\x0D":
				keyPresses <- enter
			case input == "\x03":
				keyPresses <- ctrlc
			case cursorPosRegex.MatchString(input):
				matches := cursorPosRegex.FindStringSubmatch(input)
				row, col := matches[1], matches[2]
				r, _ := strconv.Atoi(row)
				c, _ := strconv.Atoi(col)
				cursorPosReply <- cursorPos{row: r, col: c}
			default:
				keyPresses <- unknown
			}
		}
	}()

	// ask for the cursor position
	fmt.Printf("\033[6n")

	pos := <-cursorPosReply

	_, height, _ := terminal.GetSize(0)

	var lines int
	var selectionIndex int

	options := []string{"waffles", "ice cream", "candy", "biscuits"}

	draw := func() {
		fmt.Println(`Make a selection using the arrow keys:`)
		fmt.Print("\r")
		for i, v := range options {
			fmt.Print("  ")
			if i == selectionIndex {
				fmt.Print("\033[7m")
			}
			fmt.Printf("%s\n", v)
			if i == selectionIndex {
				fmt.Print("\033[0m")
			}
			fmt.Print("\r")
		}
		lines = len(options) + 2
	}

	clear := func() {
		// the line where we started is also filled with text so we don't need to
		// count it when moving up
		moveOffset := lines - 1
		// correct the position when we're at the bottom of the screen
		correct := height - pos.row
		correct = moveOffset - int(math.Min(float64(correct), float64(moveOffset)))

		// set the cursor to where we started
		fmt.Printf("\033[%d;%dH", pos.row-correct, pos.col)

		// erase from the cursor onwards
		fmt.Printf("\033[J")
	}

	draw()

	for {
		select {
		case key := <-keyPresses:
			switch key {
			case enter:
				clearLine()
				fmt.Printf("\renjoy your %s\n\r", options[selectionIndex])
				return
			case ctrlc:
				clearLine()
				fmt.Print("\rexiting...\n\r")
				return
			case up:
				selectionIndex = ((selectionIndex - 1) + len(options)) % len(options)
				clear()
				draw()
			case down:
				selectionIndex = ((selectionIndex + 1) + len(options)) % len(options)
				clear()
				draw()
			case unknown:
				clearLine()
				fmt.Printf("\ruse arrow up and down, then enter to select")
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"net/http"

	"github.com/codegangsta/negroni"
	"github.com/gh-service/infraestructure"
	"github.com/gh-service/interfaces"
	"github.com/gh-service/usecases"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
)

const defaultPath = "/etc/gh-service.conf"

// Define configuration flags
var confFilePath = flag.String("conf", defaultPath, "Custom path for configuration file")

func main() {

	flag.Parse()

	config, err := infraestructure.GetConfiguration(*confFilePath)
	if err != nil {
		fmt.Println(err.Error())
		panic("Cannot parse configuration")
	}

	ghrepo, err := interfaces.NewGithubRepository(config.ClientID, config.ClientSecret, config.Scopes)
	if err != nil {
		panic(err.Error())
	}

	ghinteractor := usecases.GHInteractor{
		GithubRepository: ghrepo,
	}

	store := sessions.NewCookieStore([]byte("something-very-secret"))

	handler := interfaces.WebServiceHandler{
		GHInteractor: ghinteractor,
		Sessions:     store,
	}

	r := mux.NewRouter()
	subrouter := r.PathPrefix("/github").Subrouter()
	subrouter.Handle("/", interfaces.Adapt(http.HandlerFunc(handler.Root), interfaces.Notify()))
	subrouter.HandleFunc("/login", handler.Login)
	subrouter.HandleFunc("/github_oauth_cb", handler.Callback)
	subrouter.Handle("/user/{username}", interfaces.Adapt(http.HandlerFunc(handler.ShowUser), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("GET")
	subrouter.Handle("/user/{username}/repos", interfaces.Adapt(http.HandlerFunc(handler.ShowRepos), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("GET")
	subrouter.Handle("/user/{username}/repos", interfaces.Adapt(http.HandlerFunc(handler.CreateRepo), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("POST")
	subrouter.Handle("/user/{username}/keys", interfaces.Adapt(http.HandlerFunc(handler.CreateRepo), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("GET")
	subrouter.Handle("/user/{username}/keys", interfaces.Adapt(http.HandlerFunc(handler.CreateKey), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("POST")
	subrouter.Handle("/user/{username}/keys/{id}", interfaces.Adapt(http.HandlerFunc(handler.ShowKey), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("GET")
	subrouter.Handle("/user/{username}/{repo}", interfaces.Adapt(http.HandlerFunc(handler.ShowRepo), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("GET")
	subrouter.Handle("/user/{username}/{repo}/addfile", interfaces.Adapt(http.HandlerFunc(handler.AddFileToRepository), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("POST")
	subrouter.Handle("/user/{username}/{repo}/addfiles", interfaces.Adapt(http.HandlerFunc(handler.AddMultipleFilesToRepository), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("POST")
	subrouter.HandleFunc("/user_info", handler.GetCurrentUser).Methods("GET")

	n := negroni.Classic()
	n.UseHandler(r)

	port := bytes.Buffer{}

	port.WriteString(":")
	port.WriteString(config.Port)

	n.Run(port.String())
}
<commit_msg>Add a subrouter with the api version<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"net/http"

	"github.com/codegangsta/negroni"
	"github.com/gh-service/infraestructure"
	"github.com/gh-service/interfaces"
	"github.com/gh-service/usecases"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
)

const defaultPath = "/etc/gh-service.conf"
const apiVersion = "/v1"

// Define configuration flags
var confFilePath = flag.String("conf", defaultPath, "Custom path for configuration file")

func main() {

	flag.Parse()

	config, err := infraestructure.GetConfiguration(*confFilePath)
	if err != nil {
		fmt.Println(err.Error())
		panic("Cannot parse configuration")
	}

	ghrepo, err := interfaces.NewGithubRepository(config.ClientID, config.ClientSecret, config.Scopes)
	if err != nil {
		panic(err.Error())
	}

	ghinteractor := usecases.GHInteractor{
		GithubRepository: ghrepo,
	}

	store := sessions.NewCookieStore([]byte("something-very-secret"))

	handler := interfaces.WebServiceHandler{
		GHInteractor: ghinteractor,
		Sessions:     store,
	}

	r := mux.NewRouter()

	versionSubrouter := r.PathPrefix(apiVersion).Subrouter()

	subrouter := versionSubrouter.PathPrefix("/github").Subrouter()
	subrouter.HandleFunc("/login", handler.Login)
	subrouter.HandleFunc("/github_oauth_cb", handler.Callback)
	subrouter.Handle("/user/{username}", interfaces.Adapt(http.HandlerFunc(handler.ShowUser), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("GET")
	subrouter.Handle("/user/{username}/repos", interfaces.Adapt(http.HandlerFunc(handler.ShowRepos), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("GET")
	subrouter.Handle("/user/{username}/repos", interfaces.Adapt(http.HandlerFunc(handler.CreateRepo), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("POST")
	subrouter.Handle("/user/{username}/keys", interfaces.Adapt(http.HandlerFunc(handler.CreateRepo), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("GET")
	subrouter.Handle("/user/{username}/keys", interfaces.Adapt(http.HandlerFunc(handler.CreateKey), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("POST")
	subrouter.Handle("/user/{username}/keys/{id}", interfaces.Adapt(http.HandlerFunc(handler.ShowKey), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("GET")
	subrouter.Handle("/user/{username}/{repo}", interfaces.Adapt(http.HandlerFunc(handler.ShowRepo), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("GET")
	subrouter.Handle("/user/{username}/{repo}/addfile", interfaces.Adapt(http.HandlerFunc(handler.AddFileToRepository), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("POST")
	subrouter.Handle("/user/{username}/{repo}/addfiles", interfaces.Adapt(http.HandlerFunc(handler.AddMultipleFilesToRepository), interfaces.Notify(), interfaces.SetToken(ghrepo))).Methods("POST")
	subrouter.HandleFunc("/user_info", handler.GetCurrentUser).Methods("GET")

	n := negroni.Classic()
	n.UseHandler(r)

	port := bytes.Buffer{}

	port.WriteString(":")
	port.WriteString(config.Port)

	n.Run(port.String())
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/pivotal-cf/rabbitmq-upgrade-preparation/rabbitmqctl"
	"github.com/pivotal-cf/rabbitmq-upgrade-preparation/versions"
)

func main() {
	logger := log.New(os.Stdout, "", 0)
	log.SetFlags(0)

	args := parseArgs()

	logger.Printf("Checking whether upgrade preparation is necessary for %s\n", args.node)
	rabbitMQCtl := rabbitmqctl.New(args.rabbitmqctlPath)
	backOffStrategy := backoff.NewExponentialBackOff()
	backOffStrategy.MaxElapsedTime = args.timeout
	backOffStrategy.Multiplier = 1.0

	var (
		status     rabbitmqctl.RabbitMQCtlStatus
		retryCount int
	)

	retryErr := backoff.Retry(func() error {
		retryCount++
		var statusErr error
		status, statusErr = rabbitMQCtl.Status(args.node)
		if statusErr != nil {
			err := statusErr.(*rabbitmqctl.Error)

			if err.Status == rabbitmqctl.UnreachableEpmd || err.Status == rabbitmqctl.StoppedRabbitNode {
				logger.Printf("RabbitMQ %s already stopped: %s\n", args.node, err)
				return nil
			}
		}

		return statusErr
	}, backOffStrategy)

	if retryErr != nil {
		err := retryErr.(*rabbitmqctl.Error)
		if err.Status == rabbitmqctl.UnreachableHost {
			log.Fatalf("Unable to connect to node %s after %d retries within %v: %s", args.node, retryCount, args.timeout, err)
		}
	}

	deployedRabbitVersion, ok := status.RabbitMQVersion()
	if !ok {
		logger.Printf("Safe to proceed without stopping RabbitMQ %s application, exiting: RabbitMQ application already stopped\n", args.node)
		return
	}

	deployedErlangVersion, _ := status.ErlangVersion()

	differences := []versions.VersionDifference{
		&versions.RabbitVersions{Desired: args.desiredRabbitMQVersion, Deployed: deployedRabbitVersion},
		&versions.ErlangVersions{Desired: args.desiredErlangVersion, Deployed: deployedErlangVersion},
	}

	for _, difference := range differences {
		logger.Printf("%s\n", difference.UpgradeMessage())
		if difference.PreparationRequired() {
			logger.Println("The cluster needs to be taken offline as it cannot run in mixed mode")
			logger.Printf("Stopping RabbitMQ on %s\n", args.node)
			if err := rabbitMQCtl.StopApp(args.node); err != nil {
				log.Fatalf("Failed to stop RabbitMQ on %s: %s", args.node, err)
			}
			return
		}
	}
	logger.Println("Safe to proceed without taking the cluster offline")
}

type Args struct {
	rabbitmqctlPath        string
	node                   string
	desiredRabbitMQVersion string
	desiredErlangVersion   string
	timeout                time.Duration
}

func parseArgs() Args {
	rabbitmqctlPath := flag.String("rabbitmqctl-path", "", "Path to rabbitmqctl")
	node := flag.String("node", "", "RabbitMQ node to prepare")
	newRabbitmqVersion := flag.String("new-rabbitmq-version", "", "Version of RabbitMQ that we are upgrading to")
	newErlangVersion := flag.String("new-erlang-version", "", "Version of Erlang that we are upgrading to")
	timeout := flag.Duration("timeout", 60*time.Second, "Maximum time in seconds that you allow rabbitmqctl status to take")
	flag.Parse()

	assertFlag(*rabbitmqctlPath, "rabbitmqctl-path")
	assertFlag(*node, "node")
	assertFlag(*newRabbitmqVersion, "new-rabbitmq-version")
	assertFlag(*newErlangVersion, "new-erlang-version")

	return Args{
		rabbitmqctlPath: *rabbitmqctlPath,
		node:            *node,
		desiredRabbitMQVersion: *newRabbitmqVersion,
		desiredErlangVersion:   *newErlangVersion,
		timeout:                *timeout,
	}
}

func assertFlag(flag, name string) {
	if flag == "" {
		log.Fatalf("Missing -%s flag\n", name)
	}
}
<commit_msg>Extract retry operation and group variables<commit_after>package main

import (
	"flag"
	"log"
	"os"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/pivotal-cf/rabbitmq-upgrade-preparation/rabbitmqctl"
	"github.com/pivotal-cf/rabbitmq-upgrade-preparation/versions"
)

func main() {
	logger := log.New(os.Stdout, "", 0)
	log.SetFlags(0)

	args := parseArgs()

	logger.Printf("Checking whether upgrade preparation is necessary for %s\n", args.node)
	rabbitMQCtl := rabbitmqctl.New(args.rabbitmqctlPath)

	backOffStrategy := backoff.NewExponentialBackOff()
	backOffStrategy.MaxElapsedTime = args.timeout
	backOffStrategy.Multiplier = 1.0

	var status rabbitmqctl.RabbitMQCtlStatus
	var statusErr error
	var retryCount int

	operation := func() error {
		retryCount++
		status, statusErr = rabbitMQCtl.Status(args.node)
		if statusErr != nil {
			err := statusErr.(*rabbitmqctl.Error)

			if err.Status == rabbitmqctl.UnreachableEpmd || err.Status == rabbitmqctl.StoppedRabbitNode {
				logger.Printf("RabbitMQ %s already stopped: %s\n", args.node, err)
				return nil
			}
		}

		return statusErr
	}
	retryErr := backoff.Retry(operation, backOffStrategy)

	if retryErr != nil {
		err := retryErr.(*rabbitmqctl.Error)
		if err.Status == rabbitmqctl.UnreachableHost {
			log.Fatalf("Unable to connect to node %s after %d retries within %v: %s", args.node, retryCount, args.timeout, err)
		}
	}

	deployedRabbitVersion, ok := status.RabbitMQVersion()
	if !ok {
		logger.Printf("Safe to proceed without stopping RabbitMQ %s application, exiting: RabbitMQ application already stopped\n", args.node)
		return
	}

	deployedErlangVersion, _ := status.ErlangVersion()

	differences := []versions.VersionDifference{
		&versions.RabbitVersions{Desired: args.desiredRabbitMQVersion, Deployed: deployedRabbitVersion},
		&versions.ErlangVersions{Desired: args.desiredErlangVersion, Deployed: deployedErlangVersion},
	}

	for _, difference := range differences {
		logger.Printf("%s\n", difference.UpgradeMessage())
		if difference.PreparationRequired() {
			logger.Println("The cluster needs to be taken offline as it cannot run in mixed mode")
			logger.Printf("Stopping RabbitMQ on %s\n", args.node)
			if err := rabbitMQCtl.StopApp(args.node); err != nil {
				log.Fatalf("Failed to stop RabbitMQ on %s: %s", args.node, err)
			}
			return
		}
	}
	logger.Println("Safe to proceed without taking the cluster offline")
}

type Args struct {
	rabbitmqctlPath        string
	node                   string
	desiredRabbitMQVersion string
	desiredErlangVersion   string
	timeout                time.Duration
}

func parseArgs() Args {
	rabbitmqctlPath := flag.String("rabbitmqctl-path", "", "Path to rabbitmqctl")
	node := flag.String("node", "", "RabbitMQ node to prepare")
	newRabbitmqVersion := flag.String("new-rabbitmq-version", "", "Version of RabbitMQ that we are upgrading to")
	newErlangVersion := flag.String("new-erlang-version", "", "Version of Erlang that we are upgrading to")
	timeout := flag.Duration("timeout", 60*time.Second, "Maximum time in seconds that you allow rabbitmqctl status to take")
	flag.Parse()

	assertFlag(*rabbitmqctlPath, "rabbitmqctl-path")
	assertFlag(*node, "node")
	assertFlag(*newRabbitmqVersion, "new-rabbitmq-version")
	assertFlag(*newErlangVersion, "new-erlang-version")

	return Args{
		rabbitmqctlPath: *rabbitmqctlPath,
		node:            *node,
		desiredRabbitMQVersion: *newRabbitmqVersion,
		desiredErlangVersion:   *newErlangVersion,
		timeout:                *timeout,
	}
}

func assertFlag(flag, name string) {
	if flag == "" {
		log.Fatalf("Missing -%s flag\n", name)
	}
}
<|endoftext|>


<commit_before>package main

func main() {
	println("anvilward, dwarf spy")
}
<commit_msg>Implement the main<commit_after>package main

import (
	"flag"
	"fmt"
)

var (
	length   uint
	alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)

func init() {
	flag.UintVar(&length, "l", 6, "length of the tokens")
}

func main() {
	println("anvilward, dwarf spy")

	for url := range Resolve(Enumerate(length, alphabet)) {
		fmt.Println(url)
	}
}
<|endoftext|>