<commit_before>package main

import (
	// Stdlib
	"bytes"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"

	// Paprika
	"github.com/paprikaci/paprika/data"

	// Cider
	"github.com/cider/go-cider/cider/services/logging"
	"github.com/cider/go-cider/cider/services/pubsub"
	"github.com/cider/go-cider/cider/services/rpc"
	zlogging "github.com/cider/go-cider/cider/transports/zmq3/logging"
	zpubsub "github.com/cider/go-cider/cider/transports/zmq3/pubsub"
	zrpc "github.com/cider/go-cider/cider/transports/zmq3/rpc"

	// Others
	"code.google.com/p/goauth2/oauth"
	"github.com/garyburd/redigo/redis"
	"github.com/google/go-github/github"
	zmq "github.com/pebbe/zmq3"
)

const RedisOutputSequenceKey = "next-build-id"

func main() {
	// Initialise the Logging service.
	logger, err := logging.NewService(func() (logging.Transport, error) {
		factory := zlogging.NewTransportFactory()
		factory.MustReadConfigFromEnv("CIDER_ZMQ3_LOGGING_").MustBeFullyConfigured()
		return factory.NewTransport(os.Getenv("CIDER_ALIAS"))
	})
	if err != nil {
		panic(err)
	}
	defer zmq.Term()
	defer logger.Close()

	if err := innerMain(logger); err != nil {
		panic(err)
	}
}

func innerMain(logger *logging.Service) error {
	// Make sure the the required environment variables are set.
	token := os.Getenv("GITHUB_TOKEN")
	if token == "" {
		return logger.Critical("GITHUB_TOKEN is not set")
	}
	canonicalURL := os.Getenv("CANONICAL_URL")
	if canonicalURL == "" {
		return logger.Critical("CANONICAL_URL is not set")
	}
	if _, err := url.Parse(canonicalURL); err != nil {
		return logger.Critical(err)
	}
	listenAddress := os.Getenv("HTTP_LISTEN")
	if listenAddress == "" {
		return logger.Critical("HTTP_LISTEN is not set")
	}
	redisAddress := os.Getenv("REDIS_ADDRESS")
	if redisAddress == "" {
		return logger.Critical("REDIS_ADDRESS is not set")
	}

	// Connect to Redis.
	redisConn, err := redis.Dial("tcp", redisAddress)
	if err != nil {
		return logger.Critical(err)
	}
	var redisConnMu sync.Mutex

	// Initialise the GitHub client.
	t := oauth.Transport{
		Token: &oauth.Token{AccessToken: token},
	}
	gh := github.NewClient(t.Client())

	// Initialise the PubSub service.
	eventBus, err := pubsub.NewService(func() (pubsub.Transport, error) {
		factory := zpubsub.NewTransportFactory()
		factory.MustReadConfigFromEnv("CIDER_ZMQ3_PUBSUB_").MustBeFullyConfigured()
		return factory.NewTransport(os.Getenv("CIDER_ALIAS"))
	})
	if err != nil {
		return logger.Critical(err)
	}
	defer func() {
		select {
		case <-eventBus.Closed():
			goto Wait
		default:
		}
		if err := eventBus.Close(); err != nil {
			logger.Critical(err)
		}
	Wait:
		if err := eventBus.Wait(); err != nil {
			logger.Critical(err)
		}
	}()

	// Initialise the RPC service.
	executor, err := rpc.NewService(func() (rpc.Transport, error) {
		factory := zrpc.NewTransportFactory()
		factory.MustReadConfigFromEnv("CIDER_ZMQ3_RPC_").MustBeFullyConfigured()
		return factory.NewTransport(os.Getenv("CIDER_ALIAS"))
	})
	if err != nil {
		return logger.Critical(err)
	}
	defer func() {
		select {
		case <-executor.Closed():
			goto Wait
		default:
		}
		if err := executor.Close(); err != nil {
			logger.Critical(err)
		}
	Wait:
		if err := executor.Wait(); err != nil {
			logger.Critical(err)
		}
	}()

	// Start catching signals.
	signalCh := make(chan os.Signal, 1)
	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)

	// Trigger Paprika build on github.pull_request.
	if _, err := eventBus.Subscribe("github.pull_request", func(event pubsub.Event) {
		// Unmarshal the event object. Once into a struct to be accessed directly,
		// once for passing it on in build events.
		var body PullRequestEvent
		if err := event.Unmarshal(&body); err != nil {
			logger.Warn(err)
			return
		}

		// Only continue if the pull request sources were modified.
		pr := body.PullRequest
		if body.Action != "opened" && body.Action != "synchronized" {
			logger.Infof("Skipping the build, pull request %v not updated", pr.HTMLURL)
			return
		}

		logger.Infof("Preparing to build %v", pr.HTMLURL)

		// Unmarshal the whole pull request object as well so that we can later
		// pass it on in the build events.
		var prMap struct {
			PullRequest map[string]interface{} `codec:"pull_request"`
		}
		if err := event.Unmarshal(&prMap); err != nil {
			logger.Warn(err)
			return
		}

		// Emit paprika.build.enqueued at the beginning.
		if len(prMap.PullRequest) == 0 {
			logger.Error("Invalid github.pull_request event")
			return
		}

		logger.Infof("Emitting paprika.build.enqueued for pull request %v", pr.HTMLURL)
		if err := eventBus.Publish("paprika.build.enqueued", &BuildEnqueuedEvent{
			PullRequest: prMap.PullRequest,
		}); err != nil {
			logger.Error(err)
			return
		}

		// Emit paprika.build.finished.{success|failure|error} on return.
		var (
			call       *rpc.RemoteCall
			err        error
			buildError error
			outputKey  int64
		)
		defer func() {
			var result string
			if err != nil {
				result = "error"
			} else {
				switch rc := call.ReturnCode(); {
				case rc == 0:
					result = "success"
				case rc == 1:
					result = "failure"
				default:
					result = "error"
				}
			}

			var outputURL string
			if result != "error" {
				if !strings.HasSuffix(canonicalURL, "/") {
					canonicalURL += "/"
				}
				outputURL = fmt.Sprintf("%vbuild/%v", canonicalURL, outputKey)
			}

			finishedEvent := &BuildFinishedEvent{
				Result:      result,
				PullRequest: prMap.PullRequest,
				OutputURL:   outputURL,
			}
			if buildError != nil {
				finishedEvent.Error = buildError.Error()
			}

			kind := "paprika.build.finished." + result
			logger.Infof("Emitting %v for pull request %v", kind, pr.HTMLURL)
			err = eventBus.Publish(kind, finishedEvent)
			if err != nil {
				logger.Error(err)
				return
			}
		}()

		// Fetch paprika.yml first.
		logger.Debugf("Fetching %v for pull request %v", data.ConfigFileName, pr.HTMLURL)
		head := pr.Head
		opts := &github.RepositoryContentGetOptions{head.SHA}
		content, _, _, err := gh.Repositories.GetContents(head.Owner.Login,
			head.Repository.Name, data.ConfigFileName, opts)
		if err != nil {
			logger.Warn(err)
			buildError = err
			return
		}
		if content == nil {
			err = fmt.Errorf("%v is not a regular file", data.ConfigFileName)
			logger.Info(err)
			buildError = err
			return
		}

		// Decode the config file.
		decodedContent, err := content.Decode()
		if err != nil {
			logger.Warn(err)
			buildError = err
			return
		}

		logger.Debugf("Parsing %v for pull request %v", data.ConfigFileName, pr.HTMLURL)
		config, err := data.ParseConfig(decodedContent)
		if err != nil {
			logger.Info(err)
			buildError = err
			return
		}

		// Generate a build request and dispatch it.
		method, args, err := data.ParseArgs(config.Slave.Label, config.Repository.URL,
			config.Script.Path, config.Script.Runner, config.Script.Env)
		if err != nil {
			logger.Info(err)
			buildError = err
			return
		}

		var output bytes.Buffer
		call = executor.NewRemoteCall(method, args)
		// XXX: output is not thread-safe, but the client is single-threaded
		// right now, so it is fine, but not good to depend on that.
		call.Stdout = &output
		call.Stderr = &output
		logger.Debugf("Dispatching build request for pull request %v", pr.HTMLURL)
		err = call.Execute()
		if err != nil {
			logger.Error(err)
			return
		}
		if rc := call.ReturnCode(); rc > 1 {
			logger.Errorf("Paprika returned an error return code: %v", rc)
			return
		}

		// Save the output.
		logger.Debugf("Saving build output for pull request %v", pr.HTMLURL)
		redisConnMu.Lock()
		err = redisConn.Err()
		if err != nil {
			logger.Error(err)
			redisConn.Close()
			redisConn, err = redis.Dial("tcp", redisAddress)
			if err != nil {
				logger.Error(err)
				return
			}
		}
		redisConnMu.Unlock()

		outputKey, err = redis.Int64(redisConn.Do("INCR", RedisOutputSequenceKey))
		if err != nil {
			logger.Error(err)
			return
		}

		_, err = redisConn.Do("SET", outputKey, output.Bytes())
		if err != nil {
			logger.Error(err)
			return
		}
	}); err != nil {
		return logger.Critical(err)
	}

	// Set up the HTTP server that serves the build output.
	http.HandleFunc("/build/", func(w http.ResponseWriter, r *http.Request) {
		// Parse the URL parameter, which is the build ID.
		fragments := strings.Split(r.URL.Path, "/")
		if len(fragments) != 3 || fragments[2] == "" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		key, err := strconv.ParseInt(fragments[2], 10, 64)
		if err != nil {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}

		// Fetch the output from the database.
		redisConnMu.Lock()
		defer redisConnMu.Unlock()

		err = redisConn.Err()
		if err != nil {
			logger.Error(err)
			redisConn.Close()
			redisConn, err = redis.Dial("tcp", redisAddress)
			if err != nil {
				logger.Error(err)
				http.Error(w, "Internal Server Error", http.StatusInternalServerError)
				return
			}
		}

		v, err := redisConn.Do("GET", key)
		if err != nil {
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
			return
		}

		output := v.([]byte)
		if output == nil {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}

		// Write the output to the response.
		w.Header().Set("Content-Type", "text/plain")
		io.Copy(w, bytes.NewReader(output))
	})

	// Go listening for incoming HTTP requests.
	serverErrCh := make(chan error, 1)
	go func() {
		serverErrCh <- http.ListenAndServe(listenAddress, nil)
	}()

	// Start processing signals, block until crashed or terminated.
	select {
	case err := <-serverErrCh:
		return logger.Critical(err)
	case <-eventBus.Closed():
		return eventBus.Wait()
	case <-executor.Closed():
		return executor.Wait()
	case <-signalCh:
	}
	return nil
}
<commit_msg>Rename Cider to Meeko and refactor<commit_after>// Copyright (c) 2013-2014 The cider-github-builder AUTHORS
//
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package main

import (
	// Stdlib
	"bytes"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"

	// Cider
	"github.com/cider/cider/data"

	// Meeko
	"github.com/meeko/go-meeko/agent"
	"github.com/meeko/go-meeko/meeko/services/pubsub"
	"github.com/meeko/go-meeko/meeko/services/rpc"

	// Others
	"code.google.com/p/goauth2/oauth"
	"github.com/garyburd/redigo/redis"
	"github.com/google/go-github/github"
)

const RedisOutputSequenceKey = "next-build-id"

func main() {
	// Make sure the Meeko agent is terminated properly.
	defer agent.Terminate()

	// Run the main function.
	if err := run(); err != nil {
		os.Exit(1)
	}
}

