<commit_before>package main

import (
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
)

type tcpOpts struct {
	Service  string `long:"service" description:"Service name. e.g. ftp, smtp, pop, imap and so on"`
	Hostname string `short:"H" long:"hostname" description:"Host name or IP Address"`
	exchange
	Timeout  float64 `short:"t" long:"timeout" default:"10" description:"Seconds before connection times out"`
	MaxBytes int     `short:"m" long:"maxbytes" description:"Close connection once more than this number of bytes are received"`
	Delay    float64 `short:"d" long:"delay" description:"Seconds to wait between sending string and polling for response"`
	Warning  float64 `short:"w" long:"warning" description:"Response time to result in warning status (seconds)"`
	Critical float64 `short:"c" long:"critical" description:"Response time to result in critical status (seconds)"`
	Escape   bool    `short:"E" long:"escape" description:"Can use \\n, \\r, \\t or \\ in send or quit string. Must come before send or quit option. By default, nothing added to send, \\r\\n added to end of quit"`
}

type exchange struct {
	Port               int    `short:"p" long:"port" description:"Port number"`
	Send               string `short:"s" long:"send" description:"String to send to the server"`
	ExpectPattern      string `short:"e" long:"expect-pattern" description:"Regexp pattern to expect in server response"`
	Quit               string `short:"q" long:"quit" description:"String to send server to initiate a clean close of the connection"`
	SSL                bool   `short:"S" long:"ssl" description:"Use SSL for the connection."`
	UnixSock           string `short:"U" long:"unix-sock" description:"Unix Domain Socket"`
	NoCheckCertificate bool   `long:"no-check-certificate" description:"Do not check certificate"`
	expectReg          *regexp.Regexp
}

func main() {
	opts, err := parseArgs(os.Args[1:])
	if err != nil {
		os.Exit(1)
	}
	ckr := opts.run()
	ckr.Name = "TCP"
	if opts.Service != "" {
		ckr.Name = opts.Service
	}
	ckr.Exit()
}

func parseArgs(args []string) (*tcpOpts, error) {
	opts := &tcpOpts{}
	_, err := flags.ParseArgs(opts, args)
	return opts, err
}

var defaultExchangeMap = map[string]exchange{
	"FTP": {
		Port:          21,
		ExpectPattern: `^220`,
		Quit:          "QUIT",
	},
	"POP": {
		Port:          110,
		ExpectPattern: `^\+OK`,
		Quit:          "QUIT",
	},
	"SPOP": {
		Port:          995,
		ExpectPattern: `^\+OK`,
		Quit:          "QUIT",
		SSL:           true,
	},
	"IMAP": {
		Port:          143,
		ExpectPattern: `^\* OK`,
		Quit:          "a1 LOGOUT",
	},
	"SIMAP": {
		Port:          993,
		ExpectPattern: `^\* OK`,
		Quit:          "a1 LOGOUT",
		SSL:           true,
	},
	"SMTP": {
		Port:          25,
		ExpectPattern: `^220`,
		Quit:          "QUIT",
	},
	"SSMTP": {
		Port:          465,
		ExpectPattern: `^220`,
		Quit:          "QUIT",
		SSL:           true,
	},
	"GEARMAN": {
		Port:          7003,
		Send:          "version\n",
		ExpectPattern: `\A[0-9]+\.[0-9]+\n\z`,
	},
}

func (opts *tcpOpts) prepare() error {
	opts.Service = strings.ToUpper(opts.Service)

	if opts.Service != "" {
		defaultEx, ok := defaultExchangeMap[opts.Service]
		if !ok {
			return fmt.Errorf("check-tcp called with unknown service: %s", opts.Service)
		}
		opts.merge(defaultEx)
	}

	if opts.Escape {
		opts.Quit = escapedString(opts.Quit)
		opts.Send = escapedString(opts.Send)
	} else if opts.Quit != "" {
		opts.Quit += "\r\n"
	}
	var err error
	if opts.ExpectPattern != "" {
		opts.expectReg, err = regexp.Compile(opts.ExpectPattern)
	}
	return err
}

func (opts *tcpOpts) merge(ex exchange) {
	if opts.Port == 0 {
		opts.Port = ex.Port
	}
	if opts.Send == "" {
		opts.Send = ex.Send
	}
	if opts.ExpectPattern == "" {
		opts.ExpectPattern = ex.ExpectPattern
	}
	if opts.Quit == "" {
		opts.Quit = ex.Quit
	}
	if !opts.SSL {
		opts.SSL = ex.SSL
	}
}

func dial(network, address string, ssl bool, noCheckCertificate bool, timeoutDur time.Duration) (net.Conn, error) {
	if ssl {
		return tls.Dial(network, address, &tls.Config{
			InsecureSkipVerify: noCheckCertificate,
		})
	}
	return net.Dial(network, address)
}

func (opts *tcpOpts) run() *checkers.Checker {
	err := opts.prepare()
	if err != nil {
		return checkers.Unknown(err.Error())
	}
	// prevent changing output of some commands
	os.Setenv("LANG", "C")
	os.Setenv("LC_ALL", "C")

	proto := "tcp"
	addr := fmt.Sprintf("%s:%d", opts.Hostname, opts.Port)
	if opts.UnixSock != "" {
		proto = "unix"
		addr = opts.UnixSock
	}
	timeoutDur := time.Duration(opts.Timeout * float64(time.Second))
	start := time.Now()
	if opts.Delay > 0 {
		time.Sleep(time.Duration(opts.Delay) * time.Second)
	}

	conn, err := dial(proto, addr, opts.SSL, opts.NoCheckCertificate, timeoutDur)
	if err != nil {
		return checkers.Critical(err.Error())
	}
	defer conn.Close()

	if opts.Send != "" {
		err := write(conn, []byte(opts.Send), timeoutDur)
		if err != nil {
			return checkers.Critical(err.Error())
		}
	}

	res := ""
	if opts.expectReg != nil {
		buf, err := slurp(conn, opts.MaxBytes, timeoutDur)
		if err != nil {
			return checkers.Critical(err.Error())
		}
		res = string(buf)
		if !opts.expectReg.MatchString(res) {
			return checkers.Critical("Unexpected response from host/socket: " + res)
		}
	}

	if opts.Quit != "" {
		err := write(conn, []byte(opts.Quit), timeoutDur)
		if err != nil {
			return checkers.Critical(err.Error())
		}
	}
	elapsed := time.Now().Sub(start)

	chkSt := checkers.OK
	if opts.Warning > 0 && elapsed > time.Duration(opts.Warning)*time.Second {
		chkSt = checkers.WARNING
	}
	if opts.Critical > 0 && elapsed > time.Duration(opts.Critical)*time.Second {
		chkSt = checkers.CRITICAL
	}
	msg := fmt.Sprintf("%.3f seconds response time on", float64(elapsed)/float64(time.Second))
	if opts.Hostname != "" {
		msg += " " + opts.Hostname
	}
	if opts.Port > 0 {
		msg += fmt.Sprintf(" port %d", opts.Port)
	}
	if res != "" {
		msg += fmt.Sprintf(" [%s]", strings.Trim(res, "\r\n"))
	}
	return checkers.NewChecker(chkSt, msg)
}

func write(conn net.Conn, content []byte, timeoutDur time.Duration) error {
	if timeoutDur > 0 {
		conn.SetWriteDeadline(time.Now().Add(timeoutDur))
	}
	_, err := conn.Write(content)
	return err
}

func slurp(conn net.Conn, maxbytes int, timeoutDur time.Duration) ([]byte, error) {
	buf := []byte{}
	readLimit := 32 * 1024
	if maxbytes > 0 {
		readLimit = maxbytes
	}
	readBytes := 0
	if timeoutDur > 0 {
		conn.SetReadDeadline(time.Now().Add(timeoutDur))
	}
	for {
		tmpBuf := make([]byte, readLimit)
		i, err := conn.Read(tmpBuf)
		if i > 0 {
			buf = append(buf, tmpBuf[:i]...)
			readBytes += i
			if i < readLimit || (maxbytes > 0 && maxbytes <= readBytes) {
				break
			}
		}
		if err == io.EOF {
			return buf, nil
		}
		if err != nil {
			return buf, err
		}
	}
	return buf, nil
}

