<commit_before>package fuse

// This file contains the internal logic of the
// FileSystemConnector. The functions for satisfying the raw interface
// are in fsops.go

import (
	"log"
	"path/filepath"
	"strings"
	"sync/atomic"
	"time"
	"unsafe"

	"github.com/hanwen/go-fuse/raw"
)

// Tests should set to true.
var paranoia = false

// FilesystemConnector is a raw FUSE filesystem that manages
// in-process mounts and inodes.  Its job is twofold:
//
// * It translates between the raw kernel interface (padded structs of
// int32 and int64) and the more abstract Go-ish NodeFileSystem
// interface.
//
// * It manages mounting and unmounting of NodeFileSystems into the
// directory hierarchy
//
// To achieve this, the connector only needs a pointer to the root
// node.
type FileSystemConnector struct {
	DefaultRawFileSystem

	Debug bool

	// Callbacks for talking back to the kernel.
	fsInit RawFsInit

	// Translate between uint64 handles and *Inode.
	inodeMap HandleMap

	// The root of the FUSE file system.
	rootNode *Inode

	// Used as the generation inodes.
	generation uint64
}

func NewFileSystemOptions() *FileSystemOptions {
	return &FileSystemOptions{
		NegativeTimeout: 0,
		AttrTimeout:     time.Second,
		EntryTimeout:    time.Second,
		Owner:           CurrentOwner(),
	}
}

func NewFileSystemConnector(nodeFs NodeFileSystem, opts *FileSystemOptions) (c *FileSystemConnector) {
	c = new(FileSystemConnector)
	if opts == nil {
		opts = NewFileSystemOptions()
	}
	c.inodeMap = NewHandleMap(opts.PortableInodes)
	c.rootNode = newInode(true, nodeFs.Root())

	// Make sure we don't reuse generation numbers.
	c.generation = uint64(time.Now().UnixNano())

	c.verify()
	c.MountRoot(nodeFs, opts)

	// FUSE does not issue a LOOKUP for 1 (obviously), but it does
	// issue a forget.  This lookupUpdate is to make the counts match.
	c.lookupUpdate(c.rootNode)

	return c
}

func (c *FileSystemConnector) nextGeneration() uint64 {
	return atomic.AddUint64(&c.generation, 1)
}

// This verifies invariants of the data structure.  This routine
// acquires tree locks as it walks the inode tree.
func (c *FileSystemConnector) verify() {
	if !paranoia {
		return
	}
	root := c.rootNode
	root.verify(c.rootNode.mountPoint)
}

// Generate EntryOut and increase the lookup count for an inode.
func (c *FileSystemConnector) childLookup(out *raw.EntryOut, fsi FsNode) {
	n := fsi.Inode()
	fsi.GetAttr((*Attr)(&out.Attr), nil, nil)
	n.mount.fillEntry(out)
	out.Ino = c.lookupUpdate(n)
	out.NodeId = out.Ino
	if out.Nlink == 0 {
		// With Nlink == 0, newer kernels will refuse link
		// operations.
		out.Nlink = 1
	}
}

func (c *FileSystemConnector) findMount(parent *Inode, name string) (mount *fileSystemMount) {
	parent.treeLock.RLock()
	if parent.mounts != nil {
		mount = parent.mounts[name]
	}
	parent.treeLock.RUnlock()

	return
}

func (c *FileSystemConnector) toInode(nodeid uint64) *Inode {
	if nodeid == raw.FUSE_ROOT_ID {
		return c.rootNode
	}
	i := (*Inode)(unsafe.Pointer(c.inodeMap.Decode(nodeid)))
	return i
}

// Must run outside treeLock.  Returns the nodeId.
func (c *FileSystemConnector) lookupUpdate(node *Inode) uint64 {
	node.treeLock.Lock()
	if node.lookupCount == 0 {
		node.nodeId = c.inodeMap.Register(&node.handled, node)
	}
	node.lookupCount += 1
	node.treeLock.Unlock()

	return node.nodeId
}

// Must run outside treeLock.
func (c *FileSystemConnector) forgetUpdate(node *Inode, forgetCount int) {
	node.treeLock.Lock()
	node.lookupCount -= forgetCount
	if node.lookupCount == 0 {
		c.inodeMap.Forget(node.nodeId)
		node.nodeId = 0
	} else if node.lookupCount < 0 {
		log.Panicf("lookupCount underflow: %d: %v", node.lookupCount, c)
	}

	c.recursiveConsiderDropInode(node)
	node.treeLock.Unlock()
	c.verify()
}

// InodeCount returns the number of inodes registered with the kernel.
func (c *FileSystemConnector) InodeHandleCount() int {
	return c.inodeMap.Count()
}

// Must hold treeLock.

func (c *FileSystemConnector) recursiveConsiderDropInode(n *Inode) (drop bool) {
	delChildren := []string{}
	for k, v := range n.children {
		// Only consider children from the same mount, or
		// already unmounted mountpoints.
		if v.mountPoint == nil && c.recursiveConsiderDropInode(v) {
			delChildren = append(delChildren, k)
		}
	}
	for _, k := range delChildren {
		ch := n.rmChild(k)
		if ch == nil {
			log.Panicf("trying to del child %q, but not present", k)
		}
		ch.fsInode.OnForget()
	}

	if len(n.children) > 0 || n.lookupCount > 0 || !n.FsNode().Deletable() {
		return false
	}
	if n == c.rootNode || n.mountPoint != nil {
		return false
	}

	n.openFilesMutex.Lock()
	ok := len(n.openFiles) == 0
	n.openFilesMutex.Unlock()

	return ok
}

// Finds a node within the currently known inodes, returns the last
// known node and the remaining unknown path components.  If parent is
// nil, start from FUSE mountpoint.
func (c *FileSystemConnector) Node(parent *Inode, fullPath string) (*Inode, []string) {
	if parent == nil {
		parent = c.rootNode
	}
	if fullPath == "" {
		return parent, nil
	}

	sep := string(filepath.Separator)
	fullPath = strings.TrimLeft(filepath.Clean(fullPath), sep)
	comps := strings.Split(fullPath, sep)

	node := parent
	if node.mountPoint == nil {
		node.treeLock.RLock()
		defer node.treeLock.RUnlock()
	}

	for i, component := range comps {
		if len(component) == 0 {
			continue
		}

		if node.mountPoint != nil {
			node.treeLock.RLock()
			defer node.treeLock.RUnlock()
		}

		next := node.children[component]
		if next == nil {
			return node, comps[i:]
		}
		node = next
	}

	return node, nil
}

func (c *FileSystemConnector) LookupNode(parent *Inode, path string) *Inode {
	if path == "" {
		return parent
	}
	components := strings.Split(path, "/")
	for _, r := range components {
		var a Attr
		child, _ := c.internalLookup(&a, parent, r, nil)
		if child == nil {
			return nil
		}

		parent = child
	}

	return parent
}

////////////////////////////////////////////////////////////////

func (c *FileSystemConnector) MountRoot(nodeFs NodeFileSystem, opts *FileSystemOptions) {
	c.rootNode.mountFs(nodeFs, opts)
	c.rootNode.mount.connector = c
	nodeFs.OnMount(c)
	c.verify()
}

// Mount() generates a synthetic directory node, and mounts the file
// system there.  If opts is nil, the mount options of the root file
// system are inherited.  The encompassing filesystem should pretend
// the mount point does not exist.  If it does, it will generate an
// Inode with the same, which will cause Mount() to return EBUSY.
//
// Return values:
//
// ENOENT: the directory containing the mount point does not exist.
//
// EBUSY: the intended mount point already exists.
func (c *FileSystemConnector) Mount(parent *Inode, name string, nodeFs NodeFileSystem, opts *FileSystemOptions) Status {
	defer c.verify()
	parent.treeLock.Lock()
	defer parent.treeLock.Unlock()
	node := parent.children[name]
	if node != nil {
		return EBUSY
	}

	node = newInode(true, nodeFs.Root())
	if opts == nil {
		opts = c.rootNode.mountPoint.options
	}

	node.mountFs(nodeFs, opts)
	node.mount.connector = c
	parent.addChild(name, node)

	if parent.mounts == nil {
		parent.mounts = make(map[string]*fileSystemMount)
	}
	parent.mounts[name] = node.mountPoint
	node.mountPoint.parentInode = parent
	if c.Debug {
		log.Println("Mount: ", nodeFs, "on subdir", name,
			"parent", parent.nodeId)
	}
	nodeFs.OnMount(c)
	return OK
}

// Unmount() tries to unmount the given inode.
//
// Returns the following error codes:
//
// EINVAL: path does not exist, or is not a mount point.
//
// EBUSY: there are open files, or submounts below this node.
func (c *FileSystemConnector) Unmount(node *Inode) Status {
	if node.mountPoint == nil {
		log.Println("not a mountpoint:", node.nodeId)
		return EINVAL
	}

	// Must lock parent to update tree structure.
	parentNode := node.mountPoint.parentInode
	parentNode.treeLock.Lock()
	defer parentNode.treeLock.Unlock()

	mount := node.mountPoint
	name := node.mountPoint.mountName()
	if mount.openFiles.Count() > 0 {
		return EBUSY
	}

	node.treeLock.Lock()
	defer node.treeLock.Unlock()

	mountInode := mount.mountInode
	if !mountInode.canUnmount() {
		return EBUSY
	}

	mount.mountInode = nil
	mountInode.mountPoint = nil

	delete(parentNode.mounts, name)
	delete(parentNode.children, name)
	mount.fs.OnUnmount()

	c.EntryNotify(parentNode, name)

	return OK
}

func (c *FileSystemConnector) FileNotify(node *Inode, off int64, length int64) Status {
	n := node.nodeId
	if node == c.rootNode {
		n = raw.FUSE_ROOT_ID
	}
	if n == 0 {
		return OK
	}
	out := raw.NotifyInvalInodeOut{
		Length: length,
		Off:    off,
		Ino:    n,
	}
	return c.fsInit.InodeNotify(&out)
}

func (c *FileSystemConnector) EntryNotify(dir *Inode, name string) Status {
	n := dir.nodeId
	if dir == c.rootNode {
		n = raw.FUSE_ROOT_ID
	}
	if n == 0 {
		return OK
	}
	return c.fsInit.EntryNotify(n, name)
}

func (c *FileSystemConnector) DeleteNotify(dir *Inode, child *Inode, name string) Status {
	n := dir.nodeId
	if dir == c.rootNode {
		n = raw.FUSE_ROOT_ID
	}
	if n == 0 {
		return OK
	}
	return c.fsInit.DeleteNotify(n, child.nodeId, name)
}
<commit_msg>Use deletenotify on unmount.<commit_after>package fuse

// This file contains the internal logic of the
// FileSystemConnector. The functions for satisfying the raw interface
// are in fsops.go

import (
	"log"
	"path/filepath"
	"strings"
	"sync/atomic"
	"time"
	"unsafe"

	"github.com/hanwen/go-fuse/raw"
)

// Tests should set to true.
var paranoia = false

// FilesystemConnector is a raw FUSE filesystem that manages
// in-process mounts and inodes.  Its job is twofold:
//
// * It translates between the raw kernel interface (padded structs of
// int32 and int64) and the more abstract Go-ish NodeFileSystem
// interface.
//
// * It manages mounting and unmounting of NodeFileSystems into the
// directory hierarchy
//
// To achieve this, the connector only needs a pointer to the root
// node.
type FileSystemConnector struct {
	DefaultRawFileSystem

	Debug bool

	// Callbacks for talking back to the kernel.
	fsInit RawFsInit

	// Translate between uint64 handles and *Inode.
	inodeMap HandleMap

	// The root of the FUSE file system.
	rootNode *Inode

	// Used as the generation inodes.
	generation uint64
}

func NewFileSystemOptions() *FileSystemOptions {
	return &FileSystemOptions{
		NegativeTimeout: 0,
		AttrTimeout:     time.Second,
		EntryTimeout:    time.Second,
		Owner:           CurrentOwner(),
	}
}