func run() error {
	// Some userful shortcuts.
	var (
		log      = agent.Logging
		eventBus = agent.PubSub
		executor = agent.RPC
	)

	// Parse the environment and make sure all the environment variables are set.
	token := os.Getenv("GITHUB_TOKEN")
	if token == "" {
		return log.Critical("GITHUB_TOKEN is not set")
	}
	canonicalURL := os.Getenv("CANONICAL_URL")
	if canonicalURL == "" {
		return log.Critical("CANONICAL_URL is not set")
	}
	if _, err := url.Parse(canonicalURL); err != nil {
		return log.Critical(err)
	}
	listenAddress := os.Getenv("HTTP_LISTEN")
	if listenAddress == "" {
		return log.Critical("HTTP_LISTEN is not set")
	}
	redisAddress := os.Getenv("REDIS_ADDRESS")
	if redisAddress == "" {
		return log.Critical("REDIS_ADDRESS is not set")
	}

	// Connect to Redis.
	redisConn, err := redis.Dial("tcp", redisAddress)
	if err != nil {
		return log.Critical(err)
	}
	var redisConnMu sync.Mutex

	// Initialise the GitHub client.
	t := oauth.Transport{
		Token: &oauth.Token{AccessToken: token},
	}
	gh := github.NewClient(t.Client())

	// Start catching signals.
	signalCh := make(chan os.Signal, 1)
	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)

	// Trigger Cider build on github.pull_request.
	if _, err := eventBus.Subscribe("github.pull_request", func(event pubsub.Event) {
		// Unmarshal the event object. Once into a struct to be accessed directly,
		// once for passing it on in build events.
		var body PullRequestEvent
		if err := event.Unmarshal(&body); err != nil {
			log.Warn(err)
			return
		}

		// Only continue if the pull request sources were modified.
		pr := body.PullRequest
		if body.Action != "opened" && body.Action != "synchronized" {
			log.Infof("Skipping the build, pull request %v not updated", pr.HTMLURL)
			return
		}

		log.Infof("Preparing to build %v", pr.HTMLURL)

		// Unmarshal the whole pull request object as well so that we can later
		// pass it on in the build events.
		var prMap struct {
			PullRequest map[string]interface{} `codec:"pull_request"`
		}
		if err := event.Unmarshal(&prMap); err != nil {
			log.Warn(err)
			return
		}

		// Emit cider.build.enqueued at the beginning.
		if len(prMap.PullRequest) == 0 {
			log.Error("Invalid github.pull_request event")
			return
		}

		log.Infof("Emitting cider.build.enqueued for pull request %v", pr.HTMLURL)
		if err := eventBus.Publish("cider.build.enqueued", &BuildEnqueuedEvent{
			PullRequest: prMap.PullRequest,
		}); err != nil {
			log.Error(err)
			return
		}

		// Emit cider.build.finished.{success|failure|error} on return.
		var (
			call       *rpc.RemoteCall
			err        error
			buildError error
			outputKey  int64
		)
		defer func() {
			var result string
			if err != nil {
				result = "error"
			} else {
				switch rc := call.ReturnCode(); {
				case rc == 0:
					result = "success"
				case rc == 1:
					result = "failure"
				default:
					result = "error"
				}
			}

			var outputURL string
			if result != "error" {
				if !strings.HasSuffix(canonicalURL, "/") {
					canonicalURL += "/"
				}
				outputURL = fmt.Sprintf("%vbuild/%v", canonicalURL, outputKey)
			}

			finishedEvent := &BuildFinishedEvent{
				Result:      result,
				PullRequest: prMap.PullRequest,
				OutputURL:   outputURL,
			}
			if buildError != nil {
				finishedEvent.Error = buildError.Error()
			}

			kind := "cider.build.finished." + result
			log.Infof("Emitting %v for pull request %v", kind, pr.HTMLURL)
			err = eventBus.Publish(kind, finishedEvent)
			if err != nil {
				log.Error(err)
				return
			}
		}()

		// Fetch cider.yml first.
		log.Debugf("Fetching %v for pull request %v", data.ConfigFileName, pr.HTMLURL)
		head := pr.Head
		opts := &github.RepositoryContentGetOptions{head.SHA}
		content, _, _, err := gh.Repositories.GetContents(head.Owner.Login,
			head.Repository.Name, data.ConfigFileName, opts)
		if err != nil {
			log.Warn(err)
			buildError = err
			return
		}
		if content == nil {
			err = fmt.Errorf("%v is not a regular file", data.ConfigFileName)
			log.Info(err)
			buildError = err
			return
		}

		// Decode the config file.
		decodedContent, err := content.Decode()
		if err != nil {
			log.Warn(err)
			buildError = err
			return
		}

		log.Debugf("Parsing %v for pull request %v", data.ConfigFileName, pr.HTMLURL)
		config, err := data.ParseConfig(decodedContent)
		if err != nil {
			log.Info(err)
			buildError = err
			return
		}

		// Generate a build request and dispatch it.
		method, args, err := data.ParseArgs(config.Slave.Label, config.Repository.URL,
			config.Script.Path, config.Script.Runner, config.Script.Env)
		if err != nil {
			log.Info(err)
			buildError = err
			return
		}

		var output bytes.Buffer
		call = executor.NewRemoteCall(method, args)
		// XXX: output is not thread-safe, but the client is single-threaded
		// right now, so it is fine, but not good to depend on that.
		call.Stdout = &output
		call.Stderr = &output
		log.Debugf("Dispatching build request for pull request %v", pr.HTMLURL)
		err = call.Execute()
		if err != nil {
			log.Error(err)
			return
		}
		if rc := call.ReturnCode(); rc > 1 {
			log.Errorf("Cider returned an error return code: %v", rc)
			return
		}

		// Save the output.
		log.Debugf("Saving build output for pull request %v", pr.HTMLURL)
		redisConnMu.Lock()
		err = redisConn.Err()
		if err != nil {
			log.Error(err)
			redisConn.Close()
			redisConn, err = redis.Dial("tcp", redisAddress)
			if err != nil {
				log.Error(err)
				return
			}
		}
		redisConnMu.Unlock()

		outputKey, err = redis.Int64(redisConn.Do("INCR", RedisOutputSequenceKey))
		if err != nil {
			log.Error(err)
			return
		}

		_, err = redisConn.Do("SET", outputKey, output.Bytes())
		if err != nil {
			log.Error(err)
			return
		}
	}); err != nil {
		return log.Critical(err)
	}

	// Set up the HTTP server that serves the build output.
	http.HandleFunc("/build/", func(w http.ResponseWriter, r *http.Request) {
		// Parse the URL parameter, which is the build ID.
		fragments := strings.Split(r.URL.Path, "/")
		if len(fragments) != 3 || fragments[2] == "" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		key, err := strconv.ParseInt(fragments[2], 10, 64)
		if err != nil {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}

		// Fetch the output from the database.
		redisConnMu.Lock()
		defer redisConnMu.Unlock()

		err = redisConn.Err()
		if err != nil {
			log.Error(err)
			redisConn.Close()
			redisConn, err = redis.Dial("tcp", redisAddress)
			if err != nil {
				log.Error(err)
				http.Error(w, "Internal Server Error", http.StatusInternalServerError)
				return
			}
		}

		v, err := redisConn.Do("GET", key)
		if err != nil {
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
			return
		}

		output := v.([]byte)
		if output == nil {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}

		// Write the output to the response.
		w.Header().Set("Content-Type", "text/plain")
		io.Copy(w, bytes.NewReader(output))
	})

	// Go listening for incoming HTTP requests.
	serverErrCh := make(chan error, 1)
	go func() {
		serverErrCh <- http.ListenAndServe(listenAddress, nil)
	}()

	// Start processing signals, block until crashed or terminated.
	select {
	case err := <-serverErrCh:
		return log.Critical(err)
	case <-signalCh:
		log.Info("Signal received, exiting...")
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"context"
	"errors"
	"flag"
	"fmt"
	"os"
	"time"
)

var (
	ErrGo = errors.New("go")
)

func main() {
	var (
		defaultTime = 3 * time.Second
		moveTime    = flag.Duration("time", 0, fmt.Sprintf("computer's time per move (default %v if no depth limit set)", defaultTime))
		depth       = flag.Int("depth", 0, "the search depth")
		fen         = flag.String("fen", InitialPositionFEN, "the FEN record of the starting position")
		humanWhite  = flag.Bool("w", false, "user plays White")
		humanBlack  = flag.Bool("b", false, "user plays Black")
	)
	flag.Parse()
	if *moveTime <= 0 {
		*moveTime = 86164091 * time.Millisecond
		if *depth <= 0 {
			*moveTime = defaultTime
		}
	}
	if *depth <= 0 {
		*depth = 100
	}

	pos, err := ParseFEN(*fen)
	if err != nil {
		panic(err)
	}

	stdin := bufio.NewScanner(os.Stdin)
	players := []Player{Computer{*moveTime, *depth}, Computer{*moveTime, *depth}}
	if *humanWhite {
		players[White] = Human{stdin}
	}
	if *humanBlack {
		players[Black] = Human{stdin}
	}

	startTime := time.Now()
	var movesText string
	posZobrists := make(map[Zobrist]int)

	for {
		moveTime := time.Now()

		score, move := players[pos.ToMove].Play(pos)
		if move == (Move{}) {
			// player resigns
			switch pos.ToMove {
			case White:
				movesText += "0-1"
			case Black:
				movesText += "1-0"
			}
			break
		}

		alg := Algebraic(pos, move)
		movenum := fmt.Sprintf("%v.", pos.FullMove)
		switch pos.ToMove {
		case White:
			movesText += movenum
		case Black:
			movenum += ".."
		}
		movesText += alg + " "

		pos = Make(pos, move)
		if s, ok := score.err.(checkmateError); ok {
			score = Abs{err: s.Next()}
		}

		fmt.Printf("%v%v %v %v\n", movenum, alg, score, time.Since(moveTime).Truncate(time.Millisecond))
		fmt.Println(pos)

		// Check for end-of-game conditions
		if IsTerminal(pos) {
			if IsCheck(pos) {
				if pos.ToMove == White {
					movesText += "0-1"
				} else {
					movesText += "1-0"
				}
				break
			}
			movesText += "1/2-1/2"
			break
		}
		if s := Eval(pos); s.err == errInsufficient {
			movesText += "1/2-1/2"
			break
		}
		if posZobrists[pos.z]++; posZobrists[pos.z] == 3 {
			// threefold repetition
			movesText += "1/2-1/2"
			break
		}
		if pos.HalfMove == 100 {
			// fifty-move rule
			movesText += "1/2-1/2"
			break
		}
	}

	fmt.Println(movesText)
	fmt.Println(time.Since(startTime).Truncate(time.Millisecond))
}

type Player interface {
	// Returning the zero value Move{} indicates resignation.
	Play(Position) (Abs, Move)
}

type Computer struct {
	moveTime time.Duration
	depth    int
}

func (c Computer) Play(pos Position) (Abs, Move) {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, c.moveTime)
	defer cancel()

	results := SearchPosition(ctx, pos, c.depth)
	fmt.Println(results)
	return results[0].score, results[0].move
}

type Human struct{ s *bufio.Scanner }

func (h Human) Play(pos Position) (Abs, Move) {
	ch := make(chan Results)
	// Wait for SearchPosition to return
	defer func() { <-ch }()

	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	go func() {
		results := SearchPosition(ctx, pos, 100)
		ch <- results
		close(ch)
	}()

	for {
		m, err := h.readMove(pos)
		if err == ErrGo {
			cancel()
			results := <-ch
			return results[0].score, results[0].move
		}
		if err != nil {
			fmt.Println(err)
			continue
		}
		// get engine's opinion of Human's move
		cancel()
		results := <-ch
		for _, r := range results {
			if r.move == m {
				return r.score, m
			}
		}
		return Abs{}, m
	}
}