func escapedString(str string) (escaped string) {
	l := len(str)
	for i := 0; i < l; i++ {
		c := str[i]
		if c == '\\' && i+1 < l {
			i++
			c := str[i]
			switch c {
			case 'n':
				escaped += "\n"
			case 'r':
				escaped += "\r"
			case 't':
				escaped += "\t"
			case '\\':
				escaped += `\`
			default:
				escaped += `\` + string(c)
			}
		} else {
			escaped += string(c)
		}
	}
	return escaped
}
<commit_msg>set timeout to dialer<commit_after>package main

import (
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
)

type tcpOpts struct {
	Service  string `long:"service" description:"Service name. e.g. ftp, smtp, pop, imap and so on"`
	Hostname string `short:"H" long:"hostname" description:"Host name or IP Address"`
	exchange
	Timeout  float64 `short:"t" long:"timeout" default:"10" description:"Seconds before connection times out"`
	MaxBytes int     `short:"m" long:"maxbytes" description:"Close connection once more than this number of bytes are received"`
	Delay    float64 `short:"d" long:"delay" description:"Seconds to wait between sending string and polling for response"`
	Warning  float64 `short:"w" long:"warning" description:"Response time to result in warning status (seconds)"`
	Critical float64 `short:"c" long:"critical" description:"Response time to result in critical status (seconds)"`
	Escape   bool    `short:"E" long:"escape" description:"Can use \\n, \\r, \\t or \\ in send or quit string. Must come before send or quit option. By default, nothing added to send, \\r\\n added to end of quit"`
}

type exchange struct {
	Port               int    `short:"p" long:"port" description:"Port number"`
	Send               string `short:"s" long:"send" description:"String to send to the server"`
	ExpectPattern      string `short:"e" long:"expect-pattern" description:"Regexp pattern to expect in server response"`
	Quit               string `short:"q" long:"quit" description:"String to send server to initiate a clean close of the connection"`
	SSL                bool   `short:"S" long:"ssl" description:"Use SSL for the connection."`
	UnixSock           string `short:"U" long:"unix-sock" description:"Unix Domain Socket"`
	NoCheckCertificate bool   `long:"no-check-certificate" description:"Do not check certificate"`
	expectReg          *regexp.Regexp
}

func main() {
	opts, err := parseArgs(os.Args[1:])
	if err != nil {
		os.Exit(1)
	}
	ckr := opts.run()
	ckr.Name = "TCP"
	if opts.Service != "" {
		ckr.Name = opts.Service
	}
	ckr.Exit()
}

func parseArgs(args []string) (*tcpOpts, error) {
	opts := &tcpOpts{}
	_, err := flags.ParseArgs(opts, args)
	return opts, err
}

var defaultExchangeMap = map[string]exchange{
	"FTP": {
		Port:          21,
		ExpectPattern: `^220`,
		Quit:          "QUIT",
	},
	"POP": {
		Port:          110,
		ExpectPattern: `^\+OK`,
		Quit:          "QUIT",
	},
	"SPOP": {
		Port:          995,
		ExpectPattern: `^\+OK`,
		Quit:          "QUIT",
		SSL:           true,
	},
	"IMAP": {
		Port:          143,
		ExpectPattern: `^\* OK`,
		Quit:          "a1 LOGOUT",
	},
	"SIMAP": {
		Port:          993,
		ExpectPattern: `^\* OK`,
		Quit:          "a1 LOGOUT",
		SSL:           true,
	},
	"SMTP": {
		Port:          25,
		ExpectPattern: `^220`,
		Quit:          "QUIT",
	},
	"SSMTP": {
		Port:          465,
		ExpectPattern: `^220`,
		Quit:          "QUIT",
		SSL:           true,
	},
	"GEARMAN": {
		Port:          7003,
		Send:          "version\n",
		ExpectPattern: `\A[0-9]+\.[0-9]+\n\z`,
	},
}

func (opts *tcpOpts) prepare() error {
	opts.Service = strings.ToUpper(opts.Service)

	if opts.Service != "" {
		defaultEx, ok := defaultExchangeMap[opts.Service]
		if !ok {
			return fmt.Errorf("check-tcp called with unknown service: %s", opts.Service)
		}
		opts.merge(defaultEx)
	}

	if opts.Escape {
		opts.Quit = escapedString(opts.Quit)
		opts.Send = escapedString(opts.Send)
	} else if opts.Quit != "" {
		opts.Quit += "\r\n"
	}
	var err error
	if opts.ExpectPattern != "" {
		opts.expectReg, err = regexp.Compile(opts.ExpectPattern)
	}
	return err
}

func (opts *tcpOpts) merge(ex exchange) {
	if opts.Port == 0 {
		opts.Port = ex.Port
	}
	if opts.Send == "" {
		opts.Send = ex.Send
	}
	if opts.ExpectPattern == "" {
		opts.ExpectPattern = ex.ExpectPattern
	}
	if opts.Quit == "" {
		opts.Quit = ex.Quit
	}
	if !opts.SSL {
		opts.SSL = ex.SSL
	}
}

func dial(network, address string, ssl bool, noCheckCertificate bool, timeoutDur time.Duration) (net.Conn, error) {
	d := &net.Dialer{Timeout: timeoutDur}
	if ssl {
		return tls.DialWithDialer(d, network, address, &tls.Config{
			InsecureSkipVerify: noCheckCertificate,
		})
	}
	return d.Dial(network, address)
}

func (opts *tcpOpts) run() *checkers.Checker {
	err := opts.prepare()
	if err != nil {
		return checkers.Unknown(err.Error())
	}
	// prevent changing output of some commands
	os.Setenv("LANG", "C")
	os.Setenv("LC_ALL", "C")

	proto := "tcp"
	addr := fmt.Sprintf("%s:%d", opts.Hostname, opts.Port)
	if opts.UnixSock != "" {
		proto = "unix"
		addr = opts.UnixSock
	}
	timeoutDur := time.Duration(opts.Timeout * float64(time.Second))
	start := time.Now()
	if opts.Delay > 0 {
		time.Sleep(time.Duration(opts.Delay) * time.Second)
	}

	conn, err := dial(proto, addr, opts.SSL, opts.NoCheckCertificate, timeoutDur)
	if err != nil {
		return checkers.Critical(err.Error())
	}
	defer conn.Close()

	if opts.Send != "" {
		err := write(conn, []byte(opts.Send), timeoutDur)
		if err != nil {
			return checkers.Critical(err.Error())
		}
	}

	res := ""
	if opts.expectReg != nil {
		buf, err := slurp(conn, opts.MaxBytes, timeoutDur)
		if err != nil {
			return checkers.Critical(err.Error())
		}
		res = string(buf)
		if !opts.expectReg.MatchString(res) {
			return checkers.Critical("Unexpected response from host/socket: " + res)
		}
	}

	if opts.Quit != "" {
		err := write(conn, []byte(opts.Quit), timeoutDur)
		if err != nil {
			return checkers.Critical(err.Error())
		}
	}
	elapsed := time.Now().Sub(start)

	chkSt := checkers.OK
	if opts.Warning > 0 && elapsed > time.Duration(opts.Warning)*time.Second {
		chkSt = checkers.WARNING
	}
	if opts.Critical > 0 && elapsed > time.Duration(opts.Critical)*time.Second {
		chkSt = checkers.CRITICAL
	}
	msg := fmt.Sprintf("%.3f seconds response time on", float64(elapsed)/float64(time.Second))
	if opts.Hostname != "" {
		msg += " " + opts.Hostname
	}
	if opts.Port > 0 {
		msg += fmt.Sprintf(" port %d", opts.Port)
	}
	if res != "" {
		msg += fmt.Sprintf(" [%s]", strings.Trim(res, "\r\n"))
	}
	return checkers.NewChecker(chkSt, msg)
}

func write(conn net.Conn, content []byte, timeoutDur time.Duration) error {
	if timeoutDur > 0 {
		conn.SetWriteDeadline(time.Now().Add(timeoutDur))
	}
	_, err := conn.Write(content)
	return err
}

func slurp(conn net.Conn, maxbytes int, timeoutDur time.Duration) ([]byte, error) {
	buf := []byte{}
	readLimit := 32 * 1024
	if maxbytes > 0 {
		readLimit = maxbytes
	}
	readBytes := 0
	if timeoutDur > 0 {
		conn.SetReadDeadline(time.Now().Add(timeoutDur))
	}
	for {
		tmpBuf := make([]byte, readLimit)
		i, err := conn.Read(tmpBuf)
		if i > 0 {
			buf = append(buf, tmpBuf[:i]...)
			readBytes += i
			if i < readLimit || (maxbytes > 0 && maxbytes <= readBytes) {
				break
			}
		}
		if err == io.EOF {
			return buf, nil
		}
		if err != nil {
			return buf, err
		}
	}
	return buf, nil
}

func escapedString(str string) (escaped string) {
	l := len(str)
	for i := 0; i < l; i++ {
		c := str[i]
		if c == '\\' && i+1 < l {
			i++
			c := str[i]
			switch c {
			case 'n':
				escaped += "\n"
			case 'r':
				escaped += "\r"
			case 't':
				escaped += "\t"
			case '\\':
				escaped += `\`
			default:
				escaped += `\` + string(c)
			}
		} else {
			escaped += string(c)
		}
	}
	return escaped
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/gcfg"
	"flag"
	zmq "github.com/pebbe/zmq4"
	"log"
)

func main() {
	var cfg Config
	configFile := flag.String("cfg", "/etc/chevalier.gcfg", "Path to configuration file. This file should be in gcfg[0] format. [0] https://code.google.com/p/gcfg/")
	flag.Parse()
	err := gcfg.ReadFileInto(&cfg, *configFile)
	if err != nil {
		log.Fatalf("Could not read config file at %v: %v", *configFile, err)
	}
	sock, err := zmq.NewSocket(zmq.REP)
	if err != nil {
		log.Fatalf("Could not initialize listen socket: %v", err)
	}
	err = sock.Bind(cfg.Chevalier.ListenAddress)
	if err != nil {
		log.Fatalf("Could not listen on %v: %v", cfg.Chevalier.ListenAddress, err)
	}
	for true {
		_, err = sock.RecvMessageBytes(0)
	}
}
<commit_msg>Add event loop for req/rep<commit_after>package main

import (
	"code.google.com/p/gcfg"
	"flag"
	zmq "github.com/pebbe/zmq4"
	"github.com/anchor/picolog"
	"os"
	"log/syslog"
)

var Logger *picolog.Logger

func handleRequest(sock *zmq.Socket) error {
	msg, err := sock.RecvMessageBytes(0)
	Logger.Debugf("Got a request! %v.", msg)
	return err
}

func main() {
	var cfg Config
	Logger = picolog.NewLogger(syslog.LOG_DEBUG, "chevalier", os.Stdout)
	configFile := flag.String("cfg", "/etc/chevalier.gcfg", "Path to configuration file. This file should be in gcfg[0] format. [0] https://code.google.com/p/gcfg/")
	flag.Parse()
	err := gcfg.ReadFileInto(&cfg, *configFile)
	if err != nil {
		Logger.Fatalf("Could not read config file at %v: %v", *configFile, err)
	}
	sock, err := zmq.NewSocket(zmq.REP)
	if err != nil {
		Logger.Fatalf("Could not initialize listen socket: %v", err)
	}
	err = sock.Bind(cfg.Chevalier.ListenAddress)
	if err != nil {
		Logger.Fatalf("Could not listen on %v: %v", cfg.Chevalier.ListenAddress, err)
	}
	reactor := zmq.NewReactor()
	reactor.AddSocket(sock, zmq.POLLIN, func(e zmq.State) error { return handleRequest(sock) })
	err = reactor.Run(-1)
	if err != nil {
		Logger.Errorf("%v", err)
	}
}
<|endoftext|>


<commit_before>package civicApi

import (
	"encoding/json"

	"net/url"
)

type Response struct {
	PollingLocations []struct {
		Address struct {
			LocationName string `json:"locationName"`
			Line1        string `json:"line1"`
			City         string `json:"city"`
			State        string `json:"state"`
			Zip          string `json:"zip"`
		} `json:"address"`
		Notes        string `json:"notes"`
		PollingHours string `json:"pollingHours"`
		Sources      []struct {
			Name     string `json:"name"`
			Official bool   `json:"official"`
		} `json:"sources"`
	} `json:"pollingLocations"`
	Error struct {
		Errors []struct {
			Domain  string `json:"domain"`
			Reason  string `json:"reason"`
			Message string `json:"message"`
		} `json:"errors"`
		Code    int    `json:"code"`
		Message string `json:"message"`
	} `json:"error"`
	State []struct {
		LocalJurisdiction struct {
			ElectionAdministrationBody struct {
				ElectionOfficials []struct {
					Name              string `json:"name"`
					OfficePhoneNumber string `json:"officePhoneNumber"`
					EmailAddress      string `json:"emailAddress"`
				} `json:"electionOfficials"`
				ElectionRegistrationUrl string `json:"electionRegistrationUrl"`
			} `json:"electionAdministrationBody"`
		} `json:"local_jurisdiction"`
	} `json:"state"`
}

type Querier interface {
	Query(address string) (*Response, error)
}

type requestor func(endpoint string) ([]byte, error)

type CivicApi struct {
	endpoint     *url.URL
	key          string
	electionId   string
	officialOnly string
	makeRequest  requestor
}

func New(key string, electionId string, officialOnly string, makeRequest requestor) *CivicApi {
	endpoint, _ := url.Parse("https://www.googleapis.com/")
	endpoint.Path += "civicinfo/v2/voterinfo"

	return &CivicApi{endpoint, key, electionId, officialOnly, makeRequest}
}

func (c *CivicApi) Query(address string) (*Response, error) {
	parameters := url.Values{}
	parameters.Add("key", c.key)
	parameters.Add("electionId", c.electionId)
	parameters.Add("address", address)
	parameters.Add("officialOnly", c.officialOnly)
	c.endpoint.RawQuery = parameters.Encode()

	body, err := c.makeRequest(c.endpoint.String())
	if err != nil {
		return nil, err
	}

	res := &Response{}
	err = json.Unmarshal(body, res)
	if err != nil {
		return nil, err
	}

	return res, nil
}
<commit_msg>[TH] fix civic api call when election id blank<commit_after>package civicApi

import (
	"encoding/json"

	"net/url"
)

type Response struct {
	PollingLocations []struct {
		Address struct {
			LocationName string `json:"locationName"`
			Line1        string `json:"line1"`
			City         string `json:"city"`
			State        string `json:"state"`
			Zip          string `json:"zip"`
		} `json:"address"`
		Notes        string `json:"notes"`
		PollingHours string `json:"pollingHours"`
		Sources      []struct {
			Name     string `json:"name"`
			Official bool   `json:"official"`
		} `json:"sources"`
	} `json:"pollingLocations"`
	Error struct {
		Errors []struct {
			Domain  string `json:"domain"`
			Reason  string `json:"reason"`
			Message string `json:"message"`
		} `json:"errors"`
		Code    int    `json:"code"`
		Message string `json:"message"`
	} `json:"error"`
	State []struct {
		LocalJurisdiction struct {
			ElectionAdministrationBody struct {
				ElectionOfficials []struct {
					Name              string `json:"name"`
					OfficePhoneNumber string `json:"officePhoneNumber"`
					EmailAddress      string `json:"emailAddress"`
				} `json:"electionOfficials"`
				ElectionRegistrationUrl string `json:"electionRegistrationUrl"`
			} `json:"electionAdministrationBody"`
		} `json:"local_jurisdiction"`
	} `json:"state"`
}

type Querier interface {
	Query(address string) (*Response, error)
}

type requestor func(endpoint string) ([]byte, error)

type CivicApi struct {
	endpoint     *url.URL
	key          string
	electionId   string
	officialOnly string
	makeRequest  requestor
}

func New(key string, electionId string, officialOnly string, makeRequest requestor) *CivicApi {
	endpoint, _ := url.Parse("https://www.googleapis.com/")
	endpoint.Path += "civicinfo/v2/voterinfo"

	return &CivicApi{endpoint, key, electionId, officialOnly, makeRequest}
}

func (c *CivicApi) Query(address string) (*Response, error) {
	parameters := url.Values{}
	parameters.Add("key", c.key)
	if len(c.electionId) > 0 {
		parameters.Add("electionId", c.electionId)
	}
	parameters.Add("address", address)
	parameters.Add("officialOnly", c.officialOnly)
	c.endpoint.RawQuery = parameters.Encode()

	body, err := c.makeRequest(c.endpoint.String())
	if err != nil {
		return nil, err
	}

	res := &Response{}
	err = json.Unmarshal(body, res)
	if err != nil {
		return nil, err
	}

	return res, nil
}
<|endoftext|>


<commit_before>package goboots

import (
	"reflect"
)

type IController interface {
	SetApp(app *App)
	GetPageTitle() string
	GetLayoutName() string
	Init()
	PreFilter(in *In) *Out
	registerMethod(name string, method reflect.Value)
	getMethod(name string) (controllerMethod, bool)
}

type PageContent struct {
	Title       string
	Content     string
	UserContent string
	HeadInclude string
	FootInclude string
}

type RedirectPageContent struct {
	Title   string
	Message string
	Path    string
}

type ErrorPageContent struct {
	Title        string
	ErrorTitle   string
	ErrorMessage string
	Content      string
}

type Controller struct {
	App           *App
	Path          string
	Name          string
	LayoutName    string
	Action        string
	LayoutAction  string
	Params        []string
	PageTitle     string
	Layout        string
	ContentType   string
	customMethods map[string]controllerMethod
}

const (
	controllerMethodKindLegacy = iota
	controllerMethodKindNew
)

type controllerMethod struct {
	Val reflect.Value
}

func (c *Controller) GetPageTitle() string {
	return c.PageTitle
}

func (c *Controller) GetLayoutName() string {
	return c.Layout
}

func (c *Controller) SetApp(app *App) {
	c.App = app
}

func (c *Controller) Init() {
	c.PageTitle = ""
	c.Layout = "default"
	c.ContentType = "text/html; charset=utf-8"
}

func (c *Controller) PreFilter(in *In) *Out {
	return in.Continue()
}

func (c *Controller) registerMethod(name string, method reflect.Value) {
	if c.customMethods == nil {
		c.customMethods = make(map[string]controllerMethod, 0)
	}
	c.customMethods[name] = controllerMethod{method}
}
func (c *Controller) getMethod(name string) (controllerMethod, bool) {
	if c.customMethods == nil {
		return controllerMethod{}, false
	}
	val, ok := c.customMethods[name]
	return val, ok
}
<commit_msg>GetApp method<commit_after>package goboots

import (
	"reflect"
)

type IController interface {
	GetApp() *App
	SetApp(app *App)
	GetPageTitle() string
	GetLayoutName() string
	Init()
	PreFilter(in *In) *Out
	registerMethod(name string, method reflect.Value)
	getMethod(name string) (controllerMethod, bool)
}

type PageContent struct {
	Title       string
	Content     string
	UserContent string
	HeadInclude string
	FootInclude string
}

type RedirectPageContent struct {
	Title   string
	Message string
	Path    string
}

type ErrorPageContent struct {
	Title        string
	ErrorTitle   string
	ErrorMessage string
	Content      string
}

type Controller struct {
	App           *App
	Path          string
	Name          string
	LayoutName    string
	Action        string
	LayoutAction  string
	Params        []string
	PageTitle     string
	Layout        string
	ContentType   string
	customMethods map[string]controllerMethod
}

const (
	controllerMethodKindLegacy = iota
	controllerMethodKindNew
)

type controllerMethod struct {
	Val reflect.Value
}

func (c *Controller) GetPageTitle() string {
	return c.PageTitle
}

func (c *Controller) GetLayoutName() string {
	return c.Layout
}

func (c *Controller) GetApp() *App {
	return c.App
}

func (c *Controller) SetApp(app *App) {
	c.App = app
}

func (c *Controller) Init() {
	c.PageTitle = ""
	c.Layout = "default"
	c.ContentType = "text/html; charset=utf-8"
}

func (c *Controller) PreFilter(in *In) *Out {
	return in.Continue()
}

func (c *Controller) registerMethod(name string, method reflect.Value) {
	if c.customMethods == nil {
		c.customMethods = make(map[string]controllerMethod, 0)
	}
	c.customMethods[name] = controllerMethod{method}
}
func (c *Controller) getMethod(name string) (controllerMethod, bool) {
	if c.customMethods == nil {
		return controllerMethod{}, false
	}
	val, ok := c.customMethods[name]
	return val, ok
}
<|endoftext|>


<commit_before>package client

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/nomad/client/allocdir"
	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/driver"
	"github.com/hashicorp/nomad/nomad/structs"
)

const (
	// allocSyncRetryIntv is the interval on which we retry updating
	// the status of the allocation
	allocSyncRetryIntv = 15 * time.Second
)

// taskStatus is used to track the status of a task
type taskStatus struct {
	Status      string
	Description string
}

// AllocStateUpdater is used to update the status of an allocation
type AllocStateUpdater func(alloc *structs.Allocation) error

// AllocRunner is used to wrap an allocation and provide the execution context.
type AllocRunner struct {
	config  *config.Config
	updater AllocStateUpdater
	logger  *log.Logger

	alloc *structs.Allocation

	dirtyCh chan struct{}

	ctx      *driver.ExecContext
	tasks    map[string]*TaskRunner
	taskLock sync.RWMutex

	taskStatus     map[string]taskStatus
	taskStatusLock sync.RWMutex

	updateCh chan *structs.Allocation

	destroy     bool
	destroyCh   chan struct{}
	destroyLock sync.Mutex
}

// allocRunnerState is used to snapshot the state of the alloc runner
type allocRunnerState struct {
	Alloc      *structs.Allocation
	TaskStatus map[string]taskStatus
	Context    *driver.ExecContext
}

// NewAllocRunner is used to create a new allocation context
func NewAllocRunner(logger *log.Logger, config *config.Config, updater AllocStateUpdater, alloc *structs.Allocation) *AllocRunner {
	ar := &AllocRunner{
		config:     config,
		updater:    updater,
		logger:     logger,
		alloc:      alloc,
		dirtyCh:    make(chan struct{}, 1),
		tasks:      make(map[string]*TaskRunner),
		taskStatus: make(map[string]taskStatus),
		updateCh:   make(chan *structs.Allocation, 8),
		destroyCh:  make(chan struct{}),
	}
	return ar
}

// stateFilePath returns the path to our state file
func (r *AllocRunner) stateFilePath() string {
	return filepath.Join(r.config.StateDir, "alloc", r.alloc.ID, "state.json")
}

// RestoreState is used to restore the state of the alloc runner
func (r *AllocRunner) RestoreState() error {
	// Load the snapshot
	var snap allocRunnerState
	if err := restoreState(r.stateFilePath(), &snap); err != nil {
		return err
	}

	// Restore fields
	r.alloc = snap.Alloc
	r.taskStatus = snap.TaskStatus
	r.ctx = snap.Context

	// Restore the task runners
	var mErr multierror.Error
	for name := range r.taskStatus {
		task := &structs.Task{Name: name}
		tr := NewTaskRunner(r.logger, r.config, r.setTaskStatus, r.ctx, r.alloc.ID, task)
		r.tasks[name] = tr
		if err := tr.RestoreState(); err != nil {
			r.logger.Printf("[ERR] client: failed to restore state for alloc %s task '%s': %v", r.alloc.ID, name, err)
			mErr.Errors = append(mErr.Errors, err)
		} else {
			go tr.Run()
		}
	}
	return mErr.ErrorOrNil()
}

// SaveState is used to snapshot our state
func (r *AllocRunner) SaveState() error {
	r.taskStatusLock.RLock()
	snap := allocRunnerState{
		Alloc:      r.alloc,
		TaskStatus: r.taskStatus,
		Context:    r.ctx,
	}
	err := persistState(r.stateFilePath(), &snap)
	r.taskStatusLock.RUnlock()
	if err != nil {
		return err
	}

	// Save state for each task
	r.taskLock.RLock()
	defer r.taskLock.RUnlock()
	var mErr multierror.Error
	for name, tr := range r.tasks {
		if err := tr.SaveState(); err != nil {
			r.logger.Printf("[ERR] client: failed to save state for alloc %s task '%s': %v",
				r.alloc.ID, name, err)
			mErr.Errors = append(mErr.Errors, err)
		}
	}
	return mErr.ErrorOrNil()
}

// DestroyState is used to cleanup after ourselves
func (r *AllocRunner) DestroyState() error {
	return os.RemoveAll(filepath.Dir(r.stateFilePath()))
}

// DestroyContext is used to destroy the context
func (r *AllocRunner) DestroyContext() error {
	return r.ctx.AllocDir.Destroy()
}

// Alloc returns the associated allocation
func (r *AllocRunner) Alloc() *structs.Allocation {
	return r.alloc
}

// setAlloc is used to update the allocation of the runner
// we preserve the existing client status and description
func (r *AllocRunner) setAlloc(alloc *structs.Allocation) {
	if r.alloc != nil {
		alloc.ClientStatus = r.alloc.ClientStatus
		alloc.ClientDescription = r.alloc.ClientDescription
	}
	r.alloc = alloc
}

// dirtySyncState is used to watch for state being marked dirty to sync
func (r *AllocRunner) dirtySyncState() {
	for {
		select {
		case <-r.dirtyCh:
			r.retrySyncState(r.destroyCh)
		case <-r.destroyCh:
			return
		}
	}
}

// retrySyncState is used to retry the state sync until success
func (r *AllocRunner) retrySyncState(stopCh chan struct{}) {
	for {
		err := r.syncStatus()
		if err == nil {
			return
		}
		select {
		case <-time.After(allocSyncRetryIntv + randomStagger(allocSyncRetryIntv)):
		case <-stopCh:
			return
		}
	}
}

// syncStatus is used to run and sync the status when it changes
func (r *AllocRunner) syncStatus() error {
	// Scan the task status to termine the status of the alloc
	var pending, running, dead, failed bool
	r.taskStatusLock.RLock()
	pending = len(r.taskStatus) < len(r.tasks)
	for _, status := range r.taskStatus {
		switch status.Status {
		case structs.AllocClientStatusRunning:
			running = true
		case structs.AllocClientStatusDead:
			dead = true
		case structs.AllocClientStatusFailed:
			failed = true
		}
	}
	if len(r.taskStatus) > 0 {
		taskDesc, _ := json.Marshal(r.taskStatus)
		r.alloc.ClientDescription = string(taskDesc)
	}
	r.taskStatusLock.RUnlock()

	// Determine the alloc status
	if failed {
		r.alloc.ClientStatus = structs.AllocClientStatusFailed
	} else if running {
		r.alloc.ClientStatus = structs.AllocClientStatusRunning
	} else if dead && !pending {
		r.alloc.ClientStatus = structs.AllocClientStatusDead
	}

	// Attempt to update the status
	if err := r.updater(r.alloc); err != nil {
		r.logger.Printf("[ERR] client: failed to update alloc '%s' status to %s: %s",
			r.alloc.ID, r.alloc.ClientStatus, err)
		return err
	}
	return nil
}

// setStatus is used to update the allocation status
func (r *AllocRunner) setStatus(status, desc string) {
	r.alloc.ClientStatus = status
	r.alloc.ClientDescription = desc
	select {
	case r.dirtyCh <- struct{}{}:
	default:
	}
}

// setTaskStatus is used to set the status of a task
func (r *AllocRunner) setTaskStatus(taskName, status, desc string) {
	r.taskStatusLock.Lock()
	r.taskStatus[taskName] = taskStatus{
		Status:      status,
		Description: desc,
	}
	r.taskStatusLock.Unlock()
	select {
	case r.dirtyCh <- struct{}{}:
	default:
	}
}

// Run is a long running goroutine used to manage an allocation
func (r *AllocRunner) Run() {
	go r.dirtySyncState()

	// Check if the allocation is in a terminal status
	alloc := r.alloc
	if alloc.TerminalStatus() {
		r.logger.Printf("[DEBUG] client: aborting runner for alloc '%s', terminal status", r.alloc.ID)
		return
	}
	r.logger.Printf("[DEBUG] client: starting runner for alloc '%s'", r.alloc.ID)

	// Find the task group to run in the allocation
	tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
	if tg == nil {
		r.logger.Printf("[ERR] client: alloc '%s' for missing task group '%s'", alloc.ID, alloc.TaskGroup)
		r.setStatus(structs.AllocClientStatusFailed, fmt.Sprintf("missing task group '%s'", alloc.TaskGroup))
		return
	}

	// Create the execution context
	if r.ctx == nil {
		alloc := allocdir.NewAllocDir(filepath.Join(r.config.AllocDir, r.alloc.ID))
		alloc.Build(tg.Tasks)
		r.ctx = driver.NewExecContext(alloc)
	}

	// Start the task runners
	r.taskLock.Lock()
	for _, task := range tg.Tasks {
		// Skip tasks that were restored
		if _, ok := r.tasks[task.Name]; ok {
			continue
		}

		// Merge in the task resources
		task.Resources = alloc.TaskResources[task.Name]

		tr := NewTaskRunner(r.logger, r.config, r.setTaskStatus, r.ctx, r.alloc.ID, task)
		r.tasks[task.Name] = tr
		go tr.Run()
	}
	r.taskLock.Unlock()

OUTER:
	// Wait for updates
	for {
		select {
		case update := <-r.updateCh:
			// Check if we're in a terminal status
			if update.TerminalStatus() {
				r.setAlloc(update)
				break OUTER
			}

			// Update the task groups
			r.taskLock.RLock()
			for _, task := range tg.Tasks {
				tr := r.tasks[task.Name]

				// Merge in the task resources
				task.Resources = update.TaskResources[task.Name]
				tr.Update(task)
			}
			r.taskLock.RUnlock()

		case <-r.destroyCh:
			break OUTER
		}
	}

	// Destroy each sub-task
	r.taskLock.RLock()
	defer r.taskLock.RUnlock()
	for _, tr := range r.tasks {
		tr.Destroy()
	}

	// Wait for termination of the task runners
	for _, tr := range r.tasks {
		<-tr.WaitCh()
	}

	// Final state sync
	r.retrySyncState(nil)

	// Check if we should destroy our state
	if r.destroy {
		if err := r.DestroyContext(); err != nil {
			r.logger.Printf("[ERR] client: failed to destroy context for alloc '%s': %v",
				r.alloc.ID, err)
		}
		if err := r.DestroyState(); err != nil {
			r.logger.Printf("[ERR] client: failed to destroy state for alloc '%s': %v",
				r.alloc.ID, err)
		}
	}
	r.logger.Printf("[DEBUG] client: terminating runner for alloc '%s'", r.alloc.ID)
}

// Update is used to update the allocation of the context
func (r *AllocRunner) Update(update *structs.Allocation) {
	select {
	case r.updateCh <- update:
	default:
		r.logger.Printf("[ERR] client: dropping update to alloc '%s'", update.ID)
	}
}

// Destroy is used to indicate that the allocation context should be destroyed
func (r *AllocRunner) Destroy() {
	r.destroyLock.Lock()
	defer r.destroyLock.Unlock()

	if r.destroy {
		return
	}
	r.destroy = true
	close(r.destroyCh)
}
<commit_msg>Change allocdir variable name for clarity<commit_after>package client

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/nomad/client/allocdir"
	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/driver"
	"github.com/hashicorp/nomad/nomad/structs"
)

const (
	// allocSyncRetryIntv is the interval on which we retry updating
	// the status of the allocation
	allocSyncRetryIntv = 15 * time.Second
)

// taskStatus is used to track the status of a task
type taskStatus struct {
	Status      string
	Description string
}

// AllocStateUpdater is used to update the status of an allocation
type AllocStateUpdater func(alloc *structs.Allocation) error

// AllocRunner is used to wrap an allocation and provide the execution context.
type AllocRunner struct {
	config  *config.Config
	updater AllocStateUpdater
	logger  *log.Logger

	alloc *structs.Allocation

	dirtyCh chan struct{}

	ctx      *driver.ExecContext
	tasks    map[string]*TaskRunner
	taskLock sync.RWMutex

	taskStatus     map[string]taskStatus
	taskStatusLock sync.RWMutex

	updateCh chan *structs.Allocation

	destroy     bool
	destroyCh   chan struct{}
	destroyLock sync.Mutex
}

// allocRunnerState is used to snapshot the state of the alloc runner
type allocRunnerState struct {
	Alloc      *structs.Allocation
	TaskStatus map[string]taskStatus
	Context    *driver.ExecContext
}

// NewAllocRunner is used to create a new allocation context
func NewAllocRunner(logger *log.Logger, config *config.Config, updater AllocStateUpdater, alloc *structs.Allocation) *AllocRunner {
	ar := &AllocRunner{
		config:     config,
		updater:    updater,
		logger:     logger,
		alloc:      alloc,
		dirtyCh:    make(chan struct{}, 1),
		tasks:      make(map[string]*TaskRunner),
		taskStatus: make(map[string]taskStatus),
		updateCh:   make(chan *structs.Allocation, 8),
		destroyCh:  make(chan struct{}),
	}
	return ar
}

// stateFilePath returns the path to our state file
func (r *AllocRunner) stateFilePath() string {
	return filepath.Join(r.config.StateDir, "alloc", r.alloc.ID, "state.json")
}

// RestoreState is used to restore the state of the alloc runner
func (r *AllocRunner) RestoreState() error {
	// Load the snapshot
	var snap allocRunnerState
	if err := restoreState(r.stateFilePath(), &snap); err != nil {
		return err
	}

	// Restore fields
	r.alloc = snap.Alloc
	r.taskStatus = snap.TaskStatus
	r.ctx = snap.Context

	// Restore the task runners
	var mErr multierror.Error
	for name := range r.taskStatus {
		task := &structs.Task{Name: name}
		tr := NewTaskRunner(r.logger, r.config, r.setTaskStatus, r.ctx, r.alloc.ID, task)
		r.tasks[name] = tr
		if err := tr.RestoreState(); err != nil {
			r.logger.Printf("[ERR] client: failed to restore state for alloc %s task '%s': %v", r.alloc.ID, name, err)
			mErr.Errors = append(mErr.Errors, err)
		} else {
			go tr.Run()
		}
	}
	return mErr.ErrorOrNil()
}

// SaveState is used to snapshot our state
func (r *AllocRunner) SaveState() error {
	r.taskStatusLock.RLock()
	snap := allocRunnerState{
		Alloc:      r.alloc,
		TaskStatus: r.taskStatus,
		Context:    r.ctx,
	}
	err := persistState(r.stateFilePath(), &snap)
	r.taskStatusLock.RUnlock()
	if err != nil {
		return err
	}

	// Save state for each task
	r.taskLock.RLock()
	defer r.taskLock.RUnlock()
	var mErr multierror.Error
	for name, tr := range r.tasks {
		if err := tr.SaveState(); err != nil {
			r.logger.Printf("[ERR] client: failed to save state for alloc %s task '%s': %v",
				r.alloc.ID, name, err)
			mErr.Errors = append(mErr.Errors, err)
		}
	}
	return mErr.ErrorOrNil()
}

// DestroyState is used to cleanup after ourselves
func (r *AllocRunner) DestroyState() error {
	return os.RemoveAll(filepath.Dir(r.stateFilePath()))
}

// DestroyContext is used to destroy the context
func (r *AllocRunner) DestroyContext() error {
	return r.ctx.AllocDir.Destroy()
}

// Alloc returns the associated allocation
func (r *AllocRunner) Alloc() *structs.Allocation {
	return r.alloc
}

// setAlloc is used to update the allocation of the runner
// we preserve the existing client status and description
func (r *AllocRunner) setAlloc(alloc *structs.Allocation) {
	if r.alloc != nil {
		alloc.ClientStatus = r.alloc.ClientStatus
		alloc.ClientDescription = r.alloc.ClientDescription
	}
	r.alloc = alloc
}

// dirtySyncState is used to watch for state being marked dirty to sync
func (r *AllocRunner) dirtySyncState() {
	for {
		select {
		case <-r.dirtyCh:
			r.retrySyncState(r.destroyCh)
		case <-r.destroyCh:
			return
		}
	}
}

// retrySyncState is used to retry the state sync until success
func (r *AllocRunner) retrySyncState(stopCh chan struct{}) {
	for {
		err := r.syncStatus()
		if err == nil {
			return
		}
		select {
		case <-time.After(allocSyncRetryIntv + randomStagger(allocSyncRetryIntv)):
		case <-stopCh:
			return
		}
	}
}

// syncStatus is used to run and sync the status when it changes
func (r *AllocRunner) syncStatus() error {
	// Scan the task status to termine the status of the alloc
	var pending, running, dead, failed bool
	r.taskStatusLock.RLock()
	pending = len(r.taskStatus) < len(r.tasks)
	for _, status := range r.taskStatus {
		switch status.Status {
		case structs.AllocClientStatusRunning:
			running = true
		case structs.AllocClientStatusDead:
			dead = true
		case structs.AllocClientStatusFailed:
			failed = true
		}
	}
	if len(r.taskStatus) > 0 {
		taskDesc, _ := json.Marshal(r.taskStatus)
		r.alloc.ClientDescription = string(taskDesc)
	}
	r.taskStatusLock.RUnlock()

	// Determine the alloc status
	if failed {
		r.alloc.ClientStatus = structs.AllocClientStatusFailed
	} else if running {
		r.alloc.ClientStatus = structs.AllocClientStatusRunning
	} else if dead && !pending {
		r.alloc.ClientStatus = structs.AllocClientStatusDead
	}

	// Attempt to update the status
	if err := r.updater(r.alloc); err != nil {
		r.logger.Printf("[ERR] client: failed to update alloc '%s' status to %s: %s",
			r.alloc.ID, r.alloc.ClientStatus, err)
		return err
	}
	return nil
}

// setStatus is used to update the allocation status
func (r *AllocRunner) setStatus(status, desc string) {
	r.alloc.ClientStatus = status
	r.alloc.ClientDescription = desc
	select {
	case r.dirtyCh <- struct{}{}:
	default:
	}
}

// setTaskStatus is used to set the status of a task
func (r *AllocRunner) setTaskStatus(taskName, status, desc string) {
	r.taskStatusLock.Lock()
	r.taskStatus[taskName] = taskStatus{
		Status:      status,
		Description: desc,
	}
	r.taskStatusLock.Unlock()
	select {
	case r.dirtyCh <- struct{}{}:
	default:
	}
}

// Run is a long running goroutine used to manage an allocation
func (r *AllocRunner) Run() {
	go r.dirtySyncState()

	// Check if the allocation is in a terminal status
	alloc := r.alloc
	if alloc.TerminalStatus() {
		r.logger.Printf("[DEBUG] client: aborting runner for alloc '%s', terminal status", r.alloc.ID)
		return
	}
	r.logger.Printf("[DEBUG] client: starting runner for alloc '%s'", r.alloc.ID)

	// Find the task group to run in the allocation
	tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
	if tg == nil {
		r.logger.Printf("[ERR] client: alloc '%s' for missing task group '%s'", alloc.ID, alloc.TaskGroup)
		r.setStatus(structs.AllocClientStatusFailed, fmt.Sprintf("missing task group '%s'", alloc.TaskGroup))
		return
	}

	// Create the execution context
	if r.ctx == nil {
		allocDir := allocdir.NewAllocDir(filepath.Join(r.config.AllocDir, r.alloc.ID))
		allocDir.Build(tg.Tasks)
		r.ctx = driver.NewExecContext(allocDir)
	}

	// Start the task runners
	r.taskLock.Lock()
	for _, task := range tg.Tasks {
		// Skip tasks that were restored
		if _, ok := r.tasks[task.Name]; ok {
			continue
		}

		// Merge in the task resources
		task.Resources = alloc.TaskResources[task.Name]

		tr := NewTaskRunner(r.logger, r.config, r.setTaskStatus, r.ctx, r.alloc.ID, task)
		r.tasks[task.Name] = tr
		go tr.Run()
	}
	r.taskLock.Unlock()

OUTER:
	// Wait for updates
	for {
		select {
		case update := <-r.updateCh:
			// Check if we're in a terminal status
			if update.TerminalStatus() {
				r.setAlloc(update)
				break OUTER
			}

			// Update the task groups
			r.taskLock.RLock()
			for _, task := range tg.Tasks {
				tr := r.tasks[task.Name]

				// Merge in the task resources
				task.Resources = update.TaskResources[task.Name]
				tr.Update(task)
			}
			r.taskLock.RUnlock()

		case <-r.destroyCh:
			break OUTER
		}
	}

	// Destroy each sub-task
	r.taskLock.RLock()
	defer r.taskLock.RUnlock()
	for _, tr := range r.tasks {
		tr.Destroy()
	}

	// Wait for termination of the task runners
	for _, tr := range r.tasks {
		<-tr.WaitCh()
	}

	// Final state sync
	r.retrySyncState(nil)

	// Check if we should destroy our state
	if r.destroy {
		if err := r.DestroyContext(); err != nil {
			r.logger.Printf("[ERR] client: failed to destroy context for alloc '%s': %v",
				r.alloc.ID, err)
		}
		if err := r.DestroyState(); err != nil {
			r.logger.Printf("[ERR] client: failed to destroy state for alloc '%s': %v",
				r.alloc.ID, err)
		}
	}
	r.logger.Printf("[DEBUG] client: terminating runner for alloc '%s'", r.alloc.ID)
}

// Update is used to update the allocation of the context
func (r *AllocRunner) Update(update *structs.Allocation) {
	select {
	case r.updateCh <- update:
	default:
		r.logger.Printf("[ERR] client: dropping update to alloc '%s'", update.ID)
	}
}

// Destroy is used to indicate that the allocation context should be destroyed
func (r *AllocRunner) Destroy() {
	r.destroyLock.Lock()
	defer r.destroyLock.Unlock()

	if r.destroy {
		return
	}
	r.destroy = true
	close(r.destroyCh)
}
<|endoftext|>


<commit_before>package cloud

import (
	"bytes"
	"strings"
	"text/template"

	api "github.com/appscode/pharmer/apis/v1alpha1"
	"github.com/ghodss/yaml"
	"github.com/hashicorp/go-version"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
)

type TemplateData struct {
	BinaryVersion      string
	KubeadmToken       string
	CAKey              string
	FrontProxyKey      string
	APIServerAddress   string
	ExtraDomains       string
	NetworkProvider    string
	CloudConfig        string
	Provider           string
	ExternalProvider   bool
	KubeadmTokenLoader string

	MasterConfiguration *kubeadmapi.MasterConfiguration
	KubeletExtraArgs    map[string]string
}

func (td TemplateData) MasterConfigurationYAML() (string, error) {
	if td.MasterConfiguration == nil {
		return "", nil
	}
	cb, err := yaml.Marshal(td.MasterConfiguration)
	return string(cb), err
}

func (td TemplateData) IsPreReleaseVersion() bool {
	if v, err := version.NewVersion(td.BinaryVersion); err == nil && v.Prerelease() != "" {
		return true
	}
	return false
}

func (td TemplateData) KubeletExtraArgsStr() string {
	var buf bytes.Buffer
	for k, v := range td.KubeletExtraArgs {
		buf.WriteString("--")
		buf.WriteString(k)
		buf.WriteRune('=')
		buf.WriteString(v)
		buf.WriteRune(' ')
	}
	return buf.String()
}

func (td TemplateData) KubeletExtraArgsWithoutCloudProviderStr() string {
	var buf bytes.Buffer
	for k, v := range td.KubeletExtraArgs {
		if k == "cloud-config" {
			continue
		}
		if k == "cloud-provider" {
			v = ""
		}
		buf.WriteString("--")
		buf.WriteString(k)
		buf.WriteRune('=')
		buf.WriteString(v)
		buf.WriteRune(' ')
	}
	return buf.String()
}

func (td TemplateData) PackageList() string {
	pkgs := []string{
		"cron",
		"docker.io",
		"ebtables",
		"git",
		"glusterfs-client",
		"haveged",
		"nfs-common",
		"socat",
	}
	if !td.IsPreReleaseVersion() {
		if td.BinaryVersion == "" {
			pkgs = append(pkgs, "kubeadm", "kubelet", "kubectl")
		} else {
			pkgs = append(pkgs, "kubeadm="+td.BinaryVersion, "kubelet="+td.BinaryVersion, "kubectl="+td.BinaryVersion)
		}
	}
	if td.Provider != "gce" && td.Provider != "gke" {
		pkgs = append(pkgs, "ntp")
	}
	return strings.Join(pkgs, " ")
}

var (
	StartupScriptTemplate = template.Must(template.New(api.RoleMaster).Parse(`#!/bin/bash
set -x
set -o errexit
set -o nounset
set -o pipefail

# log to /var/log/startup-script.log
exec > >(tee -a /var/log/startup-script.log)
exec 2>&1

# kill apt processes (E: Unable to lock directory /var/lib/apt/lists/)
kill $(ps aux | grep '[a]pt' | awk '{print $2}') || true

{{ template "prepare-host" . }}

apt-get update -y
apt-get install -y apt-transport-https curl ca-certificates

curl -fSsL https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list

add-apt-repository -y ppa:gluster/glusterfs-3.10

apt-get update -y
apt-get install -y {{ .PackageList }} || true
{{ if .IsPreReleaseVersion }}
curl -Lo kubeadm https://dl.k8s.io/release/{{ .KubeadmVersion }}/bin/linux/amd64/kubeadm \
    && chmod +x kubeadm \
	&& mv kubeadm /usr/bin/
{{ end }}

curl -Lo pre-k https://cdn.appscode.com/binaries/pre-k/0.1.0-alpha.7/pre-k-linux-amd64 \
	&& chmod +x pre-k \
	&& mv pre-k /usr/bin/

systemctl enable docker
systemctl start docker

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS={{ .KubeletExtraArgsStr }}"
EOF

systemctl daemon-reload
systemctl restart kubelet

kubeadm reset

{{ template "setup-certs" . }}

{{ if .CloudConfig }}
cat > /etc/kubernetes/cloud-config <<EOF
{{ .CloudConfig }}
EOF
{{ end }}

mkdir -p /etc/kubernetes/kubeadm

{{ if .MasterConfiguration }}
cat > /etc/kubernetes/kubeadm/base.yaml <<EOF
{{ .MasterConfigurationYAML }}
EOF
{{ end }}

pre-k merge master-config \
	--config=/etc/kubernetes/kubeadm/base.yaml \
	--apiserver-advertise-address=$(pre-k get public-ips --all=false) \
	--apiserver-cert-extra-sans=$(pre-k get public-ips --routable) \
	--apiserver-cert-extra-sans=$(pre-k get private-ips) \
	--apiserver-cert-extra-sans={{ .ExtraDomains }} \
	> /etc/kubernetes/kubeadm/config.yaml
kubeadm init --config=/etc/kubernetes/kubeadm/config.yaml --skip-token-print

{{ if eq .NetworkProvider "flannel" }}
{{ template "flannel" . }}
{{ else if eq .NetworkProvider "calico" }}
{{ template "calico" . }}
{{ end }}

kubectl apply \
  -f https://raw.githubusercontent.com/appscode/pharmer/master/addons/kubeadm-probe/ds.yaml \
  --kubeconfig /etc/kubernetes/admin.conf

mkdir -p ~/.kube
sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config

{{ if .ExternalProvider }}
{{ template "ccm" . }}
{{end}}

`))

	_ = template.Must(StartupScriptTemplate.New(api.RoleNode).Parse(`#!/bin/bash
set -x
set -o errexit
set -o nounset
set -o pipefail

# log to /var/log/startup-script.log
exec > >(tee -a /var/log/startup-script.log)
exec 2>&1

# kill apt processes (E: Unable to lock directory /var/lib/apt/lists/)
kill $(ps aux | grep '[a]pt' | awk '{print $2}') || true

{{ template "prepare-host" . }}

apt-get update -y
apt-get install -y apt-transport-https curl ca-certificates

curl -fSsL https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list

add-apt-repository -y ppa:gluster/glusterfs-3.10

apt-get update -y
apt-get install -y {{ .PackageList }} || true
{{ if .IsPreReleaseVersion }}
curl -Lo kubeadm https://dl.k8s.io/release/{{ .KubeadmVersion }}/bin/linux/amd64/kubeadm \
    && chmod +x kubeadm \
	&& mv kubeadm /usr/bin/
{{ end }}

systemctl enable docker
systemctl start docker

{{ if .CloudConfig }}
cat > /etc/kubernetes/cloud-config <<EOF
{{ .CloudConfig }}
EOF
{{ end }}

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS={{ .KubeletExtraArgsStr }}"
EOF

systemctl daemon-reload
systemctl restart kubelet

kubeadm reset
{{ .KubeadmTokenLoader  }}
KUBEADM_TOKEN=${KUBEADM_TOKEN:-{{ .KubeadmToken }}}
kubeadm join --token=${KUBEADM_TOKEN} {{ .APIServerAddress }}
`))

	_ = template.Must(StartupScriptTemplate.New("prepare-host").Parse(``))

	_ = template.Must(StartupScriptTemplate.New("setup-certs").Parse(`
mkdir -p /etc/kubernetes/pki

cat > /etc/kubernetes/pki/ca.key <<EOF
{{ .CAKey }}
EOF
pre-k get cacert --common-name=ca < /etc/kubernetes/pki/ca.key > /etc/kubernetes/pki/ca.crt

cat > /etc/kubernetes/pki/front-proxy-ca.key <<EOF
{{ .FrontProxyKey }}
EOF
pre-k get cacert --common-name=front-proxy-ca < /etc/kubernetes/pki/front-proxy-ca.key > /etc/kubernetes/pki/front-proxy-ca.crt
chmod 600 /etc/kubernetes/pki/ca.key /etc/kubernetes/pki/front-proxy-ca.key
`))

	_ = template.Must(StartupScriptTemplate.New("ccm").Parse(`
kubectl apply \
	-f https://raw.githubusercontent.com/appscode/pharmer/ccm-fix/cloud/providers/digitalocean/cloud-control-manager.yaml \
	--kubeconfig /etc/kubernetes/admin.conf

# until [ $(kubectl get pods -n kube-system -l k8s-app=kube-dns -o jsonpath='{.items[0].status.phase}' --kubeconfig /etc/kubernetes/admin.conf) == "Running" ]
# do
#    echo '.'
#    sleep 5
# done
#
# kubectl apply -f "https://raw.githubusercontent.com/appscode/pharmer/master/cloud/providers/{{ .Provider }}/cloud-control-manager.yaml" --kubeconfig /etc/kubernetes/admin.conf
#
# until [ $(kubectl get pods -n kube-system -l app=cloud-controller-manager -o jsonpath='{.items[0].status.phase}' --kubeconfig /etc/kubernetes/admin.conf) == "Running" ]
# do
#    echo '.'
#    sleep 5
# done
#
# cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
# [Service]
# Environment="KUBELET_EXTRA_ARGS={{ .KubeletExtraArgsStr }}"
# EOF
#
# NODE_NAME=$(uname -n)
# kubectl taint nodes ${NODE_NAME} node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule --kubeconfig /etc/kubernetes/admin.conf
#
# systemctl daemon-reload
# systemctl restart kubelet

# sleep 10
# reboot
`))

	_ = template.Must(StartupScriptTemplate.New("calico").Parse(`
kubectl apply \
  -f https://docs.projectcalico.org/v2.6/getting-started/kubernetes/installation/hosted/kubeadm/1.6/calico.yaml \
  --kubeconfig /etc/kubernetes/admin.conf
`))

	_ = template.Must(StartupScriptTemplate.New("flannel").Parse(`
kubectl apply \
  -f https://raw.githubusercontent.com/coreos/flannel/v0.8.0/Documentation/kube-flannel.yml \
  --kubeconfig /etc/kubernetes/admin.conf
kubectl apply \
  -f https://raw.githubusercontent.com/coreos/flannel/v0.8.0/Documentation/kube-flannel-rbac.yml \
  --kubeconfig /etc/kubernetes/admin.conf
`))
)
<commit_msg>Fix path to CCM<commit_after>package cloud

import (
	"bytes"
	"strings"
	"text/template"

	api "github.com/appscode/pharmer/apis/v1alpha1"
	"github.com/ghodss/yaml"
	"github.com/hashicorp/go-version"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
)

type TemplateData struct {
	BinaryVersion      string
	KubeadmToken       string
	CAKey              string
	FrontProxyKey      string
	APIServerAddress   string
	ExtraDomains       string
	NetworkProvider    string
	CloudConfig        string
	Provider           string
	ExternalProvider   bool
	KubeadmTokenLoader string

	MasterConfiguration *kubeadmapi.MasterConfiguration
	KubeletExtraArgs    map[string]string
}

func (td TemplateData) MasterConfigurationYAML() (string, error) {
	if td.MasterConfiguration == nil {
		return "", nil
	}
	cb, err := yaml.Marshal(td.MasterConfiguration)
	return string(cb), err
}

func (td TemplateData) IsPreReleaseVersion() bool {
	if v, err := version.NewVersion(td.BinaryVersion); err == nil && v.Prerelease() != "" {
		return true
	}
	return false
}

func (td TemplateData) KubeletExtraArgsStr() string {
	var buf bytes.Buffer
	for k, v := range td.KubeletExtraArgs {
		buf.WriteString("--")
		buf.WriteString(k)
		buf.WriteRune('=')
		buf.WriteString(v)
		buf.WriteRune(' ')
	}
	return buf.String()
}

func (td TemplateData) KubeletExtraArgsWithoutCloudProviderStr() string {
	var buf bytes.Buffer
	for k, v := range td.KubeletExtraArgs {
		if k == "cloud-config" {
			continue
		}
		if k == "cloud-provider" {
			v = ""
		}
		buf.WriteString("--")
		buf.WriteString(k)
		buf.WriteRune('=')
		buf.WriteString(v)
		buf.WriteRune(' ')
	}
	return buf.String()
}

func (td TemplateData) PackageList() string {
	pkgs := []string{
		"cron",
		"docker.io",
		"ebtables",
		"git",
		"glusterfs-client",
		"haveged",
		"nfs-common",
		"socat",
	}
	if !td.IsPreReleaseVersion() {
		if td.BinaryVersion == "" {
			pkgs = append(pkgs, "kubeadm", "kubelet", "kubectl")
		} else {
			pkgs = append(pkgs, "kubeadm="+td.BinaryVersion, "kubelet="+td.BinaryVersion, "kubectl="+td.BinaryVersion)
		}
	}
	if td.Provider != "gce" && td.Provider != "gke" {
		pkgs = append(pkgs, "ntp")
	}
	return strings.Join(pkgs, " ")
}

var (
	StartupScriptTemplate = template.Must(template.New(api.RoleMaster).Parse(`#!/bin/bash
set -x
set -o errexit
set -o nounset
set -o pipefail

# log to /var/log/startup-script.log
exec > >(tee -a /var/log/startup-script.log)
exec 2>&1

# kill apt processes (E: Unable to lock directory /var/lib/apt/lists/)
kill $(ps aux | grep '[a]pt' | awk '{print $2}') || true

{{ template "prepare-host" . }}

apt-get update -y
apt-get install -y apt-transport-https curl ca-certificates

curl -fSsL https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list

add-apt-repository -y ppa:gluster/glusterfs-3.10

apt-get update -y
apt-get install -y {{ .PackageList }} || true
{{ if .IsPreReleaseVersion }}
curl -Lo kubeadm https://dl.k8s.io/release/{{ .KubeadmVersion }}/bin/linux/amd64/kubeadm \
    && chmod +x kubeadm \
	&& mv kubeadm /usr/bin/
{{ end }}

curl -Lo pre-k https://cdn.appscode.com/binaries/pre-k/0.1.0-alpha.7/pre-k-linux-amd64 \
	&& chmod +x pre-k \
	&& mv pre-k /usr/bin/

systemctl enable docker
systemctl start docker

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS={{ .KubeletExtraArgsStr }}"
EOF

systemctl daemon-reload
systemctl restart kubelet

kubeadm reset

{{ template "setup-certs" . }}

{{ if .CloudConfig }}
cat > /etc/kubernetes/cloud-config <<EOF
{{ .CloudConfig }}
EOF
{{ end }}

mkdir -p /etc/kubernetes/kubeadm

{{ if .MasterConfiguration }}
cat > /etc/kubernetes/kubeadm/base.yaml <<EOF
{{ .MasterConfigurationYAML }}
EOF
{{ end }}

pre-k merge master-config \
	--config=/etc/kubernetes/kubeadm/base.yaml \
	--apiserver-advertise-address=$(pre-k get public-ips --all=false) \
	--apiserver-cert-extra-sans=$(pre-k get public-ips --routable) \
	--apiserver-cert-extra-sans=$(pre-k get private-ips) \
	--apiserver-cert-extra-sans={{ .ExtraDomains }} \
	> /etc/kubernetes/kubeadm/config.yaml
kubeadm init --config=/etc/kubernetes/kubeadm/config.yaml --skip-token-print

{{ if eq .NetworkProvider "flannel" }}
{{ template "flannel" . }}
{{ else if eq .NetworkProvider "calico" }}
{{ template "calico" . }}
{{ end }}

kubectl apply \
  -f https://raw.githubusercontent.com/appscode/pharmer/master/addons/kubeadm-probe/ds.yaml \
  --kubeconfig /etc/kubernetes/admin.conf

mkdir -p ~/.kube
sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config

{{ if .ExternalProvider }}
{{ template "ccm" . }}
{{end}}

`))

	_ = template.Must(StartupScriptTemplate.New(api.RoleNode).Parse(`#!/bin/bash
set -x
set -o errexit
set -o nounset
set -o pipefail

# log to /var/log/startup-script.log
exec > >(tee -a /var/log/startup-script.log)
exec 2>&1

# kill apt processes (E: Unable to lock directory /var/lib/apt/lists/)
kill $(ps aux | grep '[a]pt' | awk '{print $2}') || true

{{ template "prepare-host" . }}

apt-get update -y
apt-get install -y apt-transport-https curl ca-certificates

curl -fSsL https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list

add-apt-repository -y ppa:gluster/glusterfs-3.10

apt-get update -y
apt-get install -y {{ .PackageList }} || true
{{ if .IsPreReleaseVersion }}
curl -Lo kubeadm https://dl.k8s.io/release/{{ .KubeadmVersion }}/bin/linux/amd64/kubeadm \
    && chmod +x kubeadm \
	&& mv kubeadm /usr/bin/
{{ end }}

systemctl enable docker
systemctl start docker

{{ if .CloudConfig }}
cat > /etc/kubernetes/cloud-config <<EOF
{{ .CloudConfig }}
EOF
{{ end }}

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS={{ .KubeletExtraArgsStr }}"
EOF

systemctl daemon-reload
systemctl restart kubelet

kubeadm reset
{{ .KubeadmTokenLoader  }}
KUBEADM_TOKEN=${KUBEADM_TOKEN:-{{ .KubeadmToken }}}
kubeadm join --token=${KUBEADM_TOKEN} {{ .APIServerAddress }}
`))

	_ = template.Must(StartupScriptTemplate.New("prepare-host").Parse(``))

	_ = template.Must(StartupScriptTemplate.New("setup-certs").Parse(`
mkdir -p /etc/kubernetes/pki

cat > /etc/kubernetes/pki/ca.key <<EOF
{{ .CAKey }}
EOF
pre-k get cacert --common-name=ca < /etc/kubernetes/pki/ca.key > /etc/kubernetes/pki/ca.crt

cat > /etc/kubernetes/pki/front-proxy-ca.key <<EOF
{{ .FrontProxyKey }}
EOF
pre-k get cacert --common-name=front-proxy-ca < /etc/kubernetes/pki/front-proxy-ca.key > /etc/kubernetes/pki/front-proxy-ca.crt
chmod 600 /etc/kubernetes/pki/ca.key /etc/kubernetes/pki/front-proxy-ca.key
`))

	_ = template.Must(StartupScriptTemplate.New("ccm").Parse(`
kubectl apply \
	-f https://raw.githubusercontent.com/appscode/pharmer/master/cloud/providers/digitalocean/cloud-control-manager.yaml \
	--kubeconfig /etc/kubernetes/admin.conf

# until [ $(kubectl get pods -n kube-system -l k8s-app=kube-dns -o jsonpath='{.items[0].status.phase}' --kubeconfig /etc/kubernetes/admin.conf) == "Running" ]
# do
#    echo '.'
#    sleep 5
# done
#
# kubectl apply -f "https://raw.githubusercontent.com/appscode/pharmer/master/cloud/providers/{{ .Provider }}/cloud-control-manager.yaml" --kubeconfig /etc/kubernetes/admin.conf
#
# until [ $(kubectl get pods -n kube-system -l app=cloud-controller-manager -o jsonpath='{.items[0].status.phase}' --kubeconfig /etc/kubernetes/admin.conf) == "Running" ]
# do
#    echo '.'
#    sleep 5
# done
#
# cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
# [Service]
# Environment="KUBELET_EXTRA_ARGS={{ .KubeletExtraArgsStr }}"
# EOF
#
# NODE_NAME=$(uname -n)
# kubectl taint nodes ${NODE_NAME} node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule --kubeconfig /etc/kubernetes/admin.conf
#
# systemctl daemon-reload
# systemctl restart kubelet

# sleep 10
# reboot
`))

	_ = template.Must(StartupScriptTemplate.New("calico").Parse(`
kubectl apply \
  -f https://docs.projectcalico.org/v2.6/getting-started/kubernetes/installation/hosted/kubeadm/1.6/calico.yaml \
  --kubeconfig /etc/kubernetes/admin.conf
`))

	_ = template.Must(StartupScriptTemplate.New("flannel").Parse(`
kubectl apply \
  -f https://raw.githubusercontent.com/coreos/flannel/v0.8.0/Documentation/kube-flannel.yml \
  --kubeconfig /etc/kubernetes/admin.conf
kubectl apply \
  -f https://raw.githubusercontent.com/coreos/flannel/v0.8.0/Documentation/kube-flannel-rbac.yml \
  --kubeconfig /etc/kubernetes/admin.conf
`))
)
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/sirupsen/logrus"
	flag "github.com/spf13/pflag"
	"io"
	"os"
)

var (
	log          = logrus.New()
	logLevel     bool
	version      string
	showVersion  bool
	agentVersion string
	inputFile    string
	outputDir    string
)

func setupLogger(logger *logrus.Logger, verbose bool) {
	formatter := new(logrus.TextFormatter)
	formatter.FullTimestamp = true
	formatter.TimestampFormat = "2006-01-02 15:04:05"
	formatter.ForceColors = true
	logger.Formatter = formatter
	logger.Out = os.Stdout

	if verbose {
		logger.Level = logrus.DebugLevel
	} else {
		logger.Level = logrus.InfoLevel
	}
}

func main() {

	flag.BoolVarP(&showVersion, "version", "v", false, "Output version information and exit")
	flag.StringVarP(&agentVersion, "agent-version", "a", "", "Binlogic CloudBackup agent version used to take backup")
	flag.StringVarP(&inputFile, "backup-file", "i", "", "Binlogic CloudBackup file path")
	flag.StringVarP(&outputDir, "output-dir", "o", "", "Output directory to place backup files.")
	flag.BoolVarP(&logLevel, "debug", "d", false, "Debug mode")
	flag.Parse()

	setupLogger(log, logLevel)

	if showVersion {
		fmt.Println(version)
		os.Exit(0)
	}

	log.Debugf("Agent Version: %s", agentVersion)
	log.Debugf("Backup File: %s", inputFile)
	log.Debugf("Output Directory: %s", outputDir)

	validateInputFile(inputFile)
	validateOutputDir(outputDir)

	os.Exit(0)

}

func validateInputFile(filename string) {

	if len(filename) == 0 {
		log.Fatal("Input file is mandatory.")
	}

	finfo, err := os.Stat(filename)
	if err != nil {
		if os.IsNotExist(err) {
			log.Fatal("Input file doesn't exists!.")
		}
	}

	if finfo.IsDir() {
		log.Fatalf("Expecting a file but %s is a directory.", filename)
	}
	log.Debugf("File: %s exists.", filename)
}

func validateOutputDir(path string) {

	if len(path) == 0 {
		log.Fatal("Ourput dir is mandatory.")
	}

	if _, err := os.Stat(path); os.IsNotExist(err) {
		os.MkdirAll(path, os.ModePerm)
	} else {
		isEmptyDir(path)
	}
}

func isEmptyDir(path string) {
	f, err := os.Open(path)
	if err != nil {
		log.Fatalf("Cannot access directory %s", path)
	}
	defer f.Close()

	_, err = f.Readdirnames(1)
	if err == io.EOF {
		return
	}
	log.Fatalf("Output directory %s should be empty!", path)
}
<commit_msg>First mvp complete.<commit_after>package main

import (
	"compress/zlib"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"github.com/sirupsen/logrus"
	flag "github.com/spf13/pflag"
	"io"
	"os"
)

var (
	log           = logrus.New()
	logLevel      bool
	version       string
	showVersion   bool
	agentVersion  string
	inputFile     string
	encryptionKey string
)

func setupLogger(logger *logrus.Logger, verbose bool) {
	formatter := new(logrus.TextFormatter)
	formatter.FullTimestamp = true
	formatter.TimestampFormat = "2006-01-02 15:04:05"
	formatter.ForceColors = true
	logger.Formatter = formatter
	logger.Out = os.Stderr

	if verbose {
		logger.Level = logrus.DebugLevel
	} else {
		logger.Level = logrus.InfoLevel
	}
}

func main() {

	flag.BoolVarP(&showVersion, "version", "v", false, "Output version information and exit")
	flag.StringVarP(&agentVersion, "agent-version", "a", "", "Binlogic CloudBackup agent version used to take backup")
	flag.StringVarP(&inputFile, "backup-file", "i", "", "Binlogic CloudBackup file path")
	flag.StringVarP(&encryptionKey, "encryption-key", "e", "", "Encruption key to decrypt backup file")
	flag.BoolVarP(&logLevel, "debug", "d", false, "Debug mode")
	flag.Parse()

	setupLogger(log, logLevel)

	if showVersion {
		fmt.Println(version)
		os.Exit(0)
	}

	log.Debugf("Agent Version: %s", agentVersion)
	log.Debugf("Backup File: %s", inputFile)

	validateInputFile(inputFile)

	prepareBackupFile(inputFile, encryptionKey)

	os.Exit(0)

}

func validateInputFile(filename string) {

	if len(filename) == 0 {
		log.Fatal("Input file is mandatory.")
	}

	finfo, err := os.Stat(filename)
	if err != nil {
		if os.IsNotExist(err) {
			log.Fatal("Input file doesn't exists!.")
		}
	}

	if finfo.IsDir() {
		log.Fatalf("Expecting a file but %s is a directory.", filename)
	}
	log.Debugf("File: %s exists.", filename)
}

func validateOutputDir(path string) {

	if len(path) == 0 {
		log.Fatal("Ourput dir is mandatory.")
	}

	if _, err := os.Stat(path); os.IsNotExist(err) {
		os.MkdirAll(path, os.ModePerm)
	} else {
		isEmptyDir(path)
	}
}

func isEmptyDir(path string) {
	f, err := os.Open(path)
	if err != nil {
		log.Fatalf("Cannot access directory %s", path)
	}
	defer f.Close()

	_, err = f.Readdirnames(1)
	if err == io.EOF {
		return
	}
	log.Fatalf("Output directory %s should be empty!", path)
}

func getCipherStream(key string) (cipher.Stream, error) {
	keyBytes, err := base64.URLEncoding.DecodeString(key)

	if err != nil {
		return nil, err
	}

	block, err := aes.NewCipher(keyBytes)

	if err != nil {
		return nil, err
	}

	// If the key is unique for each ciphertext, then it's ok to use a zero IV.
	// TODO: an iv that is predictable is safer in this case ?
	var iv [aes.BlockSize]byte

	return cipher.NewOFB(block, iv[:]), nil
}

func getCipherReader(key string, wrappedReader io.Reader) (io.Reader, error) {
	if key == "" {
		return wrappedReader, nil
	}

	stream, err := getCipherStream(key)

	if err != nil {
		return nil, err
	}

	return &cipher.StreamReader{S: stream, R: wrappedReader}, nil
}

func prepareBackupFile(filename, encryptionKey string) {

	if encryptionKey == "" {
		log.Fatal("Encryption key is mandatory.")
	}

	f, err := os.Open(filename)
	if err != nil {
		log.Fatalf("Fail to opening %s: %s", filename, err)
	}
	defer f.Close()

	zipReader, zerr := zlib.NewReader(f)
	if zerr != nil {
		log.Fatalf("%s opening zlib reader", zerr)
	}

	cipherReader, cerr := getCipherReader(encryptionKey, zipReader)
	if cerr != nil {
		log.Fatalf("Fail to decrypt file %s using %s: %s", filename, encryptionKey, zerr)
	}

	_, ferr := io.Copy(os.Stdout, cipherReader)
	if cerr != nil {
		log.Fatalf("Fail while preparing backup file: %s", ferr)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime/pprof"
	"time"

	"golang.org/x/net/context"

	"github.com/nelhage/taktician/ai"
	"github.com/nelhage/taktician/cli"
	"github.com/nelhage/taktician/ptn"
	"github.com/nelhage/taktician/tak"
)

var (
	all     = flag.Bool("all", false, "show all possible moves")
	tps     = flag.Bool("tps", false, "render position in tps")
	quiet   = flag.Bool("quiet", false, "don't print board diagrams")
	explain = flag.Bool("explain", false, "explain scoring")
	eval    = flag.Bool("evaluate", false, "only show static evaluation")

	move  = flag.Int("move", 0, "PTN move number to analyze")
	final = flag.Bool("final", false, "analyze final position only")
	black = flag.Bool("black", false, "only analyze black's move")
	white = flag.Bool("white", false, "only analyze white's move")

	debug     = flag.Int("debug", 1, "debug level")
	depth     = flag.Int("depth", 0, "minimax depth")
	timeLimit = flag.Duration("limit", time.Minute, "limit of how much time to use")

	seed     = flag.Int64("seed", 0, "specify a seed")
	sort     = flag.Bool("sort", true, "sort moves via history heuristic")
	table    = flag.Bool("table", true, "use the transposition table")
	nullMove = flag.Bool("nullMove", true, "use null-move pruning")

	cpuProfile = flag.String("cpuprofile", "", "write CPU profile")
)

func main() {
	flag.Parse()

	f, e := os.Open(flag.Arg(0))
	if e != nil {
		log.Fatal("open:", e)
	}
	parsed, e := ptn.ParsePTN(f)
	if e != nil {
		log.Fatal("parse:", e)
	}
	color := tak.NoColor
	switch {
	case *white && *black:
		log.Fatal("-white and -black are exclusive")
	case *white:
		color = tak.White
	case *black:
		color = tak.Black
	case *move != 0:
		color = tak.White
	}

	if *cpuProfile != "" {
		f, e := os.OpenFile(*cpuProfile, os.O_WRONLY|os.O_CREATE, 0644)
		if e != nil {
			log.Fatalf("open cpu-profile: %s: %v", *cpuProfile, e)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if *move != 0 || *final {
		p, e := parsed.PositionAtMove(*move, color)
		if e != nil {
			log.Fatal("find move:", e)
		}

		analyze(p)
	} else {
		p, e := parsed.InitialPosition()
		if e != nil {
			log.Fatal("initial:", e)
		}
		w, b := makeAI(p), makeAI(p)
		for _, o := range parsed.Ops {
			m, ok := o.(*ptn.Move)
			if !ok {
				continue
			}
			switch {
			case p.ToMove() == tak.White && color != tak.Black:
				fmt.Printf("%d. %s\n", p.MoveNumber()/2+1, ptn.FormatMove(&m.Move))
				analyzeWith(w, p)
			case p.ToMove() == tak.Black && color != tak.White:
				fmt.Printf("%d. ... %s\n", p.MoveNumber()/2+1, ptn.FormatMove(&m.Move))
				analyzeWith(b, p)
			}
			var e error
			p, e = p.Move(&m.Move)
			if e != nil {
				log.Fatalf("illegal move %s: %v",
					ptn.FormatMove(&m.Move), e)
			}
		}
	}
}

func makeAI(p *tak.Position) *ai.MinimaxAI {
	return ai.NewMinimax(ai.MinimaxConfig{
		Size:  p.Size(),
		Depth: *depth,
		Seed:  *seed,
		Debug: *debug,

		NoSort:     !*sort,
		NoTable:    !*table,
		NoNullMove: !*nullMove,
	})
}

func analyze(p *tak.Position) {
	analyzeWith(makeAI(p), p)
}

func analyzeWith(player *ai.MinimaxAI, p *tak.Position) {
	if *eval {
		val := player.Evaluate(p)
		if p.ToMove() == tak.Black {
			val = -val
		}
		fmt.Printf(" Val=%d\n", val)
		if *explain {
			ai.ExplainScore(player, os.Stdout, p)
		}
		return
	}
	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(*timeLimit))
	defer cancel()
	pv, val, _ := player.Analyze(ctx, p)
	if !*quiet {
		cli.RenderBoard(os.Stdout, p)
		if *explain {
			ai.ExplainScore(player, os.Stdout, p)
		}
	}
	fmt.Printf("AI analysis:\n")
	fmt.Printf(" pv=")
	for _, m := range pv {
		fmt.Printf("%s ", ptn.FormatMove(&m))
	}
	fmt.Printf("\n")
	fmt.Printf(" value=%d\n", val)
	if *tps {
		fmt.Printf("[TPS \"%s\"]\n", ptn.FormatTPS(p))
	}
	if *all {
		fmt.Printf(" all moves:")
		for _, m := range p.AllMoves(nil) {
			fmt.Printf(" %s", ptn.FormatMove(&m))
		}
		fmt.Printf("\n")
	}
	fmt.Println()

	for _, m := range pv {
		n, e := p.Move(&m)
		if e != nil {
			log.Printf("illegal move in pv: %s: %v", ptn.FormatMove(&m), e)
			if val < ai.WinThreshold && val > -ai.WinThreshold {
				log.Fatal("illegal move in non-terminal pv!")
			}
			return
		}
		p = n
	}

	if !*quiet {
		fmt.Println("Resulting position:")
		cli.RenderBoard(os.Stdout, p)
		if *explain {
			ai.ExplainScore(player, os.Stdout, p)
		}
		fmt.Println()
		fmt.Println()
	}
}
<commit_msg>analyzetak: accept custom weights<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"runtime/pprof"
	"time"

	"golang.org/x/net/context"

	"github.com/nelhage/taktician/ai"
	"github.com/nelhage/taktician/cli"
	"github.com/nelhage/taktician/ptn"
	"github.com/nelhage/taktician/tak"
)

var (
	all     = flag.Bool("all", false, "show all possible moves")
	tps     = flag.Bool("tps", false, "render position in tps")
	quiet   = flag.Bool("quiet", false, "don't print board diagrams")
	explain = flag.Bool("explain", false, "explain scoring")
	eval    = flag.Bool("evaluate", false, "only show static evaluation")

	move  = flag.Int("move", 0, "PTN move number to analyze")
	final = flag.Bool("final", false, "analyze final position only")
	black = flag.Bool("black", false, "only analyze black's move")
	white = flag.Bool("white", false, "only analyze white's move")

	debug     = flag.Int("debug", 1, "debug level")
	depth     = flag.Int("depth", 0, "minimax depth")
	timeLimit = flag.Duration("limit", time.Minute, "limit of how much time to use")

	seed     = flag.Int64("seed", 0, "specify a seed")
	sort     = flag.Bool("sort", true, "sort moves via history heuristic")
	table    = flag.Bool("table", true, "use the transposition table")
	nullMove = flag.Bool("nullMove", true, "use null-move pruning")

	cpuProfile = flag.String("cpuprofile", "", "write CPU profile")

	weights = flag.String("weights", "", "JSON-encoded evaluation weights")
)

func main() {
	flag.Parse()

	f, e := os.Open(flag.Arg(0))
	if e != nil {
		log.Fatal("open:", e)
	}
	parsed, e := ptn.ParsePTN(f)
	if e != nil {
		log.Fatal("parse:", e)
	}
	color := tak.NoColor
	switch {
	case *white && *black:
		log.Fatal("-white and -black are exclusive")
	case *white:
		color = tak.White
	case *black:
		color = tak.Black
	case *move != 0:
		color = tak.White
	}

	if *cpuProfile != "" {
		f, e := os.OpenFile(*cpuProfile, os.O_WRONLY|os.O_CREATE, 0644)
		if e != nil {
			log.Fatalf("open cpu-profile: %s: %v", *cpuProfile, e)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if *move != 0 || *final {
		p, e := parsed.PositionAtMove(*move, color)
		if e != nil {
			log.Fatal("find move:", e)
		}

		analyze(p)
	} else {
		p, e := parsed.InitialPosition()
		if e != nil {
			log.Fatal("initial:", e)
		}
		w, b := makeAI(p), makeAI(p)
		for _, o := range parsed.Ops {
			m, ok := o.(*ptn.Move)
			if !ok {
				continue
			}
			switch {
			case p.ToMove() == tak.White && color != tak.Black:
				fmt.Printf("%d. %s\n", p.MoveNumber()/2+1, ptn.FormatMove(&m.Move))
				analyzeWith(w, p)
			case p.ToMove() == tak.Black && color != tak.White:
				fmt.Printf("%d. ... %s\n", p.MoveNumber()/2+1, ptn.FormatMove(&m.Move))
				analyzeWith(b, p)
			}
			var e error
			p, e = p.Move(&m.Move)
			if e != nil {
				log.Fatalf("illegal move %s: %v",
					ptn.FormatMove(&m.Move), e)
			}
		}
	}
}

func makeAI(p *tak.Position) *ai.MinimaxAI {
	var w ai.Weights
	if *weights == "" {
		w = ai.DefaultWeights[p.Size()]
	} else {
		e := json.Unmarshal([]byte(*weights), &w)
		if e != nil {
			log.Fatalf("parse weights: %v", e)
		}
	}
	return ai.NewMinimax(ai.MinimaxConfig{
		Size:  p.Size(),
		Depth: *depth,
		Seed:  *seed,
		Debug: *debug,

		NoSort:     !*sort,
		NoTable:    !*table,
		NoNullMove: !*nullMove,

		Evaluate: ai.MakeEvaluator(p.Size(), &w),
	})
}

func analyze(p *tak.Position) {
	analyzeWith(makeAI(p), p)
}

func analyzeWith(player *ai.MinimaxAI, p *tak.Position) {
	if *eval {
		val := player.Evaluate(p)
		if p.ToMove() == tak.Black {
			val = -val
		}
		fmt.Printf(" Val=%d\n", val)
		if *explain {
			ai.ExplainScore(player, os.Stdout, p)
		}
		return
	}
	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(*timeLimit))
	defer cancel()
	pv, val, _ := player.Analyze(ctx, p)
	if !*quiet {
		cli.RenderBoard(os.Stdout, p)
		if *explain {
			ai.ExplainScore(player, os.Stdout, p)
		}
	}
	fmt.Printf("AI analysis:\n")
	fmt.Printf(" pv=")
	for _, m := range pv {
		fmt.Printf("%s ", ptn.FormatMove(&m))
	}
	fmt.Printf("\n")
	fmt.Printf(" value=%d\n", val)
	if *tps {
		fmt.Printf("[TPS \"%s\"]\n", ptn.FormatTPS(p))
	}
	if *all {
		fmt.Printf(" all moves:")
		for _, m := range p.AllMoves(nil) {
			fmt.Printf(" %s", ptn.FormatMove(&m))
		}
		fmt.Printf("\n")
	}
	fmt.Println()

	for _, m := range pv {
		n, e := p.Move(&m)
		if e != nil {
			log.Printf("illegal move in pv: %s: %v", ptn.FormatMove(&m), e)
			if val < ai.WinThreshold && val > -ai.WinThreshold {
				log.Fatal("illegal move in non-terminal pv!")
			}
			return
		}
		p = n
	}

	if !*quiet {
		fmt.Println("Resulting position:")
		cli.RenderBoard(os.Stdout, p)
		if *explain {
			ai.ExplainScore(player, os.Stdout, p)
		}
		fmt.Println()
		fmt.Println()
	}
}
<|endoftext|>


<commit_before>package app

import (
	"encoding/json"
	"fmt"
	"os"
	"strconv"

	"github.com/applariat/go-apl/pkg/apl"
	"github.com/fatih/camelcase"
	"github.com/ghodss/yaml"
	"github.com/olekukonko/tablewriter"
	"reflect"
	"strings"
)

// Determine if the code should print out a table for itself
// or pass the output to the results printer for generic our json|yaml
func shouldPrintTable() bool {
	return printerType == "table"
}

// checkPrinterType makes sure the printer type is correct
func checkPrinterType() error {
	if printerType != "" {
		if printerType != "yaml" && printerType != "json" && printerType != "table" {
			return fmt.Errorf("Invalid output type: %s", printerType)
		}
	}
	return nil
}

// printResults formats and prints the results based on the output flag
func printResults(data interface{}) {

	switch printerType {

	case "json":
		if data == nil {
			fmt.Println("{}")
			return
		}
		j, err := json.MarshalIndent(data, "", "  ")
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Println(string(j))

	case "yaml":
		if data == nil {
			fmt.Println("---")
			return
		}
		y, err := yaml.Marshal(data)
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Println(string(y))

	default:
		if data == nil {
			fmt.Println("No data found")
			return
		}

		// Print app.CLIError
		if cliError, ok := data.(CLIError); ok {
			data := [][]string{
				[]string{cliError.Message},
			}
			header := []string{"CLI Error"}
			printTableResults(data, header)
			return
		}

		// Print apl.APLError
		if aplError, ok := data.(apl.APIError); ok {
			data := [][]string{
				[]string{strconv.Itoa(aplError.StatusCode), aplError.Message},
			}
			header := []string{"Status Code", "Message"}
			printTableResults(data, header)
			return
		}

		// Print apl.CreateResult
		if createResult, ok := data.(apl.CreateResult); ok {
			result := createResult.Data.(string)
			data := [][]string{[]string{result}}
			header := []string{"New ID"}
			printTableResults(data, header)
			return
		}

		// Print apl.ModifyOutput
		if modifyResult, ok := data.(apl.ModifyResult); ok {
			data := [][]string{
				[]string{
					strconv.Itoa(modifyResult.Skipped),
					strconv.Itoa(modifyResult.Deleted),
					strconv.Itoa(modifyResult.Unchanged),
					strconv.Itoa(modifyResult.Errors),
					strconv.Itoa(modifyResult.Replaced),
					strconv.Itoa(modifyResult.Inserted),
				},
			}
			header := []string{"Skipped", "Deleted", "Unchanged", "Errors", "Replaced", "Inserted"}
			printTableResults(data, header)
			return
		}

	}
}

func printTableResults(data [][]string, header []string) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetAutoFormatHeaders(false)
	table.SetHeader(header)

	for _, v := range data {
		table.Append(v)
	}

	table.Render() // Send output
}

// prints out a table
func printTableResultsCustom(results interface{}, fields []string) {

	if results != nil {
		header := make([]string, len(fields))
		output := reflect.ValueOf(results)

		switch reflect.TypeOf(results).Kind() {

		case reflect.Struct:

			dataRow := make([]string, len(fields))
			for i := 0; i < len(fields); i++ {
				key, val := getSubField(fields[i], output)
				header[i] = camelCaseToSpaces(key)
				dataRow[i] = fmt.Sprint(val)
			}
			data := [][]string{dataRow}

			printTableResults(data, header)

		case reflect.Slice:

			data := make([][]string, output.Len())

			for i := 0; i < output.Len(); i++ {
				outputRow := output.Index(i)
				dataRow := make([]string, len(fields))

				for j := 0; j < len(fields); j++ {
					key, val := getSubField(fields[j], outputRow)
					header[j] = camelCaseToSpaces(key)
					dataRow[j] = fmt.Sprint(val)
				}
				data = append(data, dataRow)
			}
			printTableResults(data, header)
		}

	}

}

// split the name up by dot notation and recurse to find the field in question
// Warning, this is recursive
func getSubField(key string, item reflect.Value) (string, reflect.Value) {

	sub := strings.SplitN(key, ".", 2)

	if len(sub) > 1 {
		nextKey := sub[1]
		return getSubField(nextKey, item)
	}

	return key, item.FieldByName(key)
}

// camelCaseToSpaces takes a slice of CamelCaseNames and puts spaces in them Camel Case Names
func camelCaseToSpaces(input string) string {
	return strings.Join(camelcase.Split(input), " ")
}

//func camelCaseSliceToSpaces(input []string) []string {
//	result := make([]string, len(input))
//	for idx, val := range input {
//		result[idx] = camelCaseToSpaces(val)
//	}
//	return result
//}
<commit_msg>Updated the generic results printer. If it finds something other than a string it prints formatted json to the result column<commit_after>package app

import (
	"encoding/json"
	"fmt"
	"os"
	"strconv"

	"github.com/applariat/go-apl/pkg/apl"
	"github.com/fatih/camelcase"
	"github.com/ghodss/yaml"
	"github.com/olekukonko/tablewriter"
	"reflect"
	"strings"
)

// Determine if the code should print out a table for itself
// or pass the output to the results printer for generic our json|yaml
func shouldPrintTable() bool {
	return printerType == "table"
}

// checkPrinterType makes sure the printer type is correct
func checkPrinterType() error {
	if printerType != "" {
		if printerType != "yaml" && printerType != "json" && printerType != "table" {
			return fmt.Errorf("Invalid output type: %s", printerType)
		}
	}
	return nil
}

// printResults formats and prints the results based on the output flag
func printResults(data interface{}) {

	switch printerType {

	case "json":
		if data == nil {
			fmt.Println("{}")
			return
		}
		j, err := json.MarshalIndent(data, "", "  ")
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Println(string(j))

	case "yaml":
		if data == nil {
			fmt.Println("---")
			return
		}
		y, err := yaml.Marshal(data)
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Println(string(y))

	default:
		if data == nil {
			fmt.Println("No data found")
			return
		}

		// Print app.CLIError
		if cliError, ok := data.(CLIError); ok {
			data := [][]string{
				[]string{cliError.Message},
			}
			header := []string{"CLI Error"}
			printTableResults(data, header)
			return
		}

		// Print apl.APLError
		if aplError, ok := data.(apl.APIError); ok {
			data := [][]string{
				[]string{strconv.Itoa(aplError.StatusCode), aplError.Message},
			}
			header := []string{"Status Code", "Message"}
			printTableResults(data, header)
			return
		}

		// Print apl.CreateResult
		if createResult, ok := data.(apl.CreateResult); ok {
			var result string
			switch createResult.Data.(type) {
			case string:
				result = createResult.Data.(string)
			default:
				j, err := json.MarshalIndent(createResult.Data, "", "  ")
				if err != nil {
					fmt.Println(err)
					return
				}
				result = string(j)
			}
			data := [][]string{[]string{result}}
			header := []string{"Result"}
			printTableResults(data, header)
			return
		}

		// Print apl.ModifyOutput
		if modifyResult, ok := data.(apl.ModifyResult); ok {
			data := [][]string{
				[]string{
					strconv.Itoa(modifyResult.Skipped),
					strconv.Itoa(modifyResult.Deleted),
					strconv.Itoa(modifyResult.Unchanged),
					strconv.Itoa(modifyResult.Errors),
					strconv.Itoa(modifyResult.Replaced),
					strconv.Itoa(modifyResult.Inserted),
				},
			}
			header := []string{"Skipped", "Deleted", "Unchanged", "Errors", "Replaced", "Inserted"}
			printTableResults(data, header)
			return
		}

	}
}

func printTableResults(data [][]string, header []string) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetAutoFormatHeaders(false)
	table.SetHeader(header)

	for _, v := range data {
		table.Append(v)
	}

	table.Render() // Send output
}

// prints out a table
func printTableResultsCustom(results interface{}, fields []string) {

	if results != nil {
		header := make([]string, len(fields))
		output := reflect.ValueOf(results)

		switch reflect.TypeOf(results).Kind() {

		case reflect.Struct:

			dataRow := make([]string, len(fields))
			for i := 0; i < len(fields); i++ {
				key, val := getSubField(fields[i], output)
				header[i] = camelCaseToSpaces(key)
				dataRow[i] = fmt.Sprint(val)
			}
			data := [][]string{dataRow}

			printTableResults(data, header)

		case reflect.Slice:

			data := make([][]string, output.Len())

			for i := 0; i < output.Len(); i++ {
				outputRow := output.Index(i)
				dataRow := make([]string, len(fields))

				for j := 0; j < len(fields); j++ {
					key, val := getSubField(fields[j], outputRow)
					header[j] = camelCaseToSpaces(key)
					dataRow[j] = fmt.Sprint(val)
				}
				data = append(data, dataRow)
			}
			printTableResults(data, header)
		}

	}

}

// split the name up by dot notation and recurse to find the field in question
// Warning, this is recursive
func getSubField(key string, item reflect.Value) (string, reflect.Value) {

	sub := strings.SplitN(key, ".", 2)

	if len(sub) > 1 {
		nextKey := sub[1]
		return getSubField(nextKey, item)
	}

	return key, item.FieldByName(key)
}

// camelCaseToSpaces takes a slice of CamelCaseNames and puts spaces in them Camel Case Names
func camelCaseToSpaces(input string) string {
	return strings.Join(camelcase.Split(input), " ")
}

//func camelCaseSliceToSpaces(input []string) []string {
//	result := make([]string, len(input))
//	for idx, val := range input {
//		result[idx] = camelCaseToSpaces(val)
//	}
//	return result
//}
<|endoftext|>


<commit_before>// Copyright © 2016 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 		http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/spf13/cobra"
)

var keepDir, keepExt, parseJSON bool

// indexCmd represents the index command
var indexCmd = &cobra.Command{
	Use:   "index [index path] [data paths ...]",
	Short: "adds the files to the index",
	Long:  `The index command adds the specified files to the index.`,
	Annotations: map[string]string{
		canMutateBleveIndex: "true",
	},
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) < 2 {
			return fmt.Errorf("must specify at least one path")
		}
		for file := range handleArgs(args[1:]) {
			var doc interface{}
			// index the files
			docID := file.filename
			if !keepDir {
				_, docID = filepath.Split(docID)
			}
			if !keepExt {
				ext := filepath.Ext(docID)
				docID = docID[0 : len(docID)-len(ext)]
			}
			doc = file.contents
			var err error
			if parseJSON {
				err = json.Unmarshal(file.contents, &doc)
				if err != nil {
					return fmt.Errorf("error parsing JSON: %v", err)
				}
			}
			fmt.Printf("Indexing: %s\n", docID)
			err = idx.Index(docID, doc)
			if err != nil {
				return fmt.Errorf("error indexing: %v", err)
			}
		}
		return nil
	},
}

type file struct {
	filename string
	contents []byte
}

func handleArgs(args []string) chan file {
	rv := make(chan file)
	go getAllFiles(args, rv)
	return rv
}

func getAllFiles(args []string, rv chan file) {
	for _, arg := range args {
		arg = filepath.Clean(arg)
		err := filepath.Walk(arg, func(path string, finfo os.FileInfo, err error) error {
			if err != nil {
				log.Print(err)
				return err
			}
			if finfo.IsDir() {
				return nil
			}

			bytes, err := ioutil.ReadFile(path)
			if err != nil {
				log.Fatal(err)
			}
			rv <- file{
				filename: filepath.Base(path),
				contents: bytes,
			}
			return nil
		})
		if err != nil {
			log.Fatal(err)
		}
	}
	close(rv)
}

func init() {
	RootCmd.AddCommand(indexCmd)

	indexCmd.Flags().BoolVarP(&keepDir, "keepDir", "d", false, "Keep the directory in the dodcument id, defaults false.")
	indexCmd.Flags().BoolVarP(&keepExt, "keepExt", "x", false, "Keep the extension in the document id, defaults false.")
	indexCmd.Flags().BoolVarP(&parseJSON, "json", "j", true, "Parse the contents as JSON, defaults true.")
}
<commit_msg>fix minor typo<commit_after>// Copyright © 2016 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 		http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/spf13/cobra"
)

var keepDir, keepExt, parseJSON bool

// indexCmd represents the index command
var indexCmd = &cobra.Command{
	Use:   "index [index path] [data paths ...]",
	Short: "adds the files to the index",
	Long:  `The index command adds the specified files to the index.`,
	Annotations: map[string]string{
		canMutateBleveIndex: "true",
	},
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) < 2 {
			return fmt.Errorf("must specify at least one path")
		}
		for file := range handleArgs(args[1:]) {
			var doc interface{}
			// index the files
			docID := file.filename
			if !keepDir {
				_, docID = filepath.Split(docID)
			}
			if !keepExt {
				ext := filepath.Ext(docID)
				docID = docID[0 : len(docID)-len(ext)]
			}
			doc = file.contents
			var err error
			if parseJSON {
				err = json.Unmarshal(file.contents, &doc)
				if err != nil {
					return fmt.Errorf("error parsing JSON: %v", err)
				}
			}
			fmt.Printf("Indexing: %s\n", docID)
			err = idx.Index(docID, doc)
			if err != nil {
				return fmt.Errorf("error indexing: %v", err)
			}
		}
		return nil
	},
}

type file struct {
	filename string
	contents []byte
}

func handleArgs(args []string) chan file {
	rv := make(chan file)
	go getAllFiles(args, rv)
	return rv
}

func getAllFiles(args []string, rv chan file) {
	for _, arg := range args {
		arg = filepath.Clean(arg)
		err := filepath.Walk(arg, func(path string, finfo os.FileInfo, err error) error {
			if err != nil {
				log.Print(err)
				return err
			}
			if finfo.IsDir() {
				return nil
			}

			bytes, err := ioutil.ReadFile(path)
			if err != nil {
				log.Fatal(err)
			}
			rv <- file{
				filename: filepath.Base(path),
				contents: bytes,
			}
			return nil
		})
		if err != nil {
			log.Fatal(err)
		}
	}
	close(rv)
}

func init() {
	RootCmd.AddCommand(indexCmd)

	indexCmd.Flags().BoolVarP(&keepDir, "keepDir", "d", false, "Keep the directory in the document id, defaults false.")
	indexCmd.Flags().BoolVarP(&keepExt, "keepExt", "x", false, "Keep the extension in the document id, defaults false.")
	indexCmd.Flags().BoolVarP(&parseJSON, "json", "j", true, "Parse the contents as JSON, defaults true.")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/libgit2/git2go"
)

func main() {
	repo, err := git.OpenRepository(".")
	if err != nil {
		fmt.Printf("not a repo: %s\n", err)
		os.Exit(5)
	}

	desc, err := repo.DescribeWorkdir(&git.DescribeOptions{})
	if err != nil {
		fmt.Printf("madness: %s\n", err)
		os.Exit(6)
	}
	fmt.Printf("repo: %s\n", desc)
}
<commit_msg>ಠ_ಠ<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/libgit2/git2go"
)

func main() {
	repo, err := git.OpenRepository(".")
	if err != nil {
		fmt.Printf("not a repo: %s\n", err)
		os.Exit(5)
	}

	reallyಠ_ಠ, err := git.DefaultDescribeOptions()
	if err != nil {
		panic(err) // this is just frankly absurd
	}

	desc, err := repo.DescribeWorkdir(&reallyಠ_ಠ)
	if err != nil {
		fmt.Printf("madness: %s\n", err)
		os.Exit(6)
	}
	fmt.Printf("repo: %s\n", desc)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
)

const (
	// FotomatVersion is updated by git-hooks/pre-commit
	FotomatVersion = "2.4.187"
)

var (
	version = flag.Bool("version", false, "Show version and exit.")
)

func showVersion() {
	if *version {
		fmt.Println("Fotomat v" + FotomatVersion)
		os.Exit(0)
	}
}

func init() {
	post(showVersion)
}
<commit_msg>v2.5.<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
)

const (
	// FotomatVersion is updated by git-hooks/pre-commit
	FotomatVersion = "2.5.188"
)

var (
	version = flag.Bool("version", false, "Show version and exit.")
)

func showVersion() {
	if *version {
		fmt.Println("Fotomat v" + FotomatVersion)
		os.Exit(0)
	}
}

func init() {
	post(showVersion)
}
<|endoftext|>