func NewFileSystemConnector(nodeFs NodeFileSystem, opts *FileSystemOptions) (c *FileSystemConnector) {
	c = new(FileSystemConnector)
	if opts == nil {
		opts = NewFileSystemOptions()
	}
	c.inodeMap = NewHandleMap(opts.PortableInodes)
	c.rootNode = newInode(true, nodeFs.Root())

	// Make sure we don't reuse generation numbers.
	c.generation = uint64(time.Now().UnixNano())

	c.verify()
	c.MountRoot(nodeFs, opts)

	// FUSE does not issue a LOOKUP for 1 (obviously), but it does
	// issue a forget.  This lookupUpdate is to make the counts match.
	c.lookupUpdate(c.rootNode)

	return c
}

func (c *FileSystemConnector) nextGeneration() uint64 {
	return atomic.AddUint64(&c.generation, 1)
}

// This verifies invariants of the data structure.  This routine
// acquires tree locks as it walks the inode tree.
func (c *FileSystemConnector) verify() {
	if !paranoia {
		return
	}
	root := c.rootNode
	root.verify(c.rootNode.mountPoint)
}

// Generate EntryOut and increase the lookup count for an inode.
func (c *FileSystemConnector) childLookup(out *raw.EntryOut, fsi FsNode) {
	n := fsi.Inode()
	fsi.GetAttr((*Attr)(&out.Attr), nil, nil)
	n.mount.fillEntry(out)
	out.Ino = c.lookupUpdate(n)
	out.NodeId = out.Ino
	if out.Nlink == 0 {
		// With Nlink == 0, newer kernels will refuse link
		// operations.
		out.Nlink = 1
	}
}

func (c *FileSystemConnector) findMount(parent *Inode, name string) (mount *fileSystemMount) {
	parent.treeLock.RLock()
	if parent.mounts != nil {
		mount = parent.mounts[name]
	}
	parent.treeLock.RUnlock()

	return
}

func (c *FileSystemConnector) toInode(nodeid uint64) *Inode {
	if nodeid == raw.FUSE_ROOT_ID {
		return c.rootNode
	}
	i := (*Inode)(unsafe.Pointer(c.inodeMap.Decode(nodeid)))
	return i
}

// Must run outside treeLock.  Returns the nodeId.
func (c *FileSystemConnector) lookupUpdate(node *Inode) uint64 {
	node.treeLock.Lock()
	if node.lookupCount == 0 {
		node.nodeId = c.inodeMap.Register(&node.handled, node)
	}
	node.lookupCount += 1
	node.treeLock.Unlock()

	return node.nodeId
}

// Must run outside treeLock.
func (c *FileSystemConnector) forgetUpdate(node *Inode, forgetCount int) {
	node.treeLock.Lock()
	node.lookupCount -= forgetCount
	if node.lookupCount == 0 {
		c.inodeMap.Forget(node.nodeId)
		node.nodeId = 0
	} else if node.lookupCount < 0 {
		log.Panicf("lookupCount underflow: %d: %v", node.lookupCount, c)
	}

	c.recursiveConsiderDropInode(node)
	node.treeLock.Unlock()
	c.verify()
}

// InodeCount returns the number of inodes registered with the kernel.
func (c *FileSystemConnector) InodeHandleCount() int {
	return c.inodeMap.Count()
}

// Must hold treeLock.

func (c *FileSystemConnector) recursiveConsiderDropInode(n *Inode) (drop bool) {
	delChildren := []string{}
	for k, v := range n.children {
		// Only consider children from the same mount, or
		// already unmounted mountpoints.
		if v.mountPoint == nil && c.recursiveConsiderDropInode(v) {
			delChildren = append(delChildren, k)
		}
	}
	for _, k := range delChildren {
		ch := n.rmChild(k)
		if ch == nil {
			log.Panicf("trying to del child %q, but not present", k)
		}
		ch.fsInode.OnForget()
	}

	if len(n.children) > 0 || n.lookupCount > 0 || !n.FsNode().Deletable() {
		return false
	}
	if n == c.rootNode || n.mountPoint != nil {
		return false
	}

	n.openFilesMutex.Lock()
	ok := len(n.openFiles) == 0
	n.openFilesMutex.Unlock()

	return ok
}

// Finds a node within the currently known inodes, returns the last
// known node and the remaining unknown path components.  If parent is
// nil, start from FUSE mountpoint.
func (c *FileSystemConnector) Node(parent *Inode, fullPath string) (*Inode, []string) {
	if parent == nil {
		parent = c.rootNode
	}
	if fullPath == "" {
		return parent, nil
	}

	sep := string(filepath.Separator)
	fullPath = strings.TrimLeft(filepath.Clean(fullPath), sep)
	comps := strings.Split(fullPath, sep)

	node := parent
	if node.mountPoint == nil {
		node.treeLock.RLock()
		defer node.treeLock.RUnlock()
	}

	for i, component := range comps {
		if len(component) == 0 {
			continue
		}

		if node.mountPoint != nil {
			node.treeLock.RLock()
			defer node.treeLock.RUnlock()
		}

		next := node.children[component]
		if next == nil {
			return node, comps[i:]
		}
		node = next
	}

	return node, nil
}

func (c *FileSystemConnector) LookupNode(parent *Inode, path string) *Inode {
	if path == "" {
		return parent
	}
	components := strings.Split(path, "/")
	for _, r := range components {
		var a Attr
		child, _ := c.internalLookup(&a, parent, r, nil)
		if child == nil {
			return nil
		}

		parent = child
	}

	return parent
}

////////////////////////////////////////////////////////////////

func (c *FileSystemConnector) MountRoot(nodeFs NodeFileSystem, opts *FileSystemOptions) {
	c.rootNode.mountFs(nodeFs, opts)
	c.rootNode.mount.connector = c
	nodeFs.OnMount(c)
	c.verify()
}

// Mount() generates a synthetic directory node, and mounts the file
// system there.  If opts is nil, the mount options of the root file
// system are inherited.  The encompassing filesystem should pretend
// the mount point does not exist.  If it does, it will generate an
// Inode with the same, which will cause Mount() to return EBUSY.
//
// Return values:
//
// ENOENT: the directory containing the mount point does not exist.
//
// EBUSY: the intended mount point already exists.
func (c *FileSystemConnector) Mount(parent *Inode, name string, nodeFs NodeFileSystem, opts *FileSystemOptions) Status {
	defer c.verify()
	parent.treeLock.Lock()
	defer parent.treeLock.Unlock()
	node := parent.children[name]
	if node != nil {
		return EBUSY
	}

	node = newInode(true, nodeFs.Root())
	if opts == nil {
		opts = c.rootNode.mountPoint.options
	}

	node.mountFs(nodeFs, opts)
	node.mount.connector = c
	parent.addChild(name, node)

	if parent.mounts == nil {
		parent.mounts = make(map[string]*fileSystemMount)
	}
	parent.mounts[name] = node.mountPoint
	node.mountPoint.parentInode = parent
	if c.Debug {
		log.Println("Mount: ", nodeFs, "on subdir", name,
			"parent", parent.nodeId)
	}
	nodeFs.OnMount(c)
	return OK
}

// Unmount() tries to unmount the given inode.
//
// Returns the following error codes:
//
// EINVAL: path does not exist, or is not a mount point.
//
// EBUSY: there are open files, or submounts below this node.
func (c *FileSystemConnector) Unmount(node *Inode) Status {
	if node.mountPoint == nil {
		log.Println("not a mountpoint:", node.nodeId)
		return EINVAL
	}

	// Must lock parent to update tree structure.
	parentNode := node.mountPoint.parentInode
	parentNode.treeLock.Lock()
	defer parentNode.treeLock.Unlock()

	mount := node.mountPoint
	name := node.mountPoint.mountName()
	if mount.openFiles.Count() > 0 {
		return EBUSY
	}

	node.treeLock.Lock()
	defer node.treeLock.Unlock()

	mountInode := mount.mountInode
	if !mountInode.canUnmount() {
		return EBUSY
	}

	mount.mountInode = nil
	mountInode.mountPoint = nil

	delete(parentNode.mounts, name)
	delete(parentNode.children, name)
	mount.fs.OnUnmount()

	c.DeleteNotify(parentNode, mountInode, name)

	return OK
}

func (c *FileSystemConnector) FileNotify(node *Inode, off int64, length int64) Status {
	n := node.nodeId
	if node == c.rootNode {
		n = raw.FUSE_ROOT_ID
	}
	if n == 0 {
		return OK
	}
	out := raw.NotifyInvalInodeOut{
		Length: length,
		Off:    off,
		Ino:    n,
	}
	return c.fsInit.InodeNotify(&out)
}

func (c *FileSystemConnector) EntryNotify(dir *Inode, name string) Status {
	n := dir.nodeId
	if dir == c.rootNode {
		n = raw.FUSE_ROOT_ID
	}
	if n == 0 {
		return OK
	}
	return c.fsInit.EntryNotify(n, name)
}

func (c *FileSystemConnector) DeleteNotify(dir *Inode, child *Inode, name string) Status {
	n := dir.nodeId
	if dir == c.rootNode {
		n = raw.FUSE_ROOT_ID
	}
	if n == 0 {
		return OK
	}
	return c.fsInit.DeleteNotify(n, child.nodeId, name)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"regexp"

	"github.com/grokify/gotilla/codegen"
	"github.com/grokify/gotilla/fmt/fmtutil"
	"github.com/jessevdk/go-flags"
)

type Options struct {
	Dir     string `short:"d" long:"dir" description:"Directory"`
	Pattern string `short:"p" long:"pattern" description:"Pattern"`
}

func main() {
	opts := Options{}
	_, err := flags.Parse(&opts)
	if err != nil {
		log.Fatal(err)
	}
	if opts.Dir == "" {
		opts.Dir = "."
	}
	fmtutil.PrintJSON(opts)
	if len(opts.Pattern) > 0 {
		files, err := codegen.ConvertFilesInPlaceNestedstructsToPointers(
			opts.Dir, regexp.MustCompile(opts.Pattern))
		if err != nil {
			log.Fatal(err)
		}
		fmtutil.PrintJSON(files)
	} else {
		files, err := codegen.ConvertFilesInPlaceNestedstructsToPointers(
			opts.Dir, nil)
		if err != nil {
			log.Fatal(err)
		}
		fmtutil.PrintJSON(files)
	}
	fmt.Println("DONE")
}
<commit_msg>update stylee<commit_after>package main

import (
	"fmt"
	"log"
	"regexp"

	"github.com/grokify/gotilla/codegen"
	"github.com/grokify/gotilla/fmt/fmtutil"
	"github.com/jessevdk/go-flags"
)

/*

go get github.com/grokify/gotilla/codegen/apps/nestedstruct2pointer

*/

type Options struct {
	Dir     string `short:"d" long:"dir" description:"Directory"`
	Pattern string `short:"p" long:"pattern" description:"Pattern"`
}

func main() {
	opts := Options{}
	_, err := flags.Parse(&opts)
	if err != nil {
		log.Fatal(err)
	}
	if opts.Dir == "" {
		opts.Dir = "."
	}
	fmtutil.PrintJSON(opts)
	if len(opts.Pattern) > 0 {
		files, err := codegen.ConvertFilesInPlaceNestedstructsToPointers(
			opts.Dir, regexp.MustCompile(opts.Pattern))
		if err != nil {
			log.Fatal(err)
		}
		fmtutil.PrintJSON(files)
	} else {
		files, err := codegen.ConvertFilesInPlaceNestedstructsToPointers(
			opts.Dir, nil)
		if err != nil {
			log.Fatal(err)
		}
		fmtutil.PrintJSON(files)
	}
	fmt.Println("DONE")
}
<|endoftext|>


<commit_before>package mixcoin

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

type Config struct {
	RpcAddress string // host:port for btcwallet instance
	RpcUser    string // username for btcwallet instance
	RpcPass    string // password for btcwallet instance
	CertFile   string // path to server cert file
	MixAccount string // name for account containing mix addresses
	WalletPass string // wallet passphrase

	NetParams *btcnet.Params // network type: simnet, mainnet, etc.
	ApiPort   int            // port to listen on for /chunk requests

	MinConfirmations int   // min confirmations we require
	ChunkSize        int64 // standard chunk size, satoshis

	PrivRingFile string // path to privring
	Passphrase   string // password for privring
}