func (h Human) readMove(pos Position) (m Move, err error) {
	fmt.Printf("> ")
	if ok := h.s.Scan(); !ok {
		return m, h.s.Err()
	}
	text := h.s.Text()
	var promote Piece
	switch {
	case text == "resign":
		return
	case text == "go":
		return m, ErrGo
	case len(text) == 5:
		switch text[4:] {
		case "q":
			promote = Queen
			text = text[:4]
		case "r":
			promote = Rook
			text = text[:4]
		case "b":
			promote = Bishop
			text = text[:4]
		case "n":
			promote = Knight
			text = text[:4]
		}
	}
	from, to, err := ParseUserMove(text)
	if err != nil {
		return m, err
	}
	c, p := pos.PieceOn(from)
	if p == None {
		return m, fmt.Errorf("No piece on square %v", from)
	}
	if c != pos.ToMove {
		return m, fmt.Errorf("%v piece on square %v", c, from)
	}
	cc, cp := pos.PieceOn(to)
	if cp != None && cc == c {
		return m, fmt.Errorf("%v piece on square %v", cc, to)
	}
	if promote != None && (p != Pawn || (pos.ToMove == White && to.Rank() != 7) || (pos.ToMove == Black && to.Rank() != 0)) {
		return m, fmt.Errorf("illegal promotion")
	}
	var cs Square
	if cp != None {
		cs = to
	} // TODO: en passant
	m = Move{From: from, To: to, Piece: p, CapturePiece: cp, CaptureSquare: cs, PromotePiece: promote}
	if !IsPseudoLegal(pos, m) || !IsLegal(Make(pos, m)) {
		return m, fmt.Errorf("illegal move")
	}
	return m, nil
}
<commit_msg>check score error in game loop<commit_after>package main

import (
	"bufio"
	"context"
	"errors"
	"flag"
	"fmt"
	"os"
	"time"
)

var (
	ErrGo = errors.New("go")
)

func main() {
	var (
		defaultTime = 3 * time.Second
		moveTime    = flag.Duration("time", 0, fmt.Sprintf("computer's time per move (default %v if no depth limit set)", defaultTime))
		depth       = flag.Int("depth", 0, "the search depth")
		fen         = flag.String("fen", InitialPositionFEN, "the FEN record of the starting position")
		humanWhite  = flag.Bool("w", false, "user plays White")
		humanBlack  = flag.Bool("b", false, "user plays Black")
	)
	flag.Parse()
	if *moveTime <= 0 {
		*moveTime = 86164091 * time.Millisecond
		if *depth <= 0 {
			*moveTime = defaultTime
		}
	}
	if *depth <= 0 {
		*depth = 100
	}

	pos, err := ParseFEN(*fen)
	if err != nil {
		panic(err)
	}

	stdin := bufio.NewScanner(os.Stdin)
	players := []Player{Computer{*moveTime, *depth}, Computer{*moveTime, *depth}}
	if *humanWhite {
		players[White] = Human{stdin}
	}
	if *humanBlack {
		players[Black] = Human{stdin}
	}

	startTime := time.Now()
	var movesText string
	posZobrists := make(map[Zobrist]int)

game:
	for {
		moveTime := time.Now()

		score, move := players[pos.ToMove].Play(pos)
		if move == (Move{}) {
			// player resigns
			switch pos.ToMove {
			case White:
				movesText += "0-1"
			case Black:
				movesText += "1-0"
			}
			break
		}

		alg := Algebraic(pos, move)
		movenum := fmt.Sprintf("%v.", pos.FullMove)
		switch pos.ToMove {
		case White:
			movesText += movenum
		case Black:
			movenum += ".."
		}
		movesText += alg + " "

		pos = Make(pos, move)
		if s, ok := score.err.(checkmateError); ok {
			score = Abs{err: s.Next()}
		}

		fmt.Printf("%v%v %v %v\n", movenum, alg, score, time.Since(moveTime).Truncate(time.Millisecond))
		fmt.Println(pos)

		// Check for end-of-game conditions
		switch score.err {
		case errCheckmate:
			if pos.ToMove == White {
				movesText += "0-1"
			} else {
				movesText += "1-0"
			}
			break game
		case errStalemate, errInsufficient, errFiftyMove:
			movesText += "1/2-1/2"
			break game
		}
		if posZobrists[pos.z]++; posZobrists[pos.z] == 3 {
			// threefold repetition
			movesText += "1/2-1/2"
			break
		}
	}

	fmt.Println(movesText)
	fmt.Println(time.Since(startTime).Truncate(time.Millisecond))
}

type Player interface {
	// Returning the zero value Move{} indicates resignation.
	Play(Position) (Abs, Move)
}

type Computer struct {
	moveTime time.Duration
	depth    int
}

func (c Computer) Play(pos Position) (Abs, Move) {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, c.moveTime)
	defer cancel()

	results := SearchPosition(ctx, pos, c.depth)
	fmt.Println(results)
	return results[0].score, results[0].move
}

type Human struct{ s *bufio.Scanner }

func (h Human) Play(pos Position) (Abs, Move) {
	ch := make(chan Results)
	// Wait for SearchPosition to return
	defer func() { <-ch }()

	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	go func() {
		results := SearchPosition(ctx, pos, 100)
		ch <- results
		close(ch)
	}()

	for {
		m, err := h.readMove(pos)
		if err == ErrGo {
			cancel()
			results := <-ch
			return results[0].score, results[0].move
		}
		if err != nil {
			fmt.Println(err)
			continue
		}
		// get engine's opinion of Human's move
		cancel()
		results := <-ch
		for _, r := range results {
			if r.move == m {
				return r.score, m
			}
		}
		return Abs{}, m
	}
}

func (h Human) readMove(pos Position) (m Move, err error) {
	fmt.Printf("> ")
	if ok := h.s.Scan(); !ok {
		return m, h.s.Err()
	}
	text := h.s.Text()
	var promote Piece
	switch {
	case text == "resign":
		return
	case text == "go":
		return m, ErrGo
	case len(text) == 5:
		switch text[4:] {
		case "q":
			promote = Queen
			text = text[:4]
		case "r":
			promote = Rook
			text = text[:4]
		case "b":
			promote = Bishop
			text = text[:4]
		case "n":
			promote = Knight
			text = text[:4]
		}
	}
	from, to, err := ParseUserMove(text)
	if err != nil {
		return m, err
	}
	c, p := pos.PieceOn(from)
	if p == None {
		return m, fmt.Errorf("No piece on square %v", from)
	}
	if c != pos.ToMove {
		return m, fmt.Errorf("%v piece on square %v", c, from)
	}
	cc, cp := pos.PieceOn(to)
	if cp != None && cc == c {
		return m, fmt.Errorf("%v piece on square %v", cc, to)
	}
	if promote != None && (p != Pawn || (pos.ToMove == White && to.Rank() != 7) || (pos.ToMove == Black && to.Rank() != 0)) {
		return m, fmt.Errorf("illegal promotion")
	}
	var cs Square
	if cp != None {
		cs = to
	} // TODO: en passant
	m = Move{From: from, To: to, Piece: p, CapturePiece: cp, CaptureSquare: cs, PromotePiece: promote}
	if !IsPseudoLegal(pos, m) || !IsLegal(Make(pos, m)) {
		return m, fmt.Errorf("illegal move")
	}
	return m, nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"io/ioutil"
	"os"

	"github.com/mithrandie/csvq/lib/action"
	"github.com/mithrandie/csvq/lib/cmd"
	"github.com/mithrandie/csvq/lib/query"

	"github.com/urfave/cli"
)

var version = "v0.7.6"

func main() {
	cli.AppHelpTemplate = appHHelpTemplate
	cli.CommandHelpTemplate = commandHelpTemplate

	app := cli.NewApp()

	app.Name = "csvq"
	app.Usage = "SQL like query language for csv"
	app.ArgsUsage = "[\"query\"|\"statements\"|argument]"
	app.Version = version

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "delimiter, d",
			Usage: "field delimiter. Default is \",\" for csv files, \"\\t\" for tsv files.",
		},
		cli.StringFlag{
			Name:  "encoding, e",
			Value: "UTF8",
			Usage: "file encoding. one of: UTF8|SJIS",
		},
		cli.StringFlag{
			Name:  "line-break, l",
			Value: "LF",
			Usage: "line break. one of: CRLF|LF|CR",
		},
		cli.StringFlag{
			Name:  "timezone, z",
			Value: "Local",
			Usage: "default timezone. \"Local\", \"UTC\" or a timezone name(e.g. \"America/Los_Angeles\")",
		},
		cli.StringFlag{
			Name:  "repository, r",
			Usage: "directory path where files are located",
		},
		cli.StringFlag{
			Name:  "source, s",
			Usage: "load query from `FILE`",
		},
		cli.StringFlag{
			Name:  "datetime-format, t",
			Usage: "set datetime format to parse strings",
		},
		cli.BoolFlag{
			Name:  "no-header, n",
			Usage: "import the first line as a record",
		},
		cli.BoolFlag{
			Name:  "without-null, a",
			Usage: "parse empty fields as empty strings",
		},
		cli.StringFlag{
			Name:  "write-encoding, E",
			Value: "UTF8",
			Usage: "file encoding. one of: UTF8|SJIS",
		},
		cli.StringFlag{
			Name:  "out, o",
			Usage: "write output to `FILE`",
		},
		cli.StringFlag{
			Name:  "format, f",
			Usage: "output format. one of: CSV|TSV|JSON|TEXT",
		},
		cli.StringFlag{
			Name:  "write-delimiter, D",
			Usage: "field delimiter for CSV",
		},
		cli.BoolFlag{
			Name:  "without-header, N",
			Usage: "when the file format is specified as CSV or TSV, write without the header line",
		},
		cli.BoolFlag{
			Name:  "quiet, q",
			Usage: "suppress operation log output",
		},
		cli.IntFlag{
			Name:  "cpu, p",
			Usage: "hint for the number of cpu cores to be used. 1 - number of cpu cores",
		},
		cli.BoolFlag{
			Name:  "stats, x",
			Usage: "show execution time and memory statistics",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:      "fields",
			Usage:     "Show fields in a file",
			ArgsUsage: "CSV_FILE_PATH",
			Action: func(c *cli.Context) error {
				if c.NArg() != 1 {
					cli.ShowCommandHelp(c, "fields")
					return cli.NewExitError("table is not specified", 1)
				}

				table := c.Args().First()

				err := action.ShowFields(table)
				if err != nil {
					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
		{
			Name:      "calc",
			Usage:     "Calculate a value from stdin",
			ArgsUsage: "\"expression\"",
			Action: func(c *cli.Context) error {
				if c.NArg() != 1 {
					cli.ShowCommandHelp(c, "calc")
					return cli.NewExitError("expression is empty", 1)
				}

				expr := c.Args().First()
				err := action.Calc(expr)
				if err != nil {
					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
	}

	app.Before = func(c *cli.Context) error {
		return setFlags(c)
	}

	app.Action = func(c *cli.Context) error {
		queryString, err := readQuery(c)
		if err != nil {
			cli.ShowAppHelp(c)
			return cli.NewExitError(err.Error(), 1)
		}

		if err = action.Run(queryString, cmd.GetFlags().Source); err != nil {
			code := 1
			if apperr, ok := err.(query.AppError); ok {
				code = apperr.GetCode()
			} else if ex, ok := err.(*query.Exit); ok {
				code = ex.GetCode()
			}
			return cli.NewExitError(err.Error(), code)
		}

		return nil
	}

	app.Run(os.Args)
}

func readQuery(c *cli.Context) (string, error) {
	var queryString string

	flags := cmd.GetFlags()
	if 0 < len(flags.Source) {
		fp, err := os.Open(flags.Source)
		if err != nil {
			return queryString, err
		}
		defer fp.Close()

		buf, err := ioutil.ReadAll(fp)
		if err != nil {
			return queryString, err
		}
		queryString = string(buf)

	} else {
		if c.NArg() != 1 {
			return queryString, errors.New("query is empty")
		}
		queryString = c.Args().First()
	}

	return queryString, nil
}

func setFlags(c *cli.Context) error {
	if err := cmd.SetDelimiter(c.GlobalString("delimiter")); err != nil {
		return err
	}
	if err := cmd.SetEncoding(c.GlobalString("encoding")); err != nil {
		return err
	}
	if err := cmd.SetLineBreak(c.String("line-break")); err != nil {
		return err
	}
	if err := cmd.SetLocation(c.String("timezone")); err != nil {
		return err
	}
	if err := cmd.SetRepository(c.GlobalString("repository")); err != nil {
		return err
	}
	if err := cmd.SetSource(c.GlobalString("source")); err != nil {
		return err
	}
	cmd.SetDatetimeFormat(c.GlobalString("datetime-format"))
	cmd.SetNoHeader(c.GlobalBool("no-header"))
	cmd.SetWithoutNull(c.GlobalBool("without-null"))

	if err := cmd.SetWriteEncoding(c.GlobalString("write-encoding")); err != nil {
		return err
	}
	if err := cmd.SetOut(c.GlobalString("out")); err != nil {
		return err
	}
	if err := cmd.SetFormat(c.GlobalString("format")); err != nil {
		return err
	}
	if err := cmd.SetWriteDelimiter(c.GlobalString("write-delimiter")); err != nil {
		return err
	}
	cmd.SetWithoutHeader(c.GlobalBool("without-header"))

	cmd.SetQuiet(c.GlobalBool("quiet"))
	cmd.SetCPU(c.GlobalInt("cpu"))
	cmd.SetStats(c.GlobalBool("stats"))

	return nil
}
<commit_msg>Update version for Release v0.7.7<commit_after>package main

import (
	"errors"
	"io/ioutil"
	"os"

	"github.com/mithrandie/csvq/lib/action"
	"github.com/mithrandie/csvq/lib/cmd"
	"github.com/mithrandie/csvq/lib/query"

	"github.com/urfave/cli"
)

var version = "v0.7.7"

func main() {
	cli.AppHelpTemplate = appHHelpTemplate
	cli.CommandHelpTemplate = commandHelpTemplate

	app := cli.NewApp()

	app.Name = "csvq"
	app.Usage = "SQL like query language for csv"
	app.ArgsUsage = "[\"query\"|\"statements\"|argument]"
	app.Version = version

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "delimiter, d",
			Usage: "field delimiter. Default is \",\" for csv files, \"\\t\" for tsv files.",
		},
		cli.StringFlag{
			Name:  "encoding, e",
			Value: "UTF8",
			Usage: "file encoding. one of: UTF8|SJIS",
		},
		cli.StringFlag{
			Name:  "line-break, l",
			Value: "LF",
			Usage: "line break. one of: CRLF|LF|CR",
		},
		cli.StringFlag{
			Name:  "timezone, z",
			Value: "Local",
			Usage: "default timezone. \"Local\", \"UTC\" or a timezone name(e.g. \"America/Los_Angeles\")",
		},
		cli.StringFlag{
			Name:  "repository, r",
			Usage: "directory path where files are located",
		},
		cli.StringFlag{
			Name:  "source, s",
			Usage: "load query from `FILE`",
		},
		cli.StringFlag{
			Name:  "datetime-format, t",
			Usage: "set datetime format to parse strings",
		},
		cli.BoolFlag{
			Name:  "no-header, n",
			Usage: "import the first line as a record",
		},
		cli.BoolFlag{
			Name:  "without-null, a",
			Usage: "parse empty fields as empty strings",
		},
		cli.StringFlag{
			Name:  "write-encoding, E",
			Value: "UTF8",
			Usage: "file encoding. one of: UTF8|SJIS",
		},
		cli.StringFlag{
			Name:  "out, o",
			Usage: "write output to `FILE`",
		},
		cli.StringFlag{
			Name:  "format, f",
			Usage: "output format. one of: CSV|TSV|JSON|TEXT",
		},
		cli.StringFlag{
			Name:  "write-delimiter, D",
			Usage: "field delimiter for CSV",
		},
		cli.BoolFlag{
			Name:  "without-header, N",
			Usage: "when the file format is specified as CSV or TSV, write without the header line",
		},
		cli.BoolFlag{
			Name:  "quiet, q",
			Usage: "suppress operation log output",
		},
		cli.IntFlag{
			Name:  "cpu, p",
			Usage: "hint for the number of cpu cores to be used. 1 - number of cpu cores",
		},
		cli.BoolFlag{
			Name:  "stats, x",
			Usage: "show execution time and memory statistics",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:      "fields",
			Usage:     "Show fields in a file",
			ArgsUsage: "CSV_FILE_PATH",
			Action: func(c *cli.Context) error {
				if c.NArg() != 1 {
					cli.ShowCommandHelp(c, "fields")
					return cli.NewExitError("table is not specified", 1)
				}

				table := c.Args().First()

				err := action.ShowFields(table)
				if err != nil {
					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
		{
			Name:      "calc",
			Usage:     "Calculate a value from stdin",
			ArgsUsage: "\"expression\"",
			Action: func(c *cli.Context) error {
				if c.NArg() != 1 {
					cli.ShowCommandHelp(c, "calc")
					return cli.NewExitError("expression is empty", 1)
				}

				expr := c.Args().First()
				err := action.Calc(expr)
				if err != nil {
					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
	}

	app.Before = func(c *cli.Context) error {
		return setFlags(c)
	}

	app.Action = func(c *cli.Context) error {
		queryString, err := readQuery(c)
		if err != nil {
			cli.ShowAppHelp(c)
			return cli.NewExitError(err.Error(), 1)
		}

		if err = action.Run(queryString, cmd.GetFlags().Source); err != nil {
			code := 1
			if apperr, ok := err.(query.AppError); ok {
				code = apperr.GetCode()
			} else if ex, ok := err.(*query.Exit); ok {
				code = ex.GetCode()
			}
			return cli.NewExitError(err.Error(), code)
		}

		return nil
	}

	app.Run(os.Args)
}

func readQuery(c *cli.Context) (string, error) {
	var queryString string

	flags := cmd.GetFlags()
	if 0 < len(flags.Source) {
		fp, err := os.Open(flags.Source)
		if err != nil {
			return queryString, err
		}
		defer fp.Close()

		buf, err := ioutil.ReadAll(fp)
		if err != nil {
			return queryString, err
		}
		queryString = string(buf)

	} else {
		if c.NArg() != 1 {
			return queryString, errors.New("query is empty")
		}
		queryString = c.Args().First()
	}

	return queryString, nil
}

func setFlags(c *cli.Context) error {
	if err := cmd.SetDelimiter(c.GlobalString("delimiter")); err != nil {
		return err
	}
	if err := cmd.SetEncoding(c.GlobalString("encoding")); err != nil {
		return err
	}
	if err := cmd.SetLineBreak(c.String("line-break")); err != nil {
		return err
	}
	if err := cmd.SetLocation(c.String("timezone")); err != nil {
		return err
	}
	if err := cmd.SetRepository(c.GlobalString("repository")); err != nil {
		return err
	}
	if err := cmd.SetSource(c.GlobalString("source")); err != nil {
		return err
	}
	cmd.SetDatetimeFormat(c.GlobalString("datetime-format"))
	cmd.SetNoHeader(c.GlobalBool("no-header"))
	cmd.SetWithoutNull(c.GlobalBool("without-null"))

	if err := cmd.SetWriteEncoding(c.GlobalString("write-encoding")); err != nil {
		return err
	}
	if err := cmd.SetOut(c.GlobalString("out")); err != nil {
		return err
	}
	if err := cmd.SetFormat(c.GlobalString("format")); err != nil {
		return err
	}
	if err := cmd.SetWriteDelimiter(c.GlobalString("write-delimiter")); err != nil {
		return err
	}
	cmd.SetWithoutHeader(c.GlobalBool("without-header"))

	cmd.SetQuiet(c.GlobalBool("quiet"))
	cmd.SetCPU(c.GlobalInt("cpu"))
	cmd.SetStats(c.GlobalBool("stats"))

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
)

func main() {
	host := "http://marathon.ocean"
	marathon := NewMarathon(host)
	config, err := haproxyConfig(marathon)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(config)
}

func haproxyConfigHeader() string {
	header := `global
  daemon
  maxconn 4096

defaults
  log                 global
  retries             3
  maxconn             1024
  timeout connect     5s
  timeout client      60s
  timeout server      60s
  timeout client-fin  60s
  timeout tunnel      12h

`
	return header
}

func haproxyConfig(marathon Marathon) (string, error) {
	tasksResp, err := GetTasks(marathon)
	if err != nil {
		return "", err
	}
	// make a map from appId to slice of tasks in that app
	appMap := make(map[string][]Task)
	for _, task := range tasksResp.Tasks {
		slice, ok := appMap[task.EscapedAppId()]
		if !ok {
			slice = make([]Task, 0)
		}
		appMap[task.EscapedAppId()] = append(slice, task)
	}

	// buffer containing the haproxy config
	var config bytes.Buffer

	config.WriteString(haproxyConfigHeader())

	// FIXME this is wrong in the sense that it doesnt account for multiple service ports
	for appId, tasks := range appMap {
		lines := make([]string, 0)

		i := 0
		for _, task := range tasks {
			if !task.IsAlive() {
				continue
			}
			line, err := task.ServerLine(0, i)
			if err != nil {
				continue
			}
			lines = append(lines, line)
		}

		if len(lines) > 0 {
			// put service header in config
			servicePort := tasks[0].ServicePorts[0]
			config.WriteString(fmt.Sprintf("listen %s-%d\n", appId, servicePort))
			config.WriteString(fmt.Sprintf("  bind 0.0.0.0:%d\n", servicePort))
			config.WriteString("  mode tcp\n  option tcplog\n  balance leastconn\n")

			// put each server line in config
			for _, line := range lines {
				config.WriteString("  " + line + "\n")
			}
			config.WriteString("\n")
		}
	}

	// output config
	return config.String(), nil
}
<commit_msg>add haproxy stats to header<commit_after>package main

import (
	"bytes"
	"fmt"
)

func main() {
	host := "http://marathon.ocean"
	marathon := NewMarathon(host)
	config, err := haproxyConfig(marathon)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(config)
}

func haproxyConfigHeader() string {
	header := `global
  daemon
  maxconn 4096

defaults
  log                 global
  retries             3
  maxconn             1024
  timeout connect     5s
  timeout client      60s
  timeout server      60s
  timeout client-fin  60s
  timeout tunnel      12h

listen stats :9090
    mode http
    stats enable
    stats realm HAProxy\ Statistics
    stats uri /

`
	return header
}

func haproxyConfig(marathon Marathon) (string, error) {
	tasksResp, err := GetTasks(marathon)
	if err != nil {
		return "", err
	}
	// make a map from appId to slice of tasks in that app
	appMap := make(map[string][]Task)
	for _, task := range tasksResp.Tasks {
		slice, ok := appMap[task.EscapedAppId()]
		if !ok {
			slice = make([]Task, 0)
		}
		appMap[task.EscapedAppId()] = append(slice, task)
	}

	// buffer containing the haproxy config
	var config bytes.Buffer

	config.WriteString(haproxyConfigHeader())

	// FIXME this is wrong in the sense that it doesnt account for multiple service ports
	for appId, tasks := range appMap {
		lines := make([]string, 0)

		i := 0
		for _, task := range tasks {
			if !task.IsAlive() {
				continue
			}
			line, err := task.ServerLine(0, i)
			if err != nil {
				continue
			}
			lines = append(lines, line)
		}

		if len(lines) > 0 {
			// put service header in config
			servicePort := tasks[0].ServicePorts[0]
			config.WriteString(fmt.Sprintf("listen %s-%d\n", appId, servicePort))
			config.WriteString(fmt.Sprintf("  bind 0.0.0.0:%d\n", servicePort))
			config.WriteString("  mode tcp\n  option tcplog\n  balance leastconn\n")

			// put each server line in config
			for _, line := range lines {
				config.WriteString("  " + line + "\n")
			}
			config.WriteString("\n")
		}
	}

	// output config
	return config.String(), nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"github.com/BurntSushi/toml"
	log "github.com/Sirupsen/logrus"
	"github.com/xtracdev/automated-perf-test/perfTestUtils"
	"github.com/xtracdev/automated-perf-test/testStrategies"
	"io/ioutil"
	"net/http"
	"os"
	"time"
)

var configurationSettings *perfTestUtils.Config
var checkTestReadyness bool
var osFileSystem = perfTestUtils.OsFS{}

const (
	TRAINING_MODE = 1
	TESTING_MODE  = 2
)

//----- initConfig ------------------------------------------------------------
func initConfig(args []string, fs perfTestUtils.FileSystem, exit func(code int)) {
	//----- Initialize config data structure and set defaults.
	// Note: Defaults will be overridden as needed. The user can ignore
	// unnecessary parameters in config file and command prompt.
	configurationSettings = new(perfTestUtils.Config)
	configurationSettings.SetDefaults()

	//----- Get Hostname for this machine.
	host, err := os.Hostname()
	if err != nil {
		log.Error("Failed to resolve host name. Error:", err)
		exit(1)
	}
	configurationSettings.ExecutionHost = host

	//----- Process command line args.
	// Global controls outside of Config struct:
	var configFilePath string
	flag.StringVar(&configFilePath, "configFilePath", "", "The location of the configuration file.")
	flag.BoolVar(&checkTestReadyness, "checkTestReadyness", false, "Simple check to see if system requires training.")

	// Args that override default options in Config struct:
	flag.BoolVar(&configurationSettings.GBS, "gbs", false, "Genertate Base Statistics for this server")
	flag.BoolVar(&configurationSettings.ReBaseMemory, "reBaseMemory", false, "Generate new base peak memory for this server")
	flag.BoolVar(&configurationSettings.ReBaseAll, "reBaseAll", false, "Generate new base for memory and service resposne times for this server")
	flag.StringVar(&configurationSettings.ConfigFileFormat, "configFileFormat", "xml", "The format of the configuration file {xlm, toml}")
	flag.StringVar(&configurationSettings.TestFileFormat, "testFileFormat", "xml", "The format of the test definition file {xlm, toml}")
	flag.CommandLine.Parse(args)

	//----- Parse the config file.
	if configFilePath == "" {
		log.Warn("No config file found. - Using default values.")
		return
	}

	cf, err := fs.Open(configFilePath)
	if cf != nil {
		defer cf.Close()
	}
	if err != nil {
		log.Error("No config file found at path: ", configFilePath, " - Using default values.")
	} else {
		fileContent, fileErr := ioutil.ReadAll(cf)
		if fileErr != nil {
			log.Error("No readable config file found at path: ", configFilePath, " - Using default values.")
		} else {
			switch configurationSettings.ConfigFileFormat {
			case "toml":
				err := toml.Unmarshal(fileContent, &configurationSettings)
				if err != nil {
					log.Error("Failed to parse config file ", configFilePath, ". Error:", err, " - Using default values.")
				}
			default:
				xmlError := xml.Unmarshal(fileContent, &configurationSettings)
				if xmlError != nil {
					log.Error("Failed to parse config file ", configFilePath, ". Error:", xmlError, " - Using default values.")
				}
			}
		}
	}
}

//----- main ------------------------------------------------------------------
func main() {
	log.Debugf("[START]")
	initConfig(os.Args[1:], osFileSystem, os.Exit)

	//Validate config()
	configurationSettings.PrintAndValidateConfig()

	//Generate a test suite based on configuration settings
	testSuite := new(testStrategies.TestSuite)
	testSuite.BuildTestSuite(configurationSettings)

	if checkTestReadyness {
		readyForTest, _ := perfTestUtils.IsReadyForTest(configurationSettings, osFileSystem, testSuite.Name)
		if !readyForTest {
			log.Info("System is not ready for testing.")
			os.Exit(1)
		} else {
			log.Info("System is ready for testing.")
			os.Exit(0)
		}
	}

	//Determine testing mode.
	if configurationSettings.GBS || configurationSettings.ReBaseAll {
		if configurationSettings.ReBaseAll {
			runInTrainingMode(configurationSettings.ExecutionHost, true, testSuite)
		} else {
			readyForTest, _ := perfTestUtils.IsReadyForTest(configurationSettings, osFileSystem, testSuite.Name)
			if !readyForTest {
				runInTrainingMode(configurationSettings.ExecutionHost, false, testSuite)
			} else {
				log.Info("System is ready for testing. Training is not required.")
			}
		}
	} else {
		readyForTest, basePerfStats := perfTestUtils.IsReadyForTest(configurationSettings, osFileSystem, testSuite.Name)
		if readyForTest {
			runInTestingMode(basePerfStats, configurationSettings.ExecutionHost, perfTestUtils.GenerateTemplateReport, testSuite)
		} else {
			log.Info("System is not ready for testing. Attempting to run training mode....")
			runInTrainingMode(configurationSettings.ExecutionHost, false, testSuite)
			readyForTest, basePerfStats = perfTestUtils.IsReadyForTest(configurationSettings, osFileSystem, testSuite.Name)
			if readyForTest {
				runInTestingMode(basePerfStats, configurationSettings.ExecutionHost, perfTestUtils.GenerateTemplateReport, testSuite)
			} else {
				log.Info("System is not ready for testing. Attempting to run training failed. Check logs for more details.")
				os.Exit(1)
			}
		}
	}
}

func runInTrainingMode(host string, reBaseAll bool, testSuite *testStrategies.TestSuite) {
	log.Info("Running performance test in Training mode for host ", host)

	//Start Test Timer
	executionStartTime := time.Now().UnixNano()

	var basePerfstats *perfTestUtils.BasePerfStats
	if reBaseAll {
		log.Info("Performing full rebase of performance statistics for host ", host)
		basePerfstats = &perfTestUtils.BasePerfStats{
			BaseServiceResponseTimes: make(map[string]int64),
			MemoryAudit:              make([]uint64, 0),
		}
	} else {
		//Check to see if this server already has a base perf file defined.
		//If so, only values not previously populated will be set.
		//if not, a default base perf struct is created with nil values for all fields
		f, _ := os.Open(configurationSettings.BaseStatsOutputDir + "/" + host + "-" + testSuite.Name + "-perfBaseStats")
		basePerfstats, _ = perfTestUtils.ReadBasePerfFile(f)
	}

	//initilize Performance statistics struct for this test run
	perfStatsForTest := &perfTestUtils.PerfStats{ServiceResponseTimes: make(map[string]int64), ServiceTps: make(map[string]float64)}

	//Run the test
	runTests(perfStatsForTest, TRAINING_MODE, testSuite)

	//Generate base statistics output file for this training run.
	perfTestUtils.GenerateEnvBasePerfOutputFile(perfStatsForTest, basePerfstats, configurationSettings, os.Exit, osFileSystem, testSuite.Name)

	log.Info("Training mode completed successfully. ")
	log.Info("Execution Run Time :", perfTestUtils.GetExecutionTimeDisplay(time.Now().UnixNano()-executionStartTime))
}

func runInTestingMode(basePerfstats *perfTestUtils.BasePerfStats, host string, frg func(*perfTestUtils.BasePerfStats, *perfTestUtils.PerfStats, *perfTestUtils.Config, perfTestUtils.FileSystem, string), testSuite *testStrategies.TestSuite) {
	log.Info("Running Performance test in Testing mode for host ", host)

	//Start Test Timer
	executionStartTime := time.Now().UnixNano()

	//initilize Performance statistics struct for this test run
	perfStatsForTest := &perfTestUtils.PerfStats{ServiceResponseTimes: make(map[string]int64), TestDate: time.Now(), ServiceTps: make(map[string]float64)}

	//Run the test
	runTests(perfStatsForTest, TESTING_MODE, testSuite)

	//Validate test results
	assertionFailures := runAssertions(basePerfstats, perfStatsForTest)

	//Generate performance test report
	frg(basePerfstats, perfStatsForTest, configurationSettings, osFileSystem, testSuite.Name)

	//Print test results to std out
	log.Info("=================== TEST RESULTS ===================")
	if len(assertionFailures) > 0 {
		log.Info("Number of Failures : ", len(assertionFailures))
		for _, failure := range assertionFailures {
			log.Info(failure)
		}
	} else {
		log.Info("Testing mode completed successfully")
	}

	log.Info("Execution Run Time :", perfTestUtils.GetExecutionTimeDisplay(time.Now().UnixNano()-executionStartTime))
	log.Info("=====================================================")

	if len(assertionFailures) > 0 {
		os.Exit(1)
	}
}

//This function does two thing,
//1 Start a go routine to preiodically grab the memory foot print and set the peak memory value
//2 Run all test using mock servers and gather performance stats
func runTests(perfStatsForTest *perfTestUtils.PerfStats, mode int, testSuite *testStrategies.TestSuite) {

	//Initilize Memory analysis
	var peakMemoryAllocation = new(uint64)
	memoryAudit := make([]uint64, 0)
	testPartitions := make([]perfTestUtils.TestPartition, 0)
	counter := 0
	testPartitions = append(testPartitions, perfTestUtils.TestPartition{Count: counter, TestName: "StartUp"})

	//Start go routine to grab memory in use
	//Peak memory is stored in peakMemoryAlocation variable.
	quit := make(chan bool)
	go func() {
		for {
			select {
			case <-quit:
				return
			default:

				memoryStatsUrl := "http://" + configurationSettings.TargetHost + ":" + configurationSettings.TargetPort + configurationSettings.MemoryEndpoint
				resp, err := http.Get(memoryStatsUrl)
				if err != nil {
					log.Error("Memory analysis unavailable. Failed to retrieve memory Statistics from endpoint ", memoryStatsUrl, ". Error:", err)
					quit <- true
				} else {

					body, _ := ioutil.ReadAll(resp.Body)

					defer resp.Body.Close()

					m := new(perfTestUtils.Entry)
					unmarshalErr := json.Unmarshal(body, m)
					if unmarshalErr != nil {
						log.Error("Memory analysis unavailable. Failed to unmarshal memory statistics. ", unmarshalErr)
						quit <- true
					} else {
						if m.Memstats.Alloc > *peakMemoryAllocation {
							*peakMemoryAllocation = m.Memstats.Alloc
						}
						memoryAudit = append(memoryAudit, m.Memstats.Alloc)
						counter++
						time.Sleep(time.Millisecond * 200)
					}
				}
			}
		}
	}()

	//Add a 1 second delay before running test case to allow the graph get some initial memory data before test cases are executed.
	time.Sleep(time.Second * 1)

	//Check the test strategy
	if testSuite.TestStrategy == testStrategies.SERVICE_BASED_TESTING {

		log.Info("Running Service Based Testing Strategy")

		//Determine load per concurrent user
		loadPerUser := int(configurationSettings.NumIterations / configurationSettings.ConcurrentUsers)
		remainder := configurationSettings.NumIterations % configurationSettings.ConcurrentUsers

		for index, testDefinition := range testSuite.TestCases {
			//Start Test Timer
			testStartTime := time.Now().UnixNano()

			log.Info("Running Test case ", index, " [Name:", testDefinition.TestName, "]")
			testPartitions = append(testPartitions, perfTestUtils.TestPartition{Count: counter, TestName: testDefinition.TestName})
			averageResponseTime := testStrategies.ExecuteServiceTest(testDefinition, loadPerUser, remainder, configurationSettings)

			if averageResponseTime > 0 {
				perfStatsForTest.ServiceResponseTimes[testDefinition.TestName] = averageResponseTime
			} else {
				if mode == TRAINING_MODE {
					//Fail fast on training mode if any requests fail. If training fails we cannot guarantee the results.
					log.Error("Training mode failed due to invalid response on service [Name:", testDefinition.TestName, "]")
					os.Exit(1)
				}
			}
			//Start Test Timer
			testEndTime := time.Now().UnixNano()
			perfStatsForTest.ServiceTps[testDefinition.TestName] = perfTestUtils.CalcTps(testEndTime-testStartTime, configurationSettings.NumIterations)
		}

	} else if testSuite.TestStrategy == testStrategies.SUITE_BASED_TESTING {

		//Start Test Timer
		testStartTime := time.Now().UnixNano()

		log.Info("Running Suite Based Testing Strategy. Suite:", testSuite.Name)
		allServicesResponseTimesMap := testStrategies.ExecuteTestSuiteWrapper(testSuite, configurationSettings)

		//Start Test Timer
		testEndTime := time.Now().UnixNano()
		for serviceName, serviceResponseTimes := range allServicesResponseTimesMap {
			if len(serviceResponseTimes) == (configurationSettings.NumIterations * configurationSettings.ConcurrentUsers) {
				averageResponseTime := perfTestUtils.CalcAverageResponseTime(serviceResponseTimes, configurationSettings.NumIterations)
				if averageResponseTime > 0 {
					perfStatsForTest.ServiceResponseTimes[serviceName] = averageResponseTime
				} else {
					if mode == TRAINING_MODE {
						//Fail fast on training mode if any requests fail. If training fails we cannot guarantee the results.
						log.Error("Training mode failed due to invalid response on service [Name:", serviceName, "]")
						os.Exit(1)
					}
				}
				perfStatsForTest.ServiceTps[serviceName] = perfTestUtils.CalcTps(testEndTime-testStartTime, len(serviceResponseTimes))
			}
		}
	}

	time.Sleep(time.Second * 1)
	perfStatsForTest.PeakMemory = *peakMemoryAllocation
	perfStatsForTest.MemoryAudit = memoryAudit
	perfStatsForTest.TestPartitions = testPartitions
}

//This function runs the assertions to ensure memory and service have not deviated past the allowed variance
func runAssertions(basePerfstats *perfTestUtils.BasePerfStats, perfStats *perfTestUtils.PerfStats) []string {

	assertionFailures := make([]string, 0)

	//Asserts Peak memory growth has not exceeded the allowable variance
	peakMemoryVariancePercentage := perfTestUtils.CalcPeakMemoryVariancePercentage(basePerfstats.BasePeakMemory, perfStats.PeakMemory)
	varianceOk := perfTestUtils.ValidatePeakMemoryVariance(configurationSettings.AllowablePeakMemoryVariance, peakMemoryVariancePercentage)
	if !varianceOk {
		assertionFailures = append(assertionFailures, fmt.Sprintf("Memory Failure: Peak variance exceeded by %3.2f %1s", peakMemoryVariancePercentage, "%"))
	}

	//Asserts service response times have not exceeded the allowable variance
	for serviceName, baseResponseTime := range basePerfstats.BaseServiceResponseTimes {
		averageServiceResponseTime := perfStats.ServiceResponseTimes[serviceName]
		if averageServiceResponseTime == 0 {
			assertionFailures = append(assertionFailures, fmt.Sprintf("Service Failure: Service test %-60s did not execute correctly. See logs for more details.", serviceName))
		}

		responseTimeVariancePercentage := perfTestUtils.CalcAverageResponseVariancePercentage(averageServiceResponseTime, baseResponseTime)
		varianceOk := perfTestUtils.ValidateAverageServiceResponeTimeVariance(configurationSettings.AllowableServiceResponseTimeVariance, responseTimeVariancePercentage, serviceName)
		if !varianceOk {
			assertionFailures = append(assertionFailures, fmt.Sprintf("Service Failure: Service test %-60s response time variance exceeded by %3.2f %1s", serviceName, responseTimeVariancePercentage, "%"))
		}
	}
	return assertionFailures
}
<commit_msg>updates after merge<commit_after>package main

import (
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"github.com/BurntSushi/toml"
	log "github.com/Sirupsen/logrus"
	"github.com/xtracdev/automated-perf-test/perfTestUtils"
	"github.com/xtracdev/automated-perf-test/testStrategies"
	"io/ioutil"
	"net/http"
	"os"
	"time"
)

var configurationSettings *perfTestUtils.Config
var checkTestReadyness bool
var osFileSystem = perfTestUtils.OsFS{}

const (
	TRAINING_MODE = 1
	TESTING_MODE  = 2
)

//----- initConfig ------------------------------------------------------------
func initConfig(args []string, fs perfTestUtils.FileSystem, exit func(code int)) {
	//----- Initialize config data structure and set defaults.
	// Note: Defaults will be overridden as needed. The user can ignore
	// unnecessary parameters in config file and command prompt.
	configurationSettings = new(perfTestUtils.Config)
	configurationSettings.SetDefaults()

	//----- Get Hostname for this machine.
	host, err := os.Hostname()
	if err != nil {
		log.Error("Failed to resolve host name. Error:", err)
		exit(1)
	}
	configurationSettings.ExecutionHost = host

	//----- Process command line args.
	// Global controls outside of Config struct:
	var configFilePath string
	flag.StringVar(&configFilePath, "configFilePath", "", "The location of the configuration file.")
	flag.BoolVar(&checkTestReadyness, "checkTestReadyness", false, "Simple check to see if system requires training.")

	// Args that override default options in Config struct:
	flag.BoolVar(&configurationSettings.GBS, "gbs", false, "Genertate Base Statistics for this server")
	flag.BoolVar(&configurationSettings.ReBaseMemory, "reBaseMemory", false, "Generate new base peak memory for this server")
	flag.BoolVar(&configurationSettings.ReBaseAll, "reBaseAll", false, "Generate new base for memory and service resposne times for this server")
	flag.StringVar(&configurationSettings.ConfigFileFormat, "configFileFormat", "xml", "The format of the configuration file {xlm, toml}")
	flag.StringVar(&configurationSettings.TestFileFormat, "testFileFormat", "xml", "The format of the test definition file {xlm, toml}")
	flag.CommandLine.Parse(args)

	//----- Parse the config file.
	if configFilePath == "" {
		log.Warn("No config file found. - Using default values.")
		return
	}

	cf, err := fs.Open(configFilePath)
	if cf != nil {
		defer cf.Close()
	}
	if err != nil {
		log.Error("No config file found at path: ", configFilePath, " - Using default values.")
	} else {
		fileContent, fileErr := ioutil.ReadAll(cf)
		if fileErr != nil {
			log.Error("No readable config file found at path: ", configFilePath, " - Using default values.")
		} else {
			switch configurationSettings.ConfigFileFormat {
			case "toml":
				err := toml.Unmarshal(fileContent, &configurationSettings)
				if err != nil {
					log.Error("Failed to parse config file ", configFilePath, ". Error:", err, " - Using default values.")
				}
			default:
				xmlError := xml.Unmarshal(fileContent, &configurationSettings)
				if xmlError != nil {
					log.Error("Failed to parse config file ", configFilePath, ". Error:", xmlError, " - Using default values.")
				}
			}
		}
	}
}

//----- main ------------------------------------------------------------------
func main() {
	log.Debugf("[START]")
	initConfig(os.Args[1:], osFileSystem, os.Exit)

	//Validate config()
	configurationSettings.PrintAndValidateConfig()

	//Generate a test suite based on configuration settings
	testSuite := new(testStrategies.TestSuite)
	testSuite.BuildTestSuite(configurationSettings)
	numTestCases := len(testSuite.TestCases) //convenience variable

	if checkTestReadyness {
		readyForTest, _ := perfTestUtils.IsReadyForTest(configurationSettings, testSuite.Name, numTestCases)
		if !readyForTest {
			log.Info("System is not ready for testing.")
			os.Exit(1)
		} else {
			log.Info("System is ready for testing.")
			os.Exit(0)
		}
	}

	//Determine testing mode.
	if configurationSettings.GBS || configurationSettings.ReBaseAll {
		if configurationSettings.ReBaseAll {
			runInTrainingMode(configurationSettings.ExecutionHost, true, testSuite)
		} else {
			readyForTest, _ := perfTestUtils.IsReadyForTest(configurationSettings, testSuite.Name, numTestCases)
			if !readyForTest {
				runInTrainingMode(configurationSettings.ExecutionHost, false, testSuite)
			} else {
				log.Info("System is ready for testing. Training is not required.")
			}
		}
	} else {
		readyForTest, basePerfStats := perfTestUtils.IsReadyForTest(configurationSettings, testSuite.Name, numTestCases)
		if readyForTest {
			runInTestingMode(basePerfStats, configurationSettings.ExecutionHost, perfTestUtils.GenerateTemplateReport, testSuite)
		} else {
			log.Info("System is not ready for testing. Attempting to run training mode....")
			runInTrainingMode(configurationSettings.ExecutionHost, false, testSuite)
			readyForTest, basePerfStats = perfTestUtils.IsReadyForTest(configurationSettings, testSuite.Name, numTestCases)
			if readyForTest {
				runInTestingMode(basePerfStats, configurationSettings.ExecutionHost, perfTestUtils.GenerateTemplateReport, testSuite)
			} else {
				log.Info("System is not ready for testing. Attempting to run training failed. Check logs for more details.")
				os.Exit(1)
			}
		}
	}
}

func runInTrainingMode(host string, reBaseAll bool, testSuite *testStrategies.TestSuite) {
	log.Info("Running performance test in Training mode for host ", host)

	//Start Test Timer
	executionStartTime := time.Now().UnixNano()

	var basePerfstats *perfTestUtils.BasePerfStats
	if reBaseAll {
		log.Info("Performing full rebase of performance statistics for host ", host)
		basePerfstats = &perfTestUtils.BasePerfStats{
			BaseServiceResponseTimes: make(map[string]int64),
			MemoryAudit:              make([]uint64, 0),
		}
	} else {
		//Check to see if this server already has a base perf file defined.
		//If so, only values not previously populated will be set.
		//if not, a default base perf struct is created with nil values for all fields
		f, _ := os.Open(configurationSettings.BaseStatsOutputDir + "/" + host + "-" + testSuite.Name + "-perfBaseStats")
		basePerfstats, _ = perfTestUtils.ReadBasePerfFile(f)
	}

	//initilize Performance statistics struct for this test run
	perfStatsForTest := &perfTestUtils.PerfStats{ServiceResponseTimes: make(map[string]int64), ServiceTps: make(map[string]float64)}

	//Run the test
	runTests(perfStatsForTest, TRAINING_MODE, testSuite)

	//Generate base statistics output file for this training run.
	perfTestUtils.GenerateEnvBasePerfOutputFile(perfStatsForTest, basePerfstats, configurationSettings, os.Exit, osFileSystem, testSuite.Name)

	log.Info("Training mode completed successfully. ")
	log.Info("Execution Run Time :", perfTestUtils.GetExecutionTimeDisplay(time.Now().UnixNano()-executionStartTime))
}

func runInTestingMode(basePerfstats *perfTestUtils.BasePerfStats, host string, frg func(*perfTestUtils.BasePerfStats, *perfTestUtils.PerfStats, *perfTestUtils.Config, perfTestUtils.FileSystem, string), testSuite *testStrategies.TestSuite) {
	log.Info("Running Performance test in Testing mode for host ", host)

	//Start Test Timer
	executionStartTime := time.Now().UnixNano()

	//initilize Performance statistics struct for this test run
	perfStatsForTest := &perfTestUtils.PerfStats{ServiceResponseTimes: make(map[string]int64), TestDate: time.Now(), ServiceTps: make(map[string]float64)}

	//Run the test
	runTests(perfStatsForTest, TESTING_MODE, testSuite)

	//Validate test results
	assertionFailures := runAssertions(basePerfstats, perfStatsForTest)

	//Generate performance test report
	frg(basePerfstats, perfStatsForTest, configurationSettings, osFileSystem, testSuite.Name)

	//Print test results to std out
	log.Info("=================== TEST RESULTS ===================")
	if len(assertionFailures) > 0 {
		log.Info("Number of Failures : ", len(assertionFailures))
		for _, failure := range assertionFailures {
			log.Info(failure)
		}
	} else {
		log.Info("Testing mode completed successfully")
	}

	log.Info("Execution Run Time :", perfTestUtils.GetExecutionTimeDisplay(time.Now().UnixNano()-executionStartTime))
	log.Info("=====================================================")

	if len(assertionFailures) > 0 {
		os.Exit(1)
	}
}

//This function does two thing,
//1 Start a go routine to preiodically grab the memory foot print and set the peak memory value
//2 Run all test using mock servers and gather performance stats
func runTests(perfStatsForTest *perfTestUtils.PerfStats, mode int, testSuite *testStrategies.TestSuite) {

	//Initilize Memory analysis
	var peakMemoryAllocation = new(uint64)
	memoryAudit := make([]uint64, 0)
	testPartitions := make([]perfTestUtils.TestPartition, 0)
	counter := 0
	testPartitions = append(testPartitions, perfTestUtils.TestPartition{Count: counter, TestName: "StartUp"})

	//Start go routine to grab memory in use
	//Peak memory is stored in peakMemoryAlocation variable.
	quit := make(chan bool)
	go func() {
		for {
			select {
			case <-quit:
				return
			default:

				memoryStatsUrl := "http://" + configurationSettings.TargetHost + ":" + configurationSettings.TargetPort + configurationSettings.MemoryEndpoint
				resp, err := http.Get(memoryStatsUrl)
				if err != nil {
					log.Error("Memory analysis unavailable. Failed to retrieve memory Statistics from endpoint ", memoryStatsUrl, ". Error:", err)
					quit <- true
				} else {

					body, _ := ioutil.ReadAll(resp.Body)

					defer resp.Body.Close()

					m := new(perfTestUtils.Entry)
					unmarshalErr := json.Unmarshal(body, m)
					if unmarshalErr != nil {
						log.Error("Memory analysis unavailable. Failed to unmarshal memory statistics. ", unmarshalErr)
						quit <- true
					} else {
						if m.Memstats.Alloc > *peakMemoryAllocation {
							*peakMemoryAllocation = m.Memstats.Alloc
						}
						memoryAudit = append(memoryAudit, m.Memstats.Alloc)
						counter++
						time.Sleep(time.Millisecond * 200)
					}
				}
			}
		}
	}()

	//Add a 1 second delay before running test case to allow the graph get some initial memory data before test cases are executed.
	time.Sleep(time.Second * 1)

	//Check the test strategy
	if testSuite.TestStrategy == testStrategies.SERVICE_BASED_TESTING {

		log.Info("Running Service Based Testing Strategy")

		//Determine load per concurrent user
		loadPerUser := int(configurationSettings.NumIterations / configurationSettings.ConcurrentUsers)
		remainder := configurationSettings.NumIterations % configurationSettings.ConcurrentUsers

		for index, testDefinition := range testSuite.TestCases {
			//Start Test Timer
			testStartTime := time.Now().UnixNano()

			log.Info("Running Test case ", index, " [Name:", testDefinition.TestName, "]")
			testPartitions = append(testPartitions, perfTestUtils.TestPartition{Count: counter, TestName: testDefinition.TestName})
			averageResponseTime := testStrategies.ExecuteServiceTest(testDefinition, loadPerUser, remainder, configurationSettings)

			if averageResponseTime > 0 {
				perfStatsForTest.ServiceResponseTimes[testDefinition.TestName] = averageResponseTime
			} else {
				if mode == TRAINING_MODE {
					//Fail fast on training mode if any requests fail. If training fails we cannot guarantee the results.
					log.Error("Training mode failed due to invalid response on service [Name:", testDefinition.TestName, "]")
					os.Exit(1)
				}
			}
			//Start Test Timer
			testEndTime := time.Now().UnixNano()
			perfStatsForTest.ServiceTps[testDefinition.TestName] = perfTestUtils.CalcTps(testEndTime-testStartTime, configurationSettings.NumIterations)
		}

	} else if testSuite.TestStrategy == testStrategies.SUITE_BASED_TESTING {

		//Start Test Timer
		testStartTime := time.Now().UnixNano()

		log.Info("Running Suite Based Testing Strategy. Suite:", testSuite.Name)
		allServicesResponseTimesMap := testStrategies.ExecuteTestSuiteWrapper(testSuite, configurationSettings)

		//Start Test Timer
		testEndTime := time.Now().UnixNano()
		for serviceName, serviceResponseTimes := range allServicesResponseTimesMap {
			if len(serviceResponseTimes) == (configurationSettings.NumIterations * configurationSettings.ConcurrentUsers) {
				averageResponseTime := perfTestUtils.CalcAverageResponseTime(serviceResponseTimes, configurationSettings.NumIterations)
				if averageResponseTime > 0 {
					perfStatsForTest.ServiceResponseTimes[serviceName] = averageResponseTime
				} else {
					if mode == TRAINING_MODE {
						//Fail fast on training mode if any requests fail. If training fails we cannot guarantee the results.
						log.Error("Training mode failed due to invalid response on service [Name:", serviceName, "]")
						os.Exit(1)
					}
				}
				perfStatsForTest.ServiceTps[serviceName] = perfTestUtils.CalcTps(testEndTime-testStartTime, len(serviceResponseTimes))
			}
		}
	}

	time.Sleep(time.Second * 1)
	perfStatsForTest.PeakMemory = *peakMemoryAllocation
	perfStatsForTest.MemoryAudit = memoryAudit
	perfStatsForTest.TestPartitions = testPartitions
}

//This function runs the assertions to ensure memory and service have not deviated past the allowed variance
func runAssertions(basePerfstats *perfTestUtils.BasePerfStats, perfStats *perfTestUtils.PerfStats) []string {

	assertionFailures := make([]string, 0)

	//Asserts Peak memory growth has not exceeded the allowable variance
	peakMemoryVariancePercentage := perfTestUtils.CalcPeakMemoryVariancePercentage(basePerfstats.BasePeakMemory, perfStats.PeakMemory)
	varianceOk := perfTestUtils.ValidatePeakMemoryVariance(configurationSettings.AllowablePeakMemoryVariance, peakMemoryVariancePercentage)
	if !varianceOk {
		assertionFailures = append(assertionFailures, fmt.Sprintf("Memory Failure: Peak variance exceeded by %3.2f %1s", peakMemoryVariancePercentage, "%"))
	}

	//Asserts service response times have not exceeded the allowable variance
	for serviceName, baseResponseTime := range basePerfstats.BaseServiceResponseTimes {
		averageServiceResponseTime := perfStats.ServiceResponseTimes[serviceName]
		if averageServiceResponseTime == 0 {
			assertionFailures = append(assertionFailures, fmt.Sprintf("Service Failure: Service test %-60s did not execute correctly. See logs for more details.", serviceName))
		}

		responseTimeVariancePercentage := perfTestUtils.CalcAverageResponseVariancePercentage(averageServiceResponseTime, baseResponseTime)
		varianceOk := perfTestUtils.ValidateAverageServiceResponseTimeVariance(configurationSettings.AllowableServiceResponseTimeVariance, responseTimeVariancePercentage, serviceName)
		if !varianceOk {
			assertionFailures = append(assertionFailures, fmt.Sprintf("Service Failure: Service test %-60s response time variance exceeded by %3.2f %1s", serviceName, responseTimeVariancePercentage, "%"))
		}
	}
	return assertionFailures
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"flag"
	"math/rand"

	"fmt"
	"os"

	"os/signal"
	"syscall"

	"github.com/joelanford/goscan/utils/filescanner"
	"github.com/joelanford/goscan/utils/scratch"
	"github.com/pkg/errors"
	"gopkg.in/h2non/filetype.v1"
)

type FileOpts struct {
	ScanFiles   []string
	ResultsFile string
}

var (
	rpmType = filetype.AddType("rpm", "application/x-rpm")
)

func init() {
	filetype.AddMatcher(rpmType, func(header []byte) bool {
		return len(header) >= 4 && header[0] == 0xED && header[1] == 0xAB && header[2] == 0xEE && header[3] == 0xDB
	})
}

func exit(err error, code int, ss *scratch.Scratch) {
	if ss != nil {
		if err := ss.Teardown(); err != nil {
			fmt.Fprintln(os.Stderr, err)
		}
	}
	fmt.Fprintln(os.Stderr, err)
	os.Exit(code)
}

func main() {
	//
	// Parse command line flags
	//
	scratchOpts, scanOpts, fileOpts, err := parseFlags()
	if err != nil {
		exit(err, 1, nil)
	}

	//
	// Prepare the scratch space
	//
	fmt.Printf("%+v\n", scratchOpts)
	ss := scratch.New(*scratchOpts)
	err = ss.Setup()
	if err != nil {
		exit(err, 1, nil)

	}
	defer ss.Teardown()

	//
	// Setup context and signal handlers, which will be needed
	// if we need to cleanly exit before completing the scan.
	//
	ctx, cancel := context.WithCancel(context.Background())
	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, syscall.SIGABRT, syscall.SIGINT, syscall.SIGKILL)
	go func() {
		sig := <-sigChan
		fmt.Fprintf(os.Stderr, "Received signal %s. Exiting", sig)
		cancel()
	}()

	//
	// Setup the filescanner
	//
	fs, err := filescanner.New(*scanOpts)
	if err != nil {
		exit(err, 1, ss)
	}

	//
	// Run the scan
	//
	resChan := make(chan filescanner.ScanResult)
	err = fs.Scan(ctx, resChan, fileOpts.ScanFiles...)
	if err != nil {
		exit(err, 1, ss)
	}

	//
	// Output the hits
	//
	output, err := os.Create(fileOpts.ResultsFile)
	if err != nil {
		exit(err, 1, ss)
	}
	e := json.NewEncoder(output)
	for result := range resChan {
		err := e.Encode(result)
		if err != nil {
			exit(err, 1, ss)
		}
	}
}

func parseFlags() (*scratch.Opts, *filescanner.Opts, *FileOpts, error) {
	flag.Usage = func() {
		fmt.Printf("Usage: goscan [options] <scanfiles>\n")
		flag.PrintDefaults()
	}

	var scratchOpts scratch.Opts
	var scanOpts filescanner.Opts
	var fileOpts FileOpts

	parseScratchOpts(&scratchOpts)
	flag.StringVar(&scanOpts.KeywordsFile, "scan.words", "", "YAML keywords file")
	flag.IntVar(&scanOpts.HitContext, "scan.context", 10, "Context to capture around each hit")
	flag.StringVar(&fileOpts.ResultsFile, "output", "-", "Results output file (\"-\" for stdout)")

	flag.Parse()

	if scratchOpts.Path == "" {
		scratchOpts.Path = fmt.Sprintf("/tmp/goscan-%d", rand.Int())
	}

	scanOpts.ScratchSpacePath = scratchOpts.Path

	if fileOpts.ResultsFile == "-" {
		fileOpts.ResultsFile = "/dev/stdout"
	}

	fileOpts.ScanFiles = flag.Args()

	if scanOpts.KeywordsFile == "" {
		return nil, nil, nil, errors.New("error: scan.words file must be defined")
	}

	if len(fileOpts.ScanFiles) == 0 {
		return nil, nil, nil, errors.New("error: scan files not defined")
	}
	return &scratchOpts, &scanOpts, &fileOpts, nil
}
<commit_msg>Removed debug Printf line<commit_after>package main

import (
	"context"
	"encoding/json"
	"flag"
	"math/rand"

	"fmt"
	"os"

	"os/signal"
	"syscall"

	"github.com/joelanford/goscan/utils/filescanner"
	"github.com/joelanford/goscan/utils/scratch"
	"github.com/pkg/errors"
	"gopkg.in/h2non/filetype.v1"
)

type FileOpts struct {
	ScanFiles   []string
	ResultsFile string
}

var (
	rpmType = filetype.AddType("rpm", "application/x-rpm")
)

func init() {
	filetype.AddMatcher(rpmType, func(header []byte) bool {
		return len(header) >= 4 && header[0] == 0xED && header[1] == 0xAB && header[2] == 0xEE && header[3] == 0xDB
	})
}

func exit(err error, code int, ss *scratch.Scratch) {
	if ss != nil {
		if err := ss.Teardown(); err != nil {
			fmt.Fprintln(os.Stderr, err)
		}
	}
	fmt.Fprintln(os.Stderr, err)
	os.Exit(code)
}

func main() {
	//
	// Parse command line flags
	//
	scratchOpts, scanOpts, fileOpts, err := parseFlags()
	if err != nil {
		exit(err, 1, nil)
	}

	//
	// Prepare the scratch space
	//
	ss := scratch.New(*scratchOpts)
	err = ss.Setup()
	if err != nil {
		exit(err, 1, nil)

	}
	defer ss.Teardown()

	//
	// Setup context and signal handlers, which will be needed
	// if we need to cleanly exit before completing the scan.
	//
	ctx, cancel := context.WithCancel(context.Background())
	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, syscall.SIGABRT, syscall.SIGINT, syscall.SIGKILL)
	go func() {
		sig := <-sigChan
		fmt.Fprintf(os.Stderr, "Received signal %s. Exiting", sig)
		cancel()
	}()

	//
	// Setup the filescanner
	//
	fs, err := filescanner.New(*scanOpts)
	if err != nil {
		exit(err, 1, ss)
	}

	//
	// Run the scan
	//
	resChan := make(chan filescanner.ScanResult)
	err = fs.Scan(ctx, resChan, fileOpts.ScanFiles...)
	if err != nil {
		exit(err, 1, ss)
	}

	//
	// Output the hits
	//
	output, err := os.Create(fileOpts.ResultsFile)
	if err != nil {
		exit(err, 1, ss)
	}
	e := json.NewEncoder(output)
	for result := range resChan {
		err := e.Encode(result)
		if err != nil {
			exit(err, 1, ss)
		}
	}
}

func parseFlags() (*scratch.Opts, *filescanner.Opts, *FileOpts, error) {
	flag.Usage = func() {
		fmt.Printf("Usage: goscan [options] <scanfiles>\n")
		flag.PrintDefaults()
	}

	var scratchOpts scratch.Opts
	var scanOpts filescanner.Opts
	var fileOpts FileOpts

	parseScratchOpts(&scratchOpts)
	flag.StringVar(&scanOpts.KeywordsFile, "scan.words", "", "YAML keywords file")
	flag.IntVar(&scanOpts.HitContext, "scan.context", 10, "Context to capture around each hit")
	flag.StringVar(&fileOpts.ResultsFile, "output", "-", "Results output file (\"-\" for stdout)")

	flag.Parse()

	if scratchOpts.Path == "" {
		scratchOpts.Path = fmt.Sprintf("/tmp/goscan-%d", rand.Int())
	}

	scanOpts.ScratchSpacePath = scratchOpts.Path

	if fileOpts.ResultsFile == "-" {
		fileOpts.ResultsFile = "/dev/stdout"
	}

	fileOpts.ScanFiles = flag.Args()

	if scanOpts.KeywordsFile == "" {
		return nil, nil, nil, errors.New("error: scan.words file must be defined")
	}

	if len(fileOpts.ScanFiles) == 0 {
		return nil, nil, nil, errors.New("error: scan files not defined")
	}
	return &scratchOpts, &scanOpts, &fileOpts, nil
}
<|endoftext|>


<commit_before>//go:generate goagen bootstrap -d github.com/GwentAPI/gwentapi/design

package main

import (
	"flag"
	"fmt"
	"github.com/GwentAPI/gwentapi/app"
	"github.com/GwentAPI/gwentapi/configuration"
	"github.com/GwentAPI/gwentapi/dataLayer/dal"
	"github.com/goadesign/goa"
	"github.com/goadesign/goa/logging/log15"
	"github.com/goadesign/goa/middleware"
	"github.com/goadesign/goa/middleware/gzip"
	log "github.com/inconshreveable/log15"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

var enableGzip bool = true
var gzipLevel int = -1

/*
Set this variable with go build with the -ldflags="-X main.version=<value>" parameter.
*/
var version = "undefined"

func init() {
	versionFlag := flag.Bool("v", false, "Prints current version")
	flag.Parse()

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

func main() {
	// Create service
	service := goa.New("gwentapi")

	//Create logger
	logger := log.New("module", "app/server")

	config := configuration.GetConfig()
	//Logger configuration
	logger.SetHandler(log.MultiHandler(
		log.LvlFilterHandler(log.LvlInfo, log.Must.FileHandler(config.App.LogInfoFile, log.LogfmtFormat())),
		log.LvlFilterHandler(log.LvlError, log.Must.FileHandler(config.App.LogErrorFile, log.LogfmtFormat()))))

	//Inject logger
	service.WithLogger(goalog15.New(logger))

	// Mount middleware
	service.Use(middleware.RequestID())
	service.Use(middleware.LogRequest(config.App.Verbose))
	service.Use(middleware.ErrorHandler(service, config.App.Verbose))
	service.Use(middleware.Recover())
	if enableGzip {
		service.Use(gzip.Middleware(gzipLevel))
	}
	// Mount "card" controller
	c := NewCardController(service)
	app.MountCardController(service, c)
	// Mount "faction" controller
	c2 := NewFactionController(service)
	app.MountFactionController(service, c2)
	// Mount "index" controller
	c3 := NewIndexController(service)
	app.MountIndexController(service, c3)
	// Mount "category" controller
	c4 := NewCategoryController(service)
	app.MountCategoryController(service, c4)
	// Mount "rarity" controller
	c6 := NewRarityController(service)
	app.MountRarityController(service, c6)
	// Mount "group" controller
	c8 := NewGroupController(service)
	app.MountGroupController(service, c8)

	mux := http.NewServeMux()
	mountMedia(config.App.MediaPath, mux)
	mux.HandleFunc("/", service.Mux.ServeHTTP)
	srv := &http.Server{
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  30 * time.Second,
		Handler:      mux,
		Addr:         ":8080",
	}
	// Close the main session
	defer dal.ShutDown()

	// Start service
	go func() {
		service.LogInfo("startup", "message", "Service is running.")
		if err := srv.ListenAndServe(); err != nil {
			service.LogError("startup", "err", err)
		}
	}()

	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
	<-sc
	service.LogInfo("shutdown", "message", "Server stopped ungracefully by killing all connections.")

}

func mountMedia(fileSystemPath string, mux *http.ServeMux) {
	fs := justFilesFilesystem{http.Dir("./data/input/media")}
	mux.Handle("/media/", http.StripPrefix("/media/", http.FileServer(fs)))
}

type justFilesFilesystem struct {
	Fs http.FileSystem
}

func (fs justFilesFilesystem) Open(name string) (http.File, error) {
	f, err := fs.Fs.Open(name)

	if err != nil {
		return nil, err
	}

	stat, err := f.Stat()
	if stat.IsDir() {
		return nil, os.ErrNotExist
	}
	return f, nil
}
<commit_msg>Gracefully handle shutdown.<commit_after>//go:generate goagen bootstrap -d github.com/GwentAPI/gwentapi/design

package main

import (
	"context"
	"flag"
	"fmt"
	"github.com/GwentAPI/gwentapi/app"
	"github.com/GwentAPI/gwentapi/configuration"
	"github.com/GwentAPI/gwentapi/dataLayer/dal"
	"github.com/goadesign/goa"
	"github.com/goadesign/goa/logging/log15"
	"github.com/goadesign/goa/middleware"
	"github.com/goadesign/goa/middleware/gzip"
	log "github.com/inconshreveable/log15"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

var enableGzip bool = true
var gzipLevel int = -1

/*
Set this variable with go build with the -ldflags="-X main.version=<value>" parameter.
*/
var version = "undefined"

func init() {
	versionFlag := flag.Bool("v", false, "Prints current version")
	flag.Parse()

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

func main() {
	// Create service
	service := goa.New("gwentapi")

	//Create logger
	logger := log.New("module", "app/server")

	config := configuration.GetConfig()
	//Logger configuration
	logger.SetHandler(log.MultiHandler(
		log.LvlFilterHandler(log.LvlInfo, log.Must.FileHandler(config.App.LogInfoFile, log.LogfmtFormat())),
		log.LvlFilterHandler(log.LvlError, log.Must.FileHandler(config.App.LogErrorFile, log.LogfmtFormat()))))

	//Inject logger
	service.WithLogger(goalog15.New(logger))

	// Mount middleware
	service.Use(middleware.RequestID())
	service.Use(middleware.LogRequest(config.App.Verbose))
	service.Use(middleware.ErrorHandler(service, config.App.Verbose))
	service.Use(middleware.Recover())
	if enableGzip {
		service.Use(gzip.Middleware(gzipLevel))
	}
	// Mount "card" controller
	c := NewCardController(service)
	app.MountCardController(service, c)
	// Mount "faction" controller
	c2 := NewFactionController(service)
	app.MountFactionController(service, c2)
	// Mount "index" controller
	c3 := NewIndexController(service)
	app.MountIndexController(service, c3)
	// Mount "category" controller
	c4 := NewCategoryController(service)
	app.MountCategoryController(service, c4)
	// Mount "rarity" controller
	c6 := NewRarityController(service)
	app.MountRarityController(service, c6)
	// Mount "group" controller
	c8 := NewGroupController(service)
	app.MountGroupController(service, c8)

	// Close the main session
	defer dal.ShutDown()

	// create a context that we can cancel
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// a WaitGroup for the goroutines to tell us they've stopped
	wg := sync.WaitGroup{}
	wg.Add(1)
	go server(ctx, &wg, service, config)
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
	<-sc
	//log.Debug("Received signal: shutting down.")
	//log.Debug("Telling goroutines to stop")
	cancel()

	wg.Wait()
	//log.Debug("All goroutines have told us they've finished.")
	service.LogInfo("shutdown", "message", "Server gracefully stopped.")
}

func mountMedia(fileSystemPath string, mux *http.ServeMux) {
	fs := justFilesFilesystem{http.Dir("./data/input/media")}
	mux.Handle("/media/", http.StripPrefix("/media/", http.FileServer(fs)))
}

type justFilesFilesystem struct {
	Fs http.FileSystem
}

func (fs justFilesFilesystem) Open(name string) (http.File, error) {
	f, err := fs.Fs.Open(name)

	if err != nil {
		return nil, err
	}

	stat, err := f.Stat()
	if stat.IsDir() {
		return nil, os.ErrNotExist
	}
	return f, nil
}

func server(ctx context.Context, wg *sync.WaitGroup, service *goa.Service, config configuration.GwentConfig) {
	defer wg.Done()

	mux := http.NewServeMux()
	mountMedia(config.App.MediaPath, mux)
	mux.HandleFunc("/", service.Mux.ServeHTTP)
	srv := &http.Server{
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  30 * time.Second,
		Handler:      mux,
		Addr:         ":8080",
	}

	// Start service
	go func() {
		service.LogInfo("startup", "message", "Web server is starting.")
		if err := srv.ListenAndServe(); err != nil {
			service.LogError("startup", "err", err)
		}
	}()
	<-ctx.Done()
	//log.Debug("Shutdown in progress.")

	// shut down gracefully, but wait no longer than 5 seconds before halting
	shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	srv.Shutdown(shutdownCtx)
}
<|endoftext|>


<commit_before>// Package main is the command line interface to the burner api. It can be used
// to stop and start the server.
package main

import (
  "fmt"
  "net/http"
  "bytes"
  "io"
  "encoding/json"
)

func handler(res http.ResponseWriter, req *http.Request) {
  params := GetParams(req.Body)
  fmt.Println(params.File)
  fmt.Fprint(res, "hello world!")
}

func main() {
	http.HandleFunc("/", handler)
  http.ListenAndServe(":1111", nil)
}

type Params struct {
  File string
}

// Given a reader, extract a string and parse it into a struct
func GetParams(body io.Reader) Params {
  buf := new(bytes.Buffer)
  buf.ReadFrom(body)

  var i Params
  json.Unmarshal(buf.Bytes(), &i)
  return i
}
<commit_msg>remove unnecessary io import<commit_after>// Package main is the command line interface to the burner api. It can be used
// to stop and start the server.
package main

import (
  "fmt"
  "net/http"
  "bytes"
  "encoding/json"
)

func handler(res http.ResponseWriter, req *http.Request) {
  params := GetParams(req)
  fmt.Println(params.File)
  fmt.Fprint(res, "hello world!")
}

func main() {
	http.HandleFunc("/", handler)
  http.ListenAndServe(":1111", nil)
}

type Params struct {
  File string
}

// Given a reader, extract a string and parse it into a struct
func GetParams(req *http.Request) Params {
  buf := new(bytes.Buffer)
  buf.ReadFrom(req.Body)

  var i Params
  json.Unmarshal(buf.Bytes(), &i)
  return i
}
<|endoftext|>