<commit_before>package main

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"time"

	"github.com/fatih/color"
	"github.com/koding/websocketproxy"
	"github.com/oklog/run"
	"github.com/sourcepods/sourcepods/cmd"
	"github.com/urfave/cli"
)

var (
	devFlags = []cli.Flag{
		// Global
		cli.StringFlag{
			Name:  "database-dsn",
			Usage: "The database connection data",
			Value: "postgres://root@localhost:26257/sourcepods?sslmode=disable",
		},
		cli.BoolFlag{
			Name:  "log-json",
			Usage: "Log json instead of key-value pairs",
		},
		cli.StringFlag{
			Name:  "log-level",
			Usage: "The log level to filter logs with before printing",
			Value: "debug",
		},
		cli.BoolTFlag{
			Name:  "tracing",
			Usage: "Enable tracing",
		},
		cli.BoolFlag{
			Name:  "watch,w",
			Usage: "Watch files in this project and rebuild binaries if something changes",
		},
		// API
		cli.StringFlag{
			Name:  "api-addr",
			Usage: "The address to run the API on",
			Value: ":3020",
		},
		// Storage
		cli.StringFlag{
			Name:  "storage-addr",
			Usage: "The address to run the storage on",
			Value: ":3030",
		},
		cli.StringFlag{
			Name:  "storage-root",
			Usage: "Storage's root to write to",
			Value: "./dev/storage-data",
		},
		// UI
		cli.StringFlag{
			Name:  "ui",
			Usage: "How to run the UI. Run docker container or compile and run a binary. Run Dart Dev server",
			Value: "docker",
		},
		cli.StringFlag{
			Name:  "ui-addr",
			Usage: "The address to run the UI on",
			Value: ":3010",
		},
	}
)

func devAction(c *cli.Context) error {
	// Global
	databaseDSNFlag := c.String("database-dsn")
	logJSONFlag := c.Bool("log-json")
	loglevelFlag := c.String("log-level")
	tracingFlag := c.BoolT("tracing")
	watchFlag := c.Bool("watch")

	// API
	apiAddrFlag := c.String("api-addr")

	// Storage
	storageAddrFlag := c.String("storage-addr")
	storageRootFlag := c.String("storage-root")

	// UI
	uiModeFlag := c.String("ui")
	uiAddrFlag := c.String("ui-addr")

	tracingURL := ""
	if tracingFlag {
		tracingURL = "localhost:6831"
	}

	exists, err := exists("./dev")
	if err != nil {
		return err
	}
	if !exists {
		color.HiRed("Development folder ./dev doesn't exists. Run `sourcepods-dev setup` first")
		return nil
	}

	uiRunner := NewRunner("ui", []string{
		fmt.Sprintf("%s=%s", cmd.EnvHTTPAddr, uiAddrFlag),
		fmt.Sprintf("%s=%s", cmd.EnvAPIURL, "http://localhost:3000/api"), // TODO
		fmt.Sprintf("%s=%s", cmd.EnvLogLevel, loglevelFlag),
		fmt.Sprintf("%s=%v", cmd.EnvLogJSON, logJSONFlag),
		fmt.Sprintf("%s=%v", cmd.EnvTracingURL, tracingURL),
	})

	apiRunner := NewRunner("api", []string{
		fmt.Sprintf("%s=%s", cmd.EnvHTTPAddr, apiAddrFlag),
		fmt.Sprintf("%s=%s", cmd.EnvDatabaseDSN, databaseDSNFlag),
		fmt.Sprintf("%s=%s", cmd.EnvMigrationsPath, "./schema/postgres"),
		fmt.Sprintf("%s=%s", cmd.EnvLogLevel, loglevelFlag),
		fmt.Sprintf("%s=%v", cmd.EnvLogJSON, logJSONFlag),
		fmt.Sprintf("%s=%s", cmd.EnvSecret, "secret"),
		fmt.Sprintf("%s=%s", cmd.EnvStorageGRPCURL, "localhost:3033"),
		fmt.Sprintf("%s=%s", cmd.EnvStorageHTTPURL, "http://localhost:3030"),
		fmt.Sprintf("%s=%v", cmd.EnvTracingURL, tracingURL),
	})

	storageRunner := NewRunner("storage", []string{
		fmt.Sprintf("%s=%s", cmd.EnvHTTPAddr, storageAddrFlag),
		fmt.Sprintf("%s=%s", cmd.EnvLogLevel, loglevelFlag),
		fmt.Sprintf("%s=%v", cmd.EnvLogJSON, logJSONFlag),
		fmt.Sprintf("%s=%s", cmd.EnvRoot, storageRootFlag),
		fmt.Sprintf("%s=%v", cmd.EnvTracingURL, tracingURL),
	})

	caddy := CaddyRunner{}

	if watchFlag {
		watcher := &FileWatcher{}
		watcher.Add(uiRunner, apiRunner, storageRunner)

		go watcher.Watch()
	}

	var g run.Group
	{
		stop := make(chan os.Signal, 1)
		g.Add(func() error {
			signal.Notify(stop, os.Interrupt)
			<-stop
			return nil
		}, func(err error) {
			close(stop)
		})
	}
	{
		g.Add(func() error {
			color.HiGreen("starting api")
			return apiRunner.Run()
		}, func(err error) {
			color.HiYellow("stopping api")
			apiRunner.Shutdown()
		})
	}
	{
		g.Add(func() error {
			color.HiGreen("starting storage")
			return storageRunner.Run()
		}, func(err error) {
			color.HiYellow("stopping storage")
			storageRunner.Shutdown()
		})
	}
	{
		if uiModeFlag == "binary" {
			g.Add(func() error {
				color.HiGreen("starting ui")
				return uiRunner.Run()
			}, func(err error) {
				color.HiYellow("stopping ui")
				uiRunner.Shutdown()
			})
		}
	}
	{
		g.Add(func() error {
			color.HiGreen("starting caddy")
			return caddy.Run()
		}, func(err error) {
			color.HiYellow("stopping caddy")
			caddy.Stop()
		})
	}

	if uiModeFlag == "docker" {
		err := ensureUIContainer()
		if err != nil {
			return fmt.Errorf("failed to start UI container: %v", err)
		}
	}

	if uiModeFlag == "dart" {
		{
			c := exec.Command("webdev", "serve", "--hot-reload", "web:3011")
			g.Add(func() error {
				color.HiGreen("starting dart")

				c.Dir = "ui"
				stdout, err := c.StdoutPipe()
				if err != nil {
					return err
				}
				stderr, err := c.StderrPipe()
				if err != nil {
					return err
				}

				multi := io.MultiReader(stdout, stderr)

				if err := c.Start(); err != nil {
					return err
				}

				scanner := bufio.NewScanner(multi)
				for scanner.Scan() {
					fmt.Printf("%s\t%s\n", color.HiBlueString("dart"), scanner.Text())
				}

				return c.Wait()
			}, func(err error) {
				color.HiYellow("stopping dart")
				if c == nil || c.Process == nil {
					return
				}
				c.Process.Kill()
			})
		}
		{
			redirect := func(path string) bool {
				if path == "/main.dart.js" {
					return false
				}
				if path == "/main.dart.bootstrap.js" {
					return false
				}
				if path == "/main.ddc.js" {
					return false
				}
				if path == "/main.ddc.js.map" {
					return false
				}
				if path == "/$assetDigests" {
					return false
				}
				if strings.HasPrefix(path, "/components") {
					return false
				}
				if strings.HasPrefix(path, "/img") {
					return false
				}
				if strings.HasPrefix(path, "/packages") {
					return false
				}
				return true
			}

			addr := "localhost:3011"

			director := func(r *http.Request) {
				if redirect(r.URL.Path) {
					r.URL.Path = "/"
				}
				r.URL.Scheme = "http"
				r.URL.Host = addr
			}

			wsURL, _ := url.Parse("ws://" + addr)
			ws := websocketproxy.NewProxy(wsURL)

			server := &http.Server{
				Addr: ":3010",
				Handler: proxy{
					reverse: &httputil.ReverseProxy{
						Director: director,
					},
					websocket: ws,
				},
			}

			g.Add(func() error {
				return server.ListenAndServe()
			}, func(err error) {
				ctx, cancel := context.WithTimeout(context.Background(), time.Second)
				defer cancel()
				server.Shutdown(ctx)
			})
		}
	}

	return g.Run()
}

type proxy struct {
	reverse   *httputil.ReverseProxy
	websocket *websocketproxy.WebsocketProxy
}

func (p proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Header.Get("Upgrade") == "websocket" {
		r.Header.Del("Origin")
		p.websocket.ServeHTTP(w, r)
		return
	}

	p.reverse.ServeHTTP(w, r)
}

func ensureUIContainer() error {
	name := "sourcepods-ui"
	args := []string{"run", "-d", "-p=3010:3010", "--name=" + name, "gitpods/ui:latest"}
	return ensureContainer(name, args)
}

func ensureContainer(name string, args []string) error {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return err
	}

	cmd := exec.Command(docker, []string{
		"ps",
		"--filter", "name=" + name,
	}...)
	output, err := cmd.Output()
	if err != nil {
		return err
	}

	// If more than 2 lines, container exists
	lines := strings.Split(string(output), "\n")
	if len(lines) > 2 {
		return nil
	}

	color.Blue("docker %s", strings.Join(args, " "))

	cmd = exec.Command(docker, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err = cmd.Run(); err != nil {
		return err
	}

	return nil
}
<commit_msg>Use quay.io/sourcepods/ui to run UI during development<commit_after>package main

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"time"

	"github.com/fatih/color"
	"github.com/koding/websocketproxy"
	"github.com/oklog/run"
	"github.com/sourcepods/sourcepods/cmd"
	"github.com/urfave/cli"
)

var (
	devFlags = []cli.Flag{
		// Global
		cli.StringFlag{
			Name:  "database-dsn",
			Usage: "The database connection data",
			Value: "postgres://root@localhost:26257/sourcepods?sslmode=disable",
		},
		cli.BoolFlag{
			Name:  "log-json",
			Usage: "Log json instead of key-value pairs",
		},
		cli.StringFlag{
			Name:  "log-level",
			Usage: "The log level to filter logs with before printing",
			Value: "debug",
		},
		cli.BoolTFlag{
			Name:  "tracing",
			Usage: "Enable tracing",
		},
		cli.BoolFlag{
			Name:  "watch,w",
			Usage: "Watch files in this project and rebuild binaries if something changes",
		},
		// API
		cli.StringFlag{
			Name:  "api-addr",
			Usage: "The address to run the API on",
			Value: ":3020",
		},
		// Storage
		cli.StringFlag{
			Name:  "storage-addr",
			Usage: "The address to run the storage on",
			Value: ":3030",
		},
		cli.StringFlag{
			Name:  "storage-root",
			Usage: "Storage's root to write to",
			Value: "./dev/storage-data",
		},
		// UI
		cli.StringFlag{
			Name:  "ui",
			Usage: "How to run the UI. Run docker container or compile and run a binary. Run Dart Dev server",
			Value: "docker",
		},
		cli.StringFlag{
			Name:  "ui-addr",
			Usage: "The address to run the UI on",
			Value: ":3010",
		},
	}
)

func devAction(c *cli.Context) error {
	// Global
	databaseDSNFlag := c.String("database-dsn")
	logJSONFlag := c.Bool("log-json")
	loglevelFlag := c.String("log-level")
	tracingFlag := c.BoolT("tracing")
	watchFlag := c.Bool("watch")

	// API
	apiAddrFlag := c.String("api-addr")

	// Storage
	storageAddrFlag := c.String("storage-addr")
	storageRootFlag := c.String("storage-root")

	// UI
	uiModeFlag := c.String("ui")
	uiAddrFlag := c.String("ui-addr")

	tracingURL := ""
	if tracingFlag {
		tracingURL = "localhost:6831"
	}

	exists, err := exists("./dev")
	if err != nil {
		return err
	}
	if !exists {
		color.HiRed("Development folder ./dev doesn't exists. Run `sourcepods-dev setup` first")
		return nil
	}

	uiRunner := NewRunner("ui", []string{
		fmt.Sprintf("%s=%s", cmd.EnvHTTPAddr, uiAddrFlag),
		fmt.Sprintf("%s=%s", cmd.EnvAPIURL, "http://localhost:3000/api"), // TODO
		fmt.Sprintf("%s=%s", cmd.EnvLogLevel, loglevelFlag),
		fmt.Sprintf("%s=%v", cmd.EnvLogJSON, logJSONFlag),
		fmt.Sprintf("%s=%v", cmd.EnvTracingURL, tracingURL),
	})

	apiRunner := NewRunner("api", []string{
		fmt.Sprintf("%s=%s", cmd.EnvHTTPAddr, apiAddrFlag),
		fmt.Sprintf("%s=%s", cmd.EnvDatabaseDSN, databaseDSNFlag),
		fmt.Sprintf("%s=%s", cmd.EnvMigrationsPath, "./schema/postgres"),
		fmt.Sprintf("%s=%s", cmd.EnvLogLevel, loglevelFlag),
		fmt.Sprintf("%s=%v", cmd.EnvLogJSON, logJSONFlag),
		fmt.Sprintf("%s=%s", cmd.EnvSecret, "secret"),
		fmt.Sprintf("%s=%s", cmd.EnvStorageGRPCURL, "localhost:3033"),
		fmt.Sprintf("%s=%s", cmd.EnvStorageHTTPURL, "http://localhost:3030"),
		fmt.Sprintf("%s=%v", cmd.EnvTracingURL, tracingURL),
	})

	storageRunner := NewRunner("storage", []string{
		fmt.Sprintf("%s=%s", cmd.EnvHTTPAddr, storageAddrFlag),
		fmt.Sprintf("%s=%s", cmd.EnvLogLevel, loglevelFlag),
		fmt.Sprintf("%s=%v", cmd.EnvLogJSON, logJSONFlag),
		fmt.Sprintf("%s=%s", cmd.EnvRoot, storageRootFlag),
		fmt.Sprintf("%s=%v", cmd.EnvTracingURL, tracingURL),
	})

	caddy := CaddyRunner{}

	if watchFlag {
		watcher := &FileWatcher{}
		watcher.Add(uiRunner, apiRunner, storageRunner)

		go watcher.Watch()
	}

	var g run.Group
	{
		stop := make(chan os.Signal, 1)
		g.Add(func() error {
			signal.Notify(stop, os.Interrupt)
			<-stop
			return nil
		}, func(err error) {
			close(stop)
		})
	}
	{
		g.Add(func() error {
			color.HiGreen("starting api")
			return apiRunner.Run()
		}, func(err error) {
			color.HiYellow("stopping api")
			apiRunner.Shutdown()
		})
	}
	{
		g.Add(func() error {
			color.HiGreen("starting storage")
			return storageRunner.Run()
		}, func(err error) {
			color.HiYellow("stopping storage")
			storageRunner.Shutdown()
		})
	}
	{
		if uiModeFlag == "binary" {
			g.Add(func() error {
				color.HiGreen("starting ui")
				return uiRunner.Run()
			}, func(err error) {
				color.HiYellow("stopping ui")
				uiRunner.Shutdown()
			})
		}
	}
	{
		g.Add(func() error {
			color.HiGreen("starting caddy")
			return caddy.Run()
		}, func(err error) {
			color.HiYellow("stopping caddy")
			caddy.Stop()
		})
	}

	if uiModeFlag == "docker" {
		err := ensureUIContainer()
		if err != nil {
			return fmt.Errorf("failed to start UI container: %v", err)
		}
	}

	if uiModeFlag == "dart" {
		{
			c := exec.Command("webdev", "serve", "--hot-reload", "web:3011")
			g.Add(func() error {
				color.HiGreen("starting dart")

				c.Dir = "ui"
				stdout, err := c.StdoutPipe()
				if err != nil {
					return err
				}
				stderr, err := c.StderrPipe()
				if err != nil {
					return err
				}

				multi := io.MultiReader(stdout, stderr)

				if err := c.Start(); err != nil {
					return err
				}

				scanner := bufio.NewScanner(multi)
				for scanner.Scan() {
					fmt.Printf("%s\t%s\n", color.HiBlueString("dart"), scanner.Text())
				}

				return c.Wait()
			}, func(err error) {
				color.HiYellow("stopping dart")
				if c == nil || c.Process == nil {
					return
				}
				c.Process.Kill()
			})
		}
		{
			redirect := func(path string) bool {
				if path == "/main.dart.js" {
					return false
				}
				if path == "/main.dart.bootstrap.js" {
					return false
				}
				if path == "/main.ddc.js" {
					return false
				}
				if path == "/main.ddc.js.map" {
					return false
				}
				if path == "/$assetDigests" {
					return false
				}
				if strings.HasPrefix(path, "/components") {
					return false
				}
				if strings.HasPrefix(path, "/img") {
					return false
				}
				if strings.HasPrefix(path, "/packages") {
					return false
				}
				return true
			}

			addr := "localhost:3011"

			director := func(r *http.Request) {
				if redirect(r.URL.Path) {
					r.URL.Path = "/"
				}
				r.URL.Scheme = "http"
				r.URL.Host = addr
			}

			wsURL, _ := url.Parse("ws://" + addr)
			ws := websocketproxy.NewProxy(wsURL)

			server := &http.Server{
				Addr: ":3010",
				Handler: proxy{
					reverse: &httputil.ReverseProxy{
						Director: director,
					},
					websocket: ws,
				},
			}

			g.Add(func() error {
				return server.ListenAndServe()
			}, func(err error) {
				ctx, cancel := context.WithTimeout(context.Background(), time.Second)
				defer cancel()
				server.Shutdown(ctx)
			})
		}
	}

	return g.Run()
}

type proxy struct {
	reverse   *httputil.ReverseProxy
	websocket *websocketproxy.WebsocketProxy
}

func (p proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Header.Get("Upgrade") == "websocket" {
		r.Header.Del("Origin")
		p.websocket.ServeHTTP(w, r)
		return
	}

	p.reverse.ServeHTTP(w, r)
}

func ensureUIContainer() error {
	name := "sourcepods-ui"
	args := []string{"run", "-d", "-p=3010:3010", "--name=" + name, "quay.io/sourcepods/ui:latest"}
	return ensureContainer(name, args)
}

func ensureContainer(name string, args []string) error {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return err
	}

	cmd := exec.Command(docker, []string{
		"ps",
		"--filter", "name=" + name,
	}...)
	output, err := cmd.Output()
	if err != nil {
		return err
	}

	// If more than 2 lines, container exists
	lines := strings.Split(string(output), "\n")
	if len(lines) > 2 {
		return nil
	}

	color.Blue("docker %s", strings.Join(args, " "))

	cmd = exec.Command(docker, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err = cmd.Run(); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package game

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
	"strings"
)

const ACPCversion = "VERSION:2.0.0\r\n"

const (
	PreFlop = iota
	Flop
	Turn
	River
	Showdown
)

func splitCards(s string) []string {
	a := make([]string, len(s)/2)
	na := 0
	for i, c := range s {
		if c == 'c' || c == 'd' || c == 'h' || c == 's' {
			a[na] = s[i-1 : i+1]
			na++
		}
	}
	return a[:na]
}

type Player interface {
	Play(g *Game) (action string)
	Observe(g *Game)
}

type Game struct {
	Bets      []float64 // The chips put in for each player this round.
	Holes     []string  // All of the viewable hole cards.
	Board     []string  // All of the board cards.
	Raises    int       // The number of raises this round.
	Folded    []bool    // Whether the player in the nth position has folded.
	Actor     int       // The player whose turn it is to act.
	*GameDiff           // The most recent changes in game state.
	*Rules              // The set of rules to use to play the game.
	pot       float64   // Chips in the pot from previous rounds.
}

func NewGame(rules string) (*Game, error) {
	r, err := ChooseRules(rules)
	if err != nil {
		return nil, err
	}
	return &Game{
		Rules:    r,
		Folded:   make([]bool, r.numPlayers),
		Bets:     make([]float64, r.numPlayers),
		GameDiff: new(GameDiff)}, nil
}

func (this *Game) String() string {
	s := fmt.Sprintln(this.Holes, this.Board)
	if this.Actor != -1 {
		s += fmt.Sprintln(this.Pot(), this.Bets, this.CallAmt(), this.RaiseAmt())
	}
	return s
}

// NumActive returns how many players are still in the hand.
func (this *Game) NumActive() int {
	var count int
	for _, folded := range this.Folded {
		if !folded {
			count++
		}
	}
	return count
}

// LegalActions returns a string containing the currently legal actions.
func (this *Game) LegalActions() string {
	actions := "c"
	if this.CallAmt() > 0 {
		actions += "f"
	}
	if this.Raises < this.maxRaises[this.Round] {
		actions += "r"
	}
	return actions
}

func (this *Game) CallAmt() float64 {
	var max float64
	for _, chips := range this.Bets {
		if chips > max {
			max = chips
		}
	}
	return max - this.Bets[this.Actor]
}

func (this *Game) RaiseAmt() float64 {
	return this.CallAmt() + this.raiseSize[this.Round]
}

func (this *Game) Pot() float64 {
	var sum float64
	for _, chips := range this.Bets {
		sum += chips
	}
	return this.pot + sum
}

func (this *Game) Update(s string) error {
	err := this.GameDiff.Update(s)
	if err != nil {
		return err
	}
	// Handle action updates.
	switch this.Action {
	case "f":
		this.Folded[this.Actor] = true
	case "c":
		this.Bets[this.Actor] += this.CallAmt()
	case "r":
		this.Raises++
		this.Bets[this.Actor] += this.CallAmt() + this.raiseSize[this.Round]
	}
	if this.NumActive() < 2 {
		this.Actor = -1
	} else {
		i := this.Actor
		for {
			i = (i + 1) % len(this.Folded)
			if !this.Folded[i] {
				this.Actor = i
				break
			}
		}
	}
	// Handle card updates.
	if len(this.Cards) > 0 {
		this.Raises = 0
		switch this.Round {
		case PreFlop:
			this.Actor = this.firstPlayer[this.Round] - 1
			this.pot = 0
			copy(this.Bets, this.blind)
			this.Holes = splitCards(this.Cards)
			this.Board = nil
			for i := range this.Folded {
				this.Folded[i] = false
			}
		case Flop, Turn, River:
			this.Actor = this.firstPlayer[this.Round] - 1
			this.pot = this.Pot()
			for i := range this.Bets {
				this.Bets[i] = 0
			}
			this.Board = append(this.Board, splitCards(this.Cards)...)
		case Showdown:
			this.Actor = -1
			this.Holes = splitCards(this.Cards)
		}
	}
	return nil
}

// Start playing a game.
//	rules -- a String naming the game to play
//	p     -- an object that implements the Player interface.
//	host  -- the InetAddress of the dealer passed as a String
//	port  -- the port the dealer is listening on for the client passed as a String
func Play(rules string, p Player, host, port string) {
	game, err := NewGame(rules)
	if err != nil {
		log.Fatalln(err)
	}
	// Connect to the dealer.
	addr := net.JoinHostPort(host, port)
	fmt.Printf("Connecting to dealer at %s...\n", addr)
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		log.Fatalln(err)
	}
	defer conn.Close()

	// Tell the dealer I am ready to start.
	fmt.Printf("Starting a game of %s...\n", rules)
	conn.Write([]byte(ACPCversion))

	// Read replies from dealer one line at a time.
	reader := bufio.NewReader(conn)
	for {
		msg, err := reader.ReadString('\n')
		switch {
		case err == io.EOF:
			fmt.Println("Shutting down...")
			return
		case err != nil:
			log.Fatalln(err)
		// ";" and "#" are comment lines.
		case len(msg) < 1 || msg[0] == ';' || msg[0] == '#':
			continue
		}
		msg = strings.TrimRight(msg, "\r\n")
		err = game.Update(msg)
		if err != nil {
			log.Fatalln(err)
		}
		if game.Actor == game.Position {
			fmt.Fprintf(conn, "%s:%s\r\n", msg, p.Play(game))
		} else {
			p.Observe(game)
		}
	}
}
<commit_msg>Use the RaiseAmt function to update the pot.<commit_after>package game

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
	"strings"
)

const ACPCversion = "VERSION:2.0.0\r\n"

const (
	PreFlop = iota
	Flop
	Turn
	River
	Showdown
)

func splitCards(s string) []string {
	a := make([]string, len(s)/2)
	na := 0
	for i, c := range s {
		if c == 'c' || c == 'd' || c == 'h' || c == 's' {
			a[na] = s[i-1 : i+1]
			na++
		}
	}
	return a[:na]
}

type Player interface {
	Play(g *Game) (action string)
	Observe(g *Game)
}

type Game struct {
	Bets      []float64 // The chips put in for each player this round.
	Holes     []string  // All of the viewable hole cards.
	Board     []string  // All of the board cards.
	Raises    int       // The number of raises this round.
	Folded    []bool    // Whether the player in the nth position has folded.
	Actor     int       // The player whose turn it is to act.
	*GameDiff           // The most recent changes in game state.
	*Rules              // The set of rules to use to play the game.
	pot       float64   // Chips in the pot from previous rounds.
}

func NewGame(rules string) (*Game, error) {
	r, err := ChooseRules(rules)
	if err != nil {
		return nil, err
	}
	return &Game{
		Rules:    r,
		Folded:   make([]bool, r.numPlayers),
		Bets:     make([]float64, r.numPlayers),
		GameDiff: new(GameDiff)}, nil
}

func (this *Game) String() string {
	s := fmt.Sprintln(this.Holes, this.Board)
	if this.Actor != -1 {
		s += fmt.Sprintln(this.Pot(), this.Bets, this.CallAmt(), this.RaiseAmt())
	}
	return s
}

// NumActive returns how many players are still in the hand.
func (this *Game) NumActive() int {
	var count int
	for _, folded := range this.Folded {
		if !folded {
			count++
		}
	}
	return count
}

// LegalActions returns a string containing the currently legal actions.
func (this *Game) LegalActions() string {
	actions := "c"
	if this.CallAmt() > 0 {
		actions += "f"
	}
	if this.Raises < this.maxRaises[this.Round] {
		actions += "r"
	}
	return actions
}

func (this *Game) CallAmt() float64 {
	var max float64
	for _, chips := range this.Bets {
		if chips > max {
			max = chips
		}
	}
	return max - this.Bets[this.Actor]
}

func (this *Game) RaiseAmt() float64 {
	return this.CallAmt() + this.raiseSize[this.Round]
}

func (this *Game) Pot() float64 {
	var sum float64
	for _, chips := range this.Bets {
		sum += chips
	}
	return this.pot + sum
}

func (this *Game) Update(s string) error {
	err := this.GameDiff.Update(s)
	if err != nil {
		return err
	}
	// Handle action updates.
	switch this.Action {
	case "f":
		this.Folded[this.Actor] = true
	case "c":
		this.Bets[this.Actor] += this.CallAmt()
	case "r":
		this.Raises++
		this.Bets[this.Actor] += this.RaiseAmt()
	}
	if this.NumActive() < 2 {
		this.Actor = -1
	} else {
		i := this.Actor
		for {
			i = (i + 1) % len(this.Folded)
			if !this.Folded[i] {
				this.Actor = i
				break
			}
		}
	}
	// Handle card updates.
	if len(this.Cards) > 0 {
		this.Raises = 0
		switch this.Round {
		case PreFlop:
			this.Actor = this.firstPlayer[this.Round] - 1
			this.pot = 0
			copy(this.Bets, this.blind)
			this.Holes = splitCards(this.Cards)
			this.Board = nil
			for i := range this.Folded {
				this.Folded[i] = false
			}
		case Flop, Turn, River:
			this.Actor = this.firstPlayer[this.Round] - 1
			this.pot = this.Pot()
			for i := range this.Bets {
				this.Bets[i] = 0
			}
			this.Board = append(this.Board, splitCards(this.Cards)...)
		case Showdown:
			this.Actor = -1
			this.Holes = splitCards(this.Cards)
		}
	}
	return nil
}

// Start playing a game.
//	rules -- a String naming the game to play
//	p     -- an object that implements the Player interface.
//	host  -- the InetAddress of the dealer passed as a String
//	port  -- the port the dealer is listening on for the client passed as a String
func Play(rules string, p Player, host, port string) {
	game, err := NewGame(rules)
	if err != nil {
		log.Fatalln(err)
	}
	// Connect to the dealer.
	addr := net.JoinHostPort(host, port)
	fmt.Printf("Connecting to dealer at %s...\n", addr)
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		log.Fatalln(err)
	}
	defer conn.Close()

	// Tell the dealer I am ready to start.
	fmt.Printf("Starting a game of %s...\n", rules)
	conn.Write([]byte(ACPCversion))

	// Read replies from dealer one line at a time.
	reader := bufio.NewReader(conn)
	for {
		msg, err := reader.ReadString('\n')
		switch {
		case err == io.EOF:
			fmt.Println("Shutting down...")
			return
		case err != nil:
			log.Fatalln(err)
		// ";" and "#" are comment lines.
		case len(msg) < 1 || msg[0] == ';' || msg[0] == '#':
			continue
		}
		msg = strings.TrimRight(msg, "\r\n")
		err = game.Update(msg)
		if err != nil {
			log.Fatalln(err)
		}
		if game.Actor == game.Position {
			fmt.Fprintf(conn, "%s:%s\r\n", msg, p.Play(game))
		} else {
			p.Observe(game)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/stianeikeland/go-rpio"
	"net/http"
)

type DoorStatus int

const (
	OPEN DoorStatus = iota
	CLOSED
)

type StatusRequest struct {
	resultChan chan DoorStatus
}

func main() {
	statusChan := make(chan *StatusRequest)
	go doorMonitor(statusChan)

	http.Handle("/", &StatusPage{statusChan})
	http.ListenAndServe(":80", nil)
}

func doorMonitor(queue chan *StatusRequest) {
	// Open and map memory to access gpio, check for errors.
	if err := rpio.Open(); err != nil {
		panic(err)
	}
	// Unmap gpio memory when done.
	defer rpio.Close()

	pin := rpio.Pin(23)
	pin.PullUp()

	// Process request queue.
	for req := range queue {
		doorStatus := pin.Read()
		if doorStatus == 0 {
			req.resultChan <- CLOSED
		} else {
			req.resultChan <- OPEN
		}
	}
}

type StatusPage struct {
	statusChan chan *StatusRequest
}

func (s *StatusPage) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	req := &StatusRequest{make(chan DoorStatus)}
	s.statusChan <- req
	doorStatus := <-req.resultChan
	var doorString string
	if doorStatus == CLOSED {
		doorString = "closed"
	} else {
		doorString = "open"
	}

	fmt.Fprintln(w, "Garage door is:", doorString)
}
<commit_msg>buffer status response<commit_after>package main

import (
	"fmt"
	"github.com/stianeikeland/go-rpio"
	"net/http"
)

type DoorStatus int

const (
	OPEN DoorStatus = iota
	CLOSED
)

type StatusRequest struct {
	resultChan chan DoorStatus
}

func main() {
	statusChan := make(chan *StatusRequest)
	go doorMonitor(statusChan)

	http.Handle("/", &StatusPage{statusChan})
	http.ListenAndServe(":80", nil)
}

func doorMonitor(queue chan *StatusRequest) {
	// Open and map memory to access gpio, check for errors.
	if err := rpio.Open(); err != nil {
		panic(err)
	}
	// Unmap gpio memory when done.
	defer rpio.Close()

	pin := rpio.Pin(23)
	pin.PullUp()

	// Process request queue.
	for req := range queue {
		doorStatus := pin.Read()
		if doorStatus == 0 {
			req.resultChan <- CLOSED
		} else {
			req.resultChan <- OPEN
		}
	}
}

type StatusPage struct {
	statusChan chan *StatusRequest
}

func (s *StatusPage) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	req := &StatusRequest{make(chan DoorStatus, 1)}
	s.statusChan <- req
	doorStatus := <-req.resultChan
	var doorString string
	if doorStatus == CLOSED {
		doorString = "closed"
	} else {
		doorString = "open"
	}

	fmt.Fprintln(w, "Garage door is:", doorString)
}
<|endoftext|>


<commit_before>package file

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

	"github.com/pkg/errors"
)

// Reset an auto-generated source file, writing a new empty file header.
func Reset(path string, imports []string) error {
	content := fmt.Sprintf(`// +build linux,cgo,!agent

package %s

// The code below was generated by %s - DO NOT EDIT!

import (
`, os.Getenv("GOPACKAGE"), os.Args[0])

	for _, uri := range imports {
		content += fmt.Sprintf("\t%q\n", uri)
	}

	content += ")\n\n"

	// FIXME: we should only import what's needed.
	content += "var _ = api.ServerEnvironment{}\n"

	bytes := []byte(content)

	var err error

	if path == "-" {
		_, err = os.Stdout.Write(bytes)
	} else {
		err = ioutil.WriteFile(path, []byte(content), 0644)
	}

	if err != nil {
		errors.Wrapf(err, "Reset target source file '%s'", path)
	}

	return nil
}

// Append a code snippet to a file.
func Append(path string, snippet Snippet) error {
	buffer := newBuffer()
	buffer.N()

	err := snippet.Generate(buffer)
	if err != nil {
		return errors.Wrap(err, "Generate code snippet")
	}

	var file *os.File

	if path == "-" {
		file = os.Stdout
	} else {
		file, err = os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
		if err != nil {
			return errors.Wrapf(err, "Open target source code file '%s'", path)
		}
		defer file.Close()
	}

	bytes, err := buffer.code()
	if err != nil {
		return err
	}

	_, err = file.Write(bytes)
	if err != nil {
		return errors.Wrapf(err, "Append snippet to target source code file '%s'", path)
	}

	return nil
}
<commit_msg>lxd/db/generate/file/write: add alternate build comment to generated files<commit_after>package file

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

	"github.com/pkg/errors"
)

// Reset an auto-generated source file, writing a new empty file header.
func Reset(path string, imports []string) error {
	content := fmt.Sprintf(`//go:build linux && cgo && !agent
// +build linux,cgo,!agent

package %s

// The code below was generated by %s - DO NOT EDIT!

import (
`, os.Getenv("GOPACKAGE"), os.Args[0])

	for _, uri := range imports {
		content += fmt.Sprintf("\t%q\n", uri)
	}

	content += ")\n\n"

	// FIXME: we should only import what's needed.
	content += "var _ = api.ServerEnvironment{}\n"

	bytes := []byte(content)

	var err error

	if path == "-" {
		_, err = os.Stdout.Write(bytes)
	} else {
		err = ioutil.WriteFile(path, []byte(content), 0644)
	}

	if err != nil {
		errors.Wrapf(err, "Reset target source file '%s'", path)
	}

	return nil
}

// Append a code snippet to a file.
func Append(path string, snippet Snippet) error {
	buffer := newBuffer()
	buffer.N()

	err := snippet.Generate(buffer)
	if err != nil {
		return errors.Wrap(err, "Generate code snippet")
	}

	var file *os.File

	if path == "-" {
		file = os.Stdout
	} else {
		file, err = os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
		if err != nil {
			return errors.Wrapf(err, "Open target source code file '%s'", path)
		}
		defer file.Close()
	}

	bytes, err := buffer.code()
	if err != nil {
		return err
	}

	_, err = file.Write(bytes)
	if err != nil {
		return errors.Wrapf(err, "Append snippet to target source code file '%s'", path)
	}

	return nil
}
<|endoftext|>


<commit_before>package firewall

import (
	"github.com/lxc/lxd/lxd/firewall/drivers"
)

const driverXtables = "xtables"
const driverNftables = "nftables"

// New returns an appropriate firewall implementation.
// Uses xtables if nftables isn't compatible or isn't in use already, otherwise uses nftables.
func New() Firewall {
	nftables := drivers.Nftables{}
	xtables := drivers.Xtables{}

	// If nftables is compatible and already in use, then we prefer to use the nftables driver irrespective of
	// whether xtables is in use or not.
	nftablesCompat, nftablesInUse := nftables.Compat()
	if nftablesCompat && nftablesInUse {
		return nftables
	} else if !nftablesCompat {
		// Note: If nftables isn't compatible, we fallback to xtables without considering whether xtables
		// is itself compatible. This continues the existing behaviour of allowing LXD to start with
		// potentially an incomplete firewall backend, so that only networks and instances using those
		// features will fail to start later.
		return xtables
	}

	// If xtables is compatible and already in use, then we prefer to stick with the xtables driver rather than
	// mix the use of firewall drivers on the system.
	xtablesCompat, xtablesInUse := xtables.Compat()
	if xtablesCompat && xtablesInUse {
		return xtables
	}

	// Otherwise prefer nftables as default.
	return nftables
}
<commit_msg>lxd/firewall: Removes unused constants<commit_after>package firewall

import (
	"github.com/lxc/lxd/lxd/firewall/drivers"
)

// New returns an appropriate firewall implementation.
// Uses xtables if nftables isn't compatible or isn't in use already, otherwise uses nftables.
func New() Firewall {
	nftables := drivers.Nftables{}
	xtables := drivers.Xtables{}

	// If nftables is compatible and already in use, then we prefer to use the nftables driver irrespective of
	// whether xtables is in use or not.
	nftablesCompat, nftablesInUse := nftables.Compat()
	if nftablesCompat && nftablesInUse {
		return nftables
	} else if !nftablesCompat {
		// Note: If nftables isn't compatible, we fallback to xtables without considering whether xtables
		// is itself compatible. This continues the existing behaviour of allowing LXD to start with
		// potentially an incomplete firewall backend, so that only networks and instances using those
		// features will fail to start later.
		return xtables
	}

	// If xtables is compatible and already in use, then we prefer to stick with the xtables driver rather than
	// mix the use of firewall drivers on the system.
	xtablesCompat, xtablesInUse := xtables.Compat()
	if xtablesCompat && xtablesInUse {
		return xtables
	}

	// Otherwise prefer nftables as default.
	return nftables
}
<|endoftext|>


<commit_before>// **********************************************************************
//    Copyright (c) 2017 Henry Seurer
//
//   Permission is hereby granted, free of charge, to any person
//    obtaining a copy of this software and associated documentation
//    files (the "Software"), to deal in the Software without
//    restriction, including without limitation the rights to use,
//    copy, modify, merge, publish, distribute, sublicense, and/or sell
//    copies of the Software, and to permit persons to whom the
//    Software is furnished to do so, subject to the following
//    conditions:
//
//   The above copyright notice and this permission notice shall be
//   included in all copies or substantial portions of the Software.
//
//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//    OTHER DEALINGS IN THE SOFTWARE.
//
// **********************************************************************

package wiringpi

/*
#include <time.h>

unsigned long long as_nanoseconds(struct timespec* ts) {
    return ts->tv_sec * (unsigned long long)1000000000L + ts->tv_nsec;
}

unsigned long long monotonic_time() {
    struct timespec last_t;
    clock_gettime(CLOCK_MONOTONIC, &last_t);
    return as_nanoseconds(&last_t);
}
*/
import "C"
import (
	"fmt"
	"os"
)

var (
	board2pin = []int{
		-1,
		-1,
		-1,
		8,
		-1,
		9,
		-1,
		7,
		15,
		-1,
		16,
		0,
		1,
		2,
		-1,
		-1,
		4,
		-1,
		5,
		12,
		-1,
		13,
		6,
		14,
		10,
		-1,
		11,
	}
	gpio2pin = []int{
		8,
		9,
		-1,
		-1,
		7,
		-1,
		-1,
		11,
		10,
		13,
		12,
		14,
		-1,
		-1,
		15,
		16,
		-1,
		0,
		1,
		-1,
		-1,
		2,
		3,
		4,
		5,
		6,
		-1,
		-1,
		17,
		18,
		19,
		20,
	}

	gpioModes = []string{"IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"}
)

//noinspection GoUnusedConst
const (
	PIN_GPIO_0  = 0
	PIN_GPIO_1  = 1
	PIN_GPIO_2  = 2
	PIN_GPIO_3  = 3
	PIN_GPIO_4  = 4
	PIN_GPIO_5  = 5
	PIN_GPIO_6  = 6
	PIN_GPIO_7  = 7
	PIN_SDA     = 8
	PIN_SCL     = 9
	PIN_CE0     = 10
	PIN_CE1     = 11
	PIN_MOSI    = 12
	PIN_MOSO    = 13
	PIN_SCLK    = 14
	PIN_TXD     = 15
	PIN_RXD     = 16
	PIN_GPIO_8  = 17
	PIN_GPIO_9  = 18
	PIN_GPIO_10 = 19
	PIN_GPIO_11 = 20

	MODE_IN   = 0
	MODE_OUT  = 1
	MODE_ALT5 = 2
	MODE_ALT4 = 3
	MODE_ALT0 = 4
	MODE_ALT1 = 5
	MODE_ALT2 = 6
	MODE_ALT3 = 7
)

//use RPi.GPIO's BOARD numbering
//noinspection GoUnusedExportedFunction
func BoardToPin(pin int) int {
	if pin < 1 || pin >= len(board2pin) {
		panic(fmt.Sprintf("Invalid board pin number: %d", pin))
	}
	return board2pin[pin]
}

//noinspection GoUnusedExportedFunction
func GpioToPin(pin int) int {
	if pin < 0 || pin >= len(gpio2pin) {
		panic(fmt.Sprintf("Invalid bcm gpio number: %d", pin))
	}
	return gpio2pin[pin]
}

//noinspection GoUnusedExportedFunction
func PinToGpio(pin int) int {
	return internalPinToGpio(pin)
}

// This initialises wiringPi and assumes that the calling program is going to be using the wiringPi pin numbering scheme.
// This is a simplified numbering scheme which provides a mapping from virtual pin numbers 0 through 16 to the real
// underlying Broadcom GPIO pin numbers. See the pins page for a table which maps the wiringPi pin number to the
// Broadcom GPIO pin number to the physical location on the edge connector.
//
// This function needs to be called with root privileges.
//
//noinspection GoUnusedExportedFunction
func Setup() int {
	return internalSetup()
}

//This is identical to above, however it allows the calling programs to use the Broadcom GPIO pin numbers
// directly with no re-mapping.
//
// As above, this function needs to be called with root privileges, and note that some pins are different
// from revision 1 to revision 2 boards.
//
//noinspection GoUnusedExportedFunction
func SetupGpio() int {
	return internalSetupGpio()
}

// Identical to above, however it allows the calling programs to use the physical pin numbers on the P1 connector only.
//
// As above, this function needs to be called with root privileges.
//
//noinspection GoUnusedExportedFunction
func SetupPhys() int {
	return internalSetupPhys()
}

// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly.
// This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program.
// Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above, so be
// aware of the differences between Rev 1 and Rev 2 boards.
//
// Note: In this mode you can only use the pins which have been exported via the /sys/class/gpio interface
// before you run your program. You can do this in a separate shell-script, or by using the system() function
// from inside your program to call the gpio program.
//
//Also note that some functions have no effect when using this mode as they’re not currently possible to action unless called with root privileges. (although you can use system() to call gpio to set/change modes if needed)
//
//noinspection GoUnusedExportedFunction
func SetupSys() int {
	return internalSetupSys()
}

// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK. Note that only wiringPi pin 1
// (BCM_GPIO 18) supports PWM output and only wiringPi pin 7 (BCM_GPIO 4) supports CLOCK output modes.
//
// This function has no effect when in Sys mode. If you need to change the pin mode, then you can do it with the
// gpio program in a script before you start your program.
//
//noinspection GoUnusedExportedFunction
func PinMode(pin int, mode int) {
	internalPinMode(pin, mode)
}

// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input. Unlike the
// Arduino, the BCM2835 has both pull-up an down internal resistors. The parameter pud should be; PUD_OFF, (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) The internal pull up/down resistors have a value of approximately 50KΩ on the Raspberry Pi.
//
// This function has no effect on the Raspberry Pi’s GPIO pins when in Sys mode. If you need to activate a
// pull-up/pull-down, then you can do it with the gpio program in a script before you start your program.
//
//noinspection GoUnusedExportedFunction
func PullUpDnControl(pin int, pud int) {
	internalPullUpDnControl(pin, pud)
}

//Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output.
//
//WiringPi treats any non-zero number as HIGH, however 0 is the only representation of LOW.
//
//noinspection GoUnusedExportedFunction
func DigitalWrite(pin int, mode int) {
	internalDigitalWrite(pin, mode)
}

// Writes the value to the PWM register for the given pin. The Raspberry Pi has one on-board PWM pin, pin 1
// (BMC_GPIO 18, Phys 12) and the range is 0-1024. Other PWM devices may have other PWM ranges.
//
// This function is not able to control the Pi’s on-board PWM when in Sys mode.
//
//noinspection GoUnusedExportedFunction
func PwmWrite(pin int, value int) {
	internalPwmWrite(pin, value)
}

//noinspection GoUnusedExportedFunction
func DigitalRead(pin int) int {
	return internalDigitalRead(pin)
}

//noinspection GoUnusedExportedFunction
func DigitalReadStr(pin int) string {
	if internalDigitalRead(pin) == LOW {
		return "LOW"
	}
	return "HIGH"
}

func GetMode(pin int) int {
	return internalGetMode(pin)
}

//noinspection GoUnusedExportedFunction
func GetModeStr(pin int) string {
	var mode = internalGetMode(pin)

	if mode > len(gpioModes) {
		return "INVALID"
	}

	return gpioModes[GetMode(pin)]
}

//noinspection GoUnusedExportedFunction
func Delay(ms int) {
	internalDelay(ms)
}

//noinspection GoUnusedExportedFunction
func DelayMicroseconds(microSec int) {
	internalDelayMicroseconds(microSec)
}

//noinspection GoUnusedExportedFunction
func WiringISR(pin int, mode int) chan int {
	return internalWiringISR(pin, mode)
}

//noinspection GoUnusedExportedFunction
func IsRaspberryPi() bool {
	_, err := os.Stat("/opt/vc/include/bcm_host.h")
	return !os.IsNotExist(err)
}

//noinspection GoUnusedExportedFunction
func SetupI2C(devId int) int {
	return internalSetupI2C(devId)
}

//noinspection GoUnusedExportedFunction
func I2cRead(fd int) int {
	return internalI2CRead(fd)
}

//noinspection GoUnusedExportedFunction
func MonotonicTime() C.ulonglong {
	var nanoseconds C.ulonglong
	C.monotonic_time()
	return nanoseconds
}

//noinspection GoUnusedExportedFunction
func ConvertMonotonicTimeToUSec(time C.ulonglong) C.ulonglong {
	return time / 1000
}
<commit_msg>Stick with go types.<commit_after>// **********************************************************************
//    Copyright (c) 2017 Henry Seurer
//
//   Permission is hereby granted, free of charge, to any person
//    obtaining a copy of this software and associated documentation
//    files (the "Software"), to deal in the Software without
//    restriction, including without limitation the rights to use,
//    copy, modify, merge, publish, distribute, sublicense, and/or sell
//    copies of the Software, and to permit persons to whom the
//    Software is furnished to do so, subject to the following
//    conditions:
//
//   The above copyright notice and this permission notice shall be
//   included in all copies or substantial portions of the Software.
//
//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//    OTHER DEALINGS IN THE SOFTWARE.
//
// **********************************************************************

package wiringpi

/*
#include <time.h>

unsigned long long as_nanoseconds(struct timespec* ts) {
    return ts->tv_sec * (unsigned long long)1000000000L + ts->tv_nsec;
}

unsigned long long monotonic_time() {
    struct timespec last_t;
    clock_gettime(CLOCK_MONOTONIC, &last_t);
    return as_nanoseconds(&last_t);
}
*/
import "C"
import (
	"fmt"
	"os"
)

var (
	board2pin = []int{
		-1,
		-1,
		-1,
		8,
		-1,
		9,
		-1,
		7,
		15,
		-1,
		16,
		0,
		1,
		2,
		-1,
		-1,
		4,
		-1,
		5,
		12,
		-1,
		13,
		6,
		14,
		10,
		-1,
		11,
	}
	gpio2pin = []int{
		8,
		9,
		-1,
		-1,
		7,
		-1,
		-1,
		11,
		10,
		13,
		12,
		14,
		-1,
		-1,
		15,
		16,
		-1,
		0,
		1,
		-1,
		-1,
		2,
		3,
		4,
		5,
		6,
		-1,
		-1,
		17,
		18,
		19,
		20,
	}

	gpioModes = []string{"IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"}
)

//noinspection GoUnusedConst
const (
	PIN_GPIO_0  = 0
	PIN_GPIO_1  = 1
	PIN_GPIO_2  = 2
	PIN_GPIO_3  = 3
	PIN_GPIO_4  = 4
	PIN_GPIO_5  = 5
	PIN_GPIO_6  = 6
	PIN_GPIO_7  = 7
	PIN_SDA     = 8
	PIN_SCL     = 9
	PIN_CE0     = 10
	PIN_CE1     = 11
	PIN_MOSI    = 12
	PIN_MOSO    = 13
	PIN_SCLK    = 14
	PIN_TXD     = 15
	PIN_RXD     = 16
	PIN_GPIO_8  = 17
	PIN_GPIO_9  = 18
	PIN_GPIO_10 = 19
	PIN_GPIO_11 = 20

	MODE_IN   = 0
	MODE_OUT  = 1
	MODE_ALT5 = 2
	MODE_ALT4 = 3
	MODE_ALT0 = 4
	MODE_ALT1 = 5
	MODE_ALT2 = 6
	MODE_ALT3 = 7
)

//use RPi.GPIO's BOARD numbering
//noinspection GoUnusedExportedFunction
func BoardToPin(pin int) int {
	if pin < 1 || pin >= len(board2pin) {
		panic(fmt.Sprintf("Invalid board pin number: %d", pin))
	}
	return board2pin[pin]
}

//noinspection GoUnusedExportedFunction
func GpioToPin(pin int) int {
	if pin < 0 || pin >= len(gpio2pin) {
		panic(fmt.Sprintf("Invalid bcm gpio number: %d", pin))
	}
	return gpio2pin[pin]
}

//noinspection GoUnusedExportedFunction
func PinToGpio(pin int) int {
	return internalPinToGpio(pin)
}

// This initialises wiringPi and assumes that the calling program is going to be using the wiringPi pin numbering scheme.
// This is a simplified numbering scheme which provides a mapping from virtual pin numbers 0 through 16 to the real
// underlying Broadcom GPIO pin numbers. See the pins page for a table which maps the wiringPi pin number to the
// Broadcom GPIO pin number to the physical location on the edge connector.
//
// This function needs to be called with root privileges.
//
//noinspection GoUnusedExportedFunction
func Setup() int {
	return internalSetup()
}

//This is identical to above, however it allows the calling programs to use the Broadcom GPIO pin numbers
// directly with no re-mapping.
//
// As above, this function needs to be called with root privileges, and note that some pins are different
// from revision 1 to revision 2 boards.
//
//noinspection GoUnusedExportedFunction
func SetupGpio() int {
	return internalSetupGpio()
}

// Identical to above, however it allows the calling programs to use the physical pin numbers on the P1 connector only.
//
// As above, this function needs to be called with root privileges.
//
//noinspection GoUnusedExportedFunction
func SetupPhys() int {
	return internalSetupPhys()
}

// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly.
// This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program.
// Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above, so be
// aware of the differences between Rev 1 and Rev 2 boards.
//
// Note: In this mode you can only use the pins which have been exported via the /sys/class/gpio interface
// before you run your program. You can do this in a separate shell-script, or by using the system() function
// from inside your program to call the gpio program.
//
//Also note that some functions have no effect when using this mode as they’re not currently possible to action unless called with root privileges. (although you can use system() to call gpio to set/change modes if needed)
//
//noinspection GoUnusedExportedFunction
func SetupSys() int {
	return internalSetupSys()
}

// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK. Note that only wiringPi pin 1
// (BCM_GPIO 18) supports PWM output and only wiringPi pin 7 (BCM_GPIO 4) supports CLOCK output modes.
//
// This function has no effect when in Sys mode. If you need to change the pin mode, then you can do it with the
// gpio program in a script before you start your program.
//
//noinspection GoUnusedExportedFunction
func PinMode(pin int, mode int) {
	internalPinMode(pin, mode)
}

// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input. Unlike the
// Arduino, the BCM2835 has both pull-up an down internal resistors. The parameter pud should be; PUD_OFF, (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) The internal pull up/down resistors have a value of approximately 50KΩ on the Raspberry Pi.
//
// This function has no effect on the Raspberry Pi’s GPIO pins when in Sys mode. If you need to activate a
// pull-up/pull-down, then you can do it with the gpio program in a script before you start your program.
//
//noinspection GoUnusedExportedFunction
func PullUpDnControl(pin int, pud int) {
	internalPullUpDnControl(pin, pud)
}

//Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output.
//
//WiringPi treats any non-zero number as HIGH, however 0 is the only representation of LOW.
//
//noinspection GoUnusedExportedFunction
func DigitalWrite(pin int, mode int) {
	internalDigitalWrite(pin, mode)
}

// Writes the value to the PWM register for the given pin. The Raspberry Pi has one on-board PWM pin, pin 1
// (BMC_GPIO 18, Phys 12) and the range is 0-1024. Other PWM devices may have other PWM ranges.
//
// This function is not able to control the Pi’s on-board PWM when in Sys mode.
//
//noinspection GoUnusedExportedFunction
func PwmWrite(pin int, value int) {
	internalPwmWrite(pin, value)
}

//noinspection GoUnusedExportedFunction
func DigitalRead(pin int) int {
	return internalDigitalRead(pin)
}

//noinspection GoUnusedExportedFunction
func DigitalReadStr(pin int) string {
	if internalDigitalRead(pin) == LOW {
		return "LOW"
	}
	return "HIGH"
}

func GetMode(pin int) int {
	return internalGetMode(pin)
}

//noinspection GoUnusedExportedFunction
func GetModeStr(pin int) string {
	var mode = internalGetMode(pin)

	if mode > len(gpioModes) {
		return "INVALID"
	}

	return gpioModes[GetMode(pin)]
}

//noinspection GoUnusedExportedFunction
func Delay(ms int) {
	internalDelay(ms)
}

//noinspection GoUnusedExportedFunction
func DelayMicroseconds(microSec int) {
	internalDelayMicroseconds(microSec)
}

//noinspection GoUnusedExportedFunction
func WiringISR(pin int, mode int) chan int {
	return internalWiringISR(pin, mode)
}

//noinspection GoUnusedExportedFunction
func IsRaspberryPi() bool {
	_, err := os.Stat("/opt/vc/include/bcm_host.h")
	return !os.IsNotExist(err)
}

//noinspection GoUnusedExportedFunction
func SetupI2C(devId int) int {
	return internalSetupI2C(devId)
}

//noinspection GoUnusedExportedFunction
func I2cRead(fd int) int {
	return internalI2CRead(fd)
}

//noinspection GoUnusedExportedFunction
func MonotonicTime() uint64 {
	return uint64(C.monotonic_time())
}

//noinspection GoUnusedExportedFunction
func ConvertMonotonicTimeToUSec(time uint64) uint64 {
	return time / 1000
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"

	"github.com/golang/glog"
	yaml "gopkg.in/yaml.v1"
)

type InventoryGroup struct {
	Name     string   `yaml:"group"`
	Hosts    []string `yaml:"hosts,omitempty"`
	Children []string `yaml:"children,omitempty"`
}

func (g InventoryGroup) Check() error {
	// Make sure there are either hosts, or child groups specified.
	if len(g.Hosts) == 0 && len(g.Children) == 0 {
		return fmt.Errorf("no hosts or child groups specified in group")
	}
	return nil
}

func LoadInventoryFile(pth string) ([]InventoryGroup, error) {
	b, err := ioutil.ReadFile(pth)
	if err != nil {
		return nil, err
	}

	glog.V(4).Infof("Read %d bytes from %s", len(b), pth)

	var inv []InventoryGroup
	err = yaml.Unmarshal(b, &inv)
	if err != nil {
		return nil, err
	}

	return inv, err
}
<commit_msg>Removed all use of golang/glog.<commit_after>package main

import (
	"fmt"
	"io/ioutil"

	yaml "gopkg.in/yaml.v1"
)

type InventoryGroup struct {
	Name     string   `yaml:"group"`
	Hosts    []string `yaml:"hosts,omitempty"`
	Children []string `yaml:"children,omitempty"`
}

func (g InventoryGroup) Check() error {
	// Make sure there are either hosts, or child groups specified.
	if len(g.Hosts) == 0 && len(g.Children) == 0 {
		return fmt.Errorf("no hosts or child groups specified in group")
	}
	return nil
}

func LoadInventoryFile(pth string) ([]InventoryGroup, error) {
	b, err := ioutil.ReadFile(pth)
	if err != nil {
		return nil, err
	}

	var inv []InventoryGroup
	err = yaml.Unmarshal(b, &inv)
	if err != nil {
		return nil, err
	}

	return inv, err
}
<|endoftext|>


<commit_before>package balancer

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io"
	"net"
	"sync"
	"time"

	"github.com/weaveworks/flux/balancer/events"
	"github.com/weaveworks/flux/balancer/model"
	"github.com/weaveworks/flux/balancer/pool"
	"github.com/weaveworks/flux/common/daemon"
)

type forwardingConfig struct {
	netConfig
	*ipTables
	eventHandler events.Handler
	errorSink    daemon.ErrorSink
}

type forwarding struct {
	forwardingConfig
	rule     []interface{}
	listener *net.TCPListener
	stopped  bool

	lock    sync.Mutex
	service *model.Service

	pool        pool.InstancePool
	retryTicker *time.Ticker

	shim shimFunc
}

type shimFunc func(inbound, outbound *net.TCPConn, conn *events.Connection, eventHandler events.Handler) error

func (fc forwardingConfig) start(svc *model.Service) (serviceState, error) {
	log.Info("forwarding service: ", svc.Summary())
	ip, err := bridgeIP(fc.bridge)
	if err != nil {
		return nil, err
	}

	listener, err := net.ListenTCP("tcp", &net.TCPAddr{IP: ip})
	if err != nil {
		return nil, err
	}

	success := false
	defer func() {
		if !success {
			listener.Close()
		}
	}()

	rule := []interface{}{
		"-p", "tcp",
		"-d", svc.IP,
		"--dport", svc.Port,
		"-j", "DNAT",
		"--to-destination", listener.Addr(),
	}
	err = fc.ipTables.addRule("nat", rule)
	if err != nil {
		return nil, err
	}

	fwd := &forwarding{
		forwardingConfig: fc,
		rule:             rule,
		listener:         listener,
		service:          svc,
		pool:             pool.NewInstancePool(),
		retryTicker:      time.NewTicker(1 * time.Second),
	}
	fwd.pool.UpdateInstances(svc.Instances)
	go func() {
		for {
			t := <-fwd.retryTicker.C
			if t.IsZero() {
				return
			}
			fwd.lock.Lock()
			fwd.pool.ReactivateRetries(t)
			fwd.lock.Unlock()
		}
	}()

	fwd.chooseShim()
	go fwd.run()
	success = true
	return fwd, nil
}

func bridgeIP(br string) (net.IP, error) {
	iface, err := net.InterfaceByName(br)
	if err != nil {
		return nil, err
	}

	addrs, err := iface.Addrs()
	if err != nil {
		return nil, err
	}

	for _, addr := range addrs {
		if cidr, ok := addr.(*net.IPNet); ok {
			if ip := cidr.IP.To4(); ip != nil {
				return ip, nil
			}
		}
	}

	return nil, fmt.Errorf("no IPv4 address found on netdev %s", br)
}

func (fwd *forwarding) run() {
	for {
		conn, err := fwd.listener.AcceptTCP()
		if err != nil {
			if !fwd.stopped {
				fwd.errorSink.Post(err)
			}
			return
		}

		go fwd.forward(conn)
	}
}

func (fwd *forwarding) stop() {
	fwd.stopped = true
	fwd.listener.Close()
	fwd.ipTables.deleteRule("nat", fwd.rule)
}

func (fwd *forwarding) update(svc *model.Service) (bool, error) {
	if len(svc.Instances) == 0 {
		return false, nil
	}

	fwd.lock.Lock()
	defer fwd.lock.Unlock()

	// same address and same set of instances; stay as it is
	if svc.Equal(fwd.service) {
		return true, nil
	}

	if !svc.IP.Equal(fwd.service.IP) || svc.Port != fwd.service.Port {
		return false, nil
	}

	log.Info("forwarding service: ", svc.Summary())

	fwd.pool.UpdateInstances(svc.Instances)
	fwd.service = svc
	fwd.chooseShim()
	return true, nil
}

var shims = map[string]shimFunc{
	"tcp":  tcpShim,
	"http": httpShim,
}

func (fwd *forwarding) chooseShim() {
	shim := shims[fwd.service.Protocol]
	if shim == nil {
		log.Warn("service ", fwd.service.Name,
			": no support for protocol ", fwd.service.Protocol,
			", falling back to TCP forwarding")
		shim = tcpShim
	}

	fwd.shim = shim
}

func (fwd *forwarding) forward(inbound *net.TCPConn) {

retry:
	inst, shim := fwd.pickInstanceAndShim()
	if inst == nil {
		log.Errorf("ran out of instances on inbound ", inbound.LocalAddr())
		return
	}
	inAddr := inbound.RemoteAddr().(*net.TCPAddr)
	outAddr := inst.Instance().TCPAddr()

	outbound, err := net.DialTCP("tcp", nil, outAddr)
	if err != nil {
		log.Error("connecting to ", outAddr, ": ", err)
		inst.Fail()
		goto retry
	}
	inst.Keep()

	connEvent := &events.Connection{
		Service:  fwd.service,
		Instance: inst.Instance(),
		Inbound:  inAddr,
	}
	err = shim(inbound, outbound, connEvent, fwd.eventHandler)
	if err != nil {
		log.Error("forwarding from ", inAddr, " to ", outAddr, ": ",
			err)
	}
}

func (fwd *forwarding) pickInstanceAndShim() (pool.PooledInstance, shimFunc) {
	fwd.lock.Lock()
	defer fwd.lock.Unlock()
	inst := fwd.pool.PickInstance()
	return inst, fwd.shim
}

func tcpShim(inbound, outbound *net.TCPConn, connEvent *events.Connection, eh events.Handler) error {
	eh.Connection(connEvent)
	ch := make(chan error, 1)
	go func() {
		var err error
		defer func() { ch <- err }()
		_, err = io.Copy(inbound, outbound)
		outbound.CloseRead()
		inbound.CloseWrite()
	}()

	_, err1 := io.Copy(outbound, inbound)
	inbound.CloseRead()
	outbound.CloseWrite()

	err2 := <-ch
	inbound.Close()
	outbound.Close()

	if err1 != nil {
		return err1
	} else {
		return err2
	}
}
<commit_msg>Limit the number of connection attempts<commit_after>package balancer

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io"
	"net"
	"sync"
	"time"

	"github.com/weaveworks/flux/balancer/events"
	"github.com/weaveworks/flux/balancer/model"
	"github.com/weaveworks/flux/balancer/pool"
	"github.com/weaveworks/flux/common/daemon"
)

const max_connection_attempts = 5

type forwardingConfig struct {
	netConfig
	*ipTables
	eventHandler events.Handler
	errorSink    daemon.ErrorSink
}

type forwarding struct {
	forwardingConfig
	rule     []interface{}
	listener *net.TCPListener
	stopped  bool

	lock    sync.Mutex
	service *model.Service

	pool        pool.InstancePool
	retryTicker *time.Ticker

	shim shimFunc
}

type shimFunc func(inbound, outbound *net.TCPConn, conn *events.Connection, eventHandler events.Handler) error

func (fc forwardingConfig) start(svc *model.Service) (serviceState, error) {
	log.Info("forwarding service: ", svc.Summary())
	ip, err := bridgeIP(fc.bridge)
	if err != nil {
		return nil, err
	}

	listener, err := net.ListenTCP("tcp", &net.TCPAddr{IP: ip})
	if err != nil {
		return nil, err
	}

	success := false
	defer func() {
		if !success {
			listener.Close()
		}
	}()

	rule := []interface{}{
		"-p", "tcp",
		"-d", svc.IP,
		"--dport", svc.Port,
		"-j", "DNAT",
		"--to-destination", listener.Addr(),
	}
	err = fc.ipTables.addRule("nat", rule)
	if err != nil {
		return nil, err
	}

	fwd := &forwarding{
		forwardingConfig: fc,
		rule:             rule,
		listener:         listener,
		service:          svc,
		pool:             pool.NewInstancePool(),
		retryTicker:      time.NewTicker(1 * time.Second),
	}
	fwd.pool.UpdateInstances(svc.Instances)
	go func() {
		for {
			t := <-fwd.retryTicker.C
			if t.IsZero() {
				return
			}
			fwd.lock.Lock()
			fwd.pool.ReactivateRetries(t)
			fwd.lock.Unlock()
		}
	}()

	fwd.chooseShim()
	go fwd.run()
	success = true
	return fwd, nil
}

func bridgeIP(br string) (net.IP, error) {
	iface, err := net.InterfaceByName(br)
	if err != nil {
		return nil, err
	}

	addrs, err := iface.Addrs()
	if err != nil {
		return nil, err
	}

	for _, addr := range addrs {
		if cidr, ok := addr.(*net.IPNet); ok {
			if ip := cidr.IP.To4(); ip != nil {
				return ip, nil
			}
		}
	}

	return nil, fmt.Errorf("no IPv4 address found on netdev %s", br)
}

func (fwd *forwarding) run() {
	for {
		conn, err := fwd.listener.AcceptTCP()
		if err != nil {
			if !fwd.stopped {
				fwd.errorSink.Post(err)
			}
			return
		}

		go fwd.forward(conn)
	}
}

func (fwd *forwarding) stop() {
	fwd.stopped = true
	fwd.listener.Close()
	fwd.ipTables.deleteRule("nat", fwd.rule)
}

func (fwd *forwarding) update(svc *model.Service) (bool, error) {
	if len(svc.Instances) == 0 {
		return false, nil
	}

	fwd.lock.Lock()
	defer fwd.lock.Unlock()

	// same address and same set of instances; stay as it is
	if svc.Equal(fwd.service) {
		return true, nil
	}

	if !svc.IP.Equal(fwd.service.IP) || svc.Port != fwd.service.Port {
		return false, nil
	}

	log.Info("forwarding service: ", svc.Summary())

	fwd.pool.UpdateInstances(svc.Instances)
	fwd.service = svc
	fwd.chooseShim()
	return true, nil
}

var shims = map[string]shimFunc{
	"tcp":  tcpShim,
	"http": httpShim,
}

func (fwd *forwarding) chooseShim() {
	shim := shims[fwd.service.Protocol]
	if shim == nil {
		log.Warn("service ", fwd.service.Name,
			": no support for protocol ", fwd.service.Protocol,
			", falling back to TCP forwarding")
		shim = tcpShim
	}

	fwd.shim = shim
}

func (fwd *forwarding) forward(inbound *net.TCPConn) {
	inAddr := inbound.RemoteAddr().(*net.TCPAddr)

	for i := 0; i < max_connection_attempts; i++ {
		inst, shim := fwd.pickInstanceAndShim()
		if inst == nil {
			log.Errorf("ran out of instances on inbound ", inbound.LocalAddr())
			return
		}
		outAddr := inst.Instance().TCPAddr()

		outbound, err := net.DialTCP("tcp", nil, outAddr)
		if err != nil {
			log.Error("connecting to ", outAddr, ": ", err)
			inst.Fail()
			continue
		}
		inst.Keep()

		connEvent := &events.Connection{
			Service:  fwd.service,
			Instance: inst.Instance(),
			Inbound:  inAddr,
		}
		err = shim(inbound, outbound, connEvent, fwd.eventHandler)
		if err != nil {
			log.Error("forwarding from ", inAddr, " to ", outAddr, ": ",
				err)
		}
		return
	}
	inbound.Close()
	log.Errorf("abandoned connection from inbound %s, reached max of %d attempts",
		inAddr, max_connection_attempts)
}

func (fwd *forwarding) pickInstanceAndShim() (pool.PooledInstance, shimFunc) {
	fwd.lock.Lock()
	defer fwd.lock.Unlock()
	inst := fwd.pool.PickInstance()
	return inst, fwd.shim
}

func tcpShim(inbound, outbound *net.TCPConn, connEvent *events.Connection, eh events.Handler) error {
	eh.Connection(connEvent)
	ch := make(chan error, 1)
	go func() {
		var err error
		defer func() { ch <- err }()
		_, err = io.Copy(inbound, outbound)
		outbound.CloseRead()
		inbound.CloseWrite()
	}()

	_, err1 := io.Copy(outbound, inbound)
	inbound.CloseRead()
	outbound.CloseWrite()

	err2 := <-ch
	inbound.Close()
	outbound.Close()

	if err1 != nil {
		return err1
	} else {
		return err2
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
    "encoding/xml"
    "net/http"
    "io/ioutil"
    "strings"
	"github.com/yvasiyarov/newrelic_platform_go"
	"log"
	"strconv"
	"time"
)

var solrUrl = flag.String("solr-url", "127.0.0.1:8080/", "Solr url")
var newrelicLicense = flag.String("newrelic-license", "", "Newrelic license")
var verbose = flag.Bool("verbose", false, "Verbose mode")

const (
	MIN_PAUSE_TIME            = 30 //do not query sphinx often than once in 30 seconds
	SOLR_CONNECTION_TIMEOUT = 0  //no timeout
	NEWRELIC_POLL_INTERVAL    = 60 //Send data to newrelic every 60 seconds

	COMPONENT_NAME = "Solr"
	AGENT_GUID     = "com.github.yvasiyarov.Solr"
	AGENT_VERSION  = "0.0.1"
)

type SolrStatisticData map[string]ISolrHandlerStat
type ISolrHandlerStat interface{
    Parse(info *SolrQueryHandlerInfo) error
    GetName() string
}

type SolrHandlerStat struct {
    Requests float64
    Errors   float64
    Timeouts float64
    TotalTime float64
    AvgTimePerRequest float64
    AvgRequestsPerSecond float64
    Name string
    ClassName string
}

func (stat *SolrHandlerStat) GetName() string {
    return stat.Name
}

type SolrHandlerStatSearch struct {
    SolrHandlerStat
}
type SolrHandlerStatUpdate struct {
    SolrHandlerStat
}

func NewSolrHandlerStat(solrClassName string) ISolrHandlerStat{
    solrClassName = strings.TrimSpace(solrClassName)
    switch solrClassName {
        case "org.apache.solr.handler.component.SearchHandler":{
            return &SolrHandlerStatSearch{SolrHandlerStat{ClassName: solrClassName}}
        }
        case "org.apache.solr.update.DirectUpdateHandler2":{
            return &SolrHandlerStatUpdate{SolrHandlerStat{ClassName: solrClassName}}
        }
    }
    return nil
}

func (stat *SolrHandlerStatSearch) Parse(info *SolrQueryHandlerInfo) error {
    stat.Name = strings.TrimSpace(info.Name)
    for _, statItem := range info.Stats {
        value, err := strconv.ParseFloat(strings.TrimSpace(statItem.Value), 64)
        if err != nil {
            return err
        }
        switch statItem.Name {
            case "avgRequestsPerSecond":  {
                stat.AvgRequestsPerSecond = value
            }
            case "avgTimePerRequest":  {
                stat.AvgTimePerRequest = value
            }
            case "totalTime":  {
                stat.TotalTime = value
            }
            case "timeouts":  {
                stat.Timeouts = value
            }
            case "errors":  {
                stat.Errors = value
            }
            case "requests":  {
                stat.Requests = value
            }
        }
    }
    return nil
}

func (stat *SolrHandlerStatUpdate) Parse(info *SolrQueryHandlerInfo) error {
    stat.Name = strings.TrimSpace(info.Name)
    return nil
}

type MetricsDataSource struct {
	SolrUrl           string
	Port              int
	ConnectionTimeout int

	PreviousData   SolrStatisticData
	LastData       SolrStatisticData
	LastUpdateTime time.Time
}

func NewMetricsDataSource(solrUrl string, connectionTimeout int) *MetricsDataSource {
	ds := &MetricsDataSource{
		SolrUrl:           solrUrl,
		ConnectionTimeout: connectionTimeout,
	}
	return ds
}

type SolrResponse struct {
    SolrInfo SolrInfo `xml:"solr-info"`
}
type SolrInfo struct {
    QueryHandler SolrQueryHandler `xml:"QUERYHANDLER"`
}
type SolrQueryHandler struct {
    QueryHandlerInfo []SolrQueryHandlerInfo `xml:"entry"`
}
type SolrQueryHandlerInfo struct {
     Name string `xml:"name"`
     ClassName string `xml:"class"`
     Version string `xml:"version"`
     Description string `xml:"description"`
     Stats       []SolrQueryHandlerInfoItem `xml:"stats>stat"`
}
type SolrQueryHandlerInfoItem struct {
     Name string `xml:"name,attr"`
     Value string `xml:",innerxml"`
}

func (ds *MetricsDataSource) QueryData() (SolrStatisticData, error) {
    resp, err := http.Get("http://" + ds.SolrUrl + "admin/stats.jsp")

     if err != nil {
         return nil, err
     }

     defer resp.Body.Close()
     if resp.StatusCode != 200 {
         return nil, nil
     }
     body, err := ioutil.ReadAll(resp.Body)
     if err != nil {
         return nil, err
     }

    response := SolrResponse{}
    err = xml.Unmarshal(body, &response)
    if err != nil {
        return nil, err
    }

    data := make(SolrStatisticData, len(response.SolrInfo.QueryHandler.QueryHandlerInfo))
    for _, handler := range response.SolrInfo.QueryHandler.QueryHandlerInfo {
        stat := NewSolrHandlerStat(handler.ClassName)
        if stat == nil {
            continue;
        }
        err := stat.Parse(&handler)
        if err != nil {
            log.Printf("Handler stat parse error:%v. Handler: %#v", err, handler)
            continue
        }
        data[stat.GetName()] = stat
    }

    return data, nil
}

func main() {
	flag.Parse()
    /*
	if *newrelicLicense == "" {
		log.Fatalf("Please, pass a valid newrelic license key.\n Use --help to get more information about available options\n")
	}
*/
	plugin := newrelic_platform_go.NewNewrelicPlugin(AGENT_VERSION, *newrelicLicense, NEWRELIC_POLL_INTERVAL)
	component := newrelic_platform_go.NewPluginComponent(COMPONENT_NAME, AGENT_GUID)
	plugin.AddComponent(component)

	ds := NewMetricsDataSource(*solrUrl, SOLR_CONNECTION_TIMEOUT)
    d, _ := ds.QueryData()
    for k, v := range d {
        fmt.Printf("key:%v, value:%#v\n", k, v)
    }
    //fmt.Printf("Error: %v\n, data:%#v\n", err, d)
	//AddMetrcas(component, ds)

//	plugin.Verbose = *verbose
//	plugin.Harvest()
	//plugin.Run()
}

/*
func (ds *MetricsDataSource) CheckAndGetData(key string) (float64, error) {
	if err := ds.CheckAndUpdateData(); err != nil {
		return 0, err
	}

	prev, last, err := ds.GetOriginalData(key)

	if err != nil {
		return 0, err
	}
	return last - prev, nil
}
func (ds *MetricsDataSource) CheckAndGetLastData(key string) (float64, error) {
	if err := ds.CheckAndUpdateData(); err != nil {
		return 0, err
	}

	_, last, err := ds.GetOriginalData(key)

	if err != nil {
		return 0, err
	}
	return last, nil
}

func (ds *MetricsDataSource) GetOriginalData(key string) (float64, float64, error) {
	previousValue, ok := ds.PreviousData[key]
	if !ok {
		return 0, 0, fmt.Errorf("Can not get data from source \n")
	}
	currentValue, ok := ds.LastData[key]
	if !ok {
		return 0, 0, fmt.Errorf("Can not get data from source \n")
	}

	//some metric calculation can be turned off by sphinx settings
	if previousValue == "OFF" || currentValue == "OFF" {
		return 0, 0, nil
	}

	previousValueConverted, err := strconv.ParseFloat(previousValue, 64)
	if err != nil {
		return 0, 0, fmt.Errorf("Can not convert previous value of %s to int \n", key)
	}
	currentValueConverted, err := strconv.ParseFloat(currentValue, 64)
	if err != nil {
		return 0, 0, fmt.Errorf("Can not convert current value of %s to int \n", key)
	}

	return previousValueConverted, currentValueConverted, nil
}

func (ds *MetricsDataSource) CheckAndUpdateData() error {
	startTime := time.Now()
	if startTime.Sub(ds.LastUpdateTime) > time.Second*MIN_PAUSE_TIME {
		newData, err := ds.QueryData()
		if err != nil {
			return err
		}

		if ds.PreviousData == nil {
			ds.PreviousData = newData
		} else {
			ds.PreviousData = ds.LastData
		}
		ds.LastData = newData
		ds.LastUpdateTime = startTime
	}

	// check uptime
	//If uptime is less then in previous run - then server were restarted
	if prev, last, err := ds.GetOriginalData("uptime"); err != nil {
		return err
	} else {
		if last < prev {
			ds.PreviousData = ds.LastData
		}
	}
	return nil
}
*/
/*
type Metrica struct {
Name       string
Units      string
DataKey    string
DataSource *MetricsDataSource
}

func (metrica *Metrica) GetName() string {
return metrica.Name
}
func (metrica *Metrica) GetUnits() string {
return metrica.Units
}
func (metrica *Metrica) GetValue() (float64, error) {
return metrica.DataSource.CheckAndGetData(metrica.DataKey)
}

type AvgMetrica struct {
Metrica
}

func (metrica *AvgMetrica) GetValue() (float64, error) {
return metrica.DataSource.CheckAndGetLastData(metrica.DataKey)
}

func AddMetrcas(component newrelic_platform_go.IComponent, dataSource *MetricsDataSource) {
metricas := []*Metrica{
&Metrica{
DataKey: "queries",
Name:    "Queries",
Units:   "Queries/second",
},
&Metrica{
DataKey: "connections",
Name:    "Connections",
Units:   "connections/second",
},
&Metrica{
DataKey: "maxed_out",
Name:    "Maxed out",
Units:   "connections/second",
},
&Metrica{
DataKey: "command_search",
			Name:    "Command search",
			Units:   "command/second",
		},
		&Metrica{
			DataKey: "command_excerpt",
			Name:    "Command excerpt",
			Units:   "command/second",
		},
		&Metrica{
			DataKey: "command_update",
			Name:    "Command update",
			Units:   "command/second",
		},
		&Metrica{
			DataKey: "command_keywords",
			Name:    "Command keywords",
			Units:   "command/second",
		},
		&Metrica{
			DataKey: "command_persist",
			Name:    "Command persist",
			Units:   "command/second",
		},
		&Metrica{
			DataKey: "command_flushattrs",
			Name:    "Command flushattrs",
			Units:   "command/second",
		},
	}
	for _, m := range metricas {
		m.DataSource = dataSource
		component.AddMetrica(m)
	}

	avgQueryWallTime := &AvgMetrica{
		Metrica{
			DataKey: "avg_query_wall",
			Name:    "Avg Query Wall Time",
			Units:   "milisecond",
		},
	}
	avgQueryWallTime.DataSource = dataSource
	component.AddMetrica(avgQueryWallTime)
}
*/

<commit_msg>Parsing of XML updates handlers is also done<commit_after>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"github.com/yvasiyarov/newrelic_platform_go"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"
)

var solrUrl = flag.String("solr-url", "127.0.0.1:8080/", "Solr url")
var newrelicLicense = flag.String("newrelic-license", "", "Newrelic license")
var verbose = flag.Bool("verbose", false, "Verbose mode")

const (
	MIN_PAUSE_TIME          = 30 //do not query sphinx often than once in 30 seconds
	SOLR_CONNECTION_TIMEOUT = 0  //no timeout
	NEWRELIC_POLL_INTERVAL  = 60 //Send data to newrelic every 60 seconds

	COMPONENT_NAME = "Solr"
	AGENT_GUID     = "com.github.yvasiyarov.Solr"
	AGENT_VERSION  = "0.0.1"
)

type SolrStatisticData map[string]ISolrHandlerStat
type ISolrHandlerStat interface {
	Parse(info *SolrQueryHandlerInfo) error
	GetName() string
}

type SolrHandlerStat struct {
	Name      string
	ClassName string
}

func (stat *SolrHandlerStat) GetName() string {
	return stat.Name
}

type SolrHandlerStatSearch struct {
	SolrHandlerStat
	Requests             float64
	Errors               float64
	Timeouts             float64
	TotalTime            float64
	AvgTimePerRequest    float64
	AvgRequestsPerSecond float64
}
type SolrHandlerStatUpdate struct {
	SolrHandlerStat
	Commits                  float64
	Autocommits              float64
	Optimizes                float64
	Rollbacks                float64
	ExpungeDeletes           float64
	DocsPending              float64
	Adds                     float64
	DeletesById              float64
	DeletesByQuery           float64
	Errors                   float64
	CumulativeAdds           float64
	CumulativeDeletesById    float64
	CumulativeDeletesByQuery float64
	CumulativeErrors         float64
}

func NewSolrHandlerStat(solrClassName string) ISolrHandlerStat {
	solrClassName = strings.TrimSpace(solrClassName)
	switch solrClassName {
	case "org.apache.solr.handler.component.SearchHandler", "org.apache.solr.handler.XmlUpdateRequestHandler":
		{
            return &SolrHandlerStatSearch{SolrHandlerStat: SolrHandlerStat{ClassName: solrClassName}}
		}
	case "org.apache.solr.update.DirectUpdateHandler2":
		{
            return &SolrHandlerStatUpdate{SolrHandlerStat: SolrHandlerStat{ClassName: solrClassName}}
		}
	}
	return nil
}

func (stat *SolrHandlerStatSearch) Parse(info *SolrQueryHandlerInfo) error {
	stat.Name = strings.TrimSpace(info.Name)
	for _, statItem := range info.Stats {
		value, err := strconv.ParseFloat(strings.TrimSpace(statItem.Value), 64)
		if err != nil {
			return err
		}
		switch statItem.Name {
		case "avgRequestsPerSecond":
			{
				stat.AvgRequestsPerSecond = value
			}
		case "avgTimePerRequest":
			{
				stat.AvgTimePerRequest = value
			}
		case "totalTime":
			{
				stat.TotalTime = value
			}
		case "timeouts":
			{
				stat.Timeouts = value
			}
		case "errors":
			{
				stat.Errors = value
			}
		case "requests":
			{
				stat.Requests = value
			}
		}
	}
	return nil
}

func (stat *SolrHandlerStatUpdate) Parse(info *SolrQueryHandlerInfo) error {
	stat.Name = strings.TrimSpace(info.Name)
	for _, statItem := range info.Stats {
		value, err := strconv.ParseFloat(strings.TrimSpace(statItem.Value), 64)
		if err != nil {
			return err
		}
		switch statItem.Name {
		case "commits":
			{
				stat.Commits = value
			}
		case "autocommits":
			{
				stat.Autocommits = value
			}
		case "optimizes":
			{
				stat.Optimizes = value
			}
		case "rollbacks":
			{
				stat.Rollbacks = value
			}
		case "expungeDeletes":
			{
				stat.ExpungeDeletes = value
			}
		case "docsPending":
			{
				stat.DocsPending = value
			}
		case "adds":
			{
				stat.Adds = value
			}
		case "deletesById":
			{
				stat.DeletesById = value
			}
		case "deletesByQuery":
			{
				stat.DeletesByQuery = value
			}
		case "errors":
			{
				stat.Errors = value
			}
		case "cumulative_adds":
			{
				stat.CumulativeAdds = value
			}
		case "cumulative_deletesById":
			{
				stat.CumulativeDeletesById = value
			}
		case "cumulative_deletesByQuery":
			{
				stat.CumulativeDeletesByQuery = value
			}
		case "cumulative_errors":
			{
				stat.CumulativeErrors = value
			}
		}
	}
	return nil
}

type MetricsDataSource struct {
	SolrUrl           string
	Port              int
	ConnectionTimeout int

	PreviousData   SolrStatisticData
	LastData       SolrStatisticData
	LastUpdateTime time.Time
}

func NewMetricsDataSource(solrUrl string, connectionTimeout int) *MetricsDataSource {
	ds := &MetricsDataSource{
		SolrUrl:           solrUrl,
		ConnectionTimeout: connectionTimeout,
	}
	return ds
}

type SolrResponse struct {
	SolrInfo SolrInfo `xml:"solr-info"`
}
type SolrInfo struct {
	QueryHandler SolrQueryHandler `xml:"QUERYHANDLER"`
}
type SolrQueryHandler struct {
	QueryHandlerInfo []SolrQueryHandlerInfo `xml:"entry"`
}
type SolrQueryHandlerInfo struct {
	Name        string                     `xml:"name"`
	ClassName   string                     `xml:"class"`
	Version     string                     `xml:"version"`
	Description string                     `xml:"description"`
	Stats       []SolrQueryHandlerInfoItem `xml:"stats>stat"`
}
type SolrQueryHandlerInfoItem struct {
	Name  string `xml:"name,attr"`
	Value string `xml:",innerxml"`
}

func (ds *MetricsDataSource) QueryData() (SolrStatisticData, error) {
	resp, err := http.Get("http://" + ds.SolrUrl + "admin/stats.jsp")

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return nil, nil
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	response := SolrResponse{}
	err = xml.Unmarshal(body, &response)
	if err != nil {
		return nil, err
	}

	data := make(SolrStatisticData, len(response.SolrInfo.QueryHandler.QueryHandlerInfo))
	for _, handler := range response.SolrInfo.QueryHandler.QueryHandlerInfo {
		stat := NewSolrHandlerStat(handler.ClassName)
		if stat == nil {
			continue
		}
		err := stat.Parse(&handler)
		if err != nil {
			log.Printf("Handler stat parse error:%v. Handler: %#v", err, handler)
			continue
		}
		data[stat.GetName()] = stat
	}

	return data, nil
}

func main() {
	flag.Parse()
	/*
		if *newrelicLicense == "" {
			log.Fatalf("Please, pass a valid newrelic license key.\n Use --help to get more information about available options\n")
		}
	*/
	plugin := newrelic_platform_go.NewNewrelicPlugin(AGENT_VERSION, *newrelicLicense, NEWRELIC_POLL_INTERVAL)
	component := newrelic_platform_go.NewPluginComponent(COMPONENT_NAME, AGENT_GUID)
	plugin.AddComponent(component)

	ds := NewMetricsDataSource(*solrUrl, SOLR_CONNECTION_TIMEOUT)
	d, _ := ds.QueryData()
	for k, v := range d {
		fmt.Printf("key:%v, value:%#v\n", k, v)
	}
	//fmt.Printf("Error: %v\n, data:%#v\n", err, d)
	//AddMetrcas(component, ds)

	//	plugin.Verbose = *verbose
	//	plugin.Harvest()
	//plugin.Run()
}

/*
func (ds *MetricsDataSource) CheckAndGetData(key string) (float64, error) {
	if err := ds.CheckAndUpdateData(); err != nil {
		return 0, err
	}

	prev, last, err := ds.GetOriginalData(key)

	if err != nil {
		return 0, err
	}
	return last - prev, nil
}
func (ds *MetricsDataSource) CheckAndGetLastData(key string) (float64, error) {
	if err := ds.CheckAndUpdateData(); err != nil {
		return 0, err
	}

	_, last, err := ds.GetOriginalData(key)

	if err != nil {
		return 0, err
	}
	return last, nil
}

func (ds *MetricsDataSource) GetOriginalData(key string) (float64, float64, error) {
	previousValue, ok := ds.PreviousData[key]
	if !ok {
		return 0, 0, fmt.Errorf("Can not get data from source \n")
	}
	currentValue, ok := ds.LastData[key]
	if !ok {
		return 0, 0, fmt.Errorf("Can not get data from source \n")
	}

	//some metric calculation can be turned off by sphinx settings
	if previousValue == "OFF" || currentValue == "OFF" {
		return 0, 0, nil
	}

	previousValueConverted, err := strconv.ParseFloat(previousValue, 64)
	if err != nil {
		return 0, 0, fmt.Errorf("Can not convert previous value of %s to int \n", key)
	}
	currentValueConverted, err := strconv.ParseFloat(currentValue, 64)
	if err != nil {
		return 0, 0, fmt.Errorf("Can not convert current value of %s to int \n", key)
	}

	return previousValueConverted, currentValueConverted, nil
}

func (ds *MetricsDataSource) CheckAndUpdateData() error {
	startTime := time.Now()
	if startTime.Sub(ds.LastUpdateTime) > time.Second*MIN_PAUSE_TIME {
		newData, err := ds.QueryData()
		if err != nil {
			return err
		}

		if ds.PreviousData == nil {
			ds.PreviousData = newData
		} else {
			ds.PreviousData = ds.LastData
		}
		ds.LastData = newData
		ds.LastUpdateTime = startTime
	}

	// check uptime
	//If uptime is less then in previous run - then server were restarted
	if prev, last, err := ds.GetOriginalData("uptime"); err != nil {
		return err
	} else {
		if last < prev {
			ds.PreviousData = ds.LastData
		}
	}
	return nil
}
*/
/*
type Metrica struct {
Name       string
Units      string
DataKey    string
DataSource *MetricsDataSource
}

func (metrica *Metrica) GetName() string {
return metrica.Name
}
func (metrica *Metrica) GetUnits() string {
return metrica.Units
}
func (metrica *Metrica) GetValue() (float64, error) {
return metrica.DataSource.CheckAndGetData(metrica.DataKey)
}

type AvgMetrica struct {
Metrica
}

func (metrica *AvgMetrica) GetValue() (float64, error) {
return metrica.DataSource.CheckAndGetLastData(metrica.DataKey)
}

func AddMetrcas(component newrelic_platform_go.IComponent, dataSource *MetricsDataSource) {
metricas := []*Metrica{
&Metrica{
DataKey: "queries",
Name:    "Queries",
Units:   "Queries/second",
},
&Metrica{
DataKey: "connections",
Name:    "Connections",
Units:   "connections/second",
},
&Metrica{
DataKey: "maxed_out",
Name:    "Maxed out",
Units:   "connections/second",
},
&Metrica{
DataKey: "command_search",
			Name:    "Command search",
			Units:   "command/second",
		},
		&Metrica{
			DataKey: "command_excerpt",
			Name:    "Command excerpt",
			Units:   "command/second",
		},
		&Metrica{
			DataKey: "command_update",
			Name:    "Command update",
			Units:   "command/second",
		},
		&Metrica{
			DataKey: "command_keywords",
			Name:    "Command keywords",
			Units:   "command/second",
		},
		&Metrica{
			DataKey: "command_persist",
			Name:    "Command persist",
			Units:   "command/second",
		},
		&Metrica{
			DataKey: "command_flushattrs",
			Name:    "Command flushattrs",
			Units:   "command/second",
		},
	}
	for _, m := range metricas {
		m.DataSource = dataSource
		component.AddMetrica(m)
	}

	avgQueryWallTime := &AvgMetrica{
		Metrica{
			DataKey: "avg_query_wall",
			Name:    "Avg Query Wall Time",
			Units:   "milisecond",
		},
	}
	avgQueryWallTime.DataSource = dataSource
	component.AddMetrica(avgQueryWallTime)
}
*/
<|endoftext|>