func GetConfig() *Config {
	return &config
}

var defaultConfig = Config{
	RpcAddress: "127.0.0.1:18554",
	RpcUser:    "mixcoin",
	RpcPass:    "Mixcoin1",
	CertFile:   os.Getenv("HOME") + "/.mixcoin/server.key",
	MixAccount: "mixcoin",
	WalletPass: "Mixcoin1",

	NetParams: &btcnet.SimNetParams,
	ApiPort:   8082,

	MinConfirmations: 6,
	ChunkSize:        1000,

	PrivRingFile: os.Getenv("HOME") + "/.mixcoin/secring.gpg",
	Passphrase:   "Thereis1",
}

var config Config

func init() {
	configFile := os.Getenv("HOME") + "/.mixcoin/config.json"
	log.Printf("Reading " + configFile)

	configBytes, err := ioutil.ReadFile(configFile)
	if err != nil {
		writeDefaultConfig(configFile)
		fmt.Println("Config file written to ~/.mixcoin/config.json. Please edit and re-run.")
	}

	config = Config{}
	configBuf := bytes.NewBuffer(configBytes)
	//err = json.Unmarshal(configBuf, &config)
	decoder := json.NewDecoder(configBuf)
	err = decoder.Decode(&config)
	if err != nil {
		log.Panicf("Invalid configuration file %s: %v", configFile, err)
	}
}

func writeDefaultConfig(configFile string) {
	log.Printf("Creating default config file %s", configFile)
	configBytes, err := json.MarshalIndent(defaultConfig, "", "  ")
	if err != nil {
		panic(err)
	}
	err = os.MkdirAll(filepath.Dir(configFile), 0700)
	if err != nil {
		panic(err)
	}
	err = ioutil.WriteFile(configFile, configBytes, 0600)
	if err != nil {
		panic(err)
	}
}
<commit_msg>fix default configuration<commit_after>package mixcoin

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

type Config struct {
	RpcAddress string // host:port for btcwallet instance
	RpcUser    string // username for btcwallet instance
	RpcPass    string // password for btcwallet instance
	CertFile   string // path to server cert file
	MixAccount string // name for account containing mix addresses
	WalletPass string // wallet passphrase

	NetParams *btcnet.Params // network type: simnet, mainnet, etc.
	ApiPort   int            // port to listen on for /chunk requests

	MinConfirmations int   // min confirmations we require
	ChunkSize        int64 // standard chunk size, satoshis

	PrivRingFile string // path to privring
	Passphrase   string // password for privring
}

func GetConfig() *Config {
	return &config
}

var defaultConfig = Config{
	RpcAddress: "127.0.0.1:18554",
	RpcUser:    "mixcoin",
	RpcPass:    "Mixcoin1",
	CertFile:   os.Getenv("HOME") + "/.mixcoin/mixcoinCA.cer",
	MixAccount: "mixcoin",
	WalletPass: "Mixcoin1",

	NetParams: &btcnet.SimNetParams,
	ApiPort:   8082,

	MinConfirmations: 6,
	ChunkSize:        1000,

	PrivRingFile: os.Getenv("HOME") + "/.mixcoin/secring.gpg",
	Passphrase:   "Thereis1",
}

var config Config

func init() {
	configFile := os.Getenv("HOME") + "/.mixcoin/config.json"
	log.Printf("Reading " + configFile)

	configBytes, err := ioutil.ReadFile(configFile)
	if err != nil {
		writeDefaultConfig(configFile)
		fmt.Println("Config file written to ~/.mixcoin/config.json. Please edit and re-run.")
	}

	config = Config{}
	configBuf := bytes.NewBuffer(configBytes)
	//err = json.Unmarshal(configBuf, &config)
	decoder := json.NewDecoder(configBuf)
	err = decoder.Decode(&config)
	if err != nil {
		log.Panicf("Invalid configuration file %s: %v", configFile, err)
	}
}

func writeDefaultConfig(configFile string) {
	log.Printf("Creating default config file %s", configFile)
	configBytes, err := json.MarshalIndent(defaultConfig, "", "  ")
	if err != nil {
		panic(err)
	}
	err = os.MkdirAll(filepath.Dir(configFile), 0700)
	if err != nil {
		panic(err)
	}
	err = ioutil.WriteFile(configFile, configBytes, 0600)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>// +build js

package studyhandler

import (
	"net/url"

	"github.com/flimzy/log"
	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/jquery"
	"github.com/pkg/errors"

	"github.com/FlashbackSRS/flashback/fserve"
	"github.com/FlashbackSRS/flashback/repository"
)

type stackTracer interface {
	StackTrace() errors.StackTrace
}

var jQuery = jquery.NewJQuery

type cardState struct {
	Card *repo.Card
	Face int
}

var currentCard *cardState

// BeforeTransition prepares the page to study
func BeforeTransition(event *jquery.Event, ui *js.Object, p url.Values) bool {
	u, err := repo.CurrentUser()
	if err != nil {
		log.Printf("No user logged in: %s\n", err)
		return false
	}
	log.Debugf("card = %s\n", p.Get("card"))
	go func() {
		if err := ShowCard(u); err != nil {
			log.Printf("Error showing card: %+v", err)
		}
	}()

	return true
}

func ShowCard(u *repo.User) error {
	log.Debug("ShowCard()\n")
	// Ensure the indexes are created before trying to use them
	u.DB()

	if currentCard == nil {
		card, err := u.GetNextCard()
		if err != nil {
			return errors.Wrap(err, "fetch card")
		}
		currentCard = &cardState{
			Card: card,
			Face: 0,
		}
	}
	log.Debugf("Card ID: %s\n", currentCard.Card.DocID())

	mh, err := currentCard.Card.ModelHandler()
	if err != nil {
		return err
	}
	log.Debug("Got the model handler\n")

	buttons := jQuery(":mobile-pagecontainer").Find("#answer-buttons").Find(`[data-role="button"]`)
	log.Debug("Setting up the buttons\n")
	for i, b := range mh.Buttons(currentCard.Face) {
		log.Debugf("Setting button %d to %s\n", i, b.Name)
		button := jQuery(buttons.Underlying().Index(i))
		if b.Name == "" {
			// Hack to enforce same-height buttons
			button.SetText(" ")
		} else {
			button.SetText(b.Name)
		}
		button.Call("button")
		if b.Enabled {
			button.Call("button", "enable")
		} else {
			button.Call("button", "disable")
		}
	}
	buttons.On("click", ButtonPressed)

	if err := DisplayFace(currentCard); err != nil {
		return errors.Wrap(err, "display card")
	}
	return nil
}

func ButtonPressed(e *js.Object) {
	log.Debugf("Button %s was pressed!\n", e.Get("currentTarget").Call("getAttribute", "data-id").String())
}

func DisplayFace(c *cardState) error {
	body, iframeID, err := c.Card.Body(c.Face)
	fserve.RegisterIframe(iframeID, c.Card.DocID())
	if err != nil {
		return errors.Wrap(err, "Error fetching body")
	}

	doc := js.Global.Get("document")

	iframe := doc.Call("createElement", "iframe")
	iframe.Call("setAttribute", "sandbox", "allow-scripts")
	iframe.Call("setAttribute", "seamless", nil)
	iframe.Set("id", iframeID)
	ab := js.NewArrayBuffer([]byte(body))
	b := js.Global.Get("Blob").New([]interface{}{ab}, map[string]string{"type": "text/html"})
	iframe.Set("src", js.Global.Get("URL").Call("createObjectURL", b))

	container := jQuery(":mobile-pagecontainer")

	jQuery("#cardframe", container).Append(iframe)

	jQuery(".show-until-load", container).Hide()
	jQuery(".hide-until-load", container).Show()
	return nil
}
<commit_msg>Remove an unused interface. WTF?<commit_after>// +build js

package studyhandler

import (
	"net/url"

	"github.com/flimzy/log"
	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/jquery"
	"github.com/pkg/errors"

	"github.com/FlashbackSRS/flashback/fserve"
	"github.com/FlashbackSRS/flashback/repository"
)

var jQuery = jquery.NewJQuery

type cardState struct {
	Card *repo.Card
	Face int
}

var currentCard *cardState

// BeforeTransition prepares the page to study
func BeforeTransition(event *jquery.Event, ui *js.Object, p url.Values) bool {
	u, err := repo.CurrentUser()
	if err != nil {
		log.Printf("No user logged in: %s\n", err)
		return false
	}
	log.Debugf("card = %s\n", p.Get("card"))
	go func() {
		if err := ShowCard(u); err != nil {
			log.Printf("Error showing card: %+v", err)
		}
	}()

	return true
}

func ShowCard(u *repo.User) error {
	log.Debug("ShowCard()\n")
	// Ensure the indexes are created before trying to use them
	u.DB()

	if currentCard == nil {
		card, err := u.GetNextCard()
		if err != nil {
			return errors.Wrap(err, "fetch card")
		}
		currentCard = &cardState{
			Card: card,
			Face: 0,
		}
	}
	log.Debugf("Card ID: %s\n", currentCard.Card.DocID())

	mh, err := currentCard.Card.ModelHandler()
	if err != nil {
		return err
	}
	log.Debug("Got the model handler\n")

	buttons := jQuery(":mobile-pagecontainer").Find("#answer-buttons").Find(`[data-role="button"]`)
	log.Debug("Setting up the buttons\n")
	for i, b := range mh.Buttons(currentCard.Face) {
		log.Debugf("Setting button %d to %s\n", i, b.Name)
		button := jQuery(buttons.Underlying().Index(i))
		if b.Name == "" {
			// Hack to enforce same-height buttons
			button.SetText(" ")
		} else {
			button.SetText(b.Name)
		}
		button.Call("button")
		if b.Enabled {
			button.Call("button", "enable")
		} else {
			button.Call("button", "disable")
		}
	}
	buttons.On("click", ButtonPressed)

	if err := DisplayFace(currentCard); err != nil {
		return errors.Wrap(err, "display card")
	}
	return nil
}

func ButtonPressed(e *js.Object) {
	log.Debugf("Button %s was pressed!\n", e.Get("currentTarget").Call("getAttribute", "data-id").String())
}

func DisplayFace(c *cardState) error {
	body, iframeID, err := c.Card.Body(c.Face)
	fserve.RegisterIframe(iframeID, c.Card.DocID())
	if err != nil {
		return errors.Wrap(err, "Error fetching body")
	}

	doc := js.Global.Get("document")

	iframe := doc.Call("createElement", "iframe")
	iframe.Call("setAttribute", "sandbox", "allow-scripts")
	iframe.Call("setAttribute", "seamless", nil)
	iframe.Set("id", iframeID)
	ab := js.NewArrayBuffer([]byte(body))
	b := js.Global.Get("Blob").New([]interface{}{ab}, map[string]string{"type": "text/html"})
	iframe.Set("src", js.Global.Get("URL").Call("createObjectURL", b))

	container := jQuery(":mobile-pagecontainer")

	jQuery("#cardframe", container).Append(iframe)

	jQuery(".show-until-load", container).Hide()
	jQuery(".hide-until-load", container).Show()
	return nil
}
<|endoftext|>


<commit_before>package role

import (
	"fmt"
	"math"
	"math/rand"

	"github.com/pachyderm/pachyderm/src/pfs/route"
)

type roler struct {
	addresser    route.Addresser
	sharder      route.Sharder
	server       Server
	localAddress string
	cancel       chan bool
	cancelled    bool
	numReplicas  int
}

func newRoler(addresser route.Addresser, sharder route.Sharder, server Server, localAddress string, numReplicas int) *roler {
	return &roler{addresser, sharder, server, localAddress, make(chan bool), false, numReplicas}
}

func (r *roler) Run() error {
	return r.addresser.WatchShardToAddress(r.cancel, r.findRole)
}

func (r *roler) Cancel() {
	if !r.cancelled {
		r.cancelled = true
		close(r.cancel)
	}
}

func (r *roler) hasRoleForShard(shard int, shardToMasterAddress map[int]string, shardToReplicaAddress map[int]map[int]string) bool {
	address, ok := shardToMasterAddress[shard]
	if ok && address == r.localAddress {
		return true
	}

	addresses, ok := shardToReplicaAddress[shard]
	if ok {
		for _, address := range addresses {
			if address == r.localAddress {
				return true
			}
		}
	}
	return false
}

func (r *roler) openMasterRole(shardToMasterAddress map[int]string, shardToReplicaAddress map[int]map[int]string) (int, bool) {
	for _, i := range rand.Perm(r.sharder.NumShards()) {
		_, ok := shardToMasterAddress[i]
		if !ok && !r.hasRoleForShard(i, shardToMasterAddress, shardToReplicaAddress) {
			return i, true
		}
	}
	return 0, false
}

func (r *roler) openReplicaRole(shardToMasterAddress map[int]string, shardToReplicaAddress map[int]map[int]string) (int, int, bool) {
	for _, shard := range rand.Perm(r.sharder.NumShards()) {
		addresses := shardToReplicaAddress[shard]
		if len(addresses) < r.numReplicas && !r.hasRoleForShard(shard, shardToMasterAddress, shardToReplicaAddress) {
			for _, index := range rand.Perm(r.numReplicas) {
				if _, ok := addresses[index]; !ok {
					return shard, index, true
				}
			}
		}
	}
	return 0, 0, false
}

func (r *roler) randomMasterRole(
	maxAddress string,
	shardToMasterAddress map[int]string,
	shardToReplicaAddress map[int]map[int]string,
) (int, bool) {
	// we want this function to return a random shard which belongs to address
	// so that not everyone tries to steal the same shard since Go 1 the
	// runtime randomizes iteration of maps to prevent people from depending on
	// a stable ordering. We're doing the opposite here which is depending on
	// the randomness, this seems ok to me but maybe we should change it?
	// Note we only depend on the randomness for performance reason, this code
	// is all still correct if the order isn't random.
	for shard, address := range shardToMasterAddress {
		if address == maxAddress && !r.hasRoleForShard(shard, shardToMasterAddress, shardToReplicaAddress) {
			return shard, true
		}
	}
	return 0, false
}

func (r *roler) randomReplicaRole(
	maxAddress string,
	shardToMasterAddress map[int]string,
	shardToReplicaAddress map[int]map[int]string,
) (int, int, bool) {
	for shard, addresses := range shardToReplicaAddress {
		for index, address := range addresses {
			if address == maxAddress && !r.hasRoleForShard(shard, shardToMasterAddress, shardToReplicaAddress) {
				return shard, index, true
			}
		}
	}
	return 0, 0, false
}

func (r *roler) masterCounts(shardToMasterAddress map[int]string, out map[string]int) {
	for _, address := range shardToMasterAddress {
		out[address]++
	}
}

func (r *roler) replicaCounts(shardToReplicaAddress map[int]map[int]string, out map[string]int) {
	for _, addresses := range shardToReplicaAddress {
		for _, address := range addresses {
			out[address]++
		}
	}
}

func (r *roler) minCount(counts map[string]int) (string, int) {
	address := ""
	result := math.MaxInt64
	for iAddress, count := range counts {
		if count < result {
			address = iAddress
			result = count
		}
	}
	return address, result
}

func (r *roler) maxCount(counts map[string]int) (string, int) {
	address := ""
	result := 0
	for iAddress, count := range counts {
		if count > result {
			address = iAddress
			result = count
		}
	}
	return address, result
}

func (r *roler) beMaster(shard int, prevAddress string) (uint64, error) {
	modifiedIndex, err := r.addresser.ClaimMasterAddress(shard, r.localAddress, prevAddress)
	if err != nil {
		return 0, nil
	}
	if err := r.server.Master(shard); err != nil {
		return 0, err
	}
	go func() {
		r.addresser.HoldMasterAddress(shard, r.localAddress, r.cancel)
		r.server.Clear(shard)
	}()
	return modifiedIndex, nil
}

func (r *roler) beReplica(shard int, index int, prevAddress string) (uint64, error) {
	modifiedIndex, err := r.addresser.ClaimReplicaAddress(shard, index, r.localAddress, prevAddress)
	if err != nil {
		return 0, nil
	}
	if err := r.server.Replica(shard); err != nil {
		return 0, err
	}
	go func() {
		r.addresser.HoldReplicaAddress(shard, index, r.localAddress, r.cancel)
		r.server.Clear(shard)
	}()
	return modifiedIndex, nil
}

func (r *roler) findRole(shardToMasterAddress map[int]string, shardToReplicaAddress map[int]map[int]string) (uint64, error) {
	counts := make(map[string]int)
	r.masterCounts(shardToMasterAddress, counts)
	r.replicaCounts(shardToReplicaAddress, counts)
	_, min := r.minCount(counts)
	if counts[r.localAddress] > min {
		// someone else has fewer roles than us let them claim them
		return 0, nil
	}
	// No server has fewer roles than us, that means it's our turn to claim a role.
	// We start with the most important thing for the cluster, unclaimed master
	// slots.
	shard, ok := r.openMasterRole(shardToMasterAddress, shardToReplicaAddress)
	if ok {
		return r.beMaster(shard, "")
	}

	// No open masters found. Next we look for unclaimed replica roles.
	shard, index, ok := r.openReplicaRole(shardToMasterAddress, shardToReplicaAddress)
	if ok {
		return r.beReplica(shard, index, "")
	}

	// No unclaimed roles were found. Next we check if there's someone in the
	// cluster we could steal from.
	// First we find out who the most role rich server is.
	maxAddress, max := r.maxCount(counts)
	if counts[r.localAddress]+1 <= max-1 {
		// When stealing we consider replica shards first because migrating
		// replicas is less disruptive to the cluster.
		shard, index, ok = r.randomReplicaRole(maxAddress, shardToMasterAddress, shardToReplicaAddress)
		if ok {
			return r.beReplica(shard, index, maxAddress)
		}

		// Lastly we steal a master role
		shard, ok = r.randomMasterRole(maxAddress, shardToMasterAddress, shardToReplicaAddress)
		if ok {
			return r.beMaster(shard, maxAddress)
		}
		return 0, fmt.Errorf("Error we need to be able to find a role here.")
	}
	return 0, nil
}
<commit_msg>Makes roler use route.Address.<commit_after>package role

import (
	"fmt"
	"math"
	"math/rand"

	"github.com/pachyderm/pachyderm/src/pfs/route"
)

type roler struct {
	addresser    route.Addresser
	sharder      route.Sharder
	server       Server
	localAddress string
	cancel       chan bool
	cancelled    bool
	numReplicas  int
}

func newRoler(addresser route.Addresser, sharder route.Sharder, server Server, localAddress string, numReplicas int) *roler {
	return &roler{addresser, sharder, server, localAddress, make(chan bool), false, numReplicas}
}

func (r *roler) Run() error {
	return r.addresser.WatchShardToAddress(r.cancel, r.findRole)
}

func (r *roler) Cancel() {
	if !r.cancelled {
		r.cancelled = true
		close(r.cancel)
	}
}

func (r *roler) localNormalAddress() route.Address {
	return route.Address{r.localAddress, false}
}

func (r *roler) localBackfillingAddress() route.Address {
	return route.Address{r.localAddress, true}
}

func (r *roler) hasRoleForShard(shard int, shardToMasterAddress map[int]route.Address, shardToReplicaAddress map[int]map[int]route.Address) bool {
	address, ok := shardToMasterAddress[shard]
	if ok && address.Address == r.localAddress {
		return true
	}

	addresses, ok := shardToReplicaAddress[shard]
	if ok {
		for _, address := range addresses {
			if address.Address == r.localAddress {
				return true
			}
		}
	}
	return false
}

func (r *roler) openMasterRole(shardToMasterAddress map[int]route.Address, shardToReplicaAddress map[int]map[int]route.Address) (int, bool) {
	for _, i := range rand.Perm(r.sharder.NumShards()) {
		_, ok := shardToMasterAddress[i]
		if !ok && !r.hasRoleForShard(i, shardToMasterAddress, shardToReplicaAddress) {
			return i, true
		}
	}
	return 0, false
}

func (r *roler) openReplicaRole(shardToMasterAddress map[int]route.Address, shardToReplicaAddress map[int]map[int]route.Address) (int, int, bool) {
	for _, shard := range rand.Perm(r.sharder.NumShards()) {
		addresses := shardToReplicaAddress[shard]
		if len(addresses) < r.numReplicas && !r.hasRoleForShard(shard, shardToMasterAddress, shardToReplicaAddress) {
			for _, index := range rand.Perm(r.numReplicas) {
				if _, ok := addresses[index]; !ok {
					return shard, index, true
				}
			}
		}
	}
	return 0, 0, false
}

func (r *roler) randomMasterRole(
	maxAddress string,
	shardToMasterAddress map[int]route.Address,
	shardToReplicaAddress map[int]map[int]route.Address,
) (route.Address, int, bool) {
	// we want this function to return a random shard which belongs to address
	// so that not everyone tries to steal the same shard. Since Go 1 the
	// runtime randomizes iteration of maps to prevent people from depending on
	// a stable ordering. We're doing the opposite here which is depending on
	// the randomness, this seems ok to me but maybe we should change it?
	// Note we only depend on the randomness for performance reason, this code
	// is all still correct if the order isn't random.
	for shard, address := range shardToMasterAddress {
		if address.Address == maxAddress && !r.hasRoleForShard(shard, shardToMasterAddress, shardToReplicaAddress) {
			return address, shard, true
		}
	}
	return route.Address{}, 0, false
}

func (r *roler) randomReplicaRole(
	maxAddress string,
	shardToMasterAddress map[int]route.Address,
	shardToReplicaAddress map[int]map[int]route.Address,
) (route.Address, int, int, bool) {
	for shard, addresses := range shardToReplicaAddress {
		for index, address := range addresses {
			if address.Address == maxAddress && !r.hasRoleForShard(shard, shardToMasterAddress, shardToReplicaAddress) {
				return address, shard, index, true
			}
		}
	}
	return route.Address{}, 0, 0, false
}

func (r *roler) masterCounts(shardToMasterAddress map[int]route.Address, out map[string]int) {
	for _, address := range shardToMasterAddress {
		out[address.Address]++
	}
}

func (r *roler) replicaCounts(shardToReplicaAddress map[int]map[int]route.Address, out map[string]int) {
	for _, addresses := range shardToReplicaAddress {
		for _, address := range addresses {
			out[address.Address]++
		}
	}
}

func (r *roler) minCount(counts map[string]int) (string, int) {
	address := ""
	result := math.MaxInt64
	for iAddress, count := range counts {
		if count < result {
			address = iAddress
			result = count
		}
	}
	return address, result
}

func (r *roler) maxCount(counts map[string]int) (string, int) {
	address := ""
	result := 0
	for iAddress, count := range counts {
		if count > result {
			address = iAddress
			result = count
		}
	}
	return address, result
}

func (r *roler) beMaster(shard int, prevAddress route.Address) (uint64, error) {
	modifiedIndex, err := r.addresser.ClaimMasterAddress(shard, r.localNormalAddress(), prevAddress)
	if err != nil {
		return 0, nil
	}
	if err := r.server.Master(shard); err != nil {
		return 0, err
	}
	go func() {
		r.addresser.HoldMasterAddress(shard, r.localNormalAddress(), r.cancel)
		r.server.Clear(shard)
	}()
	return modifiedIndex, nil
}

func (r *roler) beReplica(shard int, index int, prevAddress route.Address) (uint64, error) {
	modifiedIndex, err := r.addresser.ClaimReplicaAddress(shard, index, r.localNormalAddress(), prevAddress)
	if err != nil {
		return 0, nil
	}
	if err := r.server.Replica(shard); err != nil {
		return 0, err
	}
	go func() {
		r.addresser.HoldReplicaAddress(shard, index, r.localNormalAddress(), r.cancel)
		r.server.Clear(shard)
	}()
	return modifiedIndex, nil
}

func (r *roler) findRole(shardToMasterAddress map[int]route.Address, shardToReplicaAddress map[int]map[int]route.Address) (uint64, error) {
	counts := make(map[string]int)
	r.masterCounts(shardToMasterAddress, counts)
	r.replicaCounts(shardToReplicaAddress, counts)
	_, min := r.minCount(counts)
	if counts[r.localAddress] > min {
		// someone else has fewer roles than us let them claim them
		return 0, nil
	}
	// No server has fewer roles than us, that means it's our turn to claim a role.
	// We start with the most important thing for the cluster, unclaimed master
	// slots.
	shard, ok := r.openMasterRole(shardToMasterAddress, shardToReplicaAddress)
	if ok {
		return r.beMaster(shard, route.Address{})
	}

	// No open masters found. Next we look for unclaimed replica roles.
	shard, index, ok := r.openReplicaRole(shardToMasterAddress, shardToReplicaAddress)
	if ok {
		return r.beReplica(shard, index, route.Address{})
	}

	// No unclaimed roles were found. Next we check if there's someone in the
	// cluster we could steal from.
	// First we find out who the most role rich server is.
	maxAddress, max := r.maxCount(counts)
	if counts[r.localAddress]+1 <= max-1 {
		// When stealing we consider replica shards first because migrating
		// replicas is less disruptive to the cluster.
		prevAddress, shard, index, ok := r.randomReplicaRole(maxAddress, shardToMasterAddress, shardToReplicaAddress)
		if ok {
			return r.beReplica(shard, index, prevAddress)
		}

		// Lastly we steal a master role
		prevAddress, shard, ok = r.randomMasterRole(maxAddress, shardToMasterAddress, shardToReplicaAddress)
		if ok {
			return r.beMaster(shard, prevAddress)
		}
		return 0, fmt.Errorf("Error we need to be able to find a role here.")
	}
	return 0, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"strconv"
	"time"

	"github.com/brotherlogic/goserver"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
	pbg "github.com/brotherlogic/goserver/proto"
	"github.com/brotherlogic/goserver/utils"
)

const (
	intentWait = time.Second
)

// Server the main server type
type Server struct {
	*goserver.GoServer
	config     *pb.Config
	serving    bool
	LastIntent time.Time
	LastMaster time.Time
}

type mainChecker struct {
	prev   []string
	logger func(string)
}

func getIP(servertype, servername string) (string, int) {
	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err != nil {
		return "", -1
	}
	for _, s := range r.Services {
		if s.Name == servertype && s.Identifier == servername {
			return s.Ip, int(s.Port)
		}
	}

	return "", -1
}

func (t *mainChecker) getprev() []string {
	return t.prev
}
func (t *mainChecker) setprev(v []string) {
	t.prev = v
}

func (t *mainChecker) assess(server string) (*pbs.JobList, *pbs.Config) {
	list := &pbs.JobList{}
	conf := &pbs.Config{}

	ip, port := getIP("gobuildslave", server)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, err := grpc.Dial(ip+":"+strconv.Itoa(port), grpc.WithInsecure())
	defer conn.Close()
	if err != nil {
		return list, conf
	}

	slave := pbs.NewGoBuildSlaveClient(conn)
	r, err := slave.List(ctx, &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	r2, err := slave.GetConfig(ctx, &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	return r, r2
}

func (t *mainChecker) master(entry *pbd.RegistryEntry, master bool) bool {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, _ := grpc.Dial(entry.GetIp()+":"+strconv.Itoa(int(entry.GetPort())), grpc.WithInsecure())
	defer conn.Close()

	server := pbg.NewGoserverServiceClient(conn)
	_, err := server.Mote(ctx, &pbg.MoteRequest{Master: master}, grpc.FailFast(false))
	if err != nil {
		t.logger(fmt.Sprintf("Master REJECT(%v): %v", entry, err))
	}

	return err == nil
}

func runJob(job *pbs.JobSpec, server string) {
	if server != "" {
		ip, port := getIP("gobuildslave", server)
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		conn, _ := grpc.Dial(ip+":"+strconv.Itoa(port), grpc.WithInsecure())
		defer conn.Close()

		slave := pbs.NewGoBuildSlaveClient(conn)
		job.Server = server
		slave.Run(ctx, job, grpc.FailFast(false))
	}
}

func (t *mainChecker) discover() *pbd.ServiceList {
	ret := &pbd.ServiceList{}

	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err == nil {
		for _, s := range r.Services {
			ret.Services = append(ret.Services, s)
		}
	}

	return ret
}

// DoRegister Registers this server
func (s Server) DoRegister(server *grpc.Server) {
	pb.RegisterGoBuildMasterServer(server, &s)
}

// ReportHealth determines if the server is healthy
func (s Server) ReportHealth() bool {
	return true
}

// Mote promotes/demotes this server
func (s Server) Mote(master bool) error {
	return nil
}

//GetState gets the state of the server
func (s Server) GetState() []*pbg.State {
	return []*pbg.State{&pbg.State{Key: "last_intent", TimeValue: s.LastIntent.Unix()},
		&pbg.State{Key: "last_master", TimeValue: s.LastMaster.Unix()}}
}

//Compare compares current state to desired state
func (s Server) Compare(ctx context.Context, in *pb.Empty) (*pb.CompareResponse, error) {
	resp := &pb.CompareResponse{}
	list, _ := getFleetStatus(&mainChecker{logger: s.Log})
	cc := &pb.Config{}
	for _, jlist := range list {
		for _, job := range jlist.GetDetails() {
			cc.Intents = append(cc.Intents, &pb.Intent{Spec: job.GetSpec()})
		}
	}
	resp.Current = cc
	resp.Desired = s.config

	return resp, nil
}

func getConfig(c checker) *pb.Config {
	list, _ := getFleetStatus(c)
	config := &pb.Config{}

	for _, jlist := range list {
		for _, job := range jlist.Details {
			found := false
			for _, ij := range config.Intents {
				if job.Spec.Name == ij.Spec.Name {
					ij.Count++
					found = true
				}
			}

			if !found {
				config.Intents = append(config.Intents, &pb.Intent{Spec: &pbs.JobSpec{Name: job.Spec.Name}, Count: 1})
			}
		}
	}

	return config
}

// MatchIntent tries to match the intent with the state of production
func (s *Server) MatchIntent() {
	checker := &mainChecker{logger: s.Log}
	for s.serving {
		time.Sleep(intentWait)
		s.LastIntent = time.Now()

		state := getConfig(checker)
		diff := configDiff(s.config, state)
		joblist := runJobs(diff)
		for _, job := range joblist {
			runJob(job, chooseServer(job, checker))
		}
	}
}

// SetMaster sets up the master settings
func (s *Server) SetMaster() {
	checker := &mainChecker{logger: s.Log}
	for s.serving {
		time.Sleep(intentWait)
		s.LastMaster = time.Now()

		fleet := checker.discover()
		matcher := make(map[string][]*pbd.RegistryEntry)
		hasMaster := make(map[string]int)
		for _, entry := range fleet.GetServices() {
			if _, ok := matcher[entry.GetName()]; !ok {
				if entry.GetMaster() {
					hasMaster[entry.GetName()]++
				}
				matcher[entry.GetName()] = []*pbd.RegistryEntry{entry}
			} else {
				if entry.GetMaster() {
					hasMaster[entry.GetName()] = 1
					matcher[entry.GetName()] = append(matcher[entry.GetName()], entry)
				}
			}
		}

		for key, entries := range matcher {
			if hasMaster[key] > 1 {
				hasMaster[key] = 1
				seen := false
				for _, entry := range entries {
					if seen && entry.GetMaster() {
						checker.master(entry, false)
					} else if entry.GetMaster() {
						seen = true
					}
				}
			}

			if hasMaster[key] == 0 {
				for _, entry := range entries {
					if checker.master(entry, true) {
						entry.Master = true
						break
					}
				}
			}
		}
	}
}

func main() {
	config, err := loadConfig("config.pb")
	if err != nil {
		log.Fatalf("Fatal loading of config: %v", err)
	}

	var sync = flag.Bool("once", false, "One pass intent match")
	s := &Server{&goserver.GoServer{}, config, true, time.Now(), time.Now()}

	var quiet = flag.Bool("quiet", false, "Show all output")
	flag.Parse()

	if *quiet {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	if *sync {
		s.MatchIntent()
	} else {
		s.Register = s
		s.PrepServer()
		s.GoServer.Killme = false
		s.RegisterServer("gobuildmaster", false)
		s.RegisterServingTask(s.MatchIntent)
		s.RegisterServingTask(s.SetMaster)
		err := s.Serve()
		if err != nil {
			log.Fatalf("Serve error: %v", err)
		}
	}
}
<commit_msg>Give more time for Mote<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"strconv"
	"time"

	"github.com/brotherlogic/goserver"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
	pbg "github.com/brotherlogic/goserver/proto"
	"github.com/brotherlogic/goserver/utils"
)

const (
	intentWait = time.Second
)

// Server the main server type
type Server struct {
	*goserver.GoServer
	config     *pb.Config
	serving    bool
	LastIntent time.Time
	LastMaster time.Time
}

type mainChecker struct {
	prev   []string
	logger func(string)
}

func getIP(servertype, servername string) (string, int) {
	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err != nil {
		return "", -1
	}
	for _, s := range r.Services {
		if s.Name == servertype && s.Identifier == servername {
			return s.Ip, int(s.Port)
		}
	}

	return "", -1
}

func (t *mainChecker) getprev() []string {
	return t.prev
}
func (t *mainChecker) setprev(v []string) {
	t.prev = v
}

func (t *mainChecker) assess(server string) (*pbs.JobList, *pbs.Config) {
	list := &pbs.JobList{}
	conf := &pbs.Config{}

	ip, port := getIP("gobuildslave", server)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, err := grpc.Dial(ip+":"+strconv.Itoa(port), grpc.WithInsecure())
	defer conn.Close()
	if err != nil {
		return list, conf
	}

	slave := pbs.NewGoBuildSlaveClient(conn)
	r, err := slave.List(ctx, &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	r2, err := slave.GetConfig(ctx, &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	return r, r2
}

func (t *mainChecker) master(entry *pbd.RegistryEntry, master bool) bool {
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()
	conn, _ := grpc.Dial(entry.GetIp()+":"+strconv.Itoa(int(entry.GetPort())), grpc.WithInsecure())
	defer conn.Close()

	server := pbg.NewGoserverServiceClient(conn)
	_, err := server.Mote(ctx, &pbg.MoteRequest{Master: master}, grpc.FailFast(false))
	if err != nil {
		t.logger(fmt.Sprintf("Master REJECT(%v): %v", entry, err))
	}

	return err == nil
}

func runJob(job *pbs.JobSpec, server string) {
	if server != "" {
		ip, port := getIP("gobuildslave", server)
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		conn, _ := grpc.Dial(ip+":"+strconv.Itoa(port), grpc.WithInsecure())
		defer conn.Close()

		slave := pbs.NewGoBuildSlaveClient(conn)
		job.Server = server
		slave.Run(ctx, job, grpc.FailFast(false))
	}
}

func (t *mainChecker) discover() *pbd.ServiceList {
	ret := &pbd.ServiceList{}

	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err == nil {
		for _, s := range r.Services {
			ret.Services = append(ret.Services, s)
		}
	}

	return ret
}

// DoRegister Registers this server
func (s Server) DoRegister(server *grpc.Server) {
	pb.RegisterGoBuildMasterServer(server, &s)
}

// ReportHealth determines if the server is healthy
func (s Server) ReportHealth() bool {
	return true
}

// Mote promotes/demotes this server
func (s Server) Mote(master bool) error {
	return nil
}

//GetState gets the state of the server
func (s Server) GetState() []*pbg.State {
	return []*pbg.State{&pbg.State{Key: "last_intent", TimeValue: s.LastIntent.Unix()},
		&pbg.State{Key: "last_master", TimeValue: s.LastMaster.Unix()}}
}

//Compare compares current state to desired state
func (s Server) Compare(ctx context.Context, in *pb.Empty) (*pb.CompareResponse, error) {
	resp := &pb.CompareResponse{}
	list, _ := getFleetStatus(&mainChecker{logger: s.Log})
	cc := &pb.Config{}
	for _, jlist := range list {
		for _, job := range jlist.GetDetails() {
			cc.Intents = append(cc.Intents, &pb.Intent{Spec: job.GetSpec()})
		}
	}
	resp.Current = cc
	resp.Desired = s.config

	return resp, nil
}

func getConfig(c checker) *pb.Config {
	list, _ := getFleetStatus(c)
	config := &pb.Config{}

	for _, jlist := range list {
		for _, job := range jlist.Details {
			found := false
			for _, ij := range config.Intents {
				if job.Spec.Name == ij.Spec.Name {
					ij.Count++
					found = true
				}
			}

			if !found {
				config.Intents = append(config.Intents, &pb.Intent{Spec: &pbs.JobSpec{Name: job.Spec.Name}, Count: 1})
			}
		}
	}

	return config
}

// MatchIntent tries to match the intent with the state of production
func (s *Server) MatchIntent() {
	checker := &mainChecker{logger: s.Log}
	for s.serving {
		time.Sleep(intentWait)
		s.LastIntent = time.Now()

		state := getConfig(checker)
		diff := configDiff(s.config, state)
		joblist := runJobs(diff)
		for _, job := range joblist {
			runJob(job, chooseServer(job, checker))
		}
	}
}

// SetMaster sets up the master settings
func (s *Server) SetMaster() {
	checker := &mainChecker{logger: s.Log}
	for s.serving {
		time.Sleep(intentWait)
		s.LastMaster = time.Now()

		fleet := checker.discover()
		matcher := make(map[string][]*pbd.RegistryEntry)
		hasMaster := make(map[string]int)
		for _, entry := range fleet.GetServices() {
			if _, ok := matcher[entry.GetName()]; !ok {
				if entry.GetMaster() {
					hasMaster[entry.GetName()]++
				}
				matcher[entry.GetName()] = []*pbd.RegistryEntry{entry}
			} else {
				if entry.GetMaster() {
					hasMaster[entry.GetName()] = 1
					matcher[entry.GetName()] = append(matcher[entry.GetName()], entry)
				}
			}
		}

		for key, entries := range matcher {
			if hasMaster[key] > 1 {
				hasMaster[key] = 1
				seen := false
				for _, entry := range entries {
					if seen && entry.GetMaster() {
						checker.master(entry, false)
					} else if entry.GetMaster() {
						seen = true
					}
				}
			}

			if hasMaster[key] == 0 {
				for _, entry := range entries {
					if checker.master(entry, true) {
						entry.Master = true
						break
					}
				}
			}
		}
	}
}

func main() {
	config, err := loadConfig("config.pb")
	if err != nil {
		log.Fatalf("Fatal loading of config: %v", err)
	}

	var sync = flag.Bool("once", false, "One pass intent match")
	s := &Server{&goserver.GoServer{}, config, true, time.Now(), time.Now()}

	var quiet = flag.Bool("quiet", false, "Show all output")
	flag.Parse()

	if *quiet {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	if *sync {
		s.MatchIntent()
	} else {
		s.Register = s
		s.PrepServer()
		s.GoServer.Killme = false
		s.RegisterServer("gobuildmaster", false)
		s.RegisterServingTask(s.MatchIntent)
		s.RegisterServingTask(s.SetMaster)
		err := s.Serve()
		if err != nil {
			log.Fatalf("Serve error: %v", err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"strconv"
	"time"

	"github.com/brotherlogic/goserver"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
	pbg "github.com/brotherlogic/goserver/proto"
	"github.com/brotherlogic/goserver/utils"
)

const (
	intentWait = time.Second
)

// Server the main server type
type Server struct {
	*goserver.GoServer
	config  *pb.Config
	serving bool
}

type mainChecker struct {
	prev []string
}

func getIP(servertype, servername string) (string, int) {
	conn, err := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	log.Printf("Error? %v", err)
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	r, err := registry.ListAllServices(context.Background(), &pbd.Empty{})
	if err != nil {
		return "", -1
	}
	for _, s := range r.Services {
		if s.Name == servertype && s.Identifier == servername {
			return s.Ip, int(s.Port)
		}
	}

	return "", -1
}

func (t *mainChecker) getprev() []string {
	return t.prev
}
func (t *mainChecker) setprev(v []string) {
	t.prev = v
}

func (t *mainChecker) assess(server string) (*pbs.JobList, *pbs.Config) {
	list := &pbs.JobList{}
	conf := &pbs.Config{}

	ip, port := getIP("gobuildslave", server)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, err := grpc.DialContext(ctx, ip+":"+strconv.Itoa(port), grpc.WithInsecure())
	if err != nil {
		return list, conf
	}
	defer conn.Close()

	slave := pbs.NewGoBuildSlaveClient(conn)
	r, err := slave.List(context.Background(), &pbs.Empty{})
	if err != nil {
		return list, conf
	}

	r2, err := slave.GetConfig(context.Background(), &pbs.Empty{})
	if err != nil {
		return list, conf
	}

	return r, r2
}

func (t *mainChecker) master(entry *pbd.RegistryEntry) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, _ := grpc.DialContext(ctx, entry.GetIp()+":"+strconv.Itoa(int(entry.GetPort())), grpc.WithInsecure())
	defer conn.Close()

	server := pbg.NewGoserverServiceClient(conn)
	log.Printf("SETTING MASTER: %v", entry)
	_, err := server.Mote(context.Background(), &pbg.MoteRequest{Master: entry.GetMaster()})
	if err != nil {
		log.Printf("RESPONSE: %v", err)
	}
}

func runJob(job *pbs.JobSpec, server string) {
	log.Printf("Run %v on %v", job.Name, server)
	if server != "" {
		ip, port := getIP("gobuildslave", server)
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		conn, _ := grpc.DialContext(ctx, ip+":"+strconv.Itoa(port), grpc.WithInsecure())
		defer conn.Close()

		slave := pbs.NewGoBuildSlaveClient(conn)
		job.Server = server
		slave.Run(context.Background(), job)
	}
}

func (t *mainChecker) discover() *pbd.ServiceList {
	ret := &pbd.ServiceList{}

	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	r, err := registry.ListAllServices(context.Background(), &pbd.Empty{})
	if err == nil {
		for _, s := range r.Services {
			ret.Services = append(ret.Services, s)
		}
	}

	return ret
}

// DoRegister Registers this server
func (s Server) DoRegister(server *grpc.Server) {
	pb.RegisterGoBuildMasterServer(server, &s)
}

// ReportHealth determines if the server is healthy
func (s Server) ReportHealth() bool {
	return true
}

// Mote promotes/demotes this server
func (s Server) Mote(master bool) error {
	return nil
}

//Compare compares current state to desired state
func (s Server) Compare(ctx context.Context, in *pb.Empty) (*pb.CompareResponse, error) {
	resp := &pb.CompareResponse{}
	list, _ := getFleetStatus(&mainChecker{})
	cc := &pb.Config{}
	for _, jlist := range list {
		for _, job := range jlist.GetDetails() {
			cc.Intents = append(cc.Intents, &pb.Intent{Spec: job.GetSpec()})
		}
	}
	resp.Current = cc
	resp.Desired = s.config

	return resp, nil
}

func getConfig(c checker) *pb.Config {
	list, _ := getFleetStatus(c)
	config := &pb.Config{}

	for _, jlist := range list {
		for _, job := range jlist.Details {
			found := false
			for _, ij := range config.Intents {
				if job.Spec.Name == ij.Spec.Name {
					ij.Count++
					found = true
				}
			}

			if !found {
				config.Intents = append(config.Intents, &pb.Intent{Spec: &pbs.JobSpec{Name: job.Spec.Name}, Count: 1})
			}
		}
	}

	return config
}

// MatchIntent tries to match the intent with the state of production
func (s Server) MatchIntent() {
	checker := &mainChecker{}
	for s.serving {
		time.Sleep(intentWait)

		state := getConfig(checker)
		diff := configDiff(s.config, state)
		joblist := runJobs(diff)
		for _, job := range joblist {
			runJob(job, chooseServer(job, checker))
		}
	}
}

// SetMaster sets up the master settings
func (s Server) SetMaster() {
	checker := &mainChecker{}
	for s.serving {
		time.Sleep(intentWait)

		fleet := checker.discover()
		matcher := make(map[string]*pbd.RegistryEntry)
		for _, entry := range fleet.GetServices() {
			if _, ok := matcher[entry.GetName()]; !ok {
				matcher[entry.GetName()] = entry
			} else {
				if entry.GetMaster() {
					matcher[entry.GetName()] = entry
				}
			}
		}

		for _, entry := range matcher {
			if !entry.GetMaster() {
				entry.Master = true
				checker.master(entry)
			}
		}
	}
}

func main() {
	config, err := loadConfig("config.pb")
	if err != nil {
		log.Fatalf("Fatal loading of config: %v", err)
	}

	var sync = flag.Bool("once", false, "One pass intent match")
	s := Server{&goserver.GoServer{}, config, true}

	var quiet = flag.Bool("quiet", true, "Show all output")
	flag.Parse()

	if *quiet {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	if *sync {
		s.MatchIntent()
	} else {
		s.Register = s
		s.PrepServer()
		s.GoServer.Killme = false
		s.RegisterServer("gobuildmaster", false)
		s.RegisterServingTask(s.MatchIntent)
		s.RegisterServingTask(s.SetMaster)
		s.Serve()
	}
}
<commit_msg>Adds fail fasts<commit_after>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"strconv"
	"time"

	"github.com/brotherlogic/goserver"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
	pbg "github.com/brotherlogic/goserver/proto"
	"github.com/brotherlogic/goserver/utils"
)

const (
	intentWait = time.Second
)

// Server the main server type
type Server struct {
	*goserver.GoServer
	config  *pb.Config
	serving bool
}

type mainChecker struct {
	prev []string
}

func getIP(servertype, servername string) (string, int) {
	conn, err := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	log.Printf("Error? %v", err)
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	r, err := registry.ListAllServices(context.Background(), &pbd.Empty{}, grpc.FailFast(false))
	if err != nil {
		return "", -1
	}
	for _, s := range r.Services {
		if s.Name == servertype && s.Identifier == servername {
			return s.Ip, int(s.Port)
		}
	}

	return "", -1
}

func (t *mainChecker) getprev() []string {
	return t.prev
}
func (t *mainChecker) setprev(v []string) {
	t.prev = v
}

func (t *mainChecker) assess(server string) (*pbs.JobList, *pbs.Config) {
	list := &pbs.JobList{}
	conf := &pbs.Config{}

	ip, port := getIP("gobuildslave", server)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, err := grpc.DialContext(ctx, ip+":"+strconv.Itoa(port), grpc.WithInsecure())
	if err != nil {
		return list, conf
	}
	defer conn.Close()

	slave := pbs.NewGoBuildSlaveClient(conn)
	r, err := slave.List(context.Background(), &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	r2, err := slave.GetConfig(context.Background(), &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	return r, r2
}

func (t *mainChecker) master(entry *pbd.RegistryEntry) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, _ := grpc.DialContext(ctx, entry.GetIp()+":"+strconv.Itoa(int(entry.GetPort())), grpc.WithInsecure())
	defer conn.Close()

	server := pbg.NewGoserverServiceClient(conn)
	log.Printf("SETTING MASTER: %v", entry)
	_, err := server.Mote(context.Background(), &pbg.MoteRequest{Master: entry.GetMaster()}, grpc.FailFast(false))
	if err != nil {
		log.Printf("RESPONSE: %v", err)
	}
}

func runJob(job *pbs.JobSpec, server string) {
	log.Printf("Run %v on %v", job.Name, server)
	if server != "" {
		ip, port := getIP("gobuildslave", server)
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		conn, _ := grpc.DialContext(ctx, ip+":"+strconv.Itoa(port), grpc.WithInsecure())
		defer conn.Close()

		slave := pbs.NewGoBuildSlaveClient(conn)
		job.Server = server
		slave.Run(context.Background(), job, grpc.FailFast(false))
	}
}

func (t *mainChecker) discover() *pbd.ServiceList {
	ret := &pbd.ServiceList{}

	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	r, err := registry.ListAllServices(context.Background(), &pbd.Empty{}, grpc.FailFast(false))
	if err == nil {
		for _, s := range r.Services {
			ret.Services = append(ret.Services, s)
		}
	}

	return ret
}

// DoRegister Registers this server
func (s Server) DoRegister(server *grpc.Server) {
	pb.RegisterGoBuildMasterServer(server, &s)
}

// ReportHealth determines if the server is healthy
func (s Server) ReportHealth() bool {
	return true
}

// Mote promotes/demotes this server
func (s Server) Mote(master bool) error {
	return nil
}

//Compare compares current state to desired state
func (s Server) Compare(ctx context.Context, in *pb.Empty) (*pb.CompareResponse, error) {
	resp := &pb.CompareResponse{}
	list, _ := getFleetStatus(&mainChecker{})
	cc := &pb.Config{}
	for _, jlist := range list {
		for _, job := range jlist.GetDetails() {
			cc.Intents = append(cc.Intents, &pb.Intent{Spec: job.GetSpec()})
		}
	}
	resp.Current = cc
	resp.Desired = s.config

	return resp, nil
}

func getConfig(c checker) *pb.Config {
	list, _ := getFleetStatus(c)
	config := &pb.Config{}

	for _, jlist := range list {
		for _, job := range jlist.Details {
			found := false
			for _, ij := range config.Intents {
				if job.Spec.Name == ij.Spec.Name {
					ij.Count++
					found = true
				}
			}

			if !found {
				config.Intents = append(config.Intents, &pb.Intent{Spec: &pbs.JobSpec{Name: job.Spec.Name}, Count: 1})
			}
		}
	}

	return config
}

// MatchIntent tries to match the intent with the state of production
func (s Server) MatchIntent() {
	checker := &mainChecker{}
	for s.serving {
		time.Sleep(intentWait)

		state := getConfig(checker)
		diff := configDiff(s.config, state)
		joblist := runJobs(diff)
		for _, job := range joblist {
			runJob(job, chooseServer(job, checker))
		}
	}
}

// SetMaster sets up the master settings
func (s Server) SetMaster() {
	checker := &mainChecker{}
	for s.serving {
		time.Sleep(intentWait)

		fleet := checker.discover()
		matcher := make(map[string]*pbd.RegistryEntry)
		for _, entry := range fleet.GetServices() {
			if _, ok := matcher[entry.GetName()]; !ok {
				matcher[entry.GetName()] = entry
			} else {
				if entry.GetMaster() {
					matcher[entry.GetName()] = entry
				}
			}
		}

		for _, entry := range matcher {
			if !entry.GetMaster() {
				entry.Master = true
				checker.master(entry)
			}
		}
	}
}

func main() {
	config, err := loadConfig("config.pb")
	if err != nil {
		log.Fatalf("Fatal loading of config: %v", err)
	}

	var sync = flag.Bool("once", false, "One pass intent match")
	s := Server{&goserver.GoServer{}, config, true}

	var quiet = flag.Bool("quiet", true, "Show all output")
	flag.Parse()

	if *quiet {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	if *sync {
		s.MatchIntent()
	} else {
		s.Register = s
		s.PrepServer()
		s.GoServer.Killme = false
		s.RegisterServer("gobuildmaster", false)
		s.RegisterServingTask(s.MatchIntent)
		s.RegisterServingTask(s.SetMaster)
		s.Serve()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"errors"
	"fmt"
	"sort"
)

const (
	evalInf = 50000
)

var (
	errCheckmate checkmateError
	errStalemate = errors.New("stalemate")
	errFiftyMove = errors.New("fifty-move rule")
	// TODO: threefold repetition
)

type Search struct {
	allowCutoff bool
	counters    []int
}

// SearchPosition searches a Position to the specified depth via iterative deepening
// and returns the search results.
func SearchPosition(ctx context.Context, pos Position, depth int) Results {
	var rs Results
	s := Search{
		allowCutoff: true,
		counters:    make([]int, depth+1),
	}
	for d := 1; d <= depth; d++ {
		_, rs, _ = s.negamax(ctx, pos, rs, Window{-evalInf, evalInf}, d)
		if ctx.Err() != nil {
			break
		}
	}
	return rs
}

// negamax recursively searches a Position to the specified depth and returns the evaluation score
// relative to the side to move and the search results. It employs alpha-beta pruning outside of
// the specified Window. If rs is zero length, negamax will generate and search all legal
// moves; if recommended moves are provided, they must all be legal, and only they will be searched.
func (s *Search) negamax(
	ctx context.Context,
	pos Position,
	rs Results,
	w Window,
	depth int,
) (bestScore RelScore, results Results, err error) {
	s.counters[len(s.counters)-1-depth]++

	score, rs, err := checkDone(pos, rs)
	if err != nil && (s.allowCutoff || err != errInsufficient) {
		// Do not cut off during perft in the case of insufficient material
		return score, nil, err
	}
	// Invariant: len(rs) > 0 and rs contains only legal moves
	if depth == 0 {
		score, err := Eval(pos)
		return score.Rel(pos.ToMove), nil, err
	}
	if s.allowCutoff && deepEnough(rs, depth) {
		return rs[0].score.Rel(pos.ToMove), rs, rs[0].err
	}

	defer func() { rs.SortFor(pos.ToMove) }()

	for _, r := range rs {
		if r.err != nil && s.allowCutoff {
			// The move is already known not to avoid a game-ending state; no need to search it further
			rs.Update(r)
			continue
		}

		score, cont, err := s.negamax(ctx, Make(pos, r.move), r.cont, w.Neg(), depth-1)
		score *= -1

		rs.Update(Result{move: r.move, score: score.Abs(pos.ToMove), depth: depth - 1, cont: cont, err: err})

		if depth >= 3 && ctx.Err() != nil {
			break
		}

		var constrained, ok bool
		w, constrained, ok = w.Constrain(score)
		if constrained {
			// improved lower bound
		}
		if !ok && s.allowCutoff {
			break
		}
	}
	if err, ok := rs[0].err.(checkmateError); ok {
		return w.alpha, rs, err.Prev()
	}
	return w.alpha, rs, rs[0].err
}

// checkDone reports whether pos represents a game-ending position.
// If so, the RelScore and error value indicate the type of ending.
// If not, the RelScore is 0 and the error is nil.
// In either case, checkDone returns a Results of all legal moves in pos.
// If recommended is not provided, checkDone generates the legal moves first.
func checkDone(pos Position, recommended Results) (RelScore, Results, error) {
	if len(recommended) == 0 {
		moves := PseudoLegalMoves(pos)
		recommended = make(Results, 0, len(moves))
		for _, m := range moves {
			if !IsLegal(Make(pos, m)) {
				continue
			}
			recommended = append(recommended, Result{move: m})
		}
	}
	if len(recommended) == 0 {
		// no legal moves
		if IsCheck(pos) {
			return -evalInf, nil, errCheckmate
		}
		return 0, nil, errStalemate
	}
	if pos.HalfMove == 100 {
		// fifty-move rule
		return 0, nil, errFiftyMove
	}
	return 0, recommended, nil
}

// deepEnough reports whether rs stores the results of a position search to at least the specified depth.
func deepEnough(rs Results, depth int) bool {
	// rs is already deep enough if all non-terminal elements have been searched to at least depth-1,
	// as long as they have been searched to non-zero depth
	for _, r := range rs {
		if (r.depth == 0 || r.depth < depth-1) && r.err == nil {
			return false
		}
	}
	return true
}

// IsPseudoLegal returns whether a Move is pseudo-legal in a Position.
// A move is pseudo-legal if the square to be moved from contains the specified piece
// and the piece is capable of moving to the target square if doing so would not put the king in check.
func IsPseudoLegal(pos Position, move Move) bool {
	for _, m := range PieceMoves[move.Piece](pos) {
		if m == move {
			return true
		}
	}
	return false
}

// IsLegal returns whether a Position is legal.
// A position is illegal if the king of the side that just moved is in check.
func IsLegal(pos Position) bool {
	return !IsAttacked(pos, pos.KingSquare[pos.Opp()], pos.ToMove)
}

// IsCheck returns whether the king of the side to move is in check.
func IsCheck(pos Position) bool {
	return IsAttacked(pos, pos.KingSquare[pos.ToMove], pos.Opp())
}

// IsTerminal returns whether or not a Position is checkmate or stalemate.
// A position is checkmate or stalemate if the side to move has no legal moves.
func IsTerminal(pos Position) bool { return !anyLegal(pos, PseudoLegalMoves(pos)) }

// anyLegal returns whether any of the given Moves are legal in the given Position.
func anyLegal(pos Position, moves []Move) bool {
	for _, m := range moves {
		if IsLegal(Make(pos, m)) {
			return true
		}
	}
	return false
}

// A Result holds a searched Move along with its evaluated Score,
// the search depth, and the continuation Results of the search.
type Result struct {
	move  Move
	score Score
	depth int
	cont  Results
	err   error
}

// String returns a string representation of r, including its principal variation.
func (r Result) String() string {
	s := fmt.Sprintf("%v (%v) %v", r.score, r.depth, r.PV())
	if r.err != nil {
		return s + " " + r.err.Error()
	}
	return s
}

// PV returns a string representation of r's principal variation.
func (r Result) PV() string {
	if r.depth == 0 || len(r.cont) == 0 {
		return LongAlgebraic(r.move)
	}
	return LongAlgebraic(r.move) + " " + r.cont[0].PV()
}

// A Results contains the results of a search.
// Functions returning a Results should sort it before returning.
type Results []Result

// SortFor sorts rs beginning with the best move for c.
// The sort is not guaranteed to be stable.
func (rs Results) SortFor(c Color) {
	// Sort first by mate condition, then by depth decreasing,
	// then by Score decreasing/increasing for White/Black,
	// and then by origin and destination Square increasing
	sort.Slice(rs, func(i, j int) bool {
		if less, ok := rs.mateSort(i, j); ok {
			return less
		}
		if rs[i].depth != rs[j].depth {
			return rs[i].depth > rs[j].depth
		}
		if rs[i].score != rs[j].score {
			return (rs[i].score > rs[j].score) == (c == White)
		}
		return rs.squareSort(i, j)
	})
}

// SortBySquares sorts rs first by the Result moves' origin Squares and then by their destination Squares,
// and finally by promotion piece in the case of pawn promotions.
func (rs Results) SortBySquares() { sort.Slice(rs, rs.squareSort) }

// mateSort reports how two Result elements should be sorted if at least one of them leads to checkmate.
// ok reports whether either Result contains an error of type checkmateError.
// If ok is true, less reports whether the Result with index i should sort before the Result with index j.
// If ok is false, the value of less is meaningless.
func (rs Results) mateSort(i, j int) (less bool, ok bool) {
	ich, iok := rs[i].err.(checkmateError)
	jch, jok := rs[j].err.(checkmateError)
	switch {
	case iok && jok:
		// Mates stored in rs as wins for the "previous player" are wins in the position being searched
		switch iwin, jwin := ich&1 == 0, jch&1 == 0; {
		case iwin && jwin:
			// faster winning mate first
			return ich < jch, true
		case !iwin && !jwin:
			// slower losing mate first
			return ich > jch, true
		default:
			return iwin, true
		}
	case iok:
		return ich&1 == 0, true
	case jok:
		return jch&1 != 0, true
	}
	return false, false
}

// squareSort reports how two Result elements should be sorted by their moves' origin and destination Squares,
// and then finally by promotion piece in the case of promoting pawns.
func (rs Results) squareSort(i, j int) bool {
	if rs[i].move.From != rs[j].move.From {
		return rs[i].move.From < rs[j].move.From
	}
	if rs[i].move.To != rs[j].move.To {
		return rs[i].move.To < rs[j].move.To
	}
	// From and To are non-unique in the case of pawn promotion
	return rs[i].move.PromotePiece < rs[j].move.PromotePiece
}

// Update finds the Result in rs with the same Move as r and replaces it with r.
// It panics if rs does not contain any elements with r's Move.
func (rs Results) Update(r Result) {
	for i := range rs {
		if rs[i].move == r.move {
			rs[i] = r
			return
		}
	}
	// rs should contain all legal moves
	panic("unreached")
}

// String returns a string representation of all Result values in r.
func (rs Results) String() string {
	var s string
	for _, r := range rs {
		s += fmt.Sprintf("%v\n", r.String())
	}
	return s
}

// A checkmateError indicates the number of plies until a forced checkmate can be delivered.
// Odd values are wins for the player with the next move, and even values for the player with the previous move.
// The zero value of type checkmateError indicates that the current position is checkmate.
type checkmateError int

func (n checkmateError) Error() string {
	if n&1 == 0 {
		return fmt.Sprintf("-#%d", n/2)
	}
	return fmt.Sprintf("#%d", (n+1)/2)
}

// Prev returns the checkmateError corresponding to the previous ply.
func (n checkmateError) Prev() checkmateError { return n + 1 }

// A Window represents the bounds of a position's evaluation.
type Window struct{ alpha, beta RelScore }

// Constrain updates the lower bound of w, if applicable, and returns the updated window,
// whether the lower bound was changed, and whether the returned Window remains valid.
func (w Window) Constrain(n RelScore) (c Window, constrained bool, ok bool) {
	if n <= w.alpha {
		return w, false, true
	}
	return Window{n, w.beta}, true, n <= w.beta
}

// Neg returns the additive inverse of w.
func (w Window) Neg() Window { return Window{-w.beta, -w.alpha} }
<commit_msg>always return non-nil Results<commit_after>package main

import (
	"context"
	"errors"
	"fmt"
	"sort"
)

const (
	evalInf = 50000
)

var (
	errCheckmate checkmateError
	errStalemate = errors.New("stalemate")
	errFiftyMove = errors.New("fifty-move rule")
	// TODO: threefold repetition
)

type Search struct {
	allowCutoff bool
	counters    []int
}

// SearchPosition searches a Position to the specified depth via iterative deepening
// and returns the search results.
func SearchPosition(ctx context.Context, pos Position, depth int) Results {
	var rs Results
	s := Search{
		allowCutoff: true,
		counters:    make([]int, depth+1),
	}
	for d := 1; d <= depth; d++ {
		_, rs, _ = s.negamax(ctx, pos, rs, Window{-evalInf, evalInf}, d)
		if ctx.Err() != nil {
			break
		}
	}
	return rs
}

// negamax recursively searches a Position to the specified depth and returns the evaluation score
// relative to the side to move and the search results. It employs alpha-beta pruning outside of
// the specified Window. If rs is zero length, negamax will generate and search all legal
// moves; if recommended moves are provided, they must all be legal, and only they will be searched.
func (s *Search) negamax(
	ctx context.Context,
	pos Position,
	rs Results,
	w Window,
	depth int,
) (bestScore RelScore, results Results, err error) {
	s.counters[len(s.counters)-1-depth]++

	score, rs, err := checkDone(pos, rs)
	if err != nil && (s.allowCutoff || err != errInsufficient) {
		// Do not cut off during perft in the case of insufficient material
		return score, rs, err
	}
	// Invariant: len(rs) > 0 and rs contains only legal moves
	if depth == 0 {
		score, err := Eval(pos)
		return score.Rel(pos.ToMove), rs, err
	}
	if s.allowCutoff && deepEnough(rs, depth) {
		return rs[0].score.Rel(pos.ToMove), rs, rs[0].err
	}

	defer func() { rs.SortFor(pos.ToMove) }()

	for _, r := range rs {
		if r.err != nil && s.allowCutoff {
			// The move is already known not to avoid a game-ending state; no need to search it further
			rs.Update(r)
			continue
		}

		score, cont, err := s.negamax(ctx, Make(pos, r.move), r.cont, w.Neg(), depth-1)
		score *= -1

		rs.Update(Result{move: r.move, score: score.Abs(pos.ToMove), depth: depth - 1, cont: cont, err: err})

		if depth >= 3 && ctx.Err() != nil {
			break
		}

		var constrained, ok bool
		w, constrained, ok = w.Constrain(score)
		if constrained {
			// improved lower bound
		}
		if !ok && s.allowCutoff {
			break
		}
	}
	if err, ok := rs[0].err.(checkmateError); ok {
		return w.alpha, rs, err.Prev()
	}
	return w.alpha, rs, rs[0].err
}

// checkDone reports whether pos represents a game-ending position.
// If so, the RelScore and error value indicate the type of ending.
// If not, the RelScore is 0 and the error is nil.
// In either case, checkDone returns a Results of all legal moves in pos.
// If recommended is not provided, checkDone generates the legal moves first.
func checkDone(pos Position, recommended Results) (RelScore, Results, error) {
	if len(recommended) == 0 {
		moves := PseudoLegalMoves(pos)
		recommended = make(Results, 0, len(moves))
		for _, m := range moves {
			if !IsLegal(Make(pos, m)) {
				continue
			}
			recommended = append(recommended, Result{move: m})
		}
	}
	if len(recommended) == 0 {
		// no legal moves
		if IsCheck(pos) {
			return -evalInf, rs, errCheckmate
		}
		return 0, rs, errStalemate
	}
	if pos.HalfMove == 100 {
		// fifty-move rule
		return 0, rs, errFiftyMove
	}
	return 0, recommended, nil
}

// deepEnough reports whether rs stores the results of a position search to at least the specified depth.
func deepEnough(rs Results, depth int) bool {
	// rs is already deep enough if all non-terminal elements have been searched to at least depth-1,
	// as long as they have been searched to non-zero depth
	for _, r := range rs {
		if (r.depth == 0 || r.depth < depth-1) && r.err == nil {
			return false
		}
	}
	return true
}

// IsPseudoLegal returns whether a Move is pseudo-legal in a Position.
// A move is pseudo-legal if the square to be moved from contains the specified piece
// and the piece is capable of moving to the target square if doing so would not put the king in check.
func IsPseudoLegal(pos Position, move Move) bool {
	for _, m := range PieceMoves[move.Piece](pos) {
		if m == move {
			return true
		}
	}
	return false
}

// IsLegal returns whether a Position is legal.
// A position is illegal if the king of the side that just moved is in check.
func IsLegal(pos Position) bool {
	return !IsAttacked(pos, pos.KingSquare[pos.Opp()], pos.ToMove)
}

// IsCheck returns whether the king of the side to move is in check.
func IsCheck(pos Position) bool {
	return IsAttacked(pos, pos.KingSquare[pos.ToMove], pos.Opp())
}

// IsTerminal returns whether or not a Position is checkmate or stalemate.
// A position is checkmate or stalemate if the side to move has no legal moves.
func IsTerminal(pos Position) bool { return !anyLegal(pos, PseudoLegalMoves(pos)) }

// anyLegal returns whether any of the given Moves are legal in the given Position.
func anyLegal(pos Position, moves []Move) bool {
	for _, m := range moves {
		if IsLegal(Make(pos, m)) {
			return true
		}
	}
	return false
}

// A Result holds a searched Move along with its evaluated Score,
// the search depth, and the continuation Results of the search.
type Result struct {
	move  Move
	score Score
	depth int
	cont  Results
	err   error
}

// String returns a string representation of r, including its principal variation.
func (r Result) String() string {
	s := fmt.Sprintf("%v (%v) %v", r.score, r.depth, r.PV())
	if r.err != nil {
		return s + " " + r.err.Error()
	}
	return s
}

// PV returns a string representation of r's principal variation.
func (r Result) PV() string {
	if r.depth == 0 || len(r.cont) == 0 {
		return LongAlgebraic(r.move)
	}
	return LongAlgebraic(r.move) + " " + r.cont[0].PV()
}

// A Results contains the results of a search.
// Functions returning a Results should sort it before returning.
type Results []Result

// SortFor sorts rs beginning with the best move for c.
// The sort is not guaranteed to be stable.
func (rs Results) SortFor(c Color) {
	// Sort first by mate condition, then by depth decreasing,
	// then by Score decreasing/increasing for White/Black,
	// and then by origin and destination Square increasing
	sort.Slice(rs, func(i, j int) bool {
		if less, ok := rs.mateSort(i, j); ok {
			return less
		}
		if rs[i].depth != rs[j].depth {
			return rs[i].depth > rs[j].depth
		}
		if rs[i].score != rs[j].score {
			return (rs[i].score > rs[j].score) == (c == White)
		}
		return rs.squareSort(i, j)
	})
}

// SortBySquares sorts rs first by the Result moves' origin Squares and then by their destination Squares,
// and finally by promotion piece in the case of pawn promotions.
func (rs Results) SortBySquares() { sort.Slice(rs, rs.squareSort) }

// mateSort reports how two Result elements should be sorted if at least one of them leads to checkmate.
// ok reports whether either Result contains an error of type checkmateError.
// If ok is true, less reports whether the Result with index i should sort before the Result with index j.
// If ok is false, the value of less is meaningless.
func (rs Results) mateSort(i, j int) (less bool, ok bool) {
	ich, iok := rs[i].err.(checkmateError)
	jch, jok := rs[j].err.(checkmateError)
	switch {
	case iok && jok:
		// Mates stored in rs as wins for the "previous player" are wins in the position being searched
		switch iwin, jwin := ich&1 == 0, jch&1 == 0; {
		case iwin && jwin:
			// faster winning mate first
			return ich < jch, true
		case !iwin && !jwin:
			// slower losing mate first
			return ich > jch, true
		default:
			return iwin, true
		}
	case iok:
		return ich&1 == 0, true
	case jok:
		return jch&1 != 0, true
	}
	return false, false
}

// squareSort reports how two Result elements should be sorted by their moves' origin and destination Squares,
// and then finally by promotion piece in the case of promoting pawns.
func (rs Results) squareSort(i, j int) bool {
	if rs[i].move.From != rs[j].move.From {
		return rs[i].move.From < rs[j].move.From
	}
	if rs[i].move.To != rs[j].move.To {
		return rs[i].move.To < rs[j].move.To
	}
	// From and To are non-unique in the case of pawn promotion
	return rs[i].move.PromotePiece < rs[j].move.PromotePiece
}

// Update finds the Result in rs with the same Move as r and replaces it with r.
// It panics if rs does not contain any elements with r's Move.
func (rs Results) Update(r Result) {
	for i := range rs {
		if rs[i].move == r.move {
			rs[i] = r
			return
		}
	}
	// rs should contain all legal moves
	panic("unreached")
}

// String returns a string representation of all Result values in r.
func (rs Results) String() string {
	var s string
	for _, r := range rs {
		s += fmt.Sprintf("%v\n", r.String())
	}
	return s
}

// A checkmateError indicates the number of plies until a forced checkmate can be delivered.
// Odd values are wins for the player with the next move, and even values for the player with the previous move.
// The zero value of type checkmateError indicates that the current position is checkmate.
type checkmateError int

func (n checkmateError) Error() string {
	if n&1 == 0 {
		return fmt.Sprintf("-#%d", n/2)
	}
	return fmt.Sprintf("#%d", (n+1)/2)
}

// Prev returns the checkmateError corresponding to the previous ply.
func (n checkmateError) Prev() checkmateError { return n + 1 }

// A Window represents the bounds of a position's evaluation.
type Window struct{ alpha, beta RelScore }

// Constrain updates the lower bound of w, if applicable, and returns the updated window,
// whether the lower bound was changed, and whether the returned Window remains valid.
func (w Window) Constrain(n RelScore) (c Window, constrained bool, ok bool) {
	if n <= w.alpha {
		return w, false, true
	}
	return Window{n, w.beta}, true, n <= w.beta
}

// Neg returns the additive inverse of w.
func (w Window) Neg() Window { return Window{-w.beta, -w.alpha} }
<|endoftext|>