<commit_before>package chunkymonkey

import (
	"bytes"
	"fmt"
	"log"
	"net"
	"os"
	"rand"
	"regexp"
	"time"

	. "chunkymonkey/entity"
	"chunkymonkey/gamerules"
	"chunkymonkey/player"
	"chunkymonkey/proto"
	"chunkymonkey/server_auth"
	"chunkymonkey/shardserver"
	. "chunkymonkey/types"
	"chunkymonkey/worldstore"
	"nbt"
)

// We regard usernames as valid if they don't contain "dangerous" characters.
// That is: characters that might be abused in filename components, etc.
var validPlayerUsername = regexp.MustCompile(`^[\-a-zA-Z0-9_]+$`)

type Game struct {
	chunkManager     *shardserver.LocalShardManager
	mainQueue        chan func(*Game)
	playerDisconnect chan EntityId
	entityManager    EntityManager
	players          map[EntityId]*player.Player
	time             Ticks
	serverId         string
	worldStore       *worldstore.WorldStore
	// If set, logins are not allowed.
	UnderMaintenanceMsg string
}

func NewGame(worldPath string) (game *Game, err os.Error) {
	worldStore, err := worldstore.LoadWorldStore(worldPath)
	if err != nil {
		return nil, err
	}

	game = &Game{
		mainQueue:        make(chan func(*Game), 256),
		playerDisconnect: make(chan EntityId),
		players:          make(map[EntityId]*player.Player),
		time:             worldStore.Time,
		worldStore:       worldStore,
	}

	game.entityManager.Init()

	game.serverId = fmt.Sprintf("%016x", rand.NewSource(worldStore.Seed).Int63())
	//game.serverId = "-"

	game.chunkManager = shardserver.NewLocalShardManager(worldStore.ChunkStore, &game.entityManager)

	go game.mainLoop()
	return
}

// login negotiates a player client login, and adds a new player if successful.
// Note that it does not run in the game's goroutine.
func (game *Game) login(conn net.Conn) {
	var err, clientErr os.Error

	defer func() {
		if err != nil {
			log.Print(err.String())
			if clientErr == nil {
				clientErr = os.NewError("Server error.")
			}
			proto.WriteDisconnect(conn, clientErr.String())
			conn.Close()
		}
	}()

	var username string
	if username, err = proto.ServerReadHandshake(conn); err != nil {
		clientErr = os.NewError("Handshake error.")
		return
	}

	if !validPlayerUsername.MatchString(username) {
		err = os.NewError("Bad username")
		clientErr = err
		return
	}

	log.Print("Client ", conn.RemoteAddr(), " connected as ", username)

	if game.UnderMaintenanceMsg != "" {
		err = fmt.Errorf("Server under maintenance, kicking player: %q", username)
		clientErr = os.NewError(game.UnderMaintenanceMsg)
		return
	}

	// Load player permissions.
	permissions := gamerules.Permissions.UserPermissions(username)
	if !permissions.Has("login") {
		err = fmt.Errorf("Player %q does not have login permission", username)
		clientErr = os.NewError("You do not have access to this server.")
		return
	}

	if err = proto.ServerWriteHandshake(conn, game.serverId); err != nil {
		clientErr = os.NewError("Handshake error.")
		return
	}

	if game.serverId != "-" {
		var authenticated bool
		authserver := &server_auth.ServerAuth{"http://www.minecraft.net/game/checkserver.jsp"}
		authenticated, err = authserver.Authenticate(game.serverId, username)
		if !authenticated || err != nil {
			var reason string
			if err != nil {
				reason = "Authentication check failed: " + err.String()
			} else {
				reason = "Failed authentication"
			}
			err = fmt.Errorf("Client %v: %s", conn.RemoteAddr(), reason)
			clientErr = os.NewError(reason)
			return
		}
		log.Print("Client ", conn.RemoteAddr(), " passed minecraft.net authentication")
	}

	if _, err = proto.ServerReadLogin(conn); err != nil {
		clientErr = os.NewError("Login error.")
		return
	}

	entityId := game.entityManager.NewEntity()

	var playerData nbt.ITag
	if playerData, err = game.worldStore.PlayerData(username); err != nil {
		clientErr = os.NewError("Error reading user data. Please contact the server administrator.")
		return
	}

	player := player.NewPlayer(entityId, game.chunkManager, conn, username, game.worldStore.SpawnPosition, game.playerDisconnect, game)
	if playerData != nil {
		if err = player.ReadNbt(playerData); err != nil {
			// Don't let the player log in, as they will only have default inventory
			// etc., which could lose items from them. Better for an administrator to
			// sort this out.
			err = fmt.Errorf("Error parsing player data for %q: %v", username, err)
			clientErr = os.NewError("Error reading user data. Please contact the server administrator.")
			return
		}
	}

	addedChan := make(chan struct{})
	game.enqueue(func(_ *Game) {
		game.addPlayer(player)
		addedChan <- struct{}{}
	})
	_ = <-addedChan

	player.Start()
}

func (game *Game) Serve(addr string) {
	listener, e := net.Listen("tcp", addr)
	if e != nil {
		log.Fatalf("Listen: %s", e.String())
	}
	log.Print("Listening on ", addr)

	for {
		conn, e2 := listener.Accept()
		if e2 != nil {
			log.Print("Accept: ", e2.String())
			continue
		}

		go game.login(conn)
	}
}

// addPlayer adds the player to the set of connected players.
func (game *Game) addPlayer(newPlayer *player.Player) {
	game.players[newPlayer.GetEntityId()] = newPlayer
}

func (game *Game) removePlayer(entityId EntityId) {
	game.players[entityId] = nil, false
	game.entityManager.RemoveEntityById(entityId)
}

func (game *Game) multicastPacket(packet []byte, except interface{}) {
	for _, player := range game.players {
		if player == except {
			continue
		}

		player.TransmitPacket(packet)
	}
}

func (game *Game) enqueue(f func(*Game)) {
	game.mainQueue <- f
}

func (game *Game) mainLoop() {
	ticker := time.NewTicker(NanosecondsInSecond / TicksPerSecond)

	for {
		select {
		case f := <-game.mainQueue:
			f(game)
		case <-ticker.C:
			game.tick()
		case entityId := <-game.playerDisconnect:
			game.removePlayer(entityId)
		}
	}
}

func (game *Game) sendTimeUpdate() {
	buf := new(bytes.Buffer)
	proto.ServerWriteTimeUpdate(buf, game.time)

	// The "keep-alive" packet to client(s) sent here as well, as there
	// seems no particular reason to send time and keep-alive separately
	// for now.
	proto.WriteKeepAlive(buf)

	game.multicastPacket(buf.Bytes(), nil)
}

func (game *Game) tick() {
	game.time++
	if game.time%TicksPerSecond == 0 {
		game.sendTimeUpdate()
	}
}

func (game *Game) getPlayerFromName(name string) *player.Player {
	// TODO: This should be made more efficient through a lookup, etc.
	result := make(chan *player.Player)
	game.enqueue(func(_ *Game) {
		for _, player := range game.players {
			if player.Name() == name {
				result <- player
			}
		}
		close(result)
	})
	return <-result
}

// GiveItem implements ICommandHandler.GiveItem
func (game *Game) GiveItem(name string, id, quantity, data int) {
	//	player := game.getPlayerFromName(name)
	//	item := gamerules.Slot{
	//		ItemTypeId: ItemTypeId(id),
	//		Count:      ItemCount(quantity),
	//		Data:       ItemData(data),
	//	}

	// TODO: Spawn the item created at the player's block location
}

// SendMessageToPlayer implements ICommandHandler.SendMessageToPlayer
func (game *Game) SendMessageToPlayer(name, msg string) {
	player := game.getPlayerFromName(name)

	buf := new(bytes.Buffer)
	proto.WriteChatMessage(buf, msg)
	packet := buf.Bytes()
	player.TransmitPacket(packet)
}

// BroadcastMessage implements ICommandHandler.BroadcastMessage
func (game *Game) BroadcastMessage(msg string) {
	buf := new(bytes.Buffer)
	proto.WriteChatMessage(buf, msg)

	game.enqueue(func(_ *Game) {
		game.multicastPacket(buf.Bytes(), nil)
	})
}
<commit_msg>Add a mapping between player name and player.Player<commit_after>package chunkymonkey

import (
	"bytes"
	"fmt"
	"log"
	"net"
	"os"
	"rand"
	"regexp"
	"time"

	. "chunkymonkey/entity"
	"chunkymonkey/gamerules"
	"chunkymonkey/player"
	"chunkymonkey/proto"
	"chunkymonkey/server_auth"
	"chunkymonkey/shardserver"
	. "chunkymonkey/types"
	"chunkymonkey/worldstore"
	"nbt"
)

// We regard usernames as valid if they don't contain "dangerous" characters.
// That is: characters that might be abused in filename components, etc.
var validPlayerUsername = regexp.MustCompile(`^[\-a-zA-Z0-9_]+$`)

type Game struct {
	chunkManager     *shardserver.LocalShardManager
	mainQueue        chan func(*Game)
	playerDisconnect chan EntityId
	entityManager    EntityManager
	players          map[EntityId]*player.Player
	playerNames      map[string]*player.Player
	time             Ticks
	serverId         string
	worldStore       *worldstore.WorldStore
	// If set, logins are not allowed.
	UnderMaintenanceMsg string
}

func NewGame(worldPath string) (game *Game, err os.Error) {
	worldStore, err := worldstore.LoadWorldStore(worldPath)
	if err != nil {
		return nil, err
	}

	game = &Game{
		mainQueue:        make(chan func(*Game), 256),
		playerDisconnect: make(chan EntityId),
		players:          make(map[EntityId]*player.Player),
		playerNames:      make(map[string]*player.Player),
		time:             worldStore.Time,
		worldStore:       worldStore,
	}

	game.entityManager.Init()

	game.serverId = fmt.Sprintf("%016x", rand.NewSource(worldStore.Seed).Int63())
	//game.serverId = "-"

	game.chunkManager = shardserver.NewLocalShardManager(worldStore.ChunkStore, &game.entityManager)

	go game.mainLoop()
	return
}

// login negotiates a player client login, and adds a new player if successful.
// Note that it does not run in the game's goroutine.
func (game *Game) login(conn net.Conn) {
	var err, clientErr os.Error

	defer func() {
		if err != nil {
			log.Print(err.String())
			if clientErr == nil {
				clientErr = os.NewError("Server error.")
			}
			proto.WriteDisconnect(conn, clientErr.String())
			conn.Close()
		}
	}()

	var username string
	if username, err = proto.ServerReadHandshake(conn); err != nil {
		clientErr = os.NewError("Handshake error.")
		return
	}

	if !validPlayerUsername.MatchString(username) {
		err = os.NewError("Bad username")
		clientErr = err
		return
	}

	log.Print("Client ", conn.RemoteAddr(), " connected as ", username)

	if game.UnderMaintenanceMsg != "" {
		err = fmt.Errorf("Server under maintenance, kicking player: %q", username)
		clientErr = os.NewError(game.UnderMaintenanceMsg)
		return
	}

	// Load player permissions.
	permissions := gamerules.Permissions.UserPermissions(username)
	if !permissions.Has("login") {
		err = fmt.Errorf("Player %q does not have login permission", username)
		clientErr = os.NewError("You do not have access to this server.")
		return
	}

	if err = proto.ServerWriteHandshake(conn, game.serverId); err != nil {
		clientErr = os.NewError("Handshake error.")
		return
	}

	if game.serverId != "-" {
		var authenticated bool
		authserver := &server_auth.ServerAuth{"http://www.minecraft.net/game/checkserver.jsp"}
		authenticated, err = authserver.Authenticate(game.serverId, username)
		if !authenticated || err != nil {
			var reason string
			if err != nil {
				reason = "Authentication check failed: " + err.String()
			} else {
				reason = "Failed authentication"
			}
			err = fmt.Errorf("Client %v: %s", conn.RemoteAddr(), reason)
			clientErr = os.NewError(reason)
			return
		}
		log.Print("Client ", conn.RemoteAddr(), " passed minecraft.net authentication")
	}

	if _, err = proto.ServerReadLogin(conn); err != nil {
		clientErr = os.NewError("Login error.")
		return
	}

	entityId := game.entityManager.NewEntity()

	var playerData nbt.ITag
	if playerData, err = game.worldStore.PlayerData(username); err != nil {
		clientErr = os.NewError("Error reading user data. Please contact the server administrator.")
		return
	}

	player := player.NewPlayer(entityId, game.chunkManager, conn, username, game.worldStore.SpawnPosition, game.playerDisconnect, game)
	if playerData != nil {
		if err = player.ReadNbt(playerData); err != nil {
			// Don't let the player log in, as they will only have default inventory
			// etc., which could lose items from them. Better for an administrator to
			// sort this out.
			err = fmt.Errorf("Error parsing player data for %q: %v", username, err)
			clientErr = os.NewError("Error reading user data. Please contact the server administrator.")
			return
		}
	}

	addedChan := make(chan struct{})
	game.enqueue(func(_ *Game) {
		game.addPlayer(player)
		addedChan <- struct{}{}
	})
	_ = <-addedChan

	player.Start()
}

func (game *Game) Serve(addr string) {
	listener, e := net.Listen("tcp", addr)
	if e != nil {
		log.Fatalf("Listen: %s", e.String())
	}
	log.Print("Listening on ", addr)

	for {
		conn, e2 := listener.Accept()
		if e2 != nil {
			log.Print("Accept: ", e2.String())
			continue
		}

		go game.login(conn)
	}
}

// addPlayer adds the player to the set of connected players.
func (game *Game) addPlayer(newPlayer *player.Player) {
	game.players[newPlayer.GetEntityId()] = newPlayer
	game.playerNames[newPlayer.Name()] = newPlayer
}

func (game *Game) removePlayer(entityId EntityId) {
	oldPlayer := game.players[entityId]
	game.players[entityId] = nil, false
	game.playerNames[oldPlayer.Name()] = nil, false
	game.entityManager.RemoveEntityById(entityId)
}

func (game *Game) multicastPacket(packet []byte, except interface{}) {
	for _, player := range game.players {
		if player == except {
			continue
		}

		player.TransmitPacket(packet)
	}
}

func (game *Game) enqueue(f func(*Game)) {
	game.mainQueue <- f
}

func (game *Game) mainLoop() {
	ticker := time.NewTicker(NanosecondsInSecond / TicksPerSecond)

	for {
		select {
		case f := <-game.mainQueue:
			f(game)
		case <-ticker.C:
			game.tick()
		case entityId := <-game.playerDisconnect:
			game.removePlayer(entityId)
		}
	}
}

func (game *Game) sendTimeUpdate() {
	buf := new(bytes.Buffer)
	proto.ServerWriteTimeUpdate(buf, game.time)

	// The "keep-alive" packet to client(s) sent here as well, as there
	// seems no particular reason to send time and keep-alive separately
	// for now.
	proto.WriteKeepAlive(buf)

	game.multicastPacket(buf.Bytes(), nil)
}

func (game *Game) tick() {
	game.time++
	if game.time%TicksPerSecond == 0 {
		game.sendTimeUpdate()
	}
}

func (game *Game) getPlayerFromName(name string) *player.Player {
	result := make(chan *player.Player)
	game.enqueue(func(_ *Game) {
		player, ok := game.playerNames[name]
		if ok {
			result <- player
		} else {
			result <- nil
		}
		close(result)
	})
	return <-result
}

// GiveItem implements ICommandHandler.GiveItem
func (game *Game) GiveItem(name string, id, quantity, data int) {
	//	player := game.getPlayerFromName(name)
	//	item := gamerules.Slot{
	//		ItemTypeId: ItemTypeId(id),
	//		Count:      ItemCount(quantity),
	//		Data:       ItemData(data),
	//	}

	// TODO: Spawn the item created at the player's block location
}

// SendMessageToPlayer implements ICommandHandler.SendMessageToPlayer
func (game *Game) SendMessageToPlayer(name, msg string) {
	player := game.getPlayerFromName(name)

	buf := new(bytes.Buffer)
	proto.WriteChatMessage(buf, msg)
	packet := buf.Bytes()
	player.TransmitPacket(packet)
}

// BroadcastMessage implements ICommandHandler.BroadcastMessage
func (game *Game) BroadcastMessage(msg string) {
	buf := new(bytes.Buffer)
	proto.WriteChatMessage(buf, msg)

	game.enqueue(func(_ *Game) {
		game.multicastPacket(buf.Bytes(), nil)
	})
}
<|endoftext|>


<commit_before>/*
 *  Bulldozer Framework
 *  Copyright (C) DesertBit
 */

package messagebox

import (
	tr "code.desertbit.com/bulldozer/bulldozer/translate"

	"code.desertbit.com/bulldozer/bulldozer/callback"
	"code.desertbit.com/bulldozer/bulldozer/log"
	"code.desertbit.com/bulldozer/bulldozer/sessions"
	"code.desertbit.com/bulldozer/bulldozer/template"
	"code.desertbit.com/bulldozer/bulldozer/ui/dialog"
	"fmt"
	"strconv"
	"strings"
)

const (
	messageBoxTemplateUID = "budMsgbox"
	sessionValueKeyPrefix = "budMsgBox_"
	callbackPrefixName    = "budMsgBox_"
)

const (
	ButtonOk     Button = 1 << iota
	ButtonYes    Button = 1 << iota
	ButtonNo     Button = 1 << iota
	ButtonCancel Button = 1 << iota
)

const (
	TypeDefault  MessageBoxType = 1 << iota
	TypeSuccess  MessageBoxType = 1 << iota
	TypeWarning  MessageBoxType = 1 << iota
	TypeAlert    MessageBoxType = 1 << iota
	TypeInfo     MessageBoxType = 1 << iota
	TypeQuestion MessageBoxType = 1 << iota
)

type Button int
type MessageBoxType int
type Callback func(s *sessions.Session, button Button)

type MessageBox struct {
	title          string
	text           string
	buttons        Button
	messageBoxType MessageBoxType
	icon           string // CSS icon class. Predefined Kepler classes or font awesome classes...
	callbackFunc   Callback
	callbackName   string
}

type templButton struct {
	Id   string
	Text string
	Type string
}

var (
	d *dialog.Dialog
)

func init() {
	// Create the dialog and set the default values.
	d = dialog.New().
		SetSize(dialog.SizeSmall).
		SetClosable(false)

	// Parse the messagebox template.
	t, err := template.New(messageBoxTemplateUID, "msgbox").Parse(messageBoxText)
	if err != nil {
		log.L.Fatalf("failed to parse message box dialog template: %v", err)
	}

	// Register the events.
	t.RegisterEvents(new(receiver))

	// Set the template.
	d.SetTemplate(t)
}

//###########################//
//### Event Receiver type ###//
//###########################//

type receiver struct{}

func (r *receiver) EventButtonClicked(c *template.Context, b int) {
	// Close the messagebox.
	d.Close(c)

	// Save the session pointer.
	s := c.Session()

	// Create the session value access key.
	key := sessionValueKeyPrefix + c.ID()

	// Get the callbacks from the session cache.
	i, ok := s.InstancePull(key)
	if !ok {
		i, ok = s.CachePull(key)
		if !ok {
			log.L.Warning("messagebox: failed to get messagebox callback for id: '%s': this is caused, because messagebox callbacks set with messagebox.CallbackFunc are stored in the session cache and don't survive application restarts! Use messagebox.SetCallback instead...", c.ID())
			return
		}
	}

	// Assertion
	switch i.(type) {
	case string:
		// Assert and call the callback.
		name := i.(string)
		callback.Call(name, s, Button(b))
	case Callback:
		// Assert and call the callback.
		cb := i.(Callback)
		if cb != nil {
			cb(s, Button(b))
		}
	default:
		log.L.Error("messagebox: failed to get messagebox callback for id: '%s': unkown callback type!", c.ID())
		return
	}
}

//##############//
//### Public ###//
//##############//

// New creates a new MessageBox
func New() *MessageBox {
	return &MessageBox{
		buttons:        ButtonOk,
		messageBoxType: TypeDefault,
	}
}

// RegisterCallback registers a callback. This is necessary, because
// otherwise callbacks could not be called after application restarts.
// They have to be registered globally...
// One optional boolean can be passed, to force a overwrite of
// a previous registered callback with the same name.
func RegisterCallback(name string, cb Callback, vars ...bool) {
	// Register the callback.
	callback.Register(callbackPrefixName+name, cb, vars...)
}

//#######################//
//### MessageBox type ###//
//#######################//

// SetTitle sets the messagebox title
func (m *MessageBox) SetTitle(title string) *MessageBox {
	m.title = title
	return m
}

// SetText sets the messagebix text
func (m *MessageBox) SetText(text string) *MessageBox {
	m.text = text
	return m
}

// SetType sets the messagebox type
func (m *MessageBox) SetType(t MessageBoxType) *MessageBox {
	m.messageBoxType = t
	return m
}

// SetButtons sets the messagebox buttons
func (m *MessageBox) SetButtons(buttons Button) *MessageBox {
	m.buttons = buttons
	return m
}

// SetIcon sets the CSS icon class. Predefined Kepler classes or font awesome classes...
func (m *MessageBox) SetIcon(iconClass string) *MessageBox {
	m.icon = " " + strings.TrimSpace(iconClass)
	return m
}

// SetCallback sets the callback which is called as soon as any messagebox button is clicked.
// Use RegisterCallback to register a callback with a name.
func (m *MessageBox) SetCallback(callbackName string) *MessageBox {
	m.callbackName = callbackPrefixName + callbackName
	return m
}

// SetCallbackFunc sets the callback which is called as soon as any messagebox button is clicked.
// Note: This callback is saved in the session cache and it won't survive application restarts!
// Use SetCallback instead!
func (m *MessageBox) SetCallbackFunc(c Callback) *MessageBox {
	m.callbackFunc = c
	return m
}

// Show shows the messagebox. Errors are always logged.
func (m *MessageBox) Show(s *sessions.Session) (err error) {
	// Prepare the buttons
	var templButtons []templButton

	buttonCount := 0
	if m.buttons&ButtonOk == ButtonOk {
		templButtons = append(templButtons, templButton{
			"ok",
			tr.S("bud.messagebox.buttonOk"),
			strconv.Itoa(int(ButtonOk)),
		})
		buttonCount++
	}
	if m.buttons&ButtonYes == ButtonYes {
		templButtons = append(templButtons, templButton{
			"yes",
			tr.S("bud.messagebox.buttonYes"),
			strconv.Itoa(int(ButtonYes)),
		})
		buttonCount++
	}
	if m.buttons&ButtonNo == ButtonNo {
		templButtons = append(templButtons, templButton{
			"no",
			tr.S("bud.messagebox.buttonNo"),
			strconv.Itoa(int(ButtonNo)),
		})
		buttonCount++
	}
	if m.buttons&ButtonCancel == ButtonCancel {
		templButtons = append(templButtons, templButton{
			"cancel",
			tr.S("bud.messagebox.buttonCancel"),
			strconv.Itoa(int(ButtonCancel)),
		})
		buttonCount++
	}

	// Check if no buttons are passed
	if buttonCount == 0 {
		err = fmt.Errorf("failed to show message box: no buttons!")
		log.L.Error(err.Error())
		return
	}

	// Get the type class
	typeClass := ""
	if m.messageBoxType == TypeInfo {
		typeClass = " info"
	} else if m.messageBoxType == TypeSuccess {
		typeClass = " success"
	} else if m.messageBoxType == TypeWarning {
		typeClass = " warning"
	} else if m.messageBoxType == TypeAlert {
		typeClass = " alert"
	} else if m.messageBoxType == TypeQuestion {
		typeClass = " question"
	}

	// Create the template data
	data := struct {
		Title        string
		Text         string
		Buttons      []templButton
		ButtonColumn int
		IconClass    string
		TypeClass    string
	}{
		Title:        m.title,
		Text:         m.text,
		Buttons:      templButtons,
		ButtonColumn: 12 / buttonCount,
		IconClass:    m.icon,
		TypeClass:    typeClass,
	}

	// Show the message box
	c, err := d.Show(s, data)
	if err != nil {
		err = fmt.Errorf("failed to show message box: %v", err)
		log.L.Error(err.Error())
		return
	}

	// Save the callback to the session...
	key := sessionValueKeyPrefix + c.ID()
	if len(m.callbackName) > 0 {
		// Set the callback name to the session instance values.
		s.InstanceSet(key, m.callbackName)
	} else {
		// Hint: This won't survive application restarts.
		// Set the callbacks to the session cache.
		s.CacheSet(key, m.callbackFunc)
	}

	return nil
}

const messageBoxText = `

{{#.Title}}

{{#.Text}}

	{{range $b := #.Buttons}}

			{{$b.Text}}

		{{js load}}
			$("#{{id $b.Id}}").click(function() {
				var t = "{{$b.Type}}";
				{{emit ButtonClicked(t)}}
			});
		{{end js}}
	{{end}}

`
<commit_msg>small fix<commit_after>/*
 *  Bulldozer Framework
 *  Copyright (C) DesertBit
 */

package messagebox

import (
	tr "code.desertbit.com/bulldozer/bulldozer/translate"

	"code.desertbit.com/bulldozer/bulldozer/callback"
	"code.desertbit.com/bulldozer/bulldozer/log"
	"code.desertbit.com/bulldozer/bulldozer/sessions"
	"code.desertbit.com/bulldozer/bulldozer/template"
	"code.desertbit.com/bulldozer/bulldozer/ui/dialog"
	"fmt"
	"strconv"
	"strings"
)

const (
	messageBoxTemplateUID = "budMsgbox"
	sessionValueKeyPrefix = "budMsgBox_"
	callbackPrefixName    = "budMsgBox_"
)

const (
	ButtonOk     Button = 1 << iota
	ButtonYes    Button = 1 << iota
	ButtonNo     Button = 1 << iota
	ButtonCancel Button = 1 << iota
)

const (
	TypeDefault  MessageBoxType = 1 << iota
	TypeSuccess  MessageBoxType = 1 << iota
	TypeWarning  MessageBoxType = 1 << iota
	TypeAlert    MessageBoxType = 1 << iota
	TypeInfo     MessageBoxType = 1 << iota
	TypeQuestion MessageBoxType = 1 << iota
)

type Button int
type MessageBoxType int
type Callback func(s *sessions.Session, button Button)

type MessageBox struct {
	title          string
	text           string
	buttons        Button
	messageBoxType MessageBoxType
	icon           string // CSS icon class. Predefined Kepler classes or font awesome classes...
	callbackFunc   Callback
	callbackName   string
}

type templButton struct {
	Id   string
	Text string
	Type string
}

var (
	d *dialog.Dialog
)

func init() {
	// Create the dialog and set the default values.
	d = dialog.New().
		SetSize(dialog.SizeSmall).
		SetClosable(false)

	// Parse the messagebox template.
	t, err := template.New(messageBoxTemplateUID, "msgbox").Parse(messageBoxText)
	if err != nil {
		log.L.Fatalf("failed to parse message box dialog template: %v", err)
	}

	// Register the events.
	t.RegisterEvents(new(receiver))

	// Set the template.
	d.SetTemplate(t)
}

//###########################//
//### Event Receiver type ###//
//###########################//

type receiver struct{}

func (r *receiver) EventButtonClicked(c *template.Context, b int) {
	// Save the session pointer.
	s := c.Session()

	// Close the messagebox and hide the loading indicator.
	s.HideLoadingIndicator()
	d.Close(c)

	// Create the session value access key.
	key := sessionValueKeyPrefix + c.ID()

	// Get the callbacks from the session cache.
	i, ok := s.InstancePull(key)
	if !ok {
		i, ok = s.CachePull(key)
		if !ok {
			log.L.Warning("messagebox: failed to get messagebox callback for id: '%s': this is caused, because messagebox callbacks set with messagebox.CallbackFunc are stored in the session cache and don't survive application restarts! Use messagebox.SetCallback instead...", c.ID())
			return
		}
	}

	// Assertion
	switch i.(type) {
	case string:
		// Assert and call the callback.
		name := i.(string)
		callback.Call(name, s, Button(b))
	case Callback:
		// Assert and call the callback.
		cb := i.(Callback)
		if cb != nil {
			cb(s, Button(b))
		}
	default:
		log.L.Error("messagebox: failed to get messagebox callback for id: '%s': unkown callback type!", c.ID())
		return
	}
}

//##############//
//### Public ###//
//##############//

// New creates a new MessageBox
func New() *MessageBox {
	return &MessageBox{
		buttons:        ButtonOk,
		messageBoxType: TypeDefault,
	}
}

// RegisterCallback registers a callback. This is necessary, because
// otherwise callbacks could not be called after application restarts.
// They have to be registered globally...
// One optional boolean can be passed, to force a overwrite of
// a previous registered callback with the same name.
func RegisterCallback(name string, cb Callback, vars ...bool) {
	// Register the callback.
	callback.Register(callbackPrefixName+name, cb, vars...)
}

//#######################//
//### MessageBox type ###//
//#######################//

// SetTitle sets the messagebox title
func (m *MessageBox) SetTitle(title string) *MessageBox {
	m.title = title
	return m
}

// SetText sets the messagebix text
func (m *MessageBox) SetText(text string) *MessageBox {
	m.text = text
	return m
}

// SetType sets the messagebox type
func (m *MessageBox) SetType(t MessageBoxType) *MessageBox {
	m.messageBoxType = t
	return m
}

// SetButtons sets the messagebox buttons
func (m *MessageBox) SetButtons(buttons Button) *MessageBox {
	m.buttons = buttons
	return m
}

// SetIcon sets the CSS icon class. Predefined Kepler classes or font awesome classes...
func (m *MessageBox) SetIcon(iconClass string) *MessageBox {
	m.icon = " " + strings.TrimSpace(iconClass)
	return m
}

// SetCallback sets the callback which is called as soon as any messagebox button is clicked.
// Use RegisterCallback to register a callback with a name.
func (m *MessageBox) SetCallback(callbackName string) *MessageBox {
	m.callbackName = callbackPrefixName + callbackName
	return m
}

// SetCallbackFunc sets the callback which is called as soon as any messagebox button is clicked.
// Note: This callback is saved in the session cache and it won't survive application restarts!
// Use SetCallback instead!
func (m *MessageBox) SetCallbackFunc(c Callback) *MessageBox {
	m.callbackFunc = c
	return m
}

// Show shows the messagebox. Errors are always logged.
func (m *MessageBox) Show(s *sessions.Session) (err error) {
	// Prepare the buttons
	var templButtons []templButton

	buttonCount := 0
	if m.buttons&ButtonOk == ButtonOk {
		templButtons = append(templButtons, templButton{
			"ok",
			tr.S("bud.messagebox.buttonOk"),
			strconv.Itoa(int(ButtonOk)),
		})
		buttonCount++
	}
	if m.buttons&ButtonYes == ButtonYes {
		templButtons = append(templButtons, templButton{
			"yes",
			tr.S("bud.messagebox.buttonYes"),
			strconv.Itoa(int(ButtonYes)),
		})
		buttonCount++
	}
	if m.buttons&ButtonNo == ButtonNo {
		templButtons = append(templButtons, templButton{
			"no",
			tr.S("bud.messagebox.buttonNo"),
			strconv.Itoa(int(ButtonNo)),
		})
		buttonCount++
	}
	if m.buttons&ButtonCancel == ButtonCancel {
		templButtons = append(templButtons, templButton{
			"cancel",
			tr.S("bud.messagebox.buttonCancel"),
			strconv.Itoa(int(ButtonCancel)),
		})
		buttonCount++
	}

	// Check if no buttons are passed
	if buttonCount == 0 {
		err = fmt.Errorf("failed to show message box: no buttons!")
		log.L.Error(err.Error())
		return
	}

	// Get the type class
	typeClass := ""
	if m.messageBoxType == TypeInfo {
		typeClass = " info"
	} else if m.messageBoxType == TypeSuccess {
		typeClass = " success"
	} else if m.messageBoxType == TypeWarning {
		typeClass = " warning"
	} else if m.messageBoxType == TypeAlert {
		typeClass = " alert"
	} else if m.messageBoxType == TypeQuestion {
		typeClass = " question"
	}

	// Create the template data
	data := struct {
		Title        string
		Text         string
		Buttons      []templButton
		ButtonColumn int
		IconClass    string
		TypeClass    string
	}{
		Title:        m.title,
		Text:         m.text,
		Buttons:      templButtons,
		ButtonColumn: 12 / buttonCount,
		IconClass:    m.icon,
		TypeClass:    typeClass,
	}

	// Show the message box
	c, err := d.Show(s, data)
	if err != nil {
		err = fmt.Errorf("failed to show message box: %v", err)
		log.L.Error(err.Error())
		return
	}

	// Save the callback to the session...
	key := sessionValueKeyPrefix + c.ID()
	if len(m.callbackName) > 0 {
		// Set the callback name to the session instance values.
		s.InstanceSet(key, m.callbackName)
	} else {
		// Hint: This won't survive application restarts.
		// Set the callbacks to the session cache.
		s.CacheSet(key, m.callbackFunc)
	}

	return nil
}

const messageBoxText = `

{{#.Title}}

{{#.Text}}

	{{range $b := #.Buttons}}

			{{$b.Text}}

		{{js load}}
			$("#{{id $b.Id}}").click(function() {
				var t = "{{$b.Type}}";
				Bulldozer.loadingIndicator.show();
				{{emit ButtonClicked(t)}}
			});
		{{end js}}
	{{end}}

`
<|endoftext|>


<commit_before>package server

import (
	"github.com/gocraft/web"
	"os"
	"os/user"
	"runtime"
	"sync"
)

type serverStatus struct {
	*APIContext
}

func SetUpServerStatusRouter(prefix string, router *web.Router) {
	root := router.Subrouter(serverStatus{}, "")
	root.Get("/runtime_status", (*serverStatus).RuntimeStatus)
}

func (ss *serverStatus) RuntimeStatus(rw web.ResponseWriter, req *web.Request) {
	res := map[string]interface{}{
		"num_goroutine": runtime.NumGoroutine(),
		"num_cgo_call":  runtime.NumCgoCall(),
		"gomaxprocs":    runtime.GOMAXPROCS(0),
		"goroot":        runtime.GOROOT(),
		"num_cpu":       runtime.NumCPU(),
		"goversion":     runtime.Version(),
		"pid":           os.Getpid(),
	}

	var once sync.Once
	logOnce := func(name string) {
		once.Do(func() {
			ss.APIContext.Log().Warnf("runtime status '%v' isn't supported on this environment (this log is only written once)", name)
		})
	}

	if dir, err := os.Getwd(); err != nil {
		logOnce("working_directory")
	} else {
		res["working_directory"] = dir
	}
	if host, err := os.Hostname(); err != nil {
		logOnce("hostname")
	} else {
		res["hostname"] = host
	}
	if user, err := user.Current(); err != nil {
		logOnce("user")
	} else {
		res["user"] = user.Username
	}
	ss.RenderJSON(res)
}
<commit_msg>Fix each warning log output once in runtime_status<commit_after>package server

import (
	"github.com/gocraft/web"
	"os"
	"os/user"
	"runtime"
	"sync"
)

var (
	serverStatusGetwdWarnOnce       sync.Once
	serverStatusHostnameWarnOnce    sync.Once
	serverStatusUserCurrentWarnOnce sync.Once
)

type serverStatus struct {
	*APIContext
}

func SetUpServerStatusRouter(prefix string, router *web.Router) {
	root := router.Subrouter(serverStatus{}, "")
	root.Get("/runtime_status", (*serverStatus).RuntimeStatus)
}

func (ss *serverStatus) RuntimeStatus(rw web.ResponseWriter, req *web.Request) {
	res := map[string]interface{}{
		"num_goroutine": runtime.NumGoroutine(),
		"num_cgo_call":  runtime.NumCgoCall(),
		"gomaxprocs":    runtime.GOMAXPROCS(0),
		"goroot":        runtime.GOROOT(),
		"num_cpu":       runtime.NumCPU(),
		"goversion":     runtime.Version(),
		"pid":           os.Getpid(),
	}

	logOnce := func(name string, once sync.Once) {
		once.Do(func() {
			ss.APIContext.Log().Warnf("runtime status '%v' isn't supported on this environment (this log is only written once)", name)
		})
	}

	if dir, err := os.Getwd(); err != nil {
		logOnce("working_directory", serverStatusGetwdWarnOnce)
	} else {
		res["working_directory"] = dir
	}
	if host, err := os.Hostname(); err != nil {
		logOnce("hostname", serverStatusHostnameWarnOnce)
	} else {
		res["hostname"] = host
	}
	if user, err := user.Current(); err != nil {
		logOnce("user", serverStatusUserCurrentWarnOnce)
	} else {
		res["user"] = user.Username
	}
	ss.RenderJSON(res)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"math"
	"net/http"
	"sync"
	"time"
)

type SourceStatus int

const (
	SOURCE_IDLE SourceStatus = iota
	SOURCE_BUSY
	SOURCE_BAD
)

type FetchSource struct {
	Manager *FetchManager
	Id      string
	Host    string
	// Boost determines how much we boost this source's measured speed
	// at selection time.
	Boost float64
	// SpeedLimit is the maximal speed in kilobytes per second that
	// we should be fetching from this source. 0 means no limit.
	SpeedLimit       float64
	Status           SourceStatus
	ChunkSpeed       float64
	LastRecordedSpeed float64
	LastSetTime      time.Time
	TotalDownloaded  int64
	NumChunkAttempts int
}

func NewFetchSource(
	manager *FetchManager, id string, host string,
	chunkSpeed float64, speedLimit float64, boost float64) *FetchSource {
	return &FetchSource{
		Manager:        manager,
		Id:             id,
		Host:           host,
		Boost:          boost,
		SpeedLimit:     speedLimit,
		Status:         SOURCE_IDLE,
		ChunkSpeed:     chunkSpeed,
		LastSetTime:    time.Now(),
	}
}

func (s *FetchSource) RecordChunkSpeed(seconds, speed float64) {
	// We must have the decay otherwise each slow chunk throws off a
	// well-performing server for a very long time.
	decay := float64(0.5)
	s.ChunkSpeed = (1-decay)*speed + decay*s.ChunkSpeed
	s.LastSetTime = time.Now()
	s.LastRecordedSpeed = speed

	s.Manager.NumTotalChunks += 1
	s.Manager.TimeTotalChunks += seconds
}

func (s *FetchSource) RecordError(err error) {
	s.ChunkSpeed = s.Manager.AverageChunkTime()
	s.LastSetTime = time.Now()
	s.Status = SOURCE_BAD
}

func (s *FetchSource) Score() float64 {
	timeSinceLastRecording := time.Now().Sub(s.LastSetTime)

	errorPenalty := float64(1.0)
	if s.Status == SOURCE_BAD {
		penaltyTime := time.Second*time.Duration(20*s.Manager.AverageChunkTime())
		errorPenalty = 1.0 * s.Manager.UncertaintyBoost(timeSinceLastRecording - penaltyTime)
	}
	return s.EstimatedSpeed() * s.Boost * errorPenalty
}

func (s *FetchSource) EstimatedSpeed() float64 {
	timeSinceLastRecording := time.Now().Sub(s.LastSetTime)
	uncertaintyBoost := s.Manager.UncertaintyBoost(timeSinceLastRecording)
	estimatedSpeed := s.ChunkSpeed * uncertaintyBoost
	if s.SpeedLimit != 0 && estimatedSpeed > s.SpeedLimit {
		estimatedSpeed = s.SpeedLimit
	}
	return estimatedSpeed
}

type FetchManager struct {
	SourceMutex                  sync.Mutex
	Sources                      []*FetchSource
	ChunkSize                    int64
	NumWorkers                   int
	UncertaintyBoostPerChunkTime float64

	NumTotalChunks  float64
	TimeTotalChunks float64
}

func (m *FetchManager) CreateScheduler(path string, size int64) *FetchScheduler {
	return &FetchScheduler{
		Client:   http.Client{},
		Manager:  m,
		Path:     path,
		FileSize: size,
	}
}

func (m *FetchManager) PrintSources() {
	for _, source := range m.Sources {
		fmt.Printf("%v, status=%d spd=%5.0f espd=%5.0f last_speed=%5.0f score=%5.2f boost=%5.2f, Total=%5.0f Attempts=%d\n",
			source.Id,
			source.Status,
			source.ChunkSpeed/1024,
			source.EstimatedSpeed()/1024,
			source.LastRecordedSpeed/1024,
			source.Score()/1024/1024,
			m.UncertaintyBoost(time.Now().Sub(source.LastSetTime)),
			float64(source.TotalDownloaded)/1024.0,
			source.NumChunkAttempts)
	}
	if m.NumTotalChunks != 0 {
		fmt.Printf("Average chunk time=%.2f\n", m.AverageChunkTime())
	}
	fmt.Println()
}

func (m *FetchManager) UncertaintyBoost(duration time.Duration) float64 {
	chunks := duration.Seconds() / m.AverageChunkTime()
	return math.Pow(m.UncertaintyBoostPerChunkTime, chunks)
}

func (m *FetchManager) AverageChunkTime() float64 {
	if m.NumTotalChunks == 0 {
		return 10
	}
	return m.TimeTotalChunks / m.NumTotalChunks
}

func (m *FetchManager) AverageSpeed() float64 {
	if m.TimeTotalChunks == 0 {
		return 0
	}
	return float64(m.NumTotalChunks) * float64(m.ChunkSize) / m.TimeTotalChunks
}

// GetSource finds an optimal source to use for fetching.
func (d *FetchManager) GetSource() *FetchSource {
	d.SourceMutex.Lock()
	defer d.SourceMutex.Unlock()

	d.PrintSources()

	var selectedSource *FetchSource
	for _, source := range d.Sources {
		if source.Status == SOURCE_BUSY {
			continue
		}
		if selectedSource == nil {
			selectedSource = source
			continue
		}
		if source.Score() > selectedSource.Score() {
			selectedSource = source
		}

	}
	if selectedSource != nil {
		//fmt.Printf("Selected source %+v\n", *selectedSource)
		selectedSource.Status = SOURCE_BUSY
		selectedSource.NumChunkAttempts++
	} else {
		//fmt.Printf("Source not found\n")
	}
	return selectedSource
}

func (m *FetchManager) ReleaseSource(source *FetchSource) {
	m.SourceMutex.Lock()
	defer m.SourceMutex.Unlock()

	source.Status = SOURCE_IDLE
}

type FetchScheduler struct {
	Manager             *FetchManager
	Client              http.Client
	FileSize            int64
	Path                string
	taskMutex           sync.Mutex
	tasks               []*FetchSchedulerTask
	numOutstandingTasks int
	TaskDoneCh          chan *FetchSchedulerTask
}

func (f *FetchScheduler) Fetch() {
	// Create all the tasks.
	for pos := int64(0); pos < f.FileSize; pos += f.Manager.ChunkSize {
		f.tasks = append(f.tasks, &FetchSchedulerTask{
			scheduler: f,
			startPos:  pos,
			endPos:    pos + f.Manager.ChunkSize,
		})
		f.numOutstandingTasks++
	}
	// Start the workers in the worker pool.
	f.TaskDoneCh = make(chan *FetchSchedulerTask)
	for i := 0; i < f.Manager.NumWorkers; i++ {
		go f.WorkerLoop()
	}
	// Wait for all the tasks to complete.
	for ; f.numOutstandingTasks > 0; f.numOutstandingTasks-- {
		_ = <-f.TaskDoneCh
		//fmt.Printf("Done task %d-%d\n", task.startPos, task.endPos)
	}
}

func (f *FetchScheduler) GetTask() *FetchSchedulerTask {
	f.taskMutex.Lock()
	defer f.taskMutex.Unlock()

	if len(f.tasks) == 0 {
		return nil
	}
	task := f.tasks[0]
	f.tasks = f.tasks[1:]
	return task
}

func (f *FetchScheduler) WorkerLoop() {
	for {
		task := f.GetTask()
		if task == nil {
			return
		}
		task.Run()
		f.TaskDoneCh <- task
	}
}

type FetchSchedulerTask struct {
	scheduler *FetchScheduler
	startPos  int64
	endPos    int64
	startTime time.Time
	endTime   time.Time
	req       http.Request
	resp      http.Response
}

func (t *FetchSchedulerTask) Run() {
	// TODO: If there is an error, Run() must be re-attempted several times for each task,
	// with different sources.
	source := t.scheduler.Manager.GetSource()

	t.startTime = time.Now()
	err := t.RunWithSource(source)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		source.RecordError(err)
	}

	t.endTime = time.Now()

	loadDuration := t.endTime.Sub(t.startTime)
	speed := float64(t.endPos-t.startPos) / loadDuration.Seconds()
	source.RecordChunkSpeed(loadDuration.Seconds(), speed)
	source.TotalDownloaded += t.endPos - t.startPos
	t.scheduler.Manager.ReleaseSource(source)
	fmt.Printf("Done fetching task %d-%d, from %v, speed %.2f\n",
		t.startPos, t.endPos, source.Host, speed/1024)
}

func (t *FetchSchedulerTask) RunWithSource(source *FetchSource) error {
	url := "http://" + source.Host + t.scheduler.Path
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", t.startPos, t.endPos))
	resp, err := t.scheduler.Client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode / 100 != 2 {
		return fmt.Errorf("HTTP status %v", resp.Status)
	}

	// Speed limit algorithm works by limiting the maximal rate at which we
	// agree to read from this connection. With that, we trust the normal
	// buffering of the OS to perform reasonably efficiently wrt. packet transfer.
	var speedLimitTicker *time.Ticker
	bufferSize := 16*1024
	if source.SpeedLimit != 0 {
		tickInterval := time.Duration(float64(time.Second) * float64(bufferSize) / source.SpeedLimit)
		speedLimitTicker = time.NewTicker(tickInterval)
		defer speedLimitTicker.Stop()
		//fmt.Printf("Limit for %v=%.0f bps, Interval = %.3f sec\n",
		//	source.Id, source.SpeedLimit, tickInterval.Seconds())
	}

	reader := resp.Body
	buf := make([]byte, bufferSize)
	var bytesRead int64
	for {
		if speedLimitTicker != nil {
			//start := time.Now()
			<-speedLimitTicker.C
			//fmt.Printf("%v Waited for ticker: %.2f sec\n",
			//	source.Id, time.Now().Sub(start).Seconds())
		}
		n, err := reader.Read(buf)
		if err != nil && err != io.EOF {
			return err
		}
		if n != 0 {
			// One buffer of a chunk has been loaded.
			//fmt.Printf("%v Got %d bytes\n", source.Id, n)
			bytesRead += int64(n)
		} else {
			break
		}
		elapsedTime := time.Now().Sub(t.startTime).Seconds()
		momentarySpeed := float64(bytesRead) / 1024 / (elapsedTime + 1e-100)
		//fmt.Printf("%v Momentary speed at %.2f: %.2f\n", source.Id, elapsedTime, momentarySpeed )

		minAllowedSpeed := t.scheduler.Manager.AverageSpeed() / 20
		if elapsedTime > 16 && momentarySpeed < minAllowedSpeed {
			return fmt.Errorf("Server %v too slow", source.Id)
		}
	}

	return nil
}

func main() {
	manager := &FetchManager{
		ChunkSize:                    256 * 1024,
		NumWorkers:                   3,
		UncertaintyBoostPerChunkTime: 1.1,
	}
	manager.Sources = []*FetchSource{
		NewFetchSource(manager, "2", "second.server", 800*1024, 1000*1024, 3.0),
		NewFetchSource(manager, "3", "third.server", 350*1024, 0*1024, 1.0),
		NewFetchSource(manager, "4", "fourth.server", 160*1024, 0*1024, 1.0),
		NewFetchSource(manager, "22", "first.server", 50*1024, 50*1024, 1.0),
		NewFetchSource(manager, "33", "fifth.server", 1500*1024, 0*1024, 1.0),
	}

	for i := 0; i < 10; i++ {
		scheduler := manager.CreateScheduler("/direct/130310/Belarus.mwm", 57711744)
		scheduler.Fetch()
	}
}
<commit_msg>[loadersim] fixed stuff<commit_after>package main

import (
	"fmt"
	"io"
	"math"
	"net/http"
	"sync"
	"time"
)

type SourceStatus int

const (
	SOURCE_IDLE SourceStatus = iota
	SOURCE_BUSY
	SOURCE_BAD
)

type FetchSource struct {
	Manager *FetchManager
	Id      string
	Host    string
	// Boost determines how much we boost this source's measured speed
	// at selection time.
	Boost float64
	// SpeedLimit is the maximal speed in kilobytes per second that
	// we should be fetching from this source. 0 means no limit.
	SpeedLimit       float64
	Status           SourceStatus
	ChunkSpeed       float64
	LastRecordedSpeed float64
	LastSetTime      time.Time
	TotalDownloaded  int64
	NumChunkAttempts int
}

func NewFetchSource(
	manager *FetchManager, id string, host string,
	chunkSpeed float64, speedLimit float64, boost float64) *FetchSource {
	return &FetchSource{
		Manager:        manager,
		Id:             id,
		Host:           host,
		Boost:          boost,
		SpeedLimit:     speedLimit,
		Status:         SOURCE_IDLE,
		ChunkSpeed:     chunkSpeed,
		LastSetTime:    time.Now(),
	}
}

func (s *FetchSource) RecordChunkSpeed(seconds, speed float64) {
	// We must have the decay otherwise each slow chunk throws off a
	// well-performing server for a very long time.
	decay := float64(0.5)
	s.ChunkSpeed = (1-decay)*speed + decay*s.ChunkSpeed
	s.LastSetTime = time.Now()
	s.LastRecordedSpeed = speed

	s.Manager.NumTotalChunks += 1
	s.Manager.TimeTotalChunks += seconds

	maxRecordedChunks := 10.0
	if (s.Manager.NumTotalChunks > maxRecordedChunks) { // TODO: magic number, maximum number of recorded chunks (for decay)
		s.Manager.TimeTotalChunks = s.Manager.TimeTotalChunks * maxRecordedChunks / s.Manager.NumTotalChunks
		s.Manager.NumTotalChunks = maxRecordedChunks
	}
}

func (s *FetchSource) RecordError(err error) {
	s.ChunkSpeed = s.Manager.AverageChunkSpeed()
	s.LastSetTime = time.Now()
	s.Status = SOURCE_BAD
}

func (s *FetchSource) Score() float64 {
	timeSinceLastRecording := time.Now().Sub(s.LastSetTime)

	errorPenalty := float64(1.0)
	if s.Status == SOURCE_BAD {
		penaltyTime := time.Second * time.Duration(20 * s.Manager.AverageChunkTime()) // TODO: 20 - magic number, number of chunks to wait before checking server again
		errorPenalty = 1.0 * s.Manager.UncertaintyBoost(timeSinceLastRecording - penaltyTime)
	}
	return s.EstimatedSpeed() * s.Boost * errorPenalty
}

func (s *FetchSource) EstimatedSpeed() float64 {
	timeSinceLastRecording := time.Now().Sub(s.LastSetTime)
	uncertaintyBoost := s.Manager.UncertaintyBoost(timeSinceLastRecording)
	estimatedSpeed := s.ChunkSpeed * uncertaintyBoost
	if s.SpeedLimit != 0 && estimatedSpeed > s.SpeedLimit {
		estimatedSpeed = s.SpeedLimit
	}
	return estimatedSpeed
}

type FetchManager struct {
	SourceMutex                  sync.Mutex
	Sources                      []*FetchSource
	ChunkSize                    int64
	NumWorkers                   int
	UncertaintyBoostPerChunkTime float64

	NumTotalChunks  float64
	TimeTotalChunks float64
}

func (m *FetchManager) CreateScheduler(path string, size int64) *FetchScheduler {
	return &FetchScheduler{
		Client:   http.Client{},
		Manager:  m,
		Path:     path,
		FileSize: size,
	}
}

func (m *FetchManager) PrintSources() {
	for _, source := range m.Sources {
		fmt.Printf("%v, status=%d spd=%5.0f espd=%5.0f last_speed=%5.0f score=%5.2f uncertaintyBoost=%5.2f, Total=%5.0f Attempts=%d\n",
			source.Id,
			source.Status,
			source.ChunkSpeed/1024,
			source.EstimatedSpeed()/1024,
			source.LastRecordedSpeed/1024,
			source.Score()/1024/1024,
			m.UncertaintyBoost(time.Now().Sub(source.LastSetTime)),
			float64(source.TotalDownloaded)/1024.0,
			source.NumChunkAttempts)
	}
	if m.NumTotalChunks != 0 {
		fmt.Printf("Average chunk time=%.2f, avg chunk speed=%.1f KBps\n", m.AverageChunkTime(), m.AverageChunkSpeed() / 1024)
	}
	fmt.Println()
}

func (m *FetchManager) UncertaintyBoost(duration time.Duration) float64 {
	chunks := duration.Seconds() / m.AverageChunkTime()
	return math.Pow(m.UncertaintyBoostPerChunkTime, chunks)
}

func (m *FetchManager) AverageChunkTime() float64 {
	if m.NumTotalChunks == 0 {
		return 1
	}
	return m.TimeTotalChunks / m.NumTotalChunks
}

func (m *FetchManager) AverageChunkSpeed() float64 {
	return float64(m.ChunkSize) / m.AverageChunkTime()
}

// GetSource finds an optimal source to use for fetching.
func (d *FetchManager) GetSource() *FetchSource {
	d.SourceMutex.Lock()
	defer d.SourceMutex.Unlock()

	d.PrintSources()

	var selectedSource *FetchSource
	for _, source := range d.Sources {
		if source.Status == SOURCE_BUSY {
			continue
		}
		if selectedSource == nil {
			selectedSource = source
			continue
		}
		if source.Score() > selectedSource.Score() {
			selectedSource = source
		}

	}
	if selectedSource != nil {
		//fmt.Printf("Selected source %+v\n", *selectedSource)
		selectedSource.Status = SOURCE_BUSY
		selectedSource.NumChunkAttempts++
	} else {
		//fmt.Printf("Source not found\n")
	}
	return selectedSource
}

func (m *FetchManager) ReleaseSource(source *FetchSource) {
	m.SourceMutex.Lock()
	defer m.SourceMutex.Unlock()

	source.Status = SOURCE_IDLE
}

type FetchScheduler struct {
	Manager             *FetchManager
	Client              http.Client
	FileSize            int64
	Path                string
	taskMutex           sync.Mutex
	tasks               []*FetchSchedulerTask
	numOutstandingTasks int
	TaskDoneCh          chan *FetchSchedulerTask
}

func (f *FetchScheduler) Fetch() {
	// Create all the tasks.
	for pos := int64(0); pos < f.FileSize; pos += f.Manager.ChunkSize {
		f.tasks = append(f.tasks, &FetchSchedulerTask{
			scheduler: f,
			startPos:  pos,
			endPos:    pos + f.Manager.ChunkSize,
		})
		f.numOutstandingTasks++
	}
	// Start the workers in the worker pool.
	f.TaskDoneCh = make(chan *FetchSchedulerTask)
	for i := 0; i < f.Manager.NumWorkers; i++ {
		go f.WorkerLoop()
	}
	// Wait for all the tasks to complete.
	for ; f.numOutstandingTasks > 0; f.numOutstandingTasks-- {
		_ = <-f.TaskDoneCh
		//fmt.Printf("Done task %d-%d\n", task.startPos, task.endPos)
	}
}

func (f *FetchScheduler) GetTask() *FetchSchedulerTask {
	f.taskMutex.Lock()
	defer f.taskMutex.Unlock()

	if len(f.tasks) == 0 {
		return nil
	}
	task := f.tasks[0]
	f.tasks = f.tasks[1:]
	return task
}

func (f *FetchScheduler) WorkerLoop() {
	for {
		task := f.GetTask()
		if task == nil {
			return
		}
		task.Run()
		f.TaskDoneCh <- task
	}
}

type FetchSchedulerTask struct {
	scheduler *FetchScheduler
	startPos  int64
	endPos    int64
	startTime time.Time
	endTime   time.Time
	req       http.Request
	resp      http.Response
}

func (t *FetchSchedulerTask) Run() {
	// TODO: If there is an error, Run() must be re-attempted several times for each task,
	// with different sources.
	source := t.scheduler.Manager.GetSource()

	t.startTime = time.Now()
	err := t.RunWithSource(source)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		source.RecordError(err)
		return
	}

	t.endTime = time.Now()

	loadDuration := t.endTime.Sub(t.startTime)
	speed := float64(t.endPos-t.startPos) / loadDuration.Seconds()
	source.RecordChunkSpeed(loadDuration.Seconds(), speed)
	source.TotalDownloaded += t.endPos - t.startPos
	t.scheduler.Manager.ReleaseSource(source)

	fmt.Printf("Done fetching task %d-%d, from %v, speed %.2f\n",
		t.startPos, t.endPos, source.Host, speed/1024)
}

func (t *FetchSchedulerTask) RunWithSource(source *FetchSource) error {
	url := "http://" + source.Host + t.scheduler.Path
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", t.startPos, t.endPos))
	resp, err := t.scheduler.Client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode / 100 != 2 {
		return fmt.Errorf("HTTP status %v for %v", resp.Status, url)
	}

	// Speed limit algorithm works by limiting the maximal rate at which we
	// agree to read from this connection. With that, we trust the normal
	// buffering of the OS to perform reasonably efficiently wrt. packet transfer.
	var speedLimitTicker *time.Ticker
	bufferSize := 16*1024
	if source.SpeedLimit != 0 {
		tickInterval := time.Duration(float64(time.Second) * float64(bufferSize) / source.SpeedLimit)
		speedLimitTicker = time.NewTicker(tickInterval)
		defer speedLimitTicker.Stop()
		//fmt.Printf("Limit for %v=%.0f bps, Interval = %.3f sec\n",
		//	source.Id, source.SpeedLimit, tickInterval.Seconds())
	}

	reader := resp.Body
	buf := make([]byte, bufferSize)
	var bytesRead int64
	for {
		if speedLimitTicker != nil {
			//start := time.Now()
			<-speedLimitTicker.C
			//fmt.Printf("%v Waited for ticker: %.2f sec\n",
			//	source.Id, time.Now().Sub(start).Seconds())
		}
		n, err := reader.Read(buf)
		if err != nil && err != io.EOF {
			return err
		}
		if n != 0 {
			// One buffer of a chunk has been loaded.
			//fmt.Printf("%v Got %d bytes\n", source.Id, n)
			bytesRead += int64(n)
		} else {
			break
		}
		elapsedTime := time.Now().Sub(t.startTime).Seconds()
		momentarySpeed := float64(bytesRead) / 1024 / (elapsedTime + 1e-100)
		//fmt.Printf("%v Momentary speed at %.2f: %.2f\n", source.Id, elapsedTime, momentarySpeed )

		minAllowedSpeed := t.scheduler.Manager.AverageChunkSpeed() / 20 // TODO: magic numbers, review, maybe mark server as bad
		if elapsedTime > 16 && momentarySpeed < minAllowedSpeed {
			return fmt.Errorf("Server %v too slow", source.Id)
		}
	}

	return nil
}

func main() {
	manager := &FetchManager{
		ChunkSize:                    256 * 1024,
		NumWorkers:                   3,
		UncertaintyBoostPerChunkTime: 1.1,
	}
	manager.Sources = []*FetchSource{
		NewFetchSource(manager, "2", "second.server", 800*1024, 1000*1024, 3.0),
		NewFetchSource(manager, "3", "third.server", 350*1024, 0*1024, 1.0),
		NewFetchSource(manager, "4", "fourth.server", 160*1024, 0*1024, 1.0),
		NewFetchSource(manager, "22", "first.server", 50*1024, 50*1024, 1.0),
		NewFetchSource(manager, "33", "fifth.server", 1500*1024, 0*1024, 1.0),
	}

	for i := 0; i < 10; i++ {
		scheduler := manager.CreateScheduler("/direct/130310/Belarus.mwm", 57711744)
		scheduler.Fetch()
	}
}
<|endoftext|>


<commit_before>package repositories

import (
	"log"
	"strconv"

	"database/sql"
	"github.com/steffen25/golang.zone/database"
	"github.com/steffen25/golang.zone/models"
)

type PostRepository interface {
	Create(p *models.Post) error
	GetAll() ([]*models.Post, error)
	FindById(id int) (*models.Post, error)
	FindBySlug(slug string) (*models.Post, error)
	FindByUser(u *models.User) ([]*models.Post, error)
	Exists(slug string) bool
	Delete(id int) error
	Update(p *models.Post) error
	Paginate(perpage int, offset int) ([]*models.Post, error)
	GetTotalPostCount() (int, error)
}

type postRepository struct {
	*database.MySQLDB
}

func NewPostRepository(db *database.MySQLDB) PostRepository {
	return &postRepository{db}
}

func (pr *postRepository) Create(p *models.Post) error {
	exists := pr.Exists(p.Slug)
	if exists {
		err := pr.createWithSlugCount(p)
		if err != nil {
			return err
		}

		return nil
	}

	stmt, err := pr.DB.Prepare("INSERT INTO posts SET title=?, slug=?, body=?, created_at=?, user_id=?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(p.Title, p.Slug, p.Body, p.CreatedAt.Format("20060102150405"), p.UserID)
	if err != nil {
		return err
	}

	return nil
}

func (pr *postRepository) GetAll() ([]*models.Post, error) {
	var posts []*models.Post

	rows, err := pr.DB.Query("SELECT id, title, slug, body, created_at, updated_at, user_id from posts")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		p := new(models.Post)
		err := rows.Scan(&p.ID, &p.Title, &p.Slug, &p.Body, &p.CreatedAt, &p.UpdatedAt, &p.UserID)
		if err != nil {
			log.Println(err)
			return nil, err
		}
		posts = append(posts, p)
	}

	if err := rows.Err(); err != nil {
		return nil, err
	}

	return posts, nil
}

func (pr *postRepository) GetTotalPostCount() (int, error) {
	var count int
	err := pr.DB.QueryRow("SELECT COUNT(*) FROM posts").Scan(&count)
	if err != nil {
		return -1, err
	}

	return count, nil
}

func (pr *postRepository) Paginate(perpage int, offset int) ([]*models.Post, error) {
	var posts []*models.Post

	rows, err := pr.DB.Query("SELECT p.`id`, p.`title`, p.`slug`, p.`body`, p.`created_at`, p.`updated_at`, p.`user_id`, u.`name` as author FROM posts p INNER JOIN `users` as u on p.`user_id`=u.`id` LIMIT ? OFFSET ?", perpage, offset)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		p := new(models.Post)
		err := rows.Scan(&p.ID, &p.Title, &p.Slug, &p.Body, &p.CreatedAt, &p.UpdatedAt, &p.UserID, &p.Author)
		if err != nil {
			log.Println(err)
			return nil, err
		}
		posts = append(posts, p)
	}

	if err := rows.Err(); err != nil {
		return nil, err
	}

	return posts, nil
}

func (pr *postRepository) FindById(id int) (*models.Post, error) {
	post := models.Post{}

	err := pr.DB.QueryRow("SELECT id, title, slug, body, created_at, updated_at, user_id FROM posts WHERE id = ?", id).Scan(&post.ID, &post.Title, &post.Slug, &post.Body, &post.CreatedAt, &post.UpdatedAt, &post.UserID)
	if err != nil {
		return nil, err
	}

	return &post, nil
}

func (pr *postRepository) FindBySlug(slug string) (*models.Post, error) {
	post := models.Post{}
	err := pr.DB.QueryRow("SELECT id, title, slug, body, created_at, updated_at, user_id FROM posts WHERE slug LIKE ?", "%"+slug+"%").Scan(&post.ID, &post.Title, &post.Slug, &post.Body, &post.CreatedAt, &post.UpdatedAt, &post.UserID)
	if err != nil {
		return nil, err
	}

	return &post, nil
}

func (pr *postRepository) FindByUser(u *models.User) ([]*models.Post, error) {
	var posts []*models.Post

	rows, err := pr.DB.Query("SELECT p.`id`, p.`title`, p.`slug`, p.`body`, p.`created_at`, p.`updated_at`, p.`user_id`, u.`name` as author FROM posts p INNER JOIN `users` as u on p.`user_id`=?", u.ID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		p := new(models.Post)
		err := rows.Scan(&p.ID, &p.Title, &p.Slug, &p.Body, &p.CreatedAt, &p.UpdatedAt, &p.UserID, &p.Author)
		if err != nil {
			return nil, err
		}
		posts = append(posts, p)
	}

	if err := rows.Err(); err != nil {
		return nil, err
	}

	return posts, nil
}

func (pr *postRepository) Delete(id int) error {
	return nil
}

func (pr *postRepository) Update(p *models.Post) error {
	exists := pr.Exists(p.Slug)
	if !exists {
		err := pr.updatePost(p)
		if err != nil {
			return err
		}

		return nil
	}

	// Post do exists
	// Now we want to find out if the slug is the post we are updating
	var postId int
	err := pr.DB.QueryRow("SELECT id FROM posts WHERE slug=?", p.Slug).Scan(&postId)
	if err != nil && err != sql.ErrNoRows {
		return err
	}

	if p.ID == postId {
		err := pr.updatePost(p)
		if err != nil {
			return err
		}

		return nil
	}

	// If its not the same post we append the next count number of that slug
	var slugCount int
	err = pr.DB.QueryRow("SELECT COUNT(*) FROM posts where slug LIKE ?", "%"+p.Slug+"%").Scan(&slugCount)
	if err != nil {
		return err
	}
	counter := strconv.Itoa(slugCount)
	p.Slug = p.Slug + "-" + counter

	err = pr.updatePost(p)
	if err != nil {
		return err
	}

	return nil
}

// Check if a slug already exists
func (pr *postRepository) Exists(slug string) bool {
	var exists bool
	err := pr.DB.QueryRow("SELECT EXISTS (SELECT id FROM posts WHERE slug=?)", slug).Scan(&exists)
	if err != nil {
		log.Printf("[POST REPO]: Exists err %v", err)
		return true
	}

	return exists
}

// This is a 'private' function to be used in cases where a slug already exists
func (pr *postRepository) createWithSlugCount(p *models.Post) error {
	var count int
	err := pr.DB.QueryRow("SELECT COUNT(*) FROM posts where slug LIKE ?", "%"+p.Slug+"%").Scan(&count)
	if err != nil {
		return err
	}
	counter := strconv.Itoa(count)

	stmt, err := pr.DB.Prepare("INSERT INTO posts SET title=?, slug=?, body=?, created_at=?, user_id=?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(p.Title, p.Slug+"-"+counter, p.Body, p.CreatedAt.Format("20060102150405"), p.UserID)
	if err != nil {
		return err
	}

	return nil
}

func (pr *postRepository) updatePost(p *models.Post) error {
	stmt, err := pr.DB.Prepare("UPDATE posts SET title=?, slug=?, body=?, updated_at=?, user_id=? WHERE id = ?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(p.Title, p.Slug, p.Body, p.UpdatedAt, p.UserID, p.ID)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Fixed query when retrieving user posts<commit_after>package repositories

import (
	"log"
	"strconv"

	"database/sql"
	"github.com/steffen25/golang.zone/database"
	"github.com/steffen25/golang.zone/models"
)

type PostRepository interface {
	Create(p *models.Post) error
	GetAll() ([]*models.Post, error)
	FindById(id int) (*models.Post, error)
	FindBySlug(slug string) (*models.Post, error)
	FindByUser(u *models.User) ([]*models.Post, error)
	Exists(slug string) bool
	Delete(id int) error
	Update(p *models.Post) error
	Paginate(perpage int, offset int) ([]*models.Post, error)
	GetTotalPostCount() (int, error)
}

type postRepository struct {
	*database.MySQLDB
}

func NewPostRepository(db *database.MySQLDB) PostRepository {
	return &postRepository{db}
}

func (pr *postRepository) Create(p *models.Post) error {
	exists := pr.Exists(p.Slug)
	if exists {
		err := pr.createWithSlugCount(p)
		if err != nil {
			return err
		}

		return nil
	}

	stmt, err := pr.DB.Prepare("INSERT INTO posts SET title=?, slug=?, body=?, created_at=?, user_id=?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(p.Title, p.Slug, p.Body, p.CreatedAt.Format("20060102150405"), p.UserID)
	if err != nil {
		return err
	}

	return nil
}

func (pr *postRepository) GetAll() ([]*models.Post, error) {
	var posts []*models.Post

	rows, err := pr.DB.Query("SELECT id, title, slug, body, created_at, updated_at, user_id from posts")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		p := new(models.Post)
		err := rows.Scan(&p.ID, &p.Title, &p.Slug, &p.Body, &p.CreatedAt, &p.UpdatedAt, &p.UserID)
		if err != nil {
			log.Println(err)
			return nil, err
		}
		posts = append(posts, p)
	}

	if err := rows.Err(); err != nil {
		return nil, err
	}

	return posts, nil
}

func (pr *postRepository) GetTotalPostCount() (int, error) {
	var count int
	err := pr.DB.QueryRow("SELECT COUNT(*) FROM posts").Scan(&count)
	if err != nil {
		return -1, err
	}

	return count, nil
}

func (pr *postRepository) Paginate(perpage int, offset int) ([]*models.Post, error) {
	var posts []*models.Post

	rows, err := pr.DB.Query("SELECT p.`id`, p.`title`, p.`slug`, p.`body`, p.`created_at`, p.`updated_at`, p.`user_id`, u.`name` as author FROM posts p INNER JOIN `users` as u on p.`user_id`=u.`id` LIMIT ? OFFSET ?", perpage, offset)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		p := new(models.Post)
		err := rows.Scan(&p.ID, &p.Title, &p.Slug, &p.Body, &p.CreatedAt, &p.UpdatedAt, &p.UserID, &p.Author)
		if err != nil {
			log.Println(err)
			return nil, err
		}
		posts = append(posts, p)
	}

	if err := rows.Err(); err != nil {
		return nil, err
	}

	return posts, nil
}

func (pr *postRepository) FindById(id int) (*models.Post, error) {
	post := models.Post{}

	err := pr.DB.QueryRow("SELECT id, title, slug, body, created_at, updated_at, user_id FROM posts WHERE id = ?", id).Scan(&post.ID, &post.Title, &post.Slug, &post.Body, &post.CreatedAt, &post.UpdatedAt, &post.UserID)
	if err != nil {
		return nil, err
	}

	return &post, nil
}

func (pr *postRepository) FindBySlug(slug string) (*models.Post, error) {
	post := models.Post{}
	err := pr.DB.QueryRow("SELECT id, title, slug, body, created_at, updated_at, user_id FROM posts WHERE slug LIKE ?", "%"+slug+"%").Scan(&post.ID, &post.Title, &post.Slug, &post.Body, &post.CreatedAt, &post.UpdatedAt, &post.UserID)
	if err != nil {
		return nil, err
	}

	return &post, nil
}

func (pr *postRepository) FindByUser(u *models.User) ([]*models.Post, error) {
	var posts []*models.Post

	rows, err := pr.DB.Query("SELECT p.`id`, p.`title`, p.`slug`, p.`body`, p.`created_at`, p.`updated_at`, p.`user_id`, u.`name` as author FROM posts p INNER JOIN `users` as u on p.`user_id`=? WHERE u.`id`=?", u.ID, u.ID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		p := new(models.Post)
		err := rows.Scan(&p.ID, &p.Title, &p.Slug, &p.Body, &p.CreatedAt, &p.UpdatedAt, &p.UserID, &p.Author)
		if err != nil {
			return nil, err
		}
		posts = append(posts, p)
	}

	if err := rows.Err(); err != nil {
		return nil, err
	}

	return posts, nil
}

func (pr *postRepository) Delete(id int) error {
	return nil
}

func (pr *postRepository) Update(p *models.Post) error {
	exists := pr.Exists(p.Slug)
	if !exists {
		err := pr.updatePost(p)
		if err != nil {
			return err
		}

		return nil
	}

	// Post do exists
	// Now we want to find out if the slug is the post we are updating
	var postId int
	err := pr.DB.QueryRow("SELECT id FROM posts WHERE slug=?", p.Slug).Scan(&postId)
	if err != nil && err != sql.ErrNoRows {
		return err
	}

	if p.ID == postId {
		err := pr.updatePost(p)
		if err != nil {
			return err
		}

		return nil
	}

	// If its not the same post we append the next count number of that slug
	var slugCount int
	err = pr.DB.QueryRow("SELECT COUNT(*) FROM posts where slug LIKE ?", "%"+p.Slug+"%").Scan(&slugCount)
	if err != nil {
		return err
	}
	counter := strconv.Itoa(slugCount)
	p.Slug = p.Slug + "-" + counter

	err = pr.updatePost(p)
	if err != nil {
		return err
	}

	return nil
}

// Check if a slug already exists
func (pr *postRepository) Exists(slug string) bool {
	var exists bool
	err := pr.DB.QueryRow("SELECT EXISTS (SELECT id FROM posts WHERE slug=?)", slug).Scan(&exists)
	if err != nil {
		log.Printf("[POST REPO]: Exists err %v", err)
		return true
	}

	return exists
}

// This is a 'private' function to be used in cases where a slug already exists
func (pr *postRepository) createWithSlugCount(p *models.Post) error {
	var count int
	err := pr.DB.QueryRow("SELECT COUNT(*) FROM posts where slug LIKE ?", "%"+p.Slug+"%").Scan(&count)
	if err != nil {
		return err
	}
	counter := strconv.Itoa(count)

	stmt, err := pr.DB.Prepare("INSERT INTO posts SET title=?, slug=?, body=?, created_at=?, user_id=?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(p.Title, p.Slug+"-"+counter, p.Body, p.CreatedAt.Format("20060102150405"), p.UserID)
	if err != nil {
		return err
	}

	return nil
}

func (pr *postRepository) updatePost(p *models.Post) error {
	stmt, err := pr.DB.Prepare("UPDATE posts SET title=?, slug=?, body=?, updated_at=?, user_id=? WHERE id = ?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(p.Title, p.Slug, p.Body, p.UpdatedAt, p.UserID, p.ID)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"net/http"

	_ "github.com/go-sql-driver/mysql"
	"github.com/vharitonsky/iniflags"
)

var (
	DB  *sql.DB
	err error

	httpHost = flag.String("HTTP_HOST", "localhost", "Connect to host")
	httpPort = flag.String("HTTP_PORT", "9199", "Connect to host")

	sqlHost = flag.String("MYSQL_HOST", "localhost", "Connect to host")
	sqlPort = flag.String("MYSQL_PORT", "3306", "Connect to host")
	sqlUser = flag.String("MYSQL_USER", "root", "User for login to MySQL")
	sqlPass = flag.String("MYSQL_PASS", "", "Password for login to MySQL")

	donorOk = flag.Bool("DONOR_OK", false, "treat donor as regular working node")

	showVersion = flag.Bool("version", false, fmt.Sprint("Show current version: ", Commit))

	Commit = "dev"
)

func main() {
	iniflags.Parse()
	if *showVersion {
	  fmt.Println(Commit)
	  return
	}
	DB, err = sql.Open(
		"mysql",
		fmt.Sprintf("%s:%s@tcp(%s:%s)/mysql", *sqlUser, *sqlPass, *sqlHost, *sqlPort),
	)
	if err != nil {
		log.Fatal(err)
	}
	http.HandleFunc("/", statusHandler)
	http.ListenAndServe(*httpHost+":"+*httpPort, nil)
}

func statusHandler(w http.ResponseWriter, r *http.Request) {
	err = DB.Ping()
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	var key string
	var value int64
	err = DB.QueryRow("SHOW STATUS LIKE 'wsrep_local_state'").Scan(&key, &value)
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	switch {
	case 4 == value:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	case 2 == value && *donorOk:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	default:
		http.Error(w, "Galera Node is *down*. (State Mismatch)", 503)
		return
	}
}
<commit_msg>formating<commit_after>package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"net/http"

	_ "github.com/go-sql-driver/mysql"
	"github.com/vharitonsky/iniflags"
)

var (
	db  *sql.DB
	err error

	httpHost = flag.String("HTTP_HOST", "localhost", "Connect to host")
	httpPort = flag.String("HTTP_PORT", "9199", "Connect to host")

	sqlHost = flag.String("MYSQL_HOST", "localhost", "Connect to host")
	sqlPort = flag.String("MYSQL_PORT", "3306", "Connect to host")
	sqlUser = flag.String("MYSQL_USER", "root", "User for login to MySQL")
	sqlPass = flag.String("MYSQL_PASS", "", "Password for login to MySQL")

	donorOk = flag.Bool("DONOR_OK", false, "treat donor as regular working node")

	showVersion = flag.Bool("version", false, fmt.Sprint("Show current version: ", Commit))

	// Commit holds the git sha information on compile time
	Commit = "dev"
)

func main() {
	iniflags.Parse()
	if *showVersion {
		fmt.Println(Commit)
		return
	}
	db, err = sql.Open(
		"mysql",
		fmt.Sprintf("%s:%s@tcp(%s:%s)/mysql", *sqlUser, *sqlPass, *sqlHost, *sqlPort),
	)
	if err != nil {
		log.Fatal(err)
	}
	http.HandleFunc("/", databaseStatusHandler)
	http.HandleFunc("/status", watchdogStatusHandler)
	if err := http.ListenAndServe(*httpHost+":"+*httpPort, nil); err != nil {
		log.Fatal(err)
	}
}

func watchdogStatusHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func databaseStatusHandler(w http.ResponseWriter, r *http.Request) {
	err = db.Ping()
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	var key string
	var value int64
	err = db.QueryRow("SHOW STATUS LIKE 'wsrep_local_state'").Scan(&key, &value)
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	switch {
	case 4 == value:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	case 2 == value && *donorOk:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	default:
		http.Error(w, "Galera Node is *down*. (State Mismatch)", 503)
		return
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/donovanhide/ripple/data"
	"github.com/donovanhide/ripple/websockets"
	"github.com/fatih/color"
)

func main() {
	flag.Parse()
	r, err := websockets.NewRemote("wss://s-east.ripple.com:443")
	if err != nil {
		panic(err)
	}
	go r.Run()

	// Subscribe to all streams
	r.Outgoing <- websockets.Subscribe(true, true, true)
	confirmation := <-r.Incoming
	fmt.Printf(
		"Subscribed at %d to streams: %v\n",
		confirmation.(*websockets.SubscribeCommand).Result.LedgerSequence,
		confirmation.(*websockets.SubscribeCommand).Streams,
	)

	ledgerStyle := color.New(color.FgRed, color.Underline)
	transactionStyle := color.New(color.FgGreen)
	nodeStyle := color.New(color.FgBlue)
	serverStyle := color.New(color.FgMagenta)

	// Consume messages as they arrive
	for {
		msg := <-r.Incoming
		switch msg := msg.(type) {
		case *websockets.LedgerStreamMsg:
			ledgerStyle.Printf(
				"Ledger %d closed at %s with %d transactions\n",
				msg.LedgerSequence,
				msg.LedgerTime.String(),
				msg.TxnCount,
			)
		case *websockets.TransactionStreamMsg:
			transactionStyle.Printf(
				"    %s by %s\n",
				msg.Transaction.GetTransactionType().String(),
				msg.Transaction.GetAccount(),
			)
			for _, n := range msg.Transaction.MetaData.AffectedNodes {
				s := ExplainNodeEffect(&n)
				if s != "" {
					nodeStyle.Printf("        %s\n", s)
				}
			}
		case *websockets.ServerStreamMsg:
			serverStyle.Printf(
				"Server Status: %s (%d/%d)\n",
				msg.Status,
				msg.LoadFactor,
				msg.LoadBase,
			)
		}
	}
}

func ExplainNodeEffect(ne *data.NodeEffect) string {
	var op string
	var n *data.AffectedNode
	var fields interface{}

	switch {
	case ne.CreatedNode != nil:
		op = "Created"
		n = ne.CreatedNode
		fields = n.NewFields
	case ne.ModifiedNode != nil:
		op = "Modified"
		n = ne.ModifiedNode
		fields = n.FinalFields
	case ne.DeletedNode != nil:
		op = "Deleted"
		n = ne.DeletedNode
		fields = n.FinalFields
	}

	switch n.LedgerEntryType {
	case data.DIRECTORY:
		// Skip
		return ""

	case data.OFFER:
		return fmt.Sprintf("%s Offer %s %s for %s @ %s",
			op,
			fields.(*data.OfferFields).Account,
			fields.(*data.OfferFields).TakerGets,
			fields.(*data.OfferFields).TakerPays,
		)

	default:
		return fmt.Sprintf("%s %s node: %s", op, n.LedgerEntryType, n.LedgerIndex)
	}
}
<commit_msg>Remove extraneous field<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/donovanhide/ripple/data"
	"github.com/donovanhide/ripple/websockets"
	"github.com/fatih/color"
)

func main() {
	flag.Parse()
	r, err := websockets.NewRemote("wss://s-east.ripple.com:443")
	if err != nil {
		panic(err)
	}
	go r.Run()

	// Subscribe to all streams
	r.Outgoing <- websockets.Subscribe(true, true, true)
	confirmation := <-r.Incoming
	fmt.Printf(
		"Subscribed at %d to streams: %v\n",
		confirmation.(*websockets.SubscribeCommand).Result.LedgerSequence,
		confirmation.(*websockets.SubscribeCommand).Streams,
	)

	ledgerStyle := color.New(color.FgRed, color.Underline)
	transactionStyle := color.New(color.FgGreen)
	nodeStyle := color.New(color.FgBlue)
	serverStyle := color.New(color.FgMagenta)

	// Consume messages as they arrive
	for {
		msg := <-r.Incoming
		switch msg := msg.(type) {
		case *websockets.LedgerStreamMsg:
			ledgerStyle.Printf(
				"Ledger %d closed at %s with %d transactions\n",
				msg.LedgerSequence,
				msg.LedgerTime.String(),
				msg.TxnCount,
			)
		case *websockets.TransactionStreamMsg:
			transactionStyle.Printf(
				"    %s by %s\n",
				msg.Transaction.GetTransactionType().String(),
				msg.Transaction.GetAccount(),
			)
			for _, n := range msg.Transaction.MetaData.AffectedNodes {
				s := ExplainNodeEffect(&n)
				if s != "" {
					nodeStyle.Printf("        %s\n", s)
				}
			}
		case *websockets.ServerStreamMsg:
			serverStyle.Printf(
				"Server Status: %s (%d/%d)\n",
				msg.Status,
				msg.LoadFactor,
				msg.LoadBase,
			)
		}
	}
}

func ExplainNodeEffect(ne *data.NodeEffect) string {
	var op string
	var n *data.AffectedNode
	var fields interface{}

	switch {
	case ne.CreatedNode != nil:
		op = "Created"
		n = ne.CreatedNode
		fields = n.NewFields
	case ne.ModifiedNode != nil:
		op = "Modified"
		n = ne.ModifiedNode
		fields = n.FinalFields
	case ne.DeletedNode != nil:
		op = "Deleted"
		n = ne.DeletedNode
		fields = n.FinalFields
	}

	switch n.LedgerEntryType {
	case data.DIRECTORY:
		// Skip
		return ""

	case data.OFFER:
		return fmt.Sprintf("%s Offer %s %s for %s",
			op,
			fields.(*data.OfferFields).Account,
			fields.(*data.OfferFields).TakerGets,
			fields.(*data.OfferFields).TakerPays,
		)

	default:
		return fmt.Sprintf("%s %s node: %s", op, n.LedgerEntryType, n.LedgerIndex)
	}
}
<|endoftext|>


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

package main

import (
	"bufio"
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"regexp"
	"syscall"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

const (
	namespace = "varnish_request"
)

func main() {
	// TODO: add support for multiple Varnish instances (-S)
	var (
		listenAddress = flag.String("port", ":9147", "Address to listen on for web interface and telemetry.")
		metricsPath   = flag.String("metricsurl", "/metrics", "Path under which to expose metrics.")
		varnishHost   = flag.String("host", "localhost", "Virtual host to look for")
	)
	flag.Parse()

	// Listen to signals
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGINT)

	// Set up 'varnishncsa' pipe
	cmdName := "varnishncsa"
	cmdArgs := []string{ "-F", "time:%D method=\"%m\" status=%s path=\"%U\"", "-q", "ReqHeader eq \"" + *varnishHost + "\""}
	cmd := exec.Command(cmdName, cmdArgs...)
	cmdReader, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	scanner := bufio.NewScanner(cmdReader)
	re := regexp.MustCompile(`/[0-9]+/`)

	// Setup metrics
	varnishMessages := prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: namespace,
		Name:      "exporter_log_messages",
		Help:      "Current total log messages received.",
	})
	err = prometheus.Register(varnishMessages)
	if err != nil {
		log.Fatal(err)
	}
	varnishParseFailures := prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: namespace,
		Name:      "exporter_log_parse_failure",
		Help:      "Number of errors while parsing log messages.",
	})
	err = prometheus.Register(varnishParseFailures)
	if err != nil {
		log.Fatal(err)
	}
	var msgs int64

	go func() {
		for scanner.Scan() {
			varnishMessages.Inc()
			// TODO: this is very crude, should be made configurable
			content := re.ReplaceAllString(scanner.Text(), "/ID/")
			msgs++
			metrics, labels, err := parseMessage(content)
			if err != nil {
				log.Error(err)
				continue
			}
			for _, metric := range metrics {
				var collector prometheus.Collector
				collector, err = prometheus.RegisterOrGet(prometheus.NewHistogramVec(prometheus.HistogramOpts{
					Namespace: namespace,
					Name:      metric.Name,
					Help:      fmt.Sprintf("Varnish request log value for %s", metric.Name),
				}, labels.Names))
				if err != nil {
					log.Error(err)
					continue
				}
				collector.(*prometheus.HistogramVec).WithLabelValues(labels.Values...).Observe(metric.Value)
			}
		}
	}()

	// Setup HTTP server
	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
             <title>Varnish Request Exporter</title>

Varnish Request Exporter

<a href='` + *metricsPath + `'>Metrics

             `))
	})

	go func() {
		log.Infof("Starting Server: %s", *listenAddress)
		log.Fatal(http.ListenAndServe(*listenAddress, nil))
	}()

	go func() {
		err = cmd.Start()
		if err != nil {
			log.Fatal(err)
		}
		err = cmd.Wait()
		if err != nil {
			log.Fatal(err)
		}
		log.Infof("varnishncsa command exited")
		log.Infof("Messages received: %d", msgs)
		os.Exit(0)
	}()

	s := <-sigchan
	log.Infof("Received %v, terminating", s)
	log.Infof("Messages received: %d", msgs)

	os.Exit(0)
}
<commit_msg>Fix URL normalizing regex to catch URLs like /12345 (no tailing /)<commit_after>// Copyright 2016 Stig Bakken (based on the works of Markus Lindenberg)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"regexp"
	"syscall"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

const (
	namespace = "varnish_request"
)

func main() {
	// TODO: add support for multiple Varnish instances (-S)
	var (
		listenAddress = flag.String("port", ":9147", "Address to listen on for web interface and telemetry.")
		metricsPath   = flag.String("metricsurl", "/metrics", "Path under which to expose metrics.")
		varnishHost   = flag.String("host", "localhost", "Virtual host to look for")
	)
	flag.Parse()

	// Listen to signals
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGINT)

	// Set up 'varnishncsa' pipe
	cmdName := "varnishncsa"
	cmdArgs := []string{ "-F", "time:%D method=\"%m\" status=%s path=\"%U\"", "-q", "ReqHeader eq \"" + *varnishHost + "\""}
	cmd := exec.Command(cmdName, cmdArgs...)
	cmdReader, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	scanner := bufio.NewScanner(cmdReader)
	re := regexp.MustCompile(`(/[0-9]+/|/\d+$)`)

	// Setup metrics
	varnishMessages := prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: namespace,
		Name:      "exporter_log_messages",
		Help:      "Current total log messages received.",
	})
	err = prometheus.Register(varnishMessages)
	if err != nil {
		log.Fatal(err)
	}
	varnishParseFailures := prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: namespace,
		Name:      "exporter_log_parse_failure",
		Help:      "Number of errors while parsing log messages.",
	})
	err = prometheus.Register(varnishParseFailures)
	if err != nil {
		log.Fatal(err)
	}
	var msgs int64

	go func() {
		for scanner.Scan() {
			varnishMessages.Inc()
			// TODO: this is very crude, should be made configurable
			content := re.ReplaceAllString(scanner.Text(), "/ID/")
			msgs++
			metrics, labels, err := parseMessage(content)
			if err != nil {
				log.Error(err)
				continue
			}
			for _, metric := range metrics {
				var collector prometheus.Collector
				collector, err = prometheus.RegisterOrGet(prometheus.NewHistogramVec(prometheus.HistogramOpts{
					Namespace: namespace,
					Name:      metric.Name,
					Help:      fmt.Sprintf("Varnish request log value for %s", metric.Name),
				}, labels.Names))
				if err != nil {
					log.Error(err)
					continue
				}
				collector.(*prometheus.HistogramVec).WithLabelValues(labels.Values...).Observe(metric.Value)
			}
		}
	}()

	// Setup HTTP server
	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
             <title>Varnish Request Exporter</title>

Varnish Request Exporter

<a href='` + *metricsPath + `'>Metrics

             `))
	})

	go func() {
		log.Infof("Starting Server: %s", *listenAddress)
		log.Fatal(http.ListenAndServe(*listenAddress, nil))
	}()

	go func() {
		err = cmd.Start()
		if err != nil {
			log.Fatal(err)
		}
		err = cmd.Wait()
		if err != nil {
			log.Fatal(err)
		}
		log.Infof("varnishncsa command exited")
		log.Infof("Messages received: %d", msgs)
		os.Exit(0)
	}()

	s := <-sigchan
	log.Infof("Received %v, terminating", s)
	log.Infof("Messages received: %d", msgs)

	os.Exit(0)
}
<|endoftext|>


<commit_before>package docker

import (
	"io"
	"io/ioutil"

	"github.com/samalba/dockerclient"
)

var (
	LogOpts = &dockerclient.LogOptions{
		Stdout: true,
		Stderr: true,
	}

	LogOptsTail = &dockerclient.LogOptions{
		Follow: true,
		Stdout: true,
		Stderr: true,
	}
)

// Run creates the docker container, pulling images if necessary, starts
// the container and blocks until the container exits, returning the exit
// information.
func Run(client dockerclient.Client, conf *dockerclient.ContainerConfig, name string) (*dockerclient.ContainerInfo, error) {
	info, err := RunDaemon(client, conf, name)
	if err != nil {
		return nil, err
	}

	return Wait(client, info.Id)
}

// RunDaemon creates the docker container, pulling images if necessary, starts
// the container and returns the container information. It does not wait for
// the container to exit.
func RunDaemon(client dockerclient.Client, conf *dockerclient.ContainerConfig, name string) (*dockerclient.ContainerInfo, error) {

	// attempts to create the contianer
	id, err := client.CreateContainer(conf, name)
	if err != nil {
		// and pull the image and re-create if that fails
		err = client.PullImage(conf.Image, nil)
		if err != nil {
			return nil, err
		}
		id, err = client.CreateContainer(conf, name)
		if err != nil {
			client.RemoveContainer(id, true, true)
			return nil, err
		}
	}

	// fetches the container information
	info, err := client.InspectContainer(id)
	if err != nil {
		client.RemoveContainer(id, true, true)
		return nil, err
	}

	// starts the container
	err = client.StartContainer(id, &conf.HostConfig)
	if err != nil {
		client.RemoveContainer(id, true, true)
		return nil, err
	}

	return info, err
}

// Wait blocks until the named container exits, returning the exit information.
func Wait(client dockerclient.Client, name string) (*dockerclient.ContainerInfo, error) {

	defer func() {
		client.StopContainer(name, 5)
		client.KillContainer(name, "9")
	}()

	errc := make(chan error, 1)
	infoc := make(chan *dockerclient.ContainerInfo, 1)
	go func() {

		// blocks and waits for the container to finish
		// by streaming the logs (to /dev/null). Ideally
		// we could use the `wait` function instead
		rc, err := client.ContainerLogs(name, LogOptsTail)
		if err != nil {
			errc <- err
			return
		}
		io.Copy(ioutil.Discard, rc)
		rc.Close()

		info, err := client.InspectContainer(name)
		if err != nil {
			errc <- err
			return
		}
		infoc <- info
	}()

	select {
	case info := <-infoc:
		return info, nil
	case err := <-errc:
		return nil, err
	}
}
<commit_msg>resume waiting for the container to complete<commit_after>package docker

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

	log "github.com/Sirupsen/logrus"
	"github.com/samalba/dockerclient"
)

var (
	LogOpts = &dockerclient.LogOptions{
		Stdout: true,
		Stderr: true,
	}

	LogOptsTail = &dockerclient.LogOptions{
		Follow: true,
		Stdout: true,
		Stderr: true,
	}
)

// Run creates the docker container, pulling images if necessary, starts
// the container and blocks until the container exits, returning the exit
// information.
func Run(client dockerclient.Client, conf *dockerclient.ContainerConfig, name string) (*dockerclient.ContainerInfo, error) {
	info, err := RunDaemon(client, conf, name)
	if err != nil {
		return nil, err
	}

	return Wait(client, info.Id)
}

// RunDaemon creates the docker container, pulling images if necessary, starts
// the container and returns the container information. It does not wait for
// the container to exit.
func RunDaemon(client dockerclient.Client, conf *dockerclient.ContainerConfig, name string) (*dockerclient.ContainerInfo, error) {

	// attempts to create the contianer
	id, err := client.CreateContainer(conf, name)
	if err != nil {
		// and pull the image and re-create if that fails
		err = client.PullImage(conf.Image, nil)
		if err != nil {
			return nil, err
		}
		id, err = client.CreateContainer(conf, name)
		if err != nil {
			client.RemoveContainer(id, true, true)
			return nil, err
		}
	}

	// fetches the container information
	info, err := client.InspectContainer(id)
	if err != nil {
		client.RemoveContainer(id, true, true)
		return nil, err
	}

	// starts the container
	err = client.StartContainer(id, &conf.HostConfig)
	if err != nil {
		client.RemoveContainer(id, true, true)
		return nil, err
	}

	return info, err
}

// Wait blocks until the named container exits, returning the exit information.
func Wait(client dockerclient.Client, name string) (*dockerclient.ContainerInfo, error) {

	defer func() {
		client.StopContainer(name, 5)
		client.KillContainer(name, "9")
	}()

	errc := make(chan error, 1)
	infoc := make(chan *dockerclient.ContainerInfo, 1)
	go func() {
		// options to fetch the stdout and stderr logs
		// by tailing the output.
		logOptsTail := &dockerclient.LogOptions{
			Follow: true,
			Stdout: true,
			Stderr: true,
		}

		for attempts := 0; attempts < 5; attempts++ {
			if attempts > 0 {
				// When resuming the stream, only grab the last line when starting
				// the tailing.
				logOptsTail.Tail = 1
			}

			// blocks and waits for the container to finish
			// by streaming the logs (to /dev/null). Ideally
			// we could use the `wait` function instead
			rc, err := client.ContainerLogs(name, logOptsTail)
			if err != nil {
				errc <- err
				return
			}
			io.Copy(ioutil.Discard, rc)
			rc.Close()

			info, err := client.InspectContainer(name)
			if err != nil {
				errc <- err
				return
			}

			if !info.State.Running {
				// The container is no longer running, there should be no more logs to tail.
				infoc <- info
				return
			}

			log.Debugf("Attempting to resume log tailing after %d attempts.\n", attempts)
		}

		errc <- errors.New("Maximum number of attempts made while tailing logs.")
	}()

	select {
	case info := <-infoc:
		return info, nil
	case err := <-errc:
		return nil, err
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"net/http"

	_ "github.com/go-sql-driver/mysql"
	"github.com/vharitonsky/iniflags"
)

var (
	db  *sql.DB
	err error

	httpHost = flag.String("HTTP_HOST", "localhost", "Connect to host")
	httpPort = flag.String("HTTP_PORT", "9199", "Connect to host")

	sqlHost = flag.String("MYSQL_HOST", "localhost", "Connect to host")
	sqlPort = flag.String("MYSQL_PORT", "3306", "Connect to host")
	sqlUser = flag.String("MYSQL_USER", "root", "User for login to MySQL")
	sqlPass = flag.String("MYSQL_PASS", "", "Password for login to MySQL")

	donorOk = flag.Bool("DONOR_OK", false, "treat donor as regular working node")

	showVersion = flag.Bool("version", false, fmt.Sprint("Show current version: ", Commit))

	// Commit holds the git sha information on compile time
	Commit = "dev"
)

func main() {
	iniflags.Parse()
	if *showVersion {
		fmt.Println(Commit)
		return
	}
	db, err = sql.Open(
		"mysql",
		fmt.Sprintf("%s:%s@tcp(%s:%s)/mysql", *sqlUser, *sqlPass, *sqlHost, *sqlPort),
	)
	if err != nil {
		log.Fatal(err)
	}
	http.HandleFunc("/", databaseStatusHandler)
	http.HandleFunc("/status", watchdogStatusHandler)
	if err := http.ListenAndServe(*httpHost+":"+*httpPort, nil); err != nil {
		log.Fatal(err)
	}
}

func watchdogStatusHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func databaseStatusHandler(w http.ResponseWriter, r *http.Request) {
	err = db.Ping()
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	var key string
	var value int64
	err = db.QueryRow("SHOW STATUS LIKE 'wsrep_local_state'").Scan(&key, &value)
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	switch {
	case 4 == value:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	case 2 == value && *donorOk:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	default:
		http.Error(w, "Galera Node is *down*. (State Mismatch)", 503)
		return
	}
}
<commit_msg>adapt flag descriptions<commit_after>package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"net/http"

	_ "github.com/go-sql-driver/mysql"
	"github.com/vharitonsky/iniflags"
)

var (
	db  *sql.DB
	err error

	httpHost = flag.String("HTTP_HOST", "localhost", "http address")
	httpPort = flag.String("HTTP_PORT", "9199", "http port")

	sqlHost = flag.String("MYSQL_HOST", "localhost", "address of the MySQL server")
	sqlPort = flag.String("MYSQL_PORT", "3306", "port of the MySQL server")
	sqlUser = flag.String("MYSQL_USER", "root", "Username for login to MySQL")
	sqlPass = flag.String("MYSQL_PASS", "", "Password for login to MySQL")

	donorOk = flag.Bool("DONOR_OK", false, "treat donor as regular working node")

	showVersion = flag.Bool("version", false, fmt.Sprint("Show current version: ", Commit))

	// Commit holds the git sha information on compile time
	Commit = "dev"
)

func main() {
	iniflags.Parse()
	if *showVersion {
		fmt.Println(Commit)
		return
	}
	db, err = sql.Open(
		"mysql",
		fmt.Sprintf("%s:%s@tcp(%s:%s)/mysql", *sqlUser, *sqlPass, *sqlHost, *sqlPort),
	)
	if err != nil {
		log.Fatal(err)
	}
	http.HandleFunc("/", databaseStatusHandler)
	http.HandleFunc("/status", watchdogStatusHandler)
	if err := http.ListenAndServe(*httpHost+":"+*httpPort, nil); err != nil {
		log.Fatal(err)
	}
}

func watchdogStatusHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func databaseStatusHandler(w http.ResponseWriter, r *http.Request) {
	err = db.Ping()
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	var key string
	var value int64
	err = db.QueryRow("SHOW STATUS LIKE 'wsrep_local_state'").Scan(&key, &value)
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	switch {
	case 4 == value:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	case 2 == value && *donorOk:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	default:
		http.Error(w, "Galera Node is *down*. (State Mismatch)", 503)
		return
	}
}
<|endoftext|>