<commit_before>package gologger

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"time"
)

const (
	TIME_FORMAT = "2006-01-02T15:04:05.000"
	DEBUG       = iota
	INFO
	WARN
	ERROR
)

var (
	LogColors = map[int]int{
		DEBUG: 102,
		INFO:  28,
		WARN:  214,
		ERROR: 196,
	}
	LogPrefixes = map[int]string{
		DEBUG: "DEBUG",
		INFO:  "INFO ",
		WARN:  "WARN ",
		ERROR: "ERROR",
	}
	logger *Logger
)

func Colorize(c int, s string) (r string) {
	return fmt.Sprintf("\033[38;5;%dm%s\033[0m", c, s)
}

type Logger struct {
	LogLevel int
	Started  time.Time
	Prefix   string
	prefixes []string
	Colored  bool
	Caller   bool
}

func currentLogger() *Logger {
	if logger == nil {
		logger = New()
		if os.Getenv("DEBUG") == "true" {
			logger.Caller = true
			logger.LogLevel = DEBUG
		}
	}
	return logger
}

func New() *Logger {
	return &Logger{Colored: true, LogLevel: INFO}
}

func NewFromEnv() *Logger {
	logger = New()
	if os.Getenv("DEBUG") == "true" {
		logger.Caller = true
		logger.LogLevel = DEBUG
	}
	return logger
}

func DeferBenchmark() func() {
	return currentLogger().DeferBenchmark()
}

func (logger *Logger) DeferBenchmark() func() {
	logger.Start()
	return func() { logger.Stop() }
}

func DeferRestoreLogLevel(level int) func() {
	return currentLogger().DeferRestoreLogLevel(level)
}

func (logger *Logger) DeferRestoreLogLevel(level int) func() {
	oldLevel := logger.LogLevel
	logger.LogLevel = level
	return func() { logger.LogLevel = oldLevel }
}

func DeferRestoreLogPrefix(newPrefix string) func() {
	return currentLogger().DeferRestoreLogPrefix(newPrefix)
}

func (logger *Logger) DeferRestoreLogPrefix(newPrefix string) func() {
	oldPrefix := logger.Prefix
	logger.Prefix = newPrefix
	return func() { logger.Prefix = oldPrefix }
}

func Start() {
	currentLogger().Start()
}

func (self *Logger) Start() {
	self.Started = time.Now()
}

func Stop() {
	currentLogger().Stop()
}

func (self *Logger) Stop() {
	self.Started = time.Unix(0, 0)
}

func Inspect(i interface{}) {
	currentLogger().Info(i)
}

func (self *Logger) Inspect(i interface{}) {
	self.Debugf("%+v", i)
}

func Debugf(format string, n ...interface{}) {
	currentLogger().Debugf(format, n...)
}

func (l *Logger) Debugf(format string, n ...interface{}) {
	l.logf(DEBUG, format, n...)
}

func Infof(format string, n ...interface{}) {
	currentLogger().Infof(format, n...)
}

func (l *Logger) Infof(format string, n ...interface{}) {
	l.logf(INFO, format, n...)
}

func Warnf(format string, n ...interface{}) {
	currentLogger().Warnf(format, n...)
}

func (l *Logger) Warnf(format string, n ...interface{}) {
	l.logf(WARN, format, n...)
}

func Errorf(format string, n ...interface{}) {
	currentLogger().Errorf(format, n...)
}

func (l *Logger) Errorf(format string, n ...interface{}) {
	l.logf(ERROR, format, n...)
}

func Debug(n ...interface{}) {
	currentLogger().Debug(n...)
}

func (l *Logger) Debug(n ...interface{}) {
	l.log(DEBUG, n...)
}

func Info(n ...interface{}) {
	currentLogger().Info(n...)
}

func (l *Logger) Info(n ...interface{}) {
	l.log(INFO, n...)
}

func Warn(n ...interface{}) {
	currentLogger().Warn(n...)
}

func (l *Logger) Warn(n ...interface{}) {
	l.log(WARN, n...)
}

func Error(n ...interface{}) {
	currentLogger().Error(n...)
}

func (l *Logger) Error(n ...interface{}) {
	l.log(ERROR, n...)
}

func (l *Logger) logf(level int, s string, n ...interface{}) {
	if level >= l.LogLevel {
		l.write(l.logPrefix(level), fmt.Sprintf(s, n...))
	}
}

func (l *Logger) log(level int, n ...interface{}) {
	if level >= l.LogLevel {
		all := append([]interface{}{l.logPrefix(level)}, n...)
		l.write(all...)
	}
}

func (l *Logger) PushPrefix(prefix string) {
	l.prefixes = append(l.prefixes, prefix)
}

func (l *Logger) PopPrefix() string {
	popped := ""
	pLen := len(l.prefixes)
	if pLen > 0 {
		popped = l.prefixes[pLen-1]
		l.prefixes = l.prefixes[:pLen-1]
	}
	return popped
}

func (l *Logger) logPrefix(i int) (s string) {
	s = time.Now().Format(TIME_FORMAT)
	if l.Started.Unix() > 0 {
		time := fmt.Sprintf("%.3f", time.Now().Sub(l.Started).Seconds())
		s += fmt.Sprintf(" [%8s]", time)
	}
	s = s + " " + l.LogLevelPrefix(i) + " "
	if l.Prefix != "" {
		s = s + "[" + l.Prefix + "]"
		if len(l.prefixes) == 0 {
			s += " "
		}
	}
	if len(l.prefixes) > 0 {
		for i := range l.prefixes {
			s = s + "[" + l.prefixes[i] + "]"
		}
		s += " "
	}
	if l.Caller {
		_, file, line, ok := runtime.Caller(3)
		if ok {
			s += fmt.Sprintf("[%s:%d] ", filepath.Base(file), line)
		}
	}
	return s
}

func (l *Logger) LogLevelPrefix(level int) (s string) {
	prefix := LogPrefixes[level]
	if l.Colored {
		color := LogColors[level]
		return Colorize(color, prefix)
	}
	return prefix
}

func (self *Logger) write(n ...interface{}) {
	fmt.Fprintln(os.Stderr, n...)
}
<commit_msg>make the logger a Writer<commit_after>package gologger

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"time"
)

const (
	TIME_FORMAT = "2006-01-02T15:04:05.000"
	DEBUG       = iota
	INFO
	WARN
	ERROR
)

var (
	LogColors = map[int]int{
		DEBUG: 102,
		INFO:  28,
		WARN:  214,
		ERROR: 196,
	}
	LogPrefixes = map[int]string{
		DEBUG: "DEBUG",
		INFO:  "INFO ",
		WARN:  "WARN ",
		ERROR: "ERROR",
	}
	logger *Logger
)

func Colorize(c int, s string) (r string) {
	return fmt.Sprintf("\033[38;5;%dm%s\033[0m", c, s)
}

type Logger struct {
	LogLevel int
	Started  time.Time
	Prefix   string
	prefixes []string
	Colored  bool
	Caller   bool
}

func currentLogger() *Logger {
	if logger == nil {
		logger = New()
		if os.Getenv("DEBUG") == "true" {
			logger.Caller = true
			logger.LogLevel = DEBUG
		}
	}
	return logger
}

func New() *Logger {
	return &Logger{Colored: true, LogLevel: INFO}
}

func NewFromEnv() *Logger {
	logger = New()
	if os.Getenv("DEBUG") == "true" {
		logger.Caller = true
		logger.LogLevel = DEBUG
	}
	return logger
}

func DeferBenchmark() func() {
	return currentLogger().DeferBenchmark()
}

func (logger *Logger) DeferBenchmark() func() {
	logger.Start()
	return func() { logger.Stop() }
}

func DeferRestoreLogLevel(level int) func() {
	return currentLogger().DeferRestoreLogLevel(level)
}

func (logger *Logger) DeferRestoreLogLevel(level int) func() {
	oldLevel := logger.LogLevel
	logger.LogLevel = level
	return func() { logger.LogLevel = oldLevel }
}

func DeferRestoreLogPrefix(newPrefix string) func() {
	return currentLogger().DeferRestoreLogPrefix(newPrefix)
}

func (logger *Logger) DeferRestoreLogPrefix(newPrefix string) func() {
	oldPrefix := logger.Prefix
	logger.Prefix = newPrefix
	return func() { logger.Prefix = oldPrefix }
}

func Start() {
	currentLogger().Start()
}

func (self *Logger) Start() {
	self.Started = time.Now()
}

func Stop() {
	currentLogger().Stop()
}

func (self *Logger) Stop() {
	self.Started = time.Unix(0, 0)
}

func Inspect(i interface{}) {
	currentLogger().Info(i)
}

func (self *Logger) Inspect(i interface{}) {
	self.Debugf("%+v", i)
}

func Debugf(format string, n ...interface{}) {
	currentLogger().Debugf(format, n...)
}

func (l *Logger) Debugf(format string, n ...interface{}) {
	l.logf(DEBUG, format, n...)
}

func Infof(format string, n ...interface{}) {
	currentLogger().Infof(format, n...)
}

func (l *Logger) Infof(format string, n ...interface{}) {
	l.logf(INFO, format, n...)
}

func Warnf(format string, n ...interface{}) {
	currentLogger().Warnf(format, n...)
}

func (l *Logger) Warnf(format string, n ...interface{}) {
	l.logf(WARN, format, n...)
}

func Errorf(format string, n ...interface{}) {
	currentLogger().Errorf(format, n...)
}

func (l *Logger) Errorf(format string, n ...interface{}) {
	l.logf(ERROR, format, n...)
}

func Debug(n ...interface{}) {
	currentLogger().Debug(n...)
}

func (l *Logger) Debug(n ...interface{}) {
	l.log(DEBUG, n...)
}

func Info(n ...interface{}) {
	currentLogger().Info(n...)
}

func (l *Logger) Info(n ...interface{}) {
	l.log(INFO, n...)
}

func Warn(n ...interface{}) {
	currentLogger().Warn(n...)
}

func (l *Logger) Warn(n ...interface{}) {
	l.log(WARN, n...)
}

func Error(n ...interface{}) {
	currentLogger().Error(n...)
}

func (l *Logger) Error(n ...interface{}) {
	l.log(ERROR, n...)
}

func (l *Logger) logf(level int, s string, n ...interface{}) {
	if level >= l.LogLevel {
		l.write(l.logPrefix(level), fmt.Sprintf(s, n...))
	}
}

func (l *Logger) log(level int, n ...interface{}) {
	if level >= l.LogLevel {
		all := append([]interface{}{l.logPrefix(level)}, n...)
		l.write(all...)
	}
}

func (l *Logger) PushPrefix(prefix string) {
	l.prefixes = append(l.prefixes, prefix)
}

func (l *Logger) PopPrefix() string {
	popped := ""
	pLen := len(l.prefixes)
	if pLen > 0 {
		popped = l.prefixes[pLen-1]
		l.prefixes = l.prefixes[:pLen-1]
	}
	return popped
}

func (l *Logger) logPrefix(i int) (s string) {
	s = time.Now().Format(TIME_FORMAT)
	if l.Started.Unix() > 0 {
		time := fmt.Sprintf("%.3f", time.Now().Sub(l.Started).Seconds())
		s += fmt.Sprintf(" [%8s]", time)
	}
	s = s + " " + l.LogLevelPrefix(i) + " "
	if l.Prefix != "" {
		s = s + "[" + l.Prefix + "]"
		if len(l.prefixes) == 0 {
			s += " "
		}
	}
	if len(l.prefixes) > 0 {
		for i := range l.prefixes {
			s = s + "[" + l.prefixes[i] + "]"
		}
		s += " "
	}
	if l.Caller {
		_, file, line, ok := runtime.Caller(3)
		if ok {
			s += fmt.Sprintf("[%s:%d] ", filepath.Base(file), line)
		}
	}
	return s
}

func (l *Logger) LogLevelPrefix(level int) (s string) {
	prefix := LogPrefixes[level]
	if l.Colored {
		color := LogColors[level]
		return Colorize(color, prefix)
	}
	return prefix
}

func (self *Logger) write(n ...interface{}) {
	fmt.Fprintln(os.Stderr, n...)
}

func (logger *Logger) Write(p []byte) (n int, e error) {
	q := p
	n = len(q)
	logger.Infof(string(q))
	return n, e
}
<|endoftext|>


<commit_before>// Simple log replacement for go with Verbosity and Debug levels, and
// multi-stream output support.
//
// (C) by Marco Paganini <paganini AT paganini DOT net>

package logger

import (
	"fmt"
	"io"
	"os"
)

// Logger represents a logger object
type Logger struct {
	verbose      int
	debug        int
	outputs      []*os.File
	mirrorOutput *os.File
}

// New Creates a new Logger instance.
func New(prefix string) *Logger {
	return &Logger{
		verbose:      0,
		debug:        0,
		outputs:      []*os.File{os.Stderr},
		mirrorOutput: nil}
}

// SetVerboseLevel sets the verbosity level for this log instance.
func (o *Logger) SetVerboseLevel(n int) {
	o.verbose = n
}

// SetDebugLevel sets the debugging level for this log instance.
func (o *Logger) SetDebugLevel(n int) {
	o.debug = n
}

// SetOutputs sets the outputs to the slice *os.File
func (o *Logger) SetOutputs(outputs []*os.File) {
	o.outputs = outputs
}

// SetMirrorOutput sets the mirror output stream to the writers.
func (o *Logger) SetMirrorOutput(output *os.File) {
	o.mirrorOutput = output
}

// writeString sends the string to all defined outputs if the requested level
// is less than or equal to the configured verbose level. Note that if the
// mirrorOutput stream is non nil, the message is always written to it,
// independent of the error level.
func (o *Logger) writeString(level int, s string) {
	// Conditionally output to all streams
	if level <= o.verbose {
		for _, w := range o.outputs {
			io.WriteString(w, s)
		}
	}
	// Output to mirror output if non-nil
	if o.mirrorOutput != nil {
		io.WriteString(o.mirrorOutput, s)
	}
}

// Println prints the message to the output streams followed by a newline.
func (o *Logger) Println(v ...interface{}) {
	o.writeString(0, fmt.Sprintln(v...))
}

// Printf uses the formatting string and variables to print a message to the
// output streams.
func (o *Logger) Printf(format string, v ...interface{}) {
	o.writeString(0, fmt.Sprintf(format, v...))
}

// Fatalln prints the message to the output streams followed by a newline
// and calls os.Exit(1).
func (o *Logger) Fatalln(v ...interface{}) {
	o.writeString(0, fmt.Sprintln(v...))
	os.Exit(1)
}

// Fatalf prints a formatted message to the output streams and calls os.Exit(1).
func (o *Logger) Fatalf(format string, v ...interface{}) {
	o.writeString(0, fmt.Sprintf(format, v...))
	os.Exit(1)
}

// Verboseln prints the message to the output streams, followed by a newline,
// if the current verbose level is greater than or equal the specified
// verbosity level.
func (o *Logger) Verboseln(level int, v ...interface{}) {
	o.writeString(level, fmt.Sprintln(v...))
}

// Verbosef uses the formatting string and variables to print a message to the
// output streams if the current verbose level is greater than or equal to the
// specified verbose level.
func (o *Logger) Verbosef(level int, format string, v ...interface{}) {
	o.writeString(level, fmt.Sprintf(format, v...))
}

// Debugln prints the message to the output streams, followed by a newline,
// if the current debugging level is greater than or equal the specified
// debugging level.
func (o *Logger) Debugln(level int, v ...interface{}) {
	o.writeString(level, fmt.Sprintln(v...))
}

// Debugf uses the formatting string and variables to print a message to the
// output streams if the current debugging level is greater than or equal to the
// specified debugging level.
func (o *Logger) Debugf(level int, format string, v ...interface{}) {
	o.writeString(level, fmt.Sprintf(format, v...))
}
<commit_msg>Added log.Fatal() which calls log.Fatalln().<commit_after>// Simple log replacement for go with Verbosity and Debug levels, and
// multi-stream output support.
//
// (C) by Marco Paganini <paganini AT paganini DOT net>

package logger

import (
	"fmt"
	"io"
	"os"
)

// Logger represents a logger object
type Logger struct {
	verbose      int
	debug        int
	outputs      []*os.File
	mirrorOutput *os.File
}

// New Creates a new Logger instance.
func New(prefix string) *Logger {
	return &Logger{
		verbose:      0,
		debug:        0,
		outputs:      []*os.File{os.Stderr},
		mirrorOutput: nil}
}

// SetVerboseLevel sets the verbosity level for this log instance.
func (o *Logger) SetVerboseLevel(n int) {
	o.verbose = n
}

// SetDebugLevel sets the debugging level for this log instance.
func (o *Logger) SetDebugLevel(n int) {
	o.debug = n
}

// SetOutputs sets the outputs to the slice *os.File
func (o *Logger) SetOutputs(outputs []*os.File) {
	o.outputs = outputs
}

// SetMirrorOutput sets the mirror output stream to the writers.
func (o *Logger) SetMirrorOutput(output *os.File) {
	o.mirrorOutput = output
}

// writeString sends the string to all defined outputs if the requested level
// is less than or equal to the configured verbose level. Note that if the
// mirrorOutput stream is non nil, the message is always written to it,
// independent of the error level.
func (o *Logger) writeString(level int, s string) {
	// Conditionally output to all streams
	if level <= o.verbose {
		for _, w := range o.outputs {
			io.WriteString(w, s)
		}
	}
	// Output to mirror output if non-nil
	if o.mirrorOutput != nil {
		io.WriteString(o.mirrorOutput, s)
	}
}

// Println prints the message to the output streams followed by a newline.
func (o *Logger) Println(v ...interface{}) {
	o.writeString(0, fmt.Sprintln(v...))
}

// Printf uses the formatting string and variables to print a message to the
// output streams.
func (o *Logger) Printf(format string, v ...interface{}) {
	o.writeString(0, fmt.Sprintf(format, v...))
}

// Fatalln prints the message to the output streams followed by a newline
// and calls os.Exit(1).
func (o *Logger) Fatalln(v ...interface{}) {
	o.writeString(0, fmt.Sprintln(v...))
	os.Exit(1)
}

// Fatal is a convenience alias for Fatalln.
func (o *Logger) Fatal(v ...interface{}) {
	o.Fatalln(v...)
}

// Fatalf prints a formatted message to the output streams and calls os.Exit(1).
func (o *Logger) Fatalf(format string, v ...interface{}) {
	o.writeString(0, fmt.Sprintf(format, v...))
	os.Exit(1)
}

// Verboseln prints the message to the output streams, followed by a newline,
// if the current verbose level is greater than or equal the specified
// verbosity level.
func (o *Logger) Verboseln(level int, v ...interface{}) {
	o.writeString(level, fmt.Sprintln(v...))
}

// Verbosef uses the formatting string and variables to print a message to the
// output streams if the current verbose level is greater than or equal to the
// specified verbose level.
func (o *Logger) Verbosef(level int, format string, v ...interface{}) {
	o.writeString(level, fmt.Sprintf(format, v...))
}

// Debugln prints the message to the output streams, followed by a newline,
// if the current debugging level is greater than or equal the specified
// debugging level.
func (o *Logger) Debugln(level int, v ...interface{}) {
	o.writeString(level, fmt.Sprintln(v...))
}

// Debugf uses the formatting string and variables to print a message to the
// output streams if the current debugging level is greater than or equal to the
// specified debugging level.
func (o *Logger) Debugf(level int, format string, v ...interface{}) {
	o.writeString(level, fmt.Sprintf(format, v...))
}
<|endoftext|>


<commit_before>// Copyright 2017 Sevki <s@sevki.org>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package project // import "bldy.build/build/project"

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/vaughan0/go-ini"
)

var (
	file ini.File

	pp = ""

	copyToRoot = flag.Bool("r", false, "set root of the project as build out. should not be used with watch")
)

func init() {
	wd, _ := os.Getwd()
	pp = GetGitDir(wd)
	var err error
	if file, err = ini.LoadFile(filepath.Join(Root(), "bldy.cfg")); err == nil {
		if err != nil {
			log.Fatalf("error: %v", err)
		}
	}
}
func Root() (ProjectPath string) {
	return pp
}
func RelPPath(p string) string {
	rel, _ := filepath.Rel(Root(), p)
	return rel
}

func BuildOut() string {
	if *copyToRoot || Getenv("COPYTOROOT") == "true" {
		return Root()
	}

	if Getenv("BUILD_OUT") != "" {
		return Getenv("BUILD_OUT")
	} else {
		return filepath.Join(
			Root(),
			"build_out",
		)
	}
}

func GetGitDir(p string) string {
	dirs := strings.Split(p, "/")
	for i := len(dirs) - 1; i > 0; i-- {
		try := fmt.Sprintf("/%s/.git", filepath.Join(dirs[0:i+1]...))
		if _, err := os.Lstat(try); os.IsNotExist(err) {
			continue
		} else if err != nil {
			log.Fatal(err)
		}
		pr, _ := filepath.Split(try)
		return pr
	}
	return ""
}

// Getenv returns the envinroment variable. It looks for the envinroment
// variable in the following order. It checks if the current shell session has
// an envinroment variable, checks if it's set in the OS specific section in
// the .build file, and checks it for common in the .build config file.
func Getenv(s string) string {
	if os.Getenv(s) != "" {
		return os.Getenv(s)
	} else if val, exists := file.Get(runtime.GOOS, s); exists {
		return val
	} else if val, exists := file.Get("", s); exists {
		return val
	} else {
		return ""
	}
}
<commit_msg>project: only use the -r flag<commit_after>// Copyright 2017 Sevki <s@sevki.org>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package project // import "bldy.build/build/project"

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/vaughan0/go-ini"
)

var (
	file ini.File

	pp = ""

	copyToRoot = flag.Bool("r", false, "set root of the project as build out. should not be used with watch")
)

func init() {
	wd, _ := os.Getwd()
	pp = GetGitDir(wd)
	var err error
	if file, err = ini.LoadFile(filepath.Join(Root(), "bldy.cfg")); err == nil {
		if err != nil {
			log.Fatalf("error: %v", err)
		}
	}
}
func Root() (ProjectPath string) {
	return pp
}
func RelPPath(p string) string {
	rel, _ := filepath.Rel(Root(), p)
	return rel
}

func BuildOut() string {
	if *copyToRoot  {
		return Root()
	}

	if Getenv("BUILD_OUT") != "" {
		return Getenv("BUILD_OUT")
	} else {
		return filepath.Join(
			Root(),
			"build_out",
		)
	}
}

func GetGitDir(p string) string {
	dirs := strings.Split(p, "/")
	for i := len(dirs) - 1; i > 0; i-- {
		try := fmt.Sprintf("/%s/.git", filepath.Join(dirs[0:i+1]...))
		if _, err := os.Lstat(try); os.IsNotExist(err) {
			continue
		} else if err != nil {
			log.Fatal(err)
		}
		pr, _ := filepath.Split(try)
		return pr
	}
	return ""
}

// Getenv returns the envinroment variable. It looks for the envinroment
// variable in the following order. It checks if the current shell session has
// an envinroment variable, checks if it's set in the OS specific section in
// the .build file, and checks it for common in the .build config file.
func Getenv(s string) string {
	if os.Getenv(s) != "" {
		return os.Getenv(s)
	} else if val, exists := file.Get(runtime.GOOS, s); exists {
		return val
	} else if val, exists := file.Get("", s); exists {
		return val
	} else {
		return ""
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"text/tabwriter"
)

var cmdLogins = &Command{
	Run:   runLogins,
	Usage: "logins",
	Short: "List force.com logins used",
	Long: `
List force.com accounts

Examples:

  force logins
`,
}

func runLogins(cmd *Command, args []string) {
	active, _ := ActiveLogin()
	accounts, _ := Config.List("accounts")
	if len(accounts) == 0 {
		fmt.Println("no logins")
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 1, 0, 1, ' ', 0)

		for _, account := range accounts {
			var creds ForceCredentials
			data, err := Config.Load("accounts", account)
			json.Unmarshal([]byte(data), &creds)

			if err != nil {
				return
			}

			var banner = fmt.Sprintf("\t%s", creds.InstanceUrl)
			if account == active {
				account = fmt.Sprintf("\x1b[31;1m%s (active)\x1b[0m", account)
			} else {
				account = fmt.Sprintf("%s \x1b[31;1m\x1b[0m", account)
			}
			fmt.Fprintln(w, fmt.Sprintf("%s%s", account, banner))
		}
		fmt.Fprintln(w)
		w.Flush()
	}

}

func ActiveLogin() (account string, err error) {
	account, err = Config.Load("current", "account")
	if err != nil {
		accounts, _ := Config.List("accounts")
		if len(accounts) > 0 {
			SetActiveLoginDefault()
		}
	}
	return
}

func ActiveCredentials() (creds ForceCredentials, err error) {
	account, err := ActiveLogin()
	if err != nil {
		return
	}
	data, err := Config.Load("accounts", account)
	json.Unmarshal([]byte(data), &creds)
	return
}

func ActiveForce() (force *Force, err error) {
	creds, err := ActiveCredentials()
	if err != nil {
		return
	}
	force = NewForce(creds)
	return
}

func SetActiveLoginDefault() (account string) {
	accounts, _ := Config.List("accounts")
	if len(accounts) > 0 {
		account = accounts[0]
		SetActiveLogin(account)
	}
	return
}

func SetActiveLogin(account string) (err error) {
	err = Config.Save("current", "account", account)
	return
}
<commit_msg>login update terminal title bar<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"text/tabwriter"
	"strings"
)

var cmdLogins = &Command{
	Run:   runLogins,
	Usage: "logins",
	Short: "List force.com logins used",
	Long: `
List force.com accounts

Examples:

  force logins
`,
}

func runLogins(cmd *Command, args []string) {
	active, _ := ActiveLogin()
	accounts, _ := Config.List("accounts")
	if len(accounts) == 0 {
		fmt.Println("no logins")
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 1, 0, 1, ' ', 0)

		for _, account := range accounts {
			if !strings.HasPrefix(account, "."){
				var creds ForceCredentials
				data, err := Config.Load("accounts", account)
				json.Unmarshal([]byte(data), &creds)

				if err != nil {
					return
				}

				var banner = fmt.Sprintf("\t%s", creds.InstanceUrl)
				if account == active {
					account = fmt.Sprintf("\x1b[31;1m%s (active)\x1b[0m", account)
				} else {
					account = fmt.Sprintf("%s \x1b[31;1m\x1b[0m", account)
				}
				fmt.Fprintln(w, fmt.Sprintf("%s%s", account, banner))
			}
		}
		fmt.Fprintln(w)
		w.Flush()
	}

}

func ActiveLogin() (account string, err error) {
	account, err = Config.Load("current", "account")
	if err != nil {
		accounts, _ := Config.List("accounts")
		if len(accounts) > 0 {
			SetActiveLoginDefault()
		}
	}
	return
}

func ActiveCredentials() (creds ForceCredentials, err error) {
	account, err := ActiveLogin()
	if err != nil {
		return
	}
	data, err := Config.Load("accounts", account)
	json.Unmarshal([]byte(data), &creds)
	return
}

func ActiveForce() (force *Force, err error) {
	creds, err := ActiveCredentials()
	if err != nil {
		return
	}
	force = NewForce(creds)
	return
}

func SetActiveLoginDefault() (account string) {
	accounts, _ := Config.List("accounts")
	if len(accounts) > 0 {
		account = accounts[0]
		SetActiveLogin(account)
	}
	return
}

func SetActiveLogin(account string) (err error) {
	err = Config.Save("current", "account", account)
	return
}
<|endoftext|>


<commit_before>// All this package really does is add a little structure and a little color
// to the standard log package

package logsip

import (
	"fmt"
	"io"
	"log"
	"os"

	"github.com/fatih/color"
)

// Logger is a modified log.Logger which provides logging levels.
type Logger struct {
	WarnPrefix  string
	FatalPrefix string
	InfoPrefix  string
	PanicPrefix string
	DebugPrefix string
	DebugMode   bool
	*log.Logger
}

// New returns a blank Logger so you can define your own prefixes.
func New(out io.Writer) *Logger {
	return &Logger{Logger: log.New(out, "", 0)}
}

// Default returns a new Logger with the default color and prefix options.
func Default() *Logger {
	return &Logger{
		WarnPrefix:  color.New(color.FgYellow).SprintFunc()("==> Warn: "),
		InfoPrefix:  color.New(color.FgCyan).SprintFunc()("==> Info: "),
		FatalPrefix: color.New(color.FgRed).SprintFunc()("==> Fatal: "),
		PanicPrefix: color.New(color.FgRed).SprintFunc()("==> Panic: "),
		DebugPrefix: color.New(color.FgMagenta).SprintFunc()("==> Debug: "),
		DebugMode:   false,
		Logger:      log.New(os.Stdout, "", 0),
	}
}

// Info works just like log.Print, but with a Cyan "==> Info:" prefix.
func (l *Logger) Info(v ...interface{}) {
	l.Print(l.InfoPrefix + fmt.Sprint(v...))
}

// Debug works just like log.Print, but with a Magenta "==> Debug:" prefix.
func (l *Logger) Debug(v ...interface{}) {
	if l.DebugMode {
		l.Print(l.DebugPrefix + fmt.Sprint(v...))
	}
}

// Warn works just like log.Print, but with a Yellow "==> Warn:" prefix.
func (l *Logger) Warn(v ...interface{}) {
	l.Print(l.WarnPrefix + fmt.Sprint(v...))
}

// Fatal works just like log.Fatal, but with a Red "==> Fatal:" prefix.
func (l *Logger) Fatal(v ...interface{}) {
	l.Fatal(l.FatalPrefix + fmt.Sprint(v...))
}

// Panic works just like log.Panic, but with a Red "==> Panic:" prefix.
func (l *Logger) Panic(v ...interface{}) {
	l.Panic(l.PanicPrefix + fmt.Sprint(v...))
}

// Infof works just like log.Printf, but with a Cyan "==> Info:" prefix.
func (l *Logger) Infof(format string, v ...interface{}) {
	l.Print(l.InfoPrefix + fmt.Sprintf(format, v...))
}

// Debugf works just like log.Printf, but with a Magenta "==> Debug:" prefix.
func (l *Logger) Debugf(format string, v ...interface{}) {
	if l.DebugMode {
		l.Print(l.DebugPrefix + fmt.Sprintf(format, v...))
	}
}

// Warnf works just like log.Printf, but with a Yellow "==> Warn:" prefix.
func (l *Logger) Warnf(format string, v ...interface{}) {
	l.Print(l.WarnPrefix + fmt.Sprintf(format, v...))
}

// Fatalf works just like log.Fatalf, but with a Red "==> Fatal:" prefix.
func (l *Logger) Fatalf(format string, v ...interface{}) {
	l.Fatalf(l.FatalPrefix + fmt.Sprintf(format, v...))
}

// Panicf works just like log.Panicf, but with a Red "==> Panic:" prefix.
func (l *Logger) Panicf(format string, v ...interface{}) {
	l.Panicf(l.PanicPrefix + fmt.Sprintf(format, v...))
}

// Infoln works just like log.Println, but with a Cyan "==> Info:" prefix.
func (l *Logger) Infoln(v ...interface{}) {
	l.Println(l.InfoPrefix + fmt.Sprint(v...))
}

// Debugln works just like log.Println, but with a Magenta "==> Debug:" prefix.
func (l *Logger) Debugln(v ...interface{}) {
	if l.DebugMode {
		l.Println(l.DebugPrefix + fmt.Sprint(v...))
	}
}

// Warnln works just like log.Println, but with a Yellow "==> Warn:" prefix.
func (l *Logger) Warnln(v ...interface{}) {
	l.Println(l.WarnPrefix + fmt.Sprint(v...))
}

// Fatalln works just like log.Fatalln, but with a Red "==> Fatal:" prefix.
func (l *Logger) Fatalln(v ...interface{}) {
	l.Fatalln(l.FatalPrefix + fmt.Sprint(v...))
}

// Panicln works just like log.Panicln, but with a Red "==> Panic:" prefix.
func (l *Logger) Panicln(v ...interface{}) {
	l.Panicln(l.PanicPrefix + fmt.Sprint(v...))
}
<commit_msg>Fixed panic when Fatal was called<commit_after>// All this package really does is add a little structure and a little color
// to the standard log package

package logsip

import (
	"fmt"
	"io"
	"log"
	"os"

	"github.com/fatih/color"
)

// Logger is a modified log.Logger which provides logging levels.
type Logger struct {
	WarnPrefix  string
	FatalPrefix string
	InfoPrefix  string
	PanicPrefix string
	DebugPrefix string
	DebugMode   bool
	*log.Logger
}

// New returns a blank Logger so you can define your own prefixes.
func New(out io.Writer) *Logger {
	return &Logger{Logger: log.New(out, "", 0)}
}

// Default returns a new Logger with the default color and prefix options.
func Default() *Logger {
	return &Logger{
		WarnPrefix:  color.New(color.FgYellow).SprintFunc()("==> Warn: "),
		InfoPrefix:  color.New(color.FgCyan).SprintFunc()("==> Info: "),
		FatalPrefix: color.New(color.FgRed).SprintFunc()("==> Fatal: "),
		PanicPrefix: color.New(color.FgRed).SprintFunc()("==> Panic: "),
		DebugPrefix: color.New(color.FgMagenta).SprintFunc()("==> Debug: "),
		DebugMode:   false,
		Logger:      log.New(os.Stdout, "", 0),
	}
}

// Info works just like log.Print, but with a Cyan "==> Info:" prefix.
func (l *Logger) Info(v ...interface{}) {
	l.Logger.Print(l.InfoPrefix + fmt.Sprint(v...))
}

// Debug works just like log.Print, but with a Magenta "==> Debug:" prefix.
func (l *Logger) Debug(v ...interface{}) {
	if l.DebugMode {
		l.Logger.Print(l.DebugPrefix + fmt.Sprint(v...))
	}
}

// Warn works just like log.Print, but with a Yellow "==> Warn:" prefix.
func (l *Logger) Warn(v ...interface{}) {
	l.Logger.Print(l.WarnPrefix + fmt.Sprint(v...))
}

// Fatal works just like log.Fatal, but with a Red "==> Fatal:" prefix.
func (l *Logger) Fatal(v ...interface{}) {
	l.Logger.Fatal(l.FatalPrefix + fmt.Sprint(v...))
}

// Panic works just like log.Panic, but with a Red "==> Panic:" prefix.
func (l *Logger) Panic(v ...interface{}) {
	l.Logger.Panic(l.PanicPrefix + fmt.Sprint(v...))
}

// Infof works just like log.Printf, but with a Cyan "==> Info:" prefix.
func (l *Logger) Infof(format string, v ...interface{}) {
	l.Logger.Print(l.InfoPrefix + fmt.Sprintf(format, v...))
}

// Debugf works just like log.Printf, but with a Magenta "==> Debug:" prefix.
func (l *Logger) Debugf(format string, v ...interface{}) {
	if l.DebugMode {
		l.Logger.Print(l.DebugPrefix + fmt.Sprintf(format, v...))
	}
}

// Warnf works just like log.Printf, but with a Yellow "==> Warn:" prefix.
func (l *Logger) Warnf(format string, v ...interface{}) {
	l.Logger.Print(l.WarnPrefix + fmt.Sprintf(format, v...))
}

// Fatalf works just like log.Fatalf, but with a Red "==> Fatal:" prefix.
func (l *Logger) Fatalf(format string, v ...interface{}) {
	l.Logger.Fatalf(l.FatalPrefix + fmt.Sprintf(format, v...))
}

// Panicf works just like log.Panicf, but with a Red "==> Panic:" prefix.
func (l *Logger) Panicf(format string, v ...interface{}) {
	l.Logger.Panicf(l.PanicPrefix + fmt.Sprintf(format, v...))
}

// Infoln works just like log.Println, but with a Cyan "==> Info:" prefix.
func (l *Logger) Infoln(v ...interface{}) {
	l.Logger.Println(l.InfoPrefix + fmt.Sprint(v...))
}

// Debugln works just like log.Println, but with a Magenta "==> Debug:" prefix.
func (l *Logger) Debugln(v ...interface{}) {
	if l.DebugMode {
		l.Logger.Println(l.DebugPrefix + fmt.Sprint(v...))
	}
}

// Warnln works just like log.Println, but with a Yellow "==> Warn:" prefix.
func (l *Logger) Warnln(v ...interface{}) {
	l.Logger.Println(l.WarnPrefix + fmt.Sprint(v...))
}

// Fatalln works just like log.Fatalln, but with a Red "==> Fatal:" prefix.
func (l *Logger) Fatalln(v ...interface{}) {
	l.Logger.Fatalln(l.FatalPrefix + fmt.Sprint(v...))
}

// Panicln works just like log.Panicln, but with a Red "==> Panic:" prefix.
func (l *Logger) Panicln(v ...interface{}) {
	l.Logger.Panicln(l.PanicPrefix + fmt.Sprint(v...))
}
<|endoftext|>


<commit_before>package nsq

import (
	"bitly/simplejson"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"sync"
	"time"
)

// a syncronous handler that returns an error (or nil to indicate success)
type Handler interface {
	HandleMessage(message *Message) error
}

// an async handler that must send a &FinishedMessage{messageID, requeueDelay, true|false} onto
// responseChannel to indicate that a message has been finished. This is usefull
// if you want to batch work together and delay response that processing is complete
type AsyncHandler interface {
	HandleMessage(message *Message, responseChannel chan *FinishedMessage)
}

type Reader struct {
	TopicName           string // Name of Topic to subscribe to
	ChannelName         string // Named Channel to consume messages from
	IncomingMessages    chan *IncomingMessage
	ExitChan            chan int
	BufferSize          int // number of messages to allow in-flight from NSQ's at a time
	LookupdPoolInterval int // seconds between polling lookupd's (+/- random 15 seconds)

	MessagesReceived uint64
	MessagesFinished uint64
	MessagesReQueued uint64

	nsqConnections map[string]*nsqConn

	lookupdExitChan    chan int
	lookupdRecheckChan chan int
	stopFlag           bool
	runningFlag        bool
	runningHandlers    int
	messagesInFlight   int
	consumer           *Consumer
	lookupdAddresses   []*net.TCPAddr

	sync.RWMutex
}

type nsqConn struct {
	ServerAddress    *net.TCPAddr
	FinishedMessages chan *FinishedMessage

	consumer         *Consumer
	stopFlag         bool
	messagesInFlight int
	messagesReceived uint64
	messagesFinished uint64
	messagesReQueued uint64
}

type IncomingMessage struct {
	*Message
	responseChannel chan *FinishedMessage
}

type FinishedMessage struct {
	Id             []byte
	RequeueDelayMs int
	Success        bool
}

func NewReader(topic string, channel string) (*Reader, error) {
	q := &Reader{
		TopicName:           topic,
		ChannelName:         channel,
		IncomingMessages:    make(chan *IncomingMessage),
		ExitChan:            make(chan int),
		nsqConnections:      make(map[string]*nsqConn),
		BufferSize:          1,
		LookupdPoolInterval: 300,
		lookupdExitChan:     make(chan int),
		lookupdRecheckChan:  make(chan int), // used at connection close to force a possible reconnect
	}
	return q, nil
}

// get an appropriate buffer size for a single connection
// essentially the global buffer size distributed amongst # connection
func (q *Reader) getBufferSize() int {
	b := float64(q.BufferSize)
	s := b / (float64(len(q.nsqConnections)) * .75)
	return int(math.Min(math.Max(1, s), b))

}

func (q *Reader) ConnectToLookupd(addr *net.TCPAddr) error {
	// make a HTTP req to the lookupd, and ask it for endpoints that have the
	// topic we are interested in.
	// this is a go loop that fires every x seconds
	for _, x := range q.lookupdAddresses {
		if x == addr {
			return errors.New("address already exists")
		}
	}
	q.lookupdAddresses = append(q.lookupdAddresses, addr)

	// if this is the first one, kick off the go loop
	if len(q.lookupdAddresses) == 1 {
		q.QueryLookupd()
		go q.LookupdLoop()
	}
	return nil
}

// poll all known lookup servers every LookupdPoolInterval seconds
func (q *Reader) LookupdLoop() {
	r := rand.New(rand.NewSource(1))
	interval := q.LookupdPoolInterval + r.Intn(30) - 15
	if interval < 1 {
		interval = 15
	}
	ticker := time.Tick(time.Duration(interval) * time.Second)
	for {
		select {
		case <-ticker:
			q.QueryLookupd()
		case <-q.lookupdRecheckChan:
			q.QueryLookupd()
		case <-q.lookupdExitChan:
			return
		}
	}

}

// make a HTTP req to the /lookup endpoint on each lookup server
// to find what nsq's provide the topic we are consuming.
// for any new topics, initiate a connection to those NSQ's
func (q *Reader) QueryLookupd() {
	httpclient := &http.Client{}
	for _, addr := range q.lookupdAddresses {
		endpoint := fmt.Sprintf("http://%s/lookup?topic=%s", addr, url.QueryEscape(q.TopicName))
		req, err := http.NewRequest("GET", endpoint, nil)
		if err != nil {
			log.Printf("LOOKUPD %s error %s", addr, err.Error())
			continue
		}
		resp, err := httpclient.Do(req)
		if err != nil {
			log.Printf("LOOKUPD %s error %s", addr, err.Error())
			continue
		}
		body, _ := ioutil.ReadAll(resp.Body)
		log.Printf("LOOKUPD %s responded with %s", addr, body)
		data, err := simplejson.NewJson(body)
		resp.Body.Close()
		if err != nil {
			log.Printf("LOOKUPD %s error %s", addr, err.Error())
			continue
		}

		statusCode, err := data.Get("status_code").Int()
		if err != nil || statusCode != 200 {
			continue
		}

		// do something with the data
		// {"data":{"channels":[],"producers":[{"address":"jehiah-air.local","port":"4150"}],"timestamp":1340152173},"status_code":200,"status_txt":"OK"}
		producers, _ := data.Get("data").Get("producers").Array()
		for _, producer := range producers {
			producerData, _ := producer.(map[string]interface{})
			address := producerData["address"].(string)
			port := producerData["port"].(int)

			// make an address, start a connection
			nsqAddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(address, strconv.Itoa(port)))
			if err == nil {
				q.ConnectToNSQ(nsqAddr)
			}
		}
	}
}

func (q *Reader) ConnectToNSQ(addr *net.TCPAddr) error {
	if q.stopFlag {
		return errors.New("Queue has been stopped")
	}

	if q.runningHandlers == 0 {
		return errors.New("There are no handlers running")
	}

	_, ok := q.nsqConnections[addr.String()]
	if ok {
		return errors.New("already connected")
	}

	log.Printf("[%s] connecting to nsqd", addr)
	consumer := NewConsumer(addr)
	err := consumer.Connect()
	if err != nil {
		log.Printf("[%s] %s", addr, err.Error())
		return err
	}

	consumer.Version(ProtocolV2Magic)
	consumer.WriteCommand(consumer.Subscribe(q.TopicName, q.ChannelName))

	connection := &nsqConn{
		ServerAddress:    addr,
		FinishedMessages: make(chan *FinishedMessage),
		consumer:         consumer,
		stopFlag:         false,
	}

	q.nsqConnections[addr.String()] = connection

	go ConnectionReadLoop(q, connection)
	go ConnectionFinishLoop(q, connection)
	return nil
}

func handleReadError(q *Reader, c *nsqConn, errMsg string) {
	log.Printf(errMsg)
	c.stopFlag = true
	if len(q.nsqConnections) == 1 && len(q.lookupdAddresses) == 0 {
		// This is the only remaining connection, so stop the queue
		q.stopFlag = true
	}
}

func ConnectionReadLoop(q *Reader, c *nsqConn) {
	// prime our ready state
	c.consumer.WriteCommand(c.consumer.Ready(q.getBufferSize()))
	for {
		if c.stopFlag || q.stopFlag {
			// start the connection close
			q.RLock()
			cInFlight := c.messagesInFlight
			q.RUnlock()
			if cInFlight == 0 {
				log.Printf("[%s] closing FinishedMessages channel", c.ServerAddress)
				close(c.FinishedMessages)
			} else {
				log.Printf("[%s] delaying close of FinishedMesages channel; %d outstanding messages", c.ServerAddress, c.messagesInFlight)
			}
			log.Printf("[%s] stopped read loop ", c.ServerAddress)
			break
		}

		resp, err := c.consumer.ReadResponse()
		if err != nil {
			handleReadError(q, c, fmt.Sprintf("[%s] error on response %s", c.ServerAddress, err.Error()))
			continue
		}

		frameType, data, err := c.consumer.UnpackResponse(resp)
		if err != nil {
			handleReadError(q, c, fmt.Sprintf("[%s] error (%s) unpacking response %s %s", c.ServerAddress, err.Error(), frameType, data))
			continue
		}

		switch frameType {
		case FrameTypeMessage:
			c.messagesReceived += 1
			q.MessagesReceived += 1
			msg := data.(*Message)
			log.Printf("[%s] FrameTypeMessage: %s - %s", c.ServerAddress, msg.Id, msg.Body)
			q.Lock()
			c.messagesInFlight += 1
			q.messagesInFlight += 1
			q.Unlock()
			q.IncomingMessages <- &IncomingMessage{msg, c.FinishedMessages}
		case FrameTypeCloseWait:
			// server is ready for us to close (it ack'd our StartClose)
			// we can assume we will not receive any more messages over this channel
			// (but we can still write back responses)
			log.Printf("[%s] received ACK from server. now in CloseWait %d", c.ServerAddress, frameType)
			c.stopFlag = true
		default:
			log.Printf("[%s] unknown message type %d", c.ServerAddress, frameType)
		}

	}
}

func ConnectionFinishLoop(q *Reader, c *nsqConn) {
	for {
		msg, ok := <-c.FinishedMessages
		if !ok {
			log.Printf("[%s] stopping finish loop ", c.ServerAddress)
			c.consumer.Close()
			delete(q.nsqConnections, c.ServerAddress.String())

			log.Printf("there are %d connections left alive", len(q.nsqConnections))

			if len(q.nsqConnections) == 0 && len(q.lookupdAddresses) == 0 {
				// no lookupd entry means no reconnection
				if q.stopFlag {
					q.stopHandlers()
				}
			}
			if len(q.lookupdAddresses) != 0 && !q.stopFlag {
				// trigger a poll of the lookupd
				q.lookupdRecheckChan <- 1
			}
			return
		}
		if msg.Success {
			log.Printf("[%s] successfully finished %s", c.ServerAddress, msg.Id)
			c.consumer.WriteCommand(c.consumer.Finish(msg.Id))
			c.messagesFinished += 1
			q.MessagesFinished += 1
		} else {
			log.Printf("[%s] failed message %s", c.ServerAddress, msg.Id)
			c.consumer.WriteCommand(c.consumer.Requeue(msg.Id, msg.RequeueDelayMs))
			c.messagesReQueued += 1
			q.MessagesReQueued += 1
		}
		var cInFlight int
		q.Lock()
		c.messagesInFlight -= 1
		q.messagesInFlight -= 1
		cInFlight = c.messagesInFlight
		q.Unlock()

		if c.stopFlag && cInFlight == 0 {
			log.Printf("[%s] closing c.FinishedMessages", c.ServerAddress)
			close(c.FinishedMessages)
			return
		}

		// TODO: don't write this every time (for when we have batch requesting enabled)
		if !c.stopFlag {
			s := q.getBufferSize() - cInFlight
			if s >= 1 {
				log.Printf("RDY %d", s)
				c.consumer.WriteCommand(c.consumer.Ready(s))
			}
		}
	}
}

// stops a Reader gracefully
func (q *Reader) Stop() {
	q.stopFlag = true
	for _, c := range q.nsqConnections {
		c.consumer.WriteCommand(c.consumer.StartClose())
	}
	if len(q.lookupdAddresses) != 0 {
		q.lookupdExitChan <- 1
	}
}

func (q *Reader) stopHandlers() {
	log.Printf("closing IncomingMessages")
	close(q.IncomingMessages)
}

// this starts a handler on the Reader
// it's ok to start more than one handler simultaneously
func (q *Reader) AddHandler(handler Handler) {
	q.runningHandlers += 1
	log.Println("starting Handler go-routine")
	go func() {
		for {
			message, ok := <-q.IncomingMessages
			if !ok {
				log.Printf("closing Handler (after self.IncomingMessages closed)")
				q.runningHandlers -= 1
				if q.runningHandlers == 0 {
					q.ExitChan <- 1
				}
				break
			}

			err := handler.HandleMessage(message.Message)
			if err != nil {
				log.Printf("ERR: handler returned %s for msg %s %s", err.Error(), message.Id, message.Body)
			}
			// default to an exponential delay
			requeueDelay := 90000 * int(message.Retries)
			message.responseChannel <- &FinishedMessage{message.Id, requeueDelay, err == nil}
		}
	}()
}

// this starts an async handler on the Reader
// it's ok to start more than one handler simultaneously
func (q *Reader) AddAsyncHandler(handler AsyncHandler) {
	q.runningHandlers += 1
	log.Println("starting AsyncHandler go-routine")
	go func() {
		for {
			message, ok := <-q.IncomingMessages
			if !ok {
				log.Printf("closing AsyncHandler (after self.IncomingMessages closed)")
				q.runningHandlers -= 1
				if q.runningHandlers == 0 {
					q.ExitChan <- 1
				}
				break
			}
			handler.HandleMessage(message.Message, message.responseChannel)
		}
	}()
}
<commit_msg>continue so that cleanup finishes on next loop<commit_after>package nsq

import (
	"bitly/simplejson"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"sync"
	"time"
)

// a syncronous handler that returns an error (or nil to indicate success)
type Handler interface {
	HandleMessage(message *Message) error
}

// an async handler that must send a &FinishedMessage{messageID, requeueDelay, true|false} onto
// responseChannel to indicate that a message has been finished. This is usefull
// if you want to batch work together and delay response that processing is complete
type AsyncHandler interface {
	HandleMessage(message *Message, responseChannel chan *FinishedMessage)
}

type Reader struct {
	TopicName           string // Name of Topic to subscribe to
	ChannelName         string // Named Channel to consume messages from
	IncomingMessages    chan *IncomingMessage
	ExitChan            chan int
	BufferSize          int // number of messages to allow in-flight from NSQ's at a time
	LookupdPoolInterval int // seconds between polling lookupd's (+/- random 15 seconds)

	MessagesReceived uint64
	MessagesFinished uint64
	MessagesReQueued uint64

	nsqConnections map[string]*nsqConn

	lookupdExitChan    chan int
	lookupdRecheckChan chan int
	stopFlag           bool
	runningFlag        bool
	runningHandlers    int
	messagesInFlight   int
	consumer           *Consumer
	lookupdAddresses   []*net.TCPAddr

	sync.RWMutex
}

type nsqConn struct {
	ServerAddress    *net.TCPAddr
	FinishedMessages chan *FinishedMessage

	consumer         *Consumer
	stopFlag         bool
	messagesInFlight int
	messagesReceived uint64
	messagesFinished uint64
	messagesReQueued uint64
}

type IncomingMessage struct {
	*Message
	responseChannel chan *FinishedMessage
}

type FinishedMessage struct {
	Id             []byte
	RequeueDelayMs int
	Success        bool
}

func NewReader(topic string, channel string) (*Reader, error) {
	q := &Reader{
		TopicName:           topic,
		ChannelName:         channel,
		IncomingMessages:    make(chan *IncomingMessage),
		ExitChan:            make(chan int),
		nsqConnections:      make(map[string]*nsqConn),
		BufferSize:          1,
		LookupdPoolInterval: 300,
		lookupdExitChan:     make(chan int),
		lookupdRecheckChan:  make(chan int), // used at connection close to force a possible reconnect
	}
	return q, nil
}

// get an appropriate buffer size for a single connection
// essentially the global buffer size distributed amongst # connection
func (q *Reader) getBufferSize() int {
	b := float64(q.BufferSize)
	s := b / (float64(len(q.nsqConnections)) * .75)
	return int(math.Min(math.Max(1, s), b))

}

func (q *Reader) ConnectToLookupd(addr *net.TCPAddr) error {
	// make a HTTP req to the lookupd, and ask it for endpoints that have the
	// topic we are interested in.
	// this is a go loop that fires every x seconds
	for _, x := range q.lookupdAddresses {
		if x == addr {
			return errors.New("address already exists")
		}
	}
	q.lookupdAddresses = append(q.lookupdAddresses, addr)

	// if this is the first one, kick off the go loop
	if len(q.lookupdAddresses) == 1 {
		q.QueryLookupd()
		go q.LookupdLoop()
	}
	return nil
}

// poll all known lookup servers every LookupdPoolInterval seconds
func (q *Reader) LookupdLoop() {
	r := rand.New(rand.NewSource(1))
	interval := q.LookupdPoolInterval + r.Intn(30) - 15
	if interval < 1 {
		interval = 15
	}
	ticker := time.Tick(time.Duration(interval) * time.Second)
	for {
		select {
		case <-ticker:
			q.QueryLookupd()
		case <-q.lookupdRecheckChan:
			q.QueryLookupd()
		case <-q.lookupdExitChan:
			return
		}
	}

}

// make a HTTP req to the /lookup endpoint on each lookup server
// to find what nsq's provide the topic we are consuming.
// for any new topics, initiate a connection to those NSQ's
func (q *Reader) QueryLookupd() {
	httpclient := &http.Client{}
	for _, addr := range q.lookupdAddresses {
		endpoint := fmt.Sprintf("http://%s/lookup?topic=%s", addr, url.QueryEscape(q.TopicName))
		req, err := http.NewRequest("GET", endpoint, nil)
		if err != nil {
			log.Printf("LOOKUPD %s error %s", addr, err.Error())
			continue
		}
		resp, err := httpclient.Do(req)
		if err != nil {
			log.Printf("LOOKUPD %s error %s", addr, err.Error())
			continue
		}
		body, _ := ioutil.ReadAll(resp.Body)
		log.Printf("LOOKUPD %s responded with %s", addr, body)
		data, err := simplejson.NewJson(body)
		resp.Body.Close()
		if err != nil {
			log.Printf("LOOKUPD %s error %s", addr, err.Error())
			continue
		}

		statusCode, err := data.Get("status_code").Int()
		if err != nil || statusCode != 200 {
			continue
		}

		// do something with the data
		// {"data":{"channels":[],"producers":[{"address":"jehiah-air.local","port":"4150"}],"timestamp":1340152173},"status_code":200,"status_txt":"OK"}
		producers, _ := data.Get("data").Get("producers").Array()
		for _, producer := range producers {
			producerData, _ := producer.(map[string]interface{})
			address := producerData["address"].(string)
			port := producerData["port"].(int)

			// make an address, start a connection
			nsqAddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(address, strconv.Itoa(port)))
			if err == nil {
				q.ConnectToNSQ(nsqAddr)
			}
		}
	}
}

func (q *Reader) ConnectToNSQ(addr *net.TCPAddr) error {
	if q.stopFlag {
		return errors.New("Queue has been stopped")
	}

	if q.runningHandlers == 0 {
		return errors.New("There are no handlers running")
	}

	_, ok := q.nsqConnections[addr.String()]
	if ok {
		return errors.New("already connected")
	}

	log.Printf("[%s] connecting to nsqd", addr)
	consumer := NewConsumer(addr)
	err := consumer.Connect()
	if err != nil {
		log.Printf("[%s] %s", addr, err.Error())
		return err
	}

	consumer.Version(ProtocolV2Magic)
	consumer.WriteCommand(consumer.Subscribe(q.TopicName, q.ChannelName))

	connection := &nsqConn{
		ServerAddress:    addr,
		FinishedMessages: make(chan *FinishedMessage),
		consumer:         consumer,
		stopFlag:         false,
	}

	q.nsqConnections[addr.String()] = connection

	go ConnectionReadLoop(q, connection)
	go ConnectionFinishLoop(q, connection)
	return nil
}

func handleReadError(q *Reader, c *nsqConn, errMsg string) {
	log.Printf(errMsg)
	c.stopFlag = true
	if len(q.nsqConnections) == 1 && len(q.lookupdAddresses) == 0 {
		// This is the only remaining connection, so stop the queue
		q.stopFlag = true
	}
}

func ConnectionReadLoop(q *Reader, c *nsqConn) {
	// prime our ready state
	c.consumer.WriteCommand(c.consumer.Ready(q.getBufferSize()))
	for {
		if c.stopFlag || q.stopFlag {
			// start the connection close
			q.RLock()
			cInFlight := c.messagesInFlight
			q.RUnlock()
			if cInFlight == 0 {
				log.Printf("[%s] closing FinishedMessages channel", c.ServerAddress)
				close(c.FinishedMessages)
			} else {
				log.Printf("[%s] delaying close of FinishedMesages channel; %d outstanding messages", c.ServerAddress, c.messagesInFlight)
			}
			log.Printf("[%s] stopped read loop ", c.ServerAddress)
			break
		}

		resp, err := c.consumer.ReadResponse()
		if err != nil {
			handleReadError(q, c, fmt.Sprintf("[%s] error on response %s", c.ServerAddress, err.Error()))
			continue
		}

		frameType, data, err := c.consumer.UnpackResponse(resp)
		if err != nil {
			handleReadError(q, c, fmt.Sprintf("[%s] error (%s) unpacking response %s %s", c.ServerAddress, err.Error(), frameType, data))
			continue
		}

		switch frameType {
		case FrameTypeMessage:
			c.messagesReceived += 1
			q.MessagesReceived += 1
			msg := data.(*Message)
			log.Printf("[%s] FrameTypeMessage: %s - %s", c.ServerAddress, msg.Id, msg.Body)
			q.Lock()
			c.messagesInFlight += 1
			q.messagesInFlight += 1
			q.Unlock()
			q.IncomingMessages <- &IncomingMessage{msg, c.FinishedMessages}
		case FrameTypeCloseWait:
			// server is ready for us to close (it ack'd our StartClose)
			// we can assume we will not receive any more messages over this channel
			// (but we can still write back responses)
			log.Printf("[%s] received ACK from server. now in CloseWait %d", c.ServerAddress, frameType)
			c.stopFlag = true
		default:
			log.Printf("[%s] unknown message type %d", c.ServerAddress, frameType)
		}

	}
}

func ConnectionFinishLoop(q *Reader, c *nsqConn) {
	for {
		msg, ok := <-c.FinishedMessages
		if !ok {
			log.Printf("[%s] stopping finish loop ", c.ServerAddress)
			c.consumer.Close()
			delete(q.nsqConnections, c.ServerAddress.String())

			log.Printf("there are %d connections left alive", len(q.nsqConnections))

			if len(q.nsqConnections) == 0 && len(q.lookupdAddresses) == 0 {
				// no lookupd entry means no reconnection
				if q.stopFlag {
					q.stopHandlers()
				}
			}
			if len(q.lookupdAddresses) != 0 && !q.stopFlag {
				// trigger a poll of the lookupd
				q.lookupdRecheckChan <- 1
			}
			return
		}
		if msg.Success {
			log.Printf("[%s] successfully finished %s", c.ServerAddress, msg.Id)
			c.consumer.WriteCommand(c.consumer.Finish(msg.Id))
			c.messagesFinished += 1
			q.MessagesFinished += 1
		} else {
			log.Printf("[%s] failed message %s", c.ServerAddress, msg.Id)
			c.consumer.WriteCommand(c.consumer.Requeue(msg.Id, msg.RequeueDelayMs))
			c.messagesReQueued += 1
			q.MessagesReQueued += 1
		}
		var cInFlight int
		q.Lock()
		c.messagesInFlight -= 1
		q.messagesInFlight -= 1
		cInFlight = c.messagesInFlight
		q.Unlock()

		if c.stopFlag && cInFlight == 0 {
			log.Printf("[%s] closing c.FinishedMessages", c.ServerAddress)
			close(c.FinishedMessages)
			continue
		}

		// TODO: don't write this every time (for when we have batch requesting enabled)
		if !c.stopFlag {
			s := q.getBufferSize() - cInFlight
			if s >= 1 {
				log.Printf("RDY %d", s)
				c.consumer.WriteCommand(c.consumer.Ready(s))
			}
		}
	}
}

// stops a Reader gracefully
func (q *Reader) Stop() {
	q.stopFlag = true
	for _, c := range q.nsqConnections {
		c.consumer.WriteCommand(c.consumer.StartClose())
	}
	if len(q.lookupdAddresses) != 0 {
		q.lookupdExitChan <- 1
	}
}

func (q *Reader) stopHandlers() {
	log.Printf("closing IncomingMessages")
	close(q.IncomingMessages)
}

// this starts a handler on the Reader
// it's ok to start more than one handler simultaneously
func (q *Reader) AddHandler(handler Handler) {
	q.runningHandlers += 1
	log.Println("starting Handler go-routine")
	go func() {
		for {
			message, ok := <-q.IncomingMessages
			if !ok {
				log.Printf("closing Handler (after self.IncomingMessages closed)")
				q.runningHandlers -= 1
				if q.runningHandlers == 0 {
					q.ExitChan <- 1
				}
				break
			}

			err := handler.HandleMessage(message.Message)
			if err != nil {
				log.Printf("ERR: handler returned %s for msg %s %s", err.Error(), message.Id, message.Body)
			}
			// default to an exponential delay
			requeueDelay := 90000 * int(message.Retries)
			message.responseChannel <- &FinishedMessage{message.Id, requeueDelay, err == nil}
		}
	}()
}

// this starts an async handler on the Reader
// it's ok to start more than one handler simultaneously
func (q *Reader) AddAsyncHandler(handler AsyncHandler) {
	q.runningHandlers += 1
	log.Println("starting AsyncHandler go-routine")
	go func() {
		for {
			message, ok := <-q.IncomingMessages
			if !ok {
				log.Printf("closing AsyncHandler (after self.IncomingMessages closed)")
				q.runningHandlers -= 1
				if q.runningHandlers == 0 {
					q.ExitChan <- 1
				}
				break
			}
			handler.HandleMessage(message.Message, message.responseChannel)
		}
	}()
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"io/ioutil"
	"os"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/hcl"
	"github.com/hashicorp/hcl/hcl/ast"
	"github.com/mitchellh/go-homedir"
)

const (
	// DefaultConfigPath is the default path to the configuration file
	DefaultConfigPath = "~/.vault"

	// ConfigPathEnv is the environment variable that can be used to
	// override where the Vault configuration is.
	ConfigPathEnv = "VAULT_CONFIG_PATH"
)

// Config is the CLI configuration for Vault that can be specified via
// a `$HOME/.vault` file which is HCL-formatted (therefore HCL or JSON).
type DefaultConfig struct {
	// TokenHelper is the executable/command that is executed for storing
	// and retrieving the authentication token for the Vault CLI. If this
	// is not specified, then vault's internal token store will be used, which
	// stores the token on disk unencrypted.
	TokenHelper string `hcl:"token_helper"`
}

// Config loads the configuration and returns it. If the configuration
// is already loaded, it is returned.
func Config() (*DefaultConfig, error) {
	var err error
	config, err := LoadConfig("")
	if err != nil {
		return nil, err
	}

	return config, nil
}

// LoadConfig reads the configuration from the given path. If path is
// empty, then the default path will be used, or the environment variable
// if set.
func LoadConfig(path string) (*DefaultConfig, error) {
	if path == "" {
		path = DefaultConfigPath
	}
	if v := os.Getenv(ConfigPathEnv); v != "" {
		path = v
	}

	path, err := homedir.Expand(path)
	if err != nil {
		return nil, fmt.Errorf("Error expanding config path: %s", err)
	}

	contents, err := ioutil.ReadFile(path)
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	return ParseConfig(string(contents))
}

// ParseConfig parses the given configuration as a string.
func ParseConfig(contents string) (*DefaultConfig, error) {
	root, err := hcl.Parse(contents)
	if err != nil {
		return nil, err
	}

	// Top-level item should be the object list
	list, ok := root.Node.(*ast.ObjectList)
	if !ok {
		return nil, fmt.Errorf("Failed to parse config: does not contain a root object")
	}

	valid := []string{
		"token_helper",
	}
	if err := checkHCLKeys(list, valid); err != nil {
		return nil, err
	}

	var c DefaultConfig
	if err := hcl.DecodeObject(&c, list); err != nil {
		return nil, err
	}
	return &c, nil
}

func checkHCLKeys(node ast.Node, valid []string) error {
	var list *ast.ObjectList
	switch n := node.(type) {
	case *ast.ObjectList:
		list = n
	case *ast.ObjectType:
		list = n.List
	default:
		return fmt.Errorf("cannot check HCL keys of type %T", n)
	}

	validMap := make(map[string]struct{}, len(valid))
	for _, v := range valid {
		validMap[v] = struct{}{}
	}

	var result error
	for _, item := range list.Items {
		key := item.Keys[0].Token.Value().(string)
		if _, ok := validMap[key]; !ok {
			result = multierror.Append(result, fmt.Errorf(
				"invalid key '%s' on line %d", key, item.Assign.Line))
		}
	}

	return result
}
<commit_msg>Improve error handling re: homedir expansion<commit_after>package command

import (
	"fmt"
	"io/ioutil"
	"os"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/hcl"
	"github.com/hashicorp/hcl/hcl/ast"
	"github.com/mitchellh/go-homedir"
)

const (
	// DefaultConfigPath is the default path to the configuration file
	DefaultConfigPath = "~/.vault"

	// ConfigPathEnv is the environment variable that can be used to
	// override where the Vault configuration is.
	ConfigPathEnv = "VAULT_CONFIG_PATH"
)

// Config is the CLI configuration for Vault that can be specified via
// a `$HOME/.vault` file which is HCL-formatted (therefore HCL or JSON).
type DefaultConfig struct {
	// TokenHelper is the executable/command that is executed for storing
	// and retrieving the authentication token for the Vault CLI. If this
	// is not specified, then vault's internal token store will be used, which
	// stores the token on disk unencrypted.
	TokenHelper string `hcl:"token_helper"`
}

// Config loads the configuration and returns it. If the configuration
// is already loaded, it is returned.
func Config() (*DefaultConfig, error) {
	var err error
	config, err := LoadConfig("")
	if err != nil {
		return nil, err
	}

	return config, nil
}

// LoadConfig reads the configuration from the given path. If path is
// empty, then the default path will be used, or the environment variable
// if set.
func LoadConfig(path string) (*DefaultConfig, error) {
	if path == "" {
		path = DefaultConfigPath
	}
	if v := os.Getenv(ConfigPathEnv); v != "" {
		path = v
	}

	// NOTE: requires HOME env var to be set
	path, err := homedir.Expand(path)
	if err != nil {
		return nil, fmt.Errorf("Error expanding config path %s: %s", path, err)
	}

	contents, err := ioutil.ReadFile(path)
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	return ParseConfig(string(contents))
}

// ParseConfig parses the given configuration as a string.
func ParseConfig(contents string) (*DefaultConfig, error) {
	root, err := hcl.Parse(contents)
	if err != nil {
		return nil, err
	}

	// Top-level item should be the object list
	list, ok := root.Node.(*ast.ObjectList)
	if !ok {
		return nil, fmt.Errorf("Failed to parse config: does not contain a root object")
	}

	valid := []string{
		"token_helper",
	}
	if err := checkHCLKeys(list, valid); err != nil {
		return nil, err
	}

	var c DefaultConfig
	if err := hcl.DecodeObject(&c, list); err != nil {
		return nil, err
	}
	return &c, nil
}

func checkHCLKeys(node ast.Node, valid []string) error {
	var list *ast.ObjectList
	switch n := node.(type) {
	case *ast.ObjectList:
		list = n
	case *ast.ObjectType:
		list = n.List
	default:
		return fmt.Errorf("cannot check HCL keys of type %T", n)
	}

	validMap := make(map[string]struct{}, len(valid))
	for _, v := range valid {
		validMap[v] = struct{}{}
	}

	var result error
	for _, item := range list.Items {
		key := item.Keys[0].Token.Value().(string)
		if _, ok := validMap[key]; !ok {
			result = multierror.Append(result, fmt.Errorf(
				"invalid key '%s' on line %d", key, item.Assign.Line))
		}
	}

	return result
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"strings"

	nomad "github.com/hashicorp/nomad/api"

	"github.com/jrasell/levant/levant"
	"github.com/jrasell/levant/logging"
)

// DeployCommand is the command implementation that allows users to deploy a
// Nomad job based on passed templates and variables.
type DeployCommand struct {
	args []string
	Meta
}

// Help provides the help information for the deploy command.
func (c *DeployCommand) Help() string {
	helpText := `
Usage: levant deploy [options] TEMPLATE

  Deploy a Nomad job based on input templates and variable files.

General Options:

  -address=<http_address>
    The Nomad HTTP API address including port which Levant will use to make
    calls.

  -log-level=<level>
    Specify the verbosity level of Levant's logs. Valid values include DEBUG,
    INFO, and WARN, in decreasing order of verbosity. The default is INFO.

  -var-file=<file>
    Used in conjunction with the -job-file will deploy a templated job to your
	Nomad cluster.
`
	return strings.TrimSpace(helpText)
}

// Synopsis is provides a brief summary of the deploy command.
func (c *DeployCommand) Synopsis() string {
	return "Render and deploy a Nomad job from a template"
}

// Run triggers a run of the Levant template and deploy functions.
func (c *DeployCommand) Run(args []string) int {

	var variables, addr, log string
	var err error
	var job *nomad.Job

	flags := c.Meta.FlagSet("build", FlagSetVars)
	flags.Usage = func() { c.UI.Output(c.Help()) }

	flags.StringVar(&addr, "address", "", "")
	flags.StringVar(&log, "log-level", "INFO", "")
	flags.StringVar(&variables, "var-file", "", "")

	if err = flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()

	if len(args) != 1 {
		c.UI.Error(c.Help())
		return 1
	}

	logging.SetLevel(log)

	job, err = levant.RenderTemplate(args[0], variables, &c.Meta.flagVars)
	if err != nil {
		c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
		return 1
	}

	client, err := levant.NewNomadClient(addr)
	if err != nil {
		c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
		return 1
	}

	success := client.Deploy(job)
	if !success {
		c.UI.Error(fmt.Sprintf("[ERROR] levant/command: deployment of job %s failed", *job.Name))
		return 1
	}

	c.UI.Info(fmt.Sprintf("[INFO] levant/command: deployment of job %s successful", *job.Name))

	return 0
}
<commit_msg>Fix formatting issues in Deploy command help output.<commit_after>package command

import (
	"fmt"
	"strings"

	nomad "github.com/hashicorp/nomad/api"

	"github.com/jrasell/levant/levant"
	"github.com/jrasell/levant/logging"
)

// DeployCommand is the command implementation that allows users to deploy a
// Nomad job based on passed templates and variables.
type DeployCommand struct {
	args []string
	Meta
}

// Help provides the help information for the deploy command.
func (c *DeployCommand) Help() string {
	helpText := `
Usage: levant deploy [options] TEMPLATE

  Deploy a Nomad job based on input templates and variable files.

General Options:

  -address=<http_address>
    The Nomad HTTP API address including port which Levant will use to make
    calls.

  -log-level=<level>
    Specify the verbosity level of Levant's logs. Valid values include DEBUG,
    INFO, and WARN, in decreasing order of verbosity. The default is INFO.

  -var-file=<file>
    Used in conjunction with the -job-file will deploy a templated job to your
    Nomad cluster.
`
	return strings.TrimSpace(helpText)
}

// Synopsis is provides a brief summary of the deploy command.
func (c *DeployCommand) Synopsis() string {
	return "Render and deploy a Nomad job from a template"
}

// Run triggers a run of the Levant template and deploy functions.
func (c *DeployCommand) Run(args []string) int {

	var variables, addr, log string
	var err error
	var job *nomad.Job

	flags := c.Meta.FlagSet("build", FlagSetVars)
	flags.Usage = func() { c.UI.Output(c.Help()) }

	flags.StringVar(&addr, "address", "", "")
	flags.StringVar(&log, "log-level", "INFO", "")
	flags.StringVar(&variables, "var-file", "", "")

	if err = flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()

	if len(args) != 1 {
		c.UI.Error(c.Help())
		return 1
	}

	logging.SetLevel(log)

	job, err = levant.RenderTemplate(args[0], variables, &c.Meta.flagVars)
	if err != nil {
		c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
		return 1
	}

	client, err := levant.NewNomadClient(addr)
	if err != nil {
		c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
		return 1
	}

	success := client.Deploy(job)
	if !success {
		c.UI.Error(fmt.Sprintf("[ERROR] levant/command: deployment of job %s failed", *job.Name))
		return 1
	}

	c.UI.Info(fmt.Sprintf("[INFO] levant/command: deployment of job %s successful", *job.Name))

	return 0
}
<|endoftext|>


<commit_before>package commands

import (
	"context"
	"fmt"
	"os"
	"syscall"

	"github.com/zetamatta/go-getch"

	"github.com/zetamatta/nyagos/dos"
)

func cmdMkdir(ctx context.Context, cmd Param) (int, error) {
	if len(cmd.Args()) <= 1 {
		fmt.Fprintln(cmd.Err(), "Usage: mkdir [/p] DIRECTORIES...")
		return 0, nil
	}
	errorcount := 0
	mkdir := os.Mkdir
	for _, arg1 := range cmd.Args()[1:] {
		if arg1 == "/p" {
			mkdir = os.MkdirAll
			continue
		}
		err := mkdir(arg1, 0777)
		if err != nil {
			fmt.Fprintf(cmd.Err(), "%s: %s\n", arg1, err)
			errorcount++
		}
	}
	return errorcount, nil
}

func cmdRmdir(ctx context.Context, cmd Param) (int, error) {
	if len(cmd.Args()) <= 1 {
		fmt.Fprintln(cmd.Err(), "Usage: rmdir [/s] [/q] DIRECTORIES...")
		return 0, nil
	}
	s_option := false
	quiet := false
	message := "%s: Rmdir Are you sure? [Yes/No/Quit] "
	errorcount := 0
	for _, arg1 := range cmd.Args()[1:] {
		switch arg1 {
		case "/s":
			s_option = true
			message = "%s : Delete Tree. Are you sure? [Yes/No/Quit] "
			continue
		case "/q":
			quiet = true
			continue
		}
		stat, err := os.Lstat(arg1)
		if err != nil {
			fmt.Fprintf(cmd.Err(), "%s: %s\n", arg1, err)
			errorcount++
			continue
		}
		if !stat.IsDir() {
			fmt.Fprintf(cmd.Err(), "%s: not directory\n", arg1)
			errorcount++
			continue
		}
		if !quiet {
			fmt.Fprintf(cmd.Err(), message, arg1)
			ch := getch.Rune()
			fmt.Fprintf(cmd.Err(), "%c ", ch)
			switch ch {
			case 'y', 'Y':

			case 'q', 'Q':
				fmt.Fprintln(cmd.Err(), "-> canceled all")
				return errorcount, nil
			default:
				fmt.Fprintln(cmd.Err(), "-> canceled")
				continue
			}
		}
		if s_option {
			if !quiet {
				fmt.Fprintln(cmd.Out())
			}
			err = dos.Truncate(arg1, func(path string, err error) bool {
				fmt.Fprintf(cmd.Err(), "%s -> %s\n", path, err)
				return true
			}, cmd.Out())
		} else {
			err = syscall.Rmdir(arg1)
		}
		if err != nil {
			fmt.Fprintf(cmd.Err(), "-> %s\n", err)
			errorcount++
		} else {
			fmt.Fprintln(cmd.Err(), "-> done.")
		}
	}
	return errorcount, nil
}
<commit_msg>Fix #314 rmdir could not remove junctions<commit_after>package commands

import (
	"context"
	"fmt"
	"os"
	"syscall"

	"github.com/zetamatta/go-getch"

	"github.com/zetamatta/nyagos/dos"
)

func cmdMkdir(ctx context.Context, cmd Param) (int, error) {
	if len(cmd.Args()) <= 1 {
		fmt.Fprintln(cmd.Err(), "Usage: mkdir [/p] DIRECTORIES...")
		return 0, nil
	}
	errorcount := 0
	mkdir := os.Mkdir
	for _, arg1 := range cmd.Args()[1:] {
		if arg1 == "/p" {
			mkdir = os.MkdirAll
			continue
		}
		err := mkdir(arg1, 0777)
		if err != nil {
			fmt.Fprintf(cmd.Err(), "%s: %s\n", arg1, err)
			errorcount++
		}
	}
	return errorcount, nil
}

func cmdRmdir(ctx context.Context, cmd Param) (int, error) {
	if len(cmd.Args()) <= 1 {
		fmt.Fprintln(cmd.Err(), "Usage: rmdir [/s] [/q] DIRECTORIES...")
		return 0, nil
	}
	s_option := false
	quiet := false
	message := "%s: Rmdir Are you sure? [Yes/No/Quit] "
	errorcount := 0
	for _, arg1 := range cmd.Args()[1:] {
		switch arg1 {
		case "/s":
			s_option = true
			message = "%s : Delete Tree. Are you sure? [Yes/No/Quit] "
			continue
		case "/q":
			quiet = true
			continue
		}
		stat, err := os.Stat(arg1)
		if err != nil {
			fmt.Fprintf(cmd.Err(), "%s: %s\n", arg1, err)
			errorcount++
			continue
		}
		if !stat.IsDir() {
			fmt.Fprintf(cmd.Err(), "%s: not directory\n", arg1)
			errorcount++
			continue
		}
		if !quiet {
			fmt.Fprintf(cmd.Err(), message, arg1)
			ch := getch.Rune()
			fmt.Fprintf(cmd.Err(), "%c ", ch)
			switch ch {
			case 'y', 'Y':

			case 'q', 'Q':
				fmt.Fprintln(cmd.Err(), "-> canceled all")
				return errorcount, nil
			default:
				fmt.Fprintln(cmd.Err(), "-> canceled")
				continue
			}
		}
		if s_option {
			if !quiet {
				fmt.Fprintln(cmd.Out())
			}
			err = dos.Truncate(arg1, func(path string, err error) bool {
				fmt.Fprintf(cmd.Err(), "%s -> %s\n", path, err)
				return true
			}, cmd.Out())
		} else {
			err = syscall.Rmdir(arg1)
		}
		if err != nil {
			fmt.Fprintf(cmd.Err(), "-> %s\n", err)
			errorcount++
		} else {
			fmt.Fprintln(cmd.Err(), "-> done.")
		}
	}
	return errorcount, nil
}
<|endoftext|>


<commit_before>package commands

import (
	"github.com/mmcquillan/jane/configs"
	"github.com/mmcquillan/jane/outputs"
	"strings"
)

func Parse(config *configs.Config, channel string, msg string) {

	// make sure they are talking to and not about us
	tokmsg := strings.Split(strings.TrimSpace(msg), " ")
	if strings.ToLower(tokmsg[0]) != strings.ToLower(config.Name) {
		return
	}

	// remove me from the request and clean
	msg = strings.Replace(msg, tokmsg[0], "", 1)
	msg = strings.TrimSpace(msg)

	// pull off the first word as a command token
	cmd := strings.ToLower(tokmsg[1])
	msg = strings.Replace(msg, cmd, "", 1)
	msg = strings.TrimSpace(msg)

	// the big switch statement in the sky
	var r string
	switch cmd {
	case "help":
		r = Help(config)
	case "secrets":
		r = Secrets(config)
	case "build":
		r = Build(config.BambooUrl, config.BambooUser, config.BambooPass, msg)
	case "deploy":
		r = Deploy(config.BambooUrl)
	case "rename":
		r = Rename(config, msg)
	case "big":
		r = Big(msg)
	case "sensu":
		r = Sensu()
	case "env":
		r = Env(config.BambooUrl, config.BambooUser, config.BambooPass)
	case "environment":
		r = Env(config.BambooUrl, config.BambooUser, config.BambooPass)
	default:
		r = Response(config, cmd, msg)
	}

	// feedback
	message := outputs.Message{channel, r, "", "", ""}
	outputs.Output(config, message)

}

func Help(config *configs.Config) (r string) {
	helps := []string{
		"help",
		"build [ client | cloud | admin | html ]",
		"deploy",
		"env[ironment]",
		"rules",
		"motivate <name>",
		"catchphrase",
		"big <something>",
	}
	r = "*Say things like:*\n"
	for _, help := range helps {
		r += "\t" + config.Name + " " + help + "\n"
	}
	return r
}

func Secrets(config *configs.Config) (r string) {
	secrets := []string{
		"secrets",
		"jane",
		"hal",
		"rename <name>",
		"drop <something>",
		"feelings",
	}
	r = "*Quietly say things like:*\n"
	for _, secret := range secrets {
		r += "\t" + config.Name + " " + secret + "\n"
	}
	return r
}
<commit_msg>won't crash on just bot name now fixes #23<commit_after>package commands

import (
	"github.com/mmcquillan/jane/configs"
	"github.com/mmcquillan/jane/outputs"
	"strings"
)

func Parse(config *configs.Config, channel string, msg string) {

	// make sure they are talking to and not about us
	tokmsg := strings.Split(strings.TrimSpace(msg), " ")
	if strings.ToLower(tokmsg[0]) != strings.ToLower(config.Name) {
		return
	}

	// remove me from the request and clean
	msg = strings.Replace(msg, tokmsg[0], "", 1)
	msg = strings.TrimSpace(msg)

	// see if nothing is said
	if msg == "" {
		return
	}

	// pull off the first word as a command token
	cmd := strings.ToLower(tokmsg[1])
	msg = strings.Replace(msg, cmd, "", 1)
	msg = strings.TrimSpace(msg)

	// the big switch statement in the sky
	var r string
	switch cmd {
	case "help":
		r = Help(config)
	case "secrets":
		r = Secrets(config)
	case "build":
		r = Build(config.BambooUrl, config.BambooUser, config.BambooPass, msg)
	case "deploy":
		r = Deploy(config.BambooUrl)
	case "rename":
		r = Rename(config, msg)
	case "big":
		r = Big(msg)
	case "sensu":
		r = Sensu()
	case "env":
		r = Env(config.BambooUrl, config.BambooUser, config.BambooPass)
	case "environment":
		r = Env(config.BambooUrl, config.BambooUser, config.BambooPass)
	default:
		r = Response(config, cmd, msg)
	}

	// feedback
	message := outputs.Message{channel, r, "", "", ""}
	outputs.Output(config, message)

}

func Help(config *configs.Config) (r string) {
	helps := []string{
		"help",
		"build [ client | cloud | admin | html ]",
		"deploy",
		"env[ironment]",
		"rules",
		"motivate <name>",
		"catchphrase",
		"big <something>",
	}
	r = "*Say things like:*\n"
	for _, help := range helps {
		r += "\t" + config.Name + " " + help + "\n"
	}
	return r
}

func Secrets(config *configs.Config) (r string) {
	secrets := []string{
		"secrets",
		"jane",
		"hal",
		"rename <name>",
		"drop <something>",
		"feelings",
	}
	r = "*Quietly say things like:*\n"
	for _, secret := range secrets {
		r += "\t" + config.Name + " " + secret + "\n"
	}
	return r
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os/exec"
	"sync"
	"time"

	"gopkg.in/yaml.v2"
)

const (
	pluginFlashWaitDu time.Duration = 50 * time.Millisecond

	pluginMethodTCP           = "tcp"
	pluginMethodStd           = "std"
	pluginEachMessageChanSize = 3
)

type pluginState int

const (
	pluginStateClose pluginState = iota
	pluginStateEnable
	pluginStateDisable
)

type plugin struct {
	Name        string      `yaml:"name"        json:"name"`
	Description string      `yaml:"description" json:"description"`
	Version     string      `yaml:"version"     json:"version"`
	Author      string      `yaml:"author"      json:"author"`
	Method      string      `yaml:"method"      json:"method"`
	Exec        []string    `yaml:"exec"        json:"-"`
	Nagomever   string      `yaml:"nagomever"   json:"-"`
	Subscribe   []string    `yaml:"subscribe"   json:"subscribe"`
	No          int         `yaml:"-"           json:"no"`
	GetState    pluginState `yaml:"-"           json:"state"` // Don't change directly
	setStateCh  chan (pluginState)
	stateMu     sync.Mutex
	rwc         io.ReadWriteCloser
	flushTm     *time.Timer
	wg          sync.WaitGroup
	cv          *CommentViewer
	quit        chan (struct{})
	writec      chan ([]byte)
}

// NewPlugin makes new Plugin.
func newPlugin(cv *CommentViewer) *plugin {
	return &plugin{
		No:         -1,
		quit:       make(chan struct{}),
		setStateCh: make(chan pluginState),
		writec:     make(chan []byte, pluginEachMessageChanSize),
		cv:         cv,
	}
}

func (pl *plugin) Open(rwc io.ReadWriteCloser) error {
	pl.stateMu.Lock()
	defer pl.stateMu.Unlock()

	if pl.No == -1 {
		return fmt.Errorf("plugin \"%s\" is not initialized (add to CommentViewer)\n", pl.Name)
	}
	if pl.Name == "" {
		return fmt.Errorf("plugin \"%s\" no name is set\n", pl.Name)
	}
	if rwc == nil {
		return fmt.Errorf("given rw is nil\n")
	}
	if pl.GetState != pluginStateClose {
		return fmt.Errorf("already opened\n")
	}

	pl.rwc = rwc
	pl.flushTm = time.NewTimer(time.Minute)

	pl.wg.Add(1)
	go pl.evRoutine()

	pl.stateMu.Unlock()
	pl.setStateCh <- pluginStateEnable
	pl.setStateCh <- pluginStateEnable // wait for completing previous task
	pl.stateMu.Lock()

	return nil
}

func (pl *plugin) SetState(enable bool) {
	if pl.GetState == pluginStateClose {
		return
	}

	var st pluginState
	if enable {
		st = pluginStateEnable
	} else {
		st = pluginStateDisable
	}
	select {
	case pl.setStateCh <- st:
	case <-pl.quit:
		return
	}
}

func (pl *plugin) WriteMess(m *Message) (fail bool) {
	jm, err := json.Marshal(m)
	if err != nil {
		log.Println(err)
		log.Println(m)
		return
	}
	return pl.Write(jm)
}

func (pl *plugin) Write(p []byte) (fail bool) {
	if pl.GetState != pluginStateEnable {
		return true
	}
	select {
	case pl.writec <- p:
		return false
	case <-pl.quit:
	}
	return true
}

func (pl *plugin) IsSubscribe(pln string) bool {
	f := false
	for _, d := range pl.Subscribe {
		if d == pln {
			f = true
			break
		}
	}
	return f
}

func (pl *plugin) Load(filePath string) error {
	d, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}

	err = yaml.Unmarshal(d, pl)
	if err != nil {
		return err
	}

	return nil
}

func (pl *plugin) Save(filePath string) error {
	d, err := yaml.Marshal(pl)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(filePath, d, 0600)
	if err != nil {
		return err
	}

	return nil
}

func (pl *plugin) IsMain() bool {
	return pl.No == 0
}

func (pl *plugin) evRoutine() {
	defer pl.wg.Done()
	defer func() {
		err := pl.rwc.Close()
		if err != nil {
			log.Println(err)
		}
		pl.GetState = pluginStateClose
	}()
	defer log.Printf("plugin [%s] is closing", pl.Name)

	// Run decoder.  It puts a message into "mes".
	dec := json.NewDecoder(pl.rwc)
	mes := make(chan (*Message))
	pl.wg.Add(1)
	go func() {
		defer pl.wg.Done()
		for {
			m := new(Message)
			err := dec.Decode(m)
			if err != nil {
				select {
				// ignore if quitting
				case <-pl.quit:
				default:
					if err != io.EOF {
						pl.cv.CreateEvNewDialog(CtUIDialogTypeInfo, "plugin disconnected",
							fmt.Sprintf("plugin [%s] : connection disconnected", pl.Name))
						log.Println(err)
					}
				}
				m = nil
			}

			select {
			case mes <- m:
				if m == nil {
					return
				}
			case <-pl.quit:
				return
			}
		}
	}()

	bufw := bufio.NewWriter(pl.rwc)
	writeMess := func(p []byte) {
		pl.flushTm.Reset(pluginFlashWaitDu)
		_, err := fmt.Fprintf(bufw, "%s\n", p)
		if err != nil {
			log.Println(err)
			pl.cv.CreateEvNewDialog(CtUIDialogTypeInfo, "plugin", "failed to write a message : "+pl.Name)
			// quit if UI plugin disconnect
			if pl.IsMain() {
				pl.cv.Quit()
			} else {
				pl.close()
			}
		}
		return
	}
	for {
		select {
		// Process a received message
		case m := <-mes:
			if m == nil {
				// quit if main plugin is disconnected
				if pl.IsMain() {
					pl.cv.Quit()
				} else {
					pl.close()
				}
				continue
			}
			if pl.GetState != pluginStateEnable {
				continue
			}
			m.prgno = pl.No
			log.Printf("plugin message [%s] : %v", pl.Name, m)
			pl.cv.Evch <- m

		// Send a message
		case m := <-pl.writec:
			if pl.GetState != pluginStateEnable {
				continue
			}
			writeMess(m)

		// Flush plugin IO
		case <-pl.flushTm.C:
			err := bufw.Flush()
			if err != nil {
				log.Println(err)
				continue
			}

		case e := <-pl.setStateCh:
			func() {
				pl.stateMu.Lock()
				defer pl.stateMu.Unlock()

				if e == pluginStateClose {
					return
				}
				if pl.GetState == e {
					return
				}
				pl.GetState = e

				// send message
				m := &Message{
					Domain: DomainDirectngm,
				}
				if e == pluginStateEnable {
					m.Command = CommDirectngmPlugEnabled
				} else if e == pluginStateDisable {
					m.Command = CommDirectngmPlugDisabled
				}
				jm, err := json.Marshal(m)
				if err != nil {
					log.Println(err)
					log.Println(m)
					return
				}
				writeMess(jm)
			}()

		case <-pl.quit:
			return
		}
	}
}

func handleTCPPlugin(c io.ReadWriteCloser, cv *CommentViewer) {
	defer cv.wg.Done()

	endc := make(chan bool, 1)

	cv.wg.Add(1)
	go func() {
		defer cv.wg.Done()
		select {
		// For quitting while receiving first init message.
		case <-cv.quit:
			err := c.Close()
			if err != nil {
				log.Println(err)
			}
		case iserr := <-endc:
			if iserr {
				err := c.Close()
				if err != nil {
					log.Println(err)
				}
			}
		}
	}()

	dec := json.NewDecoder(c)

	m := new(Message)
	// It may stop here long time
	err := dec.Decode(m)
	if err != nil {
		log.Println(err)
		endc <- true
		return
	}
	if m.Domain != DomainDirect || m.Command != CommDirectNo {
		log.Println("send Direct.No message at first")
		endc <- true
		return
	}

	var ct CtDirectNo
	if err := json.Unmarshal(m.Content, &ct); err != nil {
		log.Println(err)
		endc <- true
		return
	}

	n := ct.No
	pl, err := cv.Plugin(n)
	if err != nil {
		log.Panicln(err)
		endc <- true
		return
	}
	err = pl.Open(c)
	if err != nil {
		log.Println(err)
		endc <- true
		return
	}
	log.Printf("loaded plugin : %s\n", pl.Name)
	endc <- false
}

func handleSTDPlugin(p *plugin, cv *CommentViewer) {
	defer cv.wg.Done()

	if len(p.Exec) < 1 {
		log.Printf("exec is not specified in plugin [%s]\n", p.Name)
		return
	}

	cmd := exec.Command(p.Exec[0], p.Exec[1:]...)
	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Println(err)
		return
	}
	needClose := true
	defer func() {
		if needClose {
			err = stdin.Close()
			if err != nil {
				log.Println(err)
			}
		}
	}()
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Println(err)
		return
	}
	defer func() {
		if needClose {
			err = stdout.Close()
			if err != nil {
				log.Println(err)
			}
		}
	}()
	err = cmd.Start()
	if err != nil {
		log.Println(err)
		return
	}

	c := &stdReadWriteCloser{stdout, stdin}
	err = p.Open(c)
	if err != nil {
		log.Println(err)
		return
	}
	needClose = false
	log.Println("loaded plugin : ", p.Name)
}

// Close closes opened plugin.
func (pl *plugin) Close() {
	pl.close()
	pl.wg.Wait()
}

func (pl *plugin) close() {
	select {
	case <-pl.quit:
	default:
		close(pl.quit)
	}
}

type stdReadWriteCloser struct {
	io.ReadCloser
	io.WriteCloser
}

func (rwc *stdReadWriteCloser) Close() error {
	errr := rwc.ReadCloser.Close()
	errw := rwc.WriteCloser.Close()
	if errr != nil {
		return errr
	}
	if errw != nil {
		return errw
	}
	return nil
}
<commit_msg>fix race at plugin status<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os/exec"
	"sync"
	"time"

	"gopkg.in/yaml.v2"
)

const (
	pluginFlashWaitDu time.Duration = 50 * time.Millisecond

	pluginMethodTCP           = "tcp"
	pluginMethodStd           = "std"
	pluginEachMessageChanSize = 3
)

type pluginState int

const (
	pluginStateClose pluginState = iota
	pluginStateEnable
	pluginStateDisable
)

type plugin struct {
	Name        string      `yaml:"name"        json:"name"`
	Description string      `yaml:"description" json:"description"`
	Version     string      `yaml:"version"     json:"version"`
	Author      string      `yaml:"author"      json:"author"`
	Method      string      `yaml:"method"      json:"method"`
	Exec        []string    `yaml:"exec"        json:"-"`
	Nagomever   string      `yaml:"nagomever"   json:"-"`
	Subscribe   []string    `yaml:"subscribe"   json:"subscribe"`
	No          int         `yaml:"-"           json:"no"`
	GetState    pluginState `yaml:"-"           json:"state"` // Don't change directly
	setStateCh  chan (pluginState)
	stateMu     sync.Mutex
	rwc         io.ReadWriteCloser
	flushTm     *time.Timer
	wg          sync.WaitGroup
	cv          *CommentViewer
	quit        chan (struct{})
	writec      chan ([]byte)
}

// NewPlugin makes new Plugin.
func newPlugin(cv *CommentViewer) *plugin {
	return &plugin{
		No:         -1,
		quit:       make(chan struct{}),
		setStateCh: make(chan pluginState),
		writec:     make(chan []byte, pluginEachMessageChanSize),
		cv:         cv,
	}
}

func (pl *plugin) Open(rwc io.ReadWriteCloser) error {
	pl.stateMu.Lock()
	defer pl.stateMu.Unlock()

	if pl.No == -1 {
		return fmt.Errorf("plugin \"%s\" is not initialized (add to CommentViewer)\n", pl.Name)
	}
	if pl.Name == "" {
		return fmt.Errorf("plugin \"%s\" no name is set\n", pl.Name)
	}
	if rwc == nil {
		return fmt.Errorf("given rw is nil\n")
	}
	if pl.GetState != pluginStateClose {
		return fmt.Errorf("already opened\n")
	}

	pl.rwc = rwc
	pl.flushTm = time.NewTimer(time.Minute)

	pl.wg.Add(1)
	go pl.evRoutine()

	pl.stateMu.Unlock()
	pl.setStateCh <- pluginStateEnable
	pl.setStateCh <- pluginStateEnable // wait for completing previous task
	pl.stateMu.Lock()

	return nil
}

func (pl *plugin) SetState(enable bool) {
	if pl.GetState == pluginStateClose {
		return
	}

	var st pluginState
	if enable {
		st = pluginStateEnable
	} else {
		st = pluginStateDisable
	}
	select {
	case pl.setStateCh <- st:
	case <-pl.quit:
		return
	}
}

func (pl *plugin) WriteMess(m *Message) (fail bool) {
	jm, err := json.Marshal(m)
	if err != nil {
		log.Println(err)
		log.Println(m)
		return
	}
	return pl.Write(jm)
}

func (pl *plugin) Write(p []byte) (fail bool) {
	pl.stateMu.Lock()
	defer pl.stateMu.Unlock()
	if pl.GetState != pluginStateEnable {
		return true
	}
	select {
	case pl.writec <- p:
		return false
	case <-pl.quit:
	}
	return true
}

func (pl *plugin) IsSubscribe(pln string) bool {
	f := false
	for _, d := range pl.Subscribe {
		if d == pln {
			f = true
			break
		}
	}
	return f
}

func (pl *plugin) Load(filePath string) error {
	d, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}

	err = yaml.Unmarshal(d, pl)
	if err != nil {
		return err
	}

	return nil
}

func (pl *plugin) Save(filePath string) error {
	d, err := yaml.Marshal(pl)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(filePath, d, 0600)
	if err != nil {
		return err
	}

	return nil
}

func (pl *plugin) IsMain() bool {
	return pl.No == 0
}

func (pl *plugin) evRoutine() {
	defer pl.wg.Done()
	defer func() {
		err := pl.rwc.Close()
		if err != nil {
			log.Println(err)
		}
		pl.stateMu.Lock()
		pl.GetState = pluginStateClose
		pl.stateMu.Unlock()
	}()
	defer log.Printf("plugin [%s] is closing", pl.Name)

	// Run decoder.  It puts a message into "mes".
	dec := json.NewDecoder(pl.rwc)
	mes := make(chan (*Message))
	pl.wg.Add(1)
	go func() {
		defer pl.wg.Done()
		for {
			m := new(Message)
			err := dec.Decode(m)
			if err != nil {
				select {
				// ignore if quitting
				case <-pl.quit:
				default:
					if err != io.EOF {
						pl.cv.CreateEvNewDialog(CtUIDialogTypeInfo, "plugin disconnected",
							fmt.Sprintf("plugin [%s] : connection disconnected", pl.Name))
						log.Println(err)
					}
				}
				m = nil
			}

			select {
			case mes <- m:
				if m == nil {
					return
				}
			case <-pl.quit:
				return
			}
		}
	}()

	bufw := bufio.NewWriter(pl.rwc)
	writeMess := func(p []byte) {
		pl.flushTm.Reset(pluginFlashWaitDu)
		_, err := fmt.Fprintf(bufw, "%s\n", p)
		if err != nil {
			log.Println(err)
			pl.cv.CreateEvNewDialog(CtUIDialogTypeInfo, "plugin", "failed to write a message : "+pl.Name)
			// quit if UI plugin disconnect
			if pl.IsMain() {
				pl.cv.Quit()
			} else {
				pl.close()
			}
		}
		return
	}
	for {
		select {
		// Process a received message
		case m := <-mes:
			if m == nil {
				// quit if main plugin is disconnected
				if pl.IsMain() {
					pl.cv.Quit()
				} else {
					pl.close()
				}
				continue
			}
			if pl.GetState != pluginStateEnable {
				continue
			}
			m.prgno = pl.No
			log.Printf("plugin message [%s] : %v", pl.Name, m)
			pl.cv.Evch <- m

		// Send a message
		case m := <-pl.writec:
			if pl.GetState != pluginStateEnable {
				continue
			}
			writeMess(m)

		// Flush plugin IO
		case <-pl.flushTm.C:
			err := bufw.Flush()
			if err != nil {
				log.Println(err)
				continue
			}

		case e := <-pl.setStateCh:
			func() {
				pl.stateMu.Lock()
				defer pl.stateMu.Unlock()

				if e == pluginStateClose {
					return
				}
				if pl.GetState == e {
					return
				}
				pl.GetState = e

				// send message
				m := &Message{
					Domain: DomainDirectngm,
				}
				if e == pluginStateEnable {
					m.Command = CommDirectngmPlugEnabled
				} else if e == pluginStateDisable {
					m.Command = CommDirectngmPlugDisabled
				}
				jm, err := json.Marshal(m)
				if err != nil {
					log.Println(err)
					log.Println(m)
					return
				}
				writeMess(jm)
			}()

		case <-pl.quit:
			return
		}
	}
}

func handleTCPPlugin(c io.ReadWriteCloser, cv *CommentViewer) {
	defer cv.wg.Done()

	endc := make(chan bool, 1)

	cv.wg.Add(1)
	go func() {
		defer cv.wg.Done()
		select {
		// For quitting while receiving first init message.
		case <-cv.quit:
			err := c.Close()
			if err != nil {
				log.Println(err)
			}
		case iserr := <-endc:
			if iserr {
				err := c.Close()
				if err != nil {
					log.Println(err)
				}
			}
		}
	}()

	dec := json.NewDecoder(c)

	m := new(Message)
	// It may stop here long time
	err := dec.Decode(m)
	if err != nil {
		log.Println(err)
		endc <- true
		return
	}
	if m.Domain != DomainDirect || m.Command != CommDirectNo {
		log.Println("send Direct.No message at first")
		endc <- true
		return
	}

	var ct CtDirectNo
	if err := json.Unmarshal(m.Content, &ct); err != nil {
		log.Println(err)
		endc <- true
		return
	}

	n := ct.No
	pl, err := cv.Plugin(n)
	if err != nil {
		log.Panicln(err)
		endc <- true
		return
	}
	err = pl.Open(c)
	if err != nil {
		log.Println(err)
		endc <- true
		return
	}
	log.Printf("loaded plugin : %s\n", pl.Name)
	endc <- false
}

func handleSTDPlugin(p *plugin, cv *CommentViewer) {
	defer cv.wg.Done()

	if len(p.Exec) < 1 {
		log.Printf("exec is not specified in plugin [%s]\n", p.Name)
		return
	}

	cmd := exec.Command(p.Exec[0], p.Exec[1:]...)
	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Println(err)
		return
	}
	needClose := true
	defer func() {
		if needClose {
			err = stdin.Close()
			if err != nil {
				log.Println(err)
			}
		}
	}()
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Println(err)
		return
	}
	defer func() {
		if needClose {
			err = stdout.Close()
			if err != nil {
				log.Println(err)
			}
		}
	}()
	err = cmd.Start()
	if err != nil {
		log.Println(err)
		return
	}

	c := &stdReadWriteCloser{stdout, stdin}
	err = p.Open(c)
	if err != nil {
		log.Println(err)
		return
	}
	needClose = false
	log.Println("loaded plugin : ", p.Name)
}

// Close closes opened plugin.
func (pl *plugin) Close() {
	pl.close()
	pl.wg.Wait()
}

func (pl *plugin) close() {
	select {
	case <-pl.quit:
	default:
		close(pl.quit)
	}
}

type stdReadWriteCloser struct {
	io.ReadCloser
	io.WriteCloser
}

func (rwc *stdReadWriteCloser) Close() error {
	errr := rwc.ReadCloser.Close()
	errw := rwc.WriteCloser.Close()
	if errr != nil {
		return errr
	}
	if errw != nil {
		return errw
	}
	return nil
}
<|endoftext